2010-06-02 20:48:27 +04: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 16:59:53 +04: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 20:48:27 +04:00
|
|
|
*/
|
|
|
|
|
2016-01-29 20:50:05 +03:00
|
|
|
#include "qemu/osdep.h"
|
block: New BlockBackend
A block device consists of a frontend device model and a backend.
A block backend has a tree of block drivers doing the actual work.
The tree is managed by the block layer.
We currently use a single abstraction BlockDriverState both for tree
nodes and the backend as a whole. Drawbacks:
* Its API includes both stuff that makes sense only at the block
backend level (root of the tree) and stuff that's only for use
within the block layer. This makes the API bigger and more complex
than necessary. Moreover, it's not obvious which interfaces are
meant for device models, and which really aren't.
* Since device models keep a reference to their backend, the backend
object can't just be destroyed. But for media change, we need to
replace the tree. Our solution is to make the BlockDriverState
generic, with actual driver state in a separate object, pointed to
by member opaque. That lets us replace the tree by deinitializing
and reinitializing its root. This special need of the root makes
the data structure awkward everywhere in the tree.
The general plan is to separate the APIs into "block backend", for use
by device models, monitor and whatever other code dealing with block
backends, and "block driver", for use by the block layer and whatever
other code (if any) dealing with trees and tree nodes.
Code dealing with block backends, device models in particular, should
become completely oblivious of BlockDriverState. This should let us
clean up both APIs, and the tree data structures.
This commit is a first step. It creates a minimal "block backend"
API: type BlockBackend and functions to create, destroy and find them.
BlockBackend objects are created and destroyed exactly when root
BlockDriverState objects are created and destroyed. "Root" in the
sense of "in bdrv_states". They're not yet used for anything; that'll
come shortly.
A root BlockDriverState is created with bdrv_new_root(), so where to
create a BlockBackend is obvious. Where these roots get destroyed
isn't always as obvious.
It is obvious in qemu-img.c, qemu-io.c and qemu-nbd.c, and in error
paths of blockdev_init(), blk_connect(). That leaves destruction of
objects successfully created by blockdev_init() and blk_connect().
blockdev_init() is used only by drive_new() and qmp_blockdev_add().
Objects created by the latter are currently indestructible (see commit
48f364d "blockdev: Refuse to drive_del something added with
blockdev-add" and commit 2d246f0 "blockdev: Introduce
DriveInfo.enable_auto_del"). Objects created by the former get
destroyed by drive_del().
Objects created by blk_connect() get destroyed by blk_disconnect().
BlockBackend is reference-counted. Its reference count never exceeds
one so far, but that's going to change.
In drive_del(), the BB's reference count is surely one now. The BDS's
reference count is greater than one when something else is holding a
reference, such as a block job. In this case, the BB is destroyed
right away, but the BDS lives on until all extra references get
dropped.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2014-10-07 15:59:04 +04:00
|
|
|
#include "sysemu/block-backend.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/blockdev.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/block/block.h"
|
2012-12-17 21:19:44 +04:00
|
|
|
#include "block/blockjob.h"
|
2015-06-08 19:17:44 +03:00
|
|
|
#include "block/throttle-groups.h"
|
2012-12-17 21:19:49 +04:00
|
|
|
#include "monitor/monitor.h"
|
2015-03-17 20:29:20 +03:00
|
|
|
#include "qemu/error-report.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/option.h"
|
|
|
|
#include "qemu/config-file.h"
|
2012-12-17 21:19:43 +04:00
|
|
|
#include "qapi/qmp/types.h"
|
2013-09-23 17:26:03 +04:00
|
|
|
#include "qapi-visit.h"
|
2015-03-17 19:22:46 +03:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2016-09-30 17:45:27 +03:00
|
|
|
#include "qapi/qobject-output-visitor.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
2017-12-06 17:45:49 +03:00
|
|
|
#include "sysemu/iothread.h"
|
2012-12-17 21:19:44 +04:00
|
|
|
#include "block/block_int.h"
|
2011-11-23 19:28:21 +04:00
|
|
|
#include "qmp-commands.h"
|
2017-01-25 19:14:15 +03:00
|
|
|
#include "block/trace.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/arch_init.h"
|
2017-05-12 13:33:49 +03:00
|
|
|
#include "sysemu/qtest.h"
|
2016-03-20 20:16:19 +03:00
|
|
|
#include "qemu/cutils.h"
|
|
|
|
#include "qemu/help_option.h"
|
2017-02-28 12:31:46 +03:00
|
|
|
#include "qemu/throttle-options.h"
|
2010-06-02 20:48:27 +04:00
|
|
|
|
2016-01-29 18:36:12 +03:00
|
|
|
static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
|
|
|
|
QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
|
|
|
|
|
2016-09-20 14:38:43 +03:00
|
|
|
static int do_open_tray(const char *blk_name, const char *qdev_id,
|
|
|
|
bool force, Error **errp);
|
2017-11-11 01:43:01 +03:00
|
|
|
static void blockdev_remove_medium(bool has_device, const char *device,
|
|
|
|
bool has_id, const char *id, Error **errp);
|
|
|
|
static void blockdev_insert_medium(bool has_device, const char *device,
|
|
|
|
bool has_id, const char *id,
|
|
|
|
const char *node_name, Error **errp);
|
2016-06-06 21:15:22 +03:00
|
|
|
|
2011-01-28 13:21:39 +03: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",
|
|
|
|
};
|
|
|
|
|
2014-10-01 22:19:25 +04:00
|
|
|
static int if_max_devs[IF_COUNT] = {
|
2011-01-28 13:21:40 +03:00
|
|
|
/*
|
|
|
|
* Do not change these numbers! They govern how drive option
|
|
|
|
* index maps to unit and bus. That mapping is ABI.
|
|
|
|
*
|
2016-04-26 13:12:43 +03:00
|
|
|
* All controllers used to implement if=T drives need to support
|
2011-01-28 13:21:40 +03:00
|
|
|
* 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 13:21:39 +03:00
|
|
|
};
|
|
|
|
|
2014-10-01 22:19:25 +04:00
|
|
|
/**
|
|
|
|
* Boards may call this to offer board-by-board overrides
|
|
|
|
* of the default, global values.
|
|
|
|
*/
|
|
|
|
void override_max_devs(BlockInterfaceType type, int max_devs)
|
|
|
|
{
|
2014-10-07 15:59:06 +04:00
|
|
|
BlockBackend *blk;
|
2014-10-01 22:19:25 +04:00
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
if (max_devs <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-07 15:59:06 +04:00
|
|
|
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
|
|
|
dinfo = blk_legacy_dinfo(blk);
|
2014-10-01 22:19:25 +04:00
|
|
|
if (dinfo->type == type) {
|
|
|
|
fprintf(stderr, "Cannot override units-per-bus property of"
|
|
|
|
" the %s interface, because a drive of that type has"
|
|
|
|
" already been added.\n", if_name[type]);
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if_max_devs[type] = max_devs;
|
|
|
|
}
|
|
|
|
|
2010-06-25 10:09:10 +04: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.
|
|
|
|
*/
|
2014-10-07 15:59:18 +04:00
|
|
|
void blockdev_mark_auto_del(BlockBackend *blk)
|
2010-06-25 10:09:10 +04:00
|
|
|
{
|
2014-10-07 15:59:06 +04:00
|
|
|
DriveInfo *dinfo = blk_legacy_dinfo(blk);
|
2014-10-07 15:59:18 +04:00
|
|
|
BlockDriverState *bs = blk_bs(blk);
|
2014-10-21 15:03:52 +04:00
|
|
|
AioContext *aio_context;
|
2010-06-25 10:09:10 +04:00
|
|
|
|
2014-10-07 15:59:22 +04:00
|
|
|
if (!dinfo) {
|
2013-09-18 17:14:47 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-19 18:53:29 +03:00
|
|
|
if (bs) {
|
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(aio_context);
|
2014-10-21 15:03:52 +04:00
|
|
|
|
2015-10-19 18:53:29 +03:00
|
|
|
if (bs->job) {
|
|
|
|
block_job_cancel(bs->job);
|
|
|
|
}
|
2014-10-21 15:03:52 +04:00
|
|
|
|
2015-10-19 18:53:29 +03:00
|
|
|
aio_context_release(aio_context);
|
|
|
|
}
|
2014-10-21 15:03:52 +04:00
|
|
|
|
2014-10-07 15:59:22 +04:00
|
|
|
dinfo->auto_del = 1;
|
2010-06-25 10:09:10 +04:00
|
|
|
}
|
|
|
|
|
2014-10-07 15:59:18 +04:00
|
|
|
void blockdev_auto_del(BlockBackend *blk)
|
2010-06-25 10:09:10 +04:00
|
|
|
{
|
2014-10-07 15:59:06 +04:00
|
|
|
DriveInfo *dinfo = blk_legacy_dinfo(blk);
|
2010-06-25 10:09:10 +04:00
|
|
|
|
2010-12-08 19:05:00 +03:00
|
|
|
if (dinfo && dinfo->auto_del) {
|
2016-03-16 21:54:38 +03:00
|
|
|
monitor_remove_blk(blk);
|
2014-10-07 15:59:09 +04:00
|
|
|
blk_unref(blk);
|
2010-06-25 10:09:10 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-01 22:19:27 +04:00
|
|
|
/**
|
|
|
|
* Returns the current mapping of how many units per bus
|
|
|
|
* a particular interface can support.
|
|
|
|
*
|
|
|
|
* A positive integer indicates n units per bus.
|
|
|
|
* 0 implies the mapping has not been established.
|
|
|
|
* -1 indicates an invalid BlockInterfaceType was given.
|
|
|
|
*/
|
|
|
|
int drive_get_max_devs(BlockInterfaceType type)
|
|
|
|
{
|
|
|
|
if (type >= IF_IDE && type < IF_COUNT) {
|
|
|
|
return if_max_devs[type];
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-01-28 13:21:43 +03: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 13:21:41 +03:00
|
|
|
QemuOpts *drive_def(const char *optstr)
|
|
|
|
{
|
QemuOpts: Wean off qerror_report_err()
qerror_report_err() is a transitional interface to help with
converting existing monitor commands to QMP. It should not be used
elsewhere.
The only remaining user in qemu-option.c is qemu_opts_parse(). Is it
used in QMP context? If not, we can simply replace
qerror_report_err() by error_report_err().
The uses in qemu-img.c, qemu-io.c, qemu-nbd.c and under tests/ are
clearly not in QMP context.
The uses in vl.c aren't either, because the only QMP command handlers
there are qmp_query_status() and qmp_query_machines(), and they don't
call it.
Remaining uses:
* drive_def(): Command line -drive and such, HMP drive_add and pci_add
* hmp_chardev_add(): HMP chardev-add
* monitor_parse_command(): HMP core
* tmp_config_parse(): Command line -tpmdev
* net_host_device_add(): HMP host_net_add
* net_client_parse(): Command line -net and -netdev
* qemu_global_option(): Command line -global
* vnc_parse_func(): Command line -display, -vnc, default display, HMP
change, QMP change. Bummer.
* qemu_pci_hot_add_nic(): HMP pci_add
* usb_net_init(): Command line -usbdevice, HMP usb_add
Propagate errors through qemu_opts_parse(). Create a convenience
function qemu_opts_parse_noisily() that passes errors to
error_report_err(). Switch all non-QMP users outside tests to it.
That leaves vnc_parse_func(). Propagate errors through it. Since I'm
touching it anyway, rename it to vnc_parse().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-02-13 14:50:26 +03:00
|
|
|
return qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
|
2011-01-28 13:21:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
2011-01-31 13:50:09 +03:00
|
|
|
const char *optstr)
|
2010-06-02 20:48:27 +04:00
|
|
|
{
|
|
|
|
QemuOpts *opts;
|
|
|
|
|
2011-01-28 13:21:41 +03:00
|
|
|
opts = drive_def(optstr);
|
2010-06-02 20:48:27 +04:00
|
|
|
if (!opts) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-01-28 13:21:41 +03:00
|
|
|
if (type != IF_DEFAULT) {
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(opts, "if", if_name[type], &error_abort);
|
2011-01-28 13:21:41 +03:00
|
|
|
}
|
|
|
|
if (index >= 0) {
|
2015-02-13 17:50:43 +03:00
|
|
|
qemu_opt_set_number(opts, "index", index, &error_abort);
|
2011-01-28 13:21:41 +03:00
|
|
|
}
|
2010-06-02 20:48:27 +04:00
|
|
|
if (file)
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(opts, "file", file, &error_abort);
|
2010-06-02 20:48:27 +04:00
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
|
|
|
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit)
|
|
|
|
{
|
2014-10-07 15:59:06 +04:00
|
|
|
BlockBackend *blk;
|
2010-06-02 20:48:27 +04:00
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
2014-10-07 15:59:06 +04:00
|
|
|
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
|
|
|
dinfo = blk_legacy_dinfo(blk);
|
|
|
|
if (dinfo && dinfo->type == type
|
|
|
|
&& dinfo->bus == bus && dinfo->unit == unit) {
|
2010-06-02 20:48:27 +04:00
|
|
|
return dinfo;
|
2014-10-07 15:59:06 +04:00
|
|
|
}
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-15 13:05:46 +03:00
|
|
|
void drive_check_orphaned(void)
|
2014-10-01 22:19:24 +04:00
|
|
|
{
|
2014-10-07 15:59:06 +04:00
|
|
|
BlockBackend *blk;
|
2014-10-01 22:19:24 +04:00
|
|
|
DriveInfo *dinfo;
|
2017-02-15 13:05:45 +03:00
|
|
|
Location loc;
|
2017-02-15 13:05:46 +03:00
|
|
|
bool orphans = false;
|
2014-10-01 22:19:24 +04:00
|
|
|
|
2014-10-07 15:59:06 +04:00
|
|
|
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
|
|
|
dinfo = blk_legacy_dinfo(blk);
|
2014-10-07 15:59:25 +04:00
|
|
|
if (!blk_get_attached_dev(blk) && !dinfo->is_default &&
|
2014-10-01 22:19:24 +04:00
|
|
|
dinfo->type != IF_NONE) {
|
2017-02-15 13:05:45 +03:00
|
|
|
loc_push_none(&loc);
|
|
|
|
qemu_opts_loc_restore(dinfo->opts);
|
2017-02-15 13:05:46 +03:00
|
|
|
error_report("machine type does not support"
|
2017-02-15 13:05:45 +03:00
|
|
|
" if=%s,bus=%d,unit=%d",
|
|
|
|
if_name[dinfo->type], dinfo->bus, dinfo->unit);
|
|
|
|
loc_pop(&loc);
|
2017-02-15 13:05:46 +03:00
|
|
|
orphans = true;
|
2014-10-01 22:19:24 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-15 13:05:46 +03:00
|
|
|
if (orphans) {
|
|
|
|
exit(1);
|
|
|
|
}
|
2014-10-01 22:19:24 +04:00
|
|
|
}
|
|
|
|
|
2011-01-28 13:21:44 +03: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 20:48:27 +04:00
|
|
|
int drive_get_max_bus(BlockInterfaceType type)
|
|
|
|
{
|
|
|
|
int max_bus;
|
2014-10-07 15:59:06 +04:00
|
|
|
BlockBackend *blk;
|
2010-06-02 20:48:27 +04:00
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
max_bus = -1;
|
2014-10-07 15:59:06 +04:00
|
|
|
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
|
|
|
dinfo = blk_legacy_dinfo(blk);
|
|
|
|
if (dinfo && dinfo->type == type && dinfo->bus > max_bus) {
|
2010-06-02 20:48:27 +04:00
|
|
|
max_bus = dinfo->bus;
|
2014-10-07 15:59:06 +04:00
|
|
|
}
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
return max_bus;
|
|
|
|
}
|
|
|
|
|
2011-01-28 13:21:37 +03: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-02 20:48:27 +04:00
|
|
|
static void bdrv_format_print(void *opaque, const char *name)
|
|
|
|
{
|
2011-01-17 21:31:27 +03:00
|
|
|
error_printf(" %s", name);
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
|
2012-01-18 18:40:50 +04:00
|
|
|
typedef struct {
|
|
|
|
QEMUBH *bh;
|
2013-08-23 05:14:51 +04:00
|
|
|
BlockDriverState *bs;
|
|
|
|
} BDRVPutRefBH;
|
2012-01-18 18:40:50 +04:00
|
|
|
|
2013-09-20 13:33:11 +04:00
|
|
|
static int parse_block_error_action(const char *buf, bool is_read, Error **errp)
|
2010-06-02 20:48:27 +04:00
|
|
|
{
|
|
|
|
if (!strcmp(buf, "ignore")) {
|
2012-09-28 19:22:55 +04:00
|
|
|
return BLOCKDEV_ON_ERROR_IGNORE;
|
2010-06-02 20:48:27 +04:00
|
|
|
} else if (!is_read && !strcmp(buf, "enospc")) {
|
2012-09-28 19:22:55 +04:00
|
|
|
return BLOCKDEV_ON_ERROR_ENOSPC;
|
2010-06-02 20:48:27 +04:00
|
|
|
} else if (!strcmp(buf, "stop")) {
|
2012-09-28 19:22:55 +04:00
|
|
|
return BLOCKDEV_ON_ERROR_STOP;
|
2010-06-02 20:48:27 +04:00
|
|
|
} else if (!strcmp(buf, "report")) {
|
2012-09-28 19:22:55 +04:00
|
|
|
return BLOCKDEV_ON_ERROR_REPORT;
|
2010-06-02 20:48:27 +04:00
|
|
|
} else {
|
2013-09-20 13:33:11 +04:00
|
|
|
error_setg(errp, "'%s' invalid %s error action",
|
|
|
|
buf, is_read ? "read" : "write");
|
2010-06-02 20:48:27 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-16 12:28:38 +03:00
|
|
|
static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
const QListEntry *entry;
|
|
|
|
for (entry = qlist_first(intervals); entry; entry = qlist_next(entry)) {
|
|
|
|
switch (qobject_type(entry->value)) {
|
|
|
|
|
|
|
|
case QTYPE_QSTRING: {
|
|
|
|
unsigned long long length;
|
|
|
|
const char *str = qstring_get_str(qobject_to_qstring(entry->value));
|
|
|
|
if (parse_uint_full(str, &length, 10) == 0 &&
|
|
|
|
length > 0 && length <= UINT_MAX) {
|
|
|
|
block_acct_add_interval(stats, (unsigned) length);
|
|
|
|
} else {
|
|
|
|
error_setg(errp, "Invalid interval length: %s", str);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-06-07 19:35:58 +03:00
|
|
|
case QTYPE_QNUM: {
|
|
|
|
int64_t length = qnum_get_int(qobject_to_qnum(entry->value));
|
|
|
|
|
2015-11-16 12:28:38 +03:00
|
|
|
if (length > 0 && length <= UINT_MAX) {
|
|
|
|
block_acct_add_interval(stats, (unsigned) length);
|
|
|
|
} else {
|
|
|
|
error_setg(errp, "Invalid interval length: %" PRId64, length);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
error_setg(errp, "The specification of stats-intervals is invalid");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-08-28 19:00:13 +04:00
|
|
|
typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
|
|
|
|
|
2015-10-19 18:53:31 +03:00
|
|
|
/* All parameters but @opts are optional and may be set to NULL. */
|
|
|
|
static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
|
|
|
|
const char **throttling_group, ThrottleConfig *throttle_cfg,
|
|
|
|
BlockdevDetectZeroesOptions *detect_zeroes, Error **errp)
|
|
|
|
{
|
|
|
|
Error *local_error = NULL;
|
|
|
|
const char *aio;
|
|
|
|
|
|
|
|
if (bdrv_flags) {
|
|
|
|
if (qemu_opt_get_bool(opts, "copy-on-read", false)) {
|
|
|
|
*bdrv_flags |= BDRV_O_COPY_ON_READ;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((aio = qemu_opt_get(opts, "aio")) != NULL) {
|
|
|
|
if (!strcmp(aio, "native")) {
|
|
|
|
*bdrv_flags |= BDRV_O_NATIVE_AIO;
|
|
|
|
} else if (!strcmp(aio, "threads")) {
|
|
|
|
/* this is the default */
|
|
|
|
} else {
|
|
|
|
error_setg(errp, "invalid aio option");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* disk I/O throttling */
|
|
|
|
if (throttling_group) {
|
|
|
|
*throttling_group = qemu_opt_get(opts, "throttling.group");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (throttle_cfg) {
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(throttle_cfg);
|
2015-10-19 18:53:31 +03:00
|
|
|
throttle_cfg->buckets[THROTTLE_BPS_TOTAL].avg =
|
|
|
|
qemu_opt_get_number(opts, "throttling.bps-total", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_BPS_READ].avg =
|
|
|
|
qemu_opt_get_number(opts, "throttling.bps-read", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_BPS_WRITE].avg =
|
|
|
|
qemu_opt_get_number(opts, "throttling.bps-write", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_OPS_TOTAL].avg =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-total", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_OPS_READ].avg =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-read", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_OPS_WRITE].avg =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-write", 0);
|
|
|
|
|
|
|
|
throttle_cfg->buckets[THROTTLE_BPS_TOTAL].max =
|
|
|
|
qemu_opt_get_number(opts, "throttling.bps-total-max", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_BPS_READ].max =
|
|
|
|
qemu_opt_get_number(opts, "throttling.bps-read-max", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_BPS_WRITE].max =
|
|
|
|
qemu_opt_get_number(opts, "throttling.bps-write-max", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_OPS_TOTAL].max =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-total-max", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_OPS_READ].max =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-read-max", 0);
|
|
|
|
throttle_cfg->buckets[THROTTLE_OPS_WRITE].max =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-write-max", 0);
|
|
|
|
|
2016-02-18 13:27:02 +03:00
|
|
|
throttle_cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
|
|
|
|
qemu_opt_get_number(opts, "throttling.bps-total-max-length", 1);
|
|
|
|
throttle_cfg->buckets[THROTTLE_BPS_READ].burst_length =
|
|
|
|
qemu_opt_get_number(opts, "throttling.bps-read-max-length", 1);
|
|
|
|
throttle_cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
|
|
|
|
qemu_opt_get_number(opts, "throttling.bps-write-max-length", 1);
|
|
|
|
throttle_cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-total-max-length", 1);
|
|
|
|
throttle_cfg->buckets[THROTTLE_OPS_READ].burst_length =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-read-max-length", 1);
|
|
|
|
throttle_cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-write-max-length", 1);
|
|
|
|
|
2015-10-19 18:53:31 +03:00
|
|
|
throttle_cfg->op_size =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-size", 0);
|
|
|
|
|
2016-02-18 13:26:59 +03:00
|
|
|
if (!throttle_is_valid(throttle_cfg, errp)) {
|
2015-10-19 18:53:31 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (detect_zeroes) {
|
|
|
|
*detect_zeroes =
|
2017-08-24 11:46:10 +03:00
|
|
|
qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup,
|
2015-10-19 18:53:31 +03:00
|
|
|
qemu_opt_get(opts, "detect-zeroes"),
|
|
|
|
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
|
|
|
|
&local_error);
|
|
|
|
if (local_error) {
|
|
|
|
error_propagate(errp, local_error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-10 14:01:20 +04:00
|
|
|
/* Takes the ownership of bs_opts */
|
2014-10-07 15:59:06 +04:00
|
|
|
static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|
|
|
Error **errp)
|
2010-06-02 20:48:27 +04:00
|
|
|
{
|
|
|
|
const char *buf;
|
|
|
|
int bdrv_flags = 0;
|
|
|
|
int on_read_error, on_write_error;
|
2015-10-28 18:33:04 +03:00
|
|
|
bool account_invalid, account_failed;
|
2016-09-15 17:53:02 +03:00
|
|
|
bool writethrough, read_only;
|
block: New BlockBackend
A block device consists of a frontend device model and a backend.
A block backend has a tree of block drivers doing the actual work.
The tree is managed by the block layer.
We currently use a single abstraction BlockDriverState both for tree
nodes and the backend as a whole. Drawbacks:
* Its API includes both stuff that makes sense only at the block
backend level (root of the tree) and stuff that's only for use
within the block layer. This makes the API bigger and more complex
than necessary. Moreover, it's not obvious which interfaces are
meant for device models, and which really aren't.
* Since device models keep a reference to their backend, the backend
object can't just be destroyed. But for media change, we need to
replace the tree. Our solution is to make the BlockDriverState
generic, with actual driver state in a separate object, pointed to
by member opaque. That lets us replace the tree by deinitializing
and reinitializing its root. This special need of the root makes
the data structure awkward everywhere in the tree.
The general plan is to separate the APIs into "block backend", for use
by device models, monitor and whatever other code dealing with block
backends, and "block driver", for use by the block layer and whatever
other code (if any) dealing with trees and tree nodes.
Code dealing with block backends, device models in particular, should
become completely oblivious of BlockDriverState. This should let us
clean up both APIs, and the tree data structures.
This commit is a first step. It creates a minimal "block backend"
API: type BlockBackend and functions to create, destroy and find them.
BlockBackend objects are created and destroyed exactly when root
BlockDriverState objects are created and destroyed. "Root" in the
sense of "in bdrv_states". They're not yet used for anything; that'll
come shortly.
A root BlockDriverState is created with bdrv_new_root(), so where to
create a BlockBackend is obvious. Where these roots get destroyed
isn't always as obvious.
It is obvious in qemu-img.c, qemu-io.c and qemu-nbd.c, and in error
paths of blockdev_init(), blk_connect(). That leaves destruction of
objects successfully created by blockdev_init() and blk_connect().
blockdev_init() is used only by drive_new() and qmp_blockdev_add().
Objects created by the latter are currently indestructible (see commit
48f364d "blockdev: Refuse to drive_del something added with
blockdev-add" and commit 2d246f0 "blockdev: Introduce
DriveInfo.enable_auto_del"). Objects created by the former get
destroyed by drive_del().
Objects created by blk_connect() get destroyed by blk_disconnect().
BlockBackend is reference-counted. Its reference count never exceeds
one so far, but that's going to change.
In drive_del(), the BB's reference count is surely one now. The BDS's
reference count is greater than one when something else is holding a
reference, such as a block job. In this case, the BB is destroyed
right away, but the BDS lives on until all extra references get
dropped.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2014-10-07 15:59:04 +04:00
|
|
|
BlockBackend *blk;
|
2014-09-12 23:26:21 +04:00
|
|
|
BlockDriverState *bs;
|
2013-09-02 16:14:39 +04:00
|
|
|
ThrottleConfig cfg;
|
2010-06-02 20:48:27 +04:00
|
|
|
int snapshot = 0;
|
2013-02-13 19:53:42 +04:00
|
|
|
Error *error = NULL;
|
2013-03-15 13:35:07 +04:00
|
|
|
QemuOpts *opts;
|
2015-11-16 12:28:38 +03:00
|
|
|
QDict *interval_dict = NULL;
|
|
|
|
QList *interval_list = NULL;
|
2013-03-15 13:35:07 +04:00
|
|
|
const char *id;
|
2015-10-19 18:53:31 +03:00
|
|
|
BlockdevDetectZeroesOptions detect_zeroes =
|
|
|
|
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
|
|
|
|
const char *throttling_group = NULL;
|
2010-06-02 20:48:27 +04:00
|
|
|
|
2013-09-10 14:01:20 +04:00
|
|
|
/* Check common options by copying from bs_opts to opts, all other options
|
|
|
|
* stay in bs_opts for processing by bdrv_open(). */
|
|
|
|
id = qdict_get_try_str(bs_opts, "id");
|
2013-03-15 13:35:07 +04:00
|
|
|
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (error) {
|
2013-09-20 13:33:11 +04:00
|
|
|
error_propagate(errp, error);
|
2014-05-28 13:17:01 +04:00
|
|
|
goto err_no_opts;
|
2013-03-15 13:35:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
qemu_opts_absorb_qdict(opts, bs_opts, &error);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (error) {
|
2013-09-20 13:33:11 +04:00
|
|
|
error_propagate(errp, error);
|
2013-10-30 17:54:30 +04:00
|
|
|
goto early_err;
|
2013-03-15 13:35:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (id) {
|
|
|
|
qdict_del(bs_opts, "id");
|
|
|
|
}
|
|
|
|
|
2010-06-02 20:48:27 +04:00
|
|
|
/* extract parameters */
|
|
|
|
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
|
|
|
|
|
2015-10-28 18:33:04 +03:00
|
|
|
account_invalid = qemu_opt_get_bool(opts, "stats-account-invalid", true);
|
|
|
|
account_failed = qemu_opt_get_bool(opts, "stats-account-failed", true);
|
|
|
|
|
2016-03-15 17:39:42 +03:00
|
|
|
writethrough = !qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true);
|
|
|
|
|
2016-07-08 17:03:00 +03:00
|
|
|
id = qemu_opts_id(opts);
|
|
|
|
|
2015-11-16 12:28:38 +03:00
|
|
|
qdict_extract_subqdict(bs_opts, &interval_dict, "stats-intervals.");
|
|
|
|
qdict_array_split(interval_dict, &interval_list);
|
|
|
|
|
|
|
|
if (qdict_size(interval_dict) != 0) {
|
|
|
|
error_setg(errp, "Invalid option stats-intervals.%s",
|
|
|
|
qdict_first(interval_dict)->key);
|
|
|
|
goto early_err;
|
|
|
|
}
|
2015-10-28 18:33:07 +03:00
|
|
|
|
2015-10-19 18:53:31 +03:00
|
|
|
extract_common_blockdev_options(opts, &bdrv_flags, &throttling_group, &cfg,
|
|
|
|
&detect_zeroes, &error);
|
|
|
|
if (error) {
|
|
|
|
error_propagate(errp, error);
|
|
|
|
goto early_err;
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "format")) != NULL) {
|
2012-08-02 16:45:54 +04:00
|
|
|
if (is_help_option(buf)) {
|
|
|
|
error_printf("Supported formats:");
|
|
|
|
bdrv_iterate_format(bdrv_format_print, NULL);
|
|
|
|
error_printf("\n");
|
2013-10-30 17:54:30 +04:00
|
|
|
goto early_err;
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
2013-07-09 13:09:02 +04:00
|
|
|
|
2015-02-05 21:58:14 +03:00
|
|
|
if (qdict_haskey(bs_opts, "driver")) {
|
|
|
|
error_setg(errp, "Cannot specify both 'driver' and 'format'");
|
2013-10-30 17:54:30 +04:00
|
|
|
goto early_err;
|
2013-08-08 18:45:16 +04:00
|
|
|
}
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(bs_opts, "driver", buf);
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
|
2012-09-28 19:22:55 +04:00
|
|
|
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
2010-06-02 20:48:27 +04:00
|
|
|
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
2013-09-20 13:33:11 +04:00
|
|
|
on_write_error = parse_block_error_action(buf, 0, &error);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (error) {
|
2013-09-20 13:33:11 +04:00
|
|
|
error_propagate(errp, error);
|
2013-10-30 17:54:30 +04:00
|
|
|
goto early_err;
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-28 19:22:55 +04:00
|
|
|
on_read_error = BLOCKDEV_ON_ERROR_REPORT;
|
2010-06-02 20:48:27 +04:00
|
|
|
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
|
2013-09-20 13:33:11 +04:00
|
|
|
on_read_error = parse_block_error_action(buf, 1, &error);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (error) {
|
2013-09-20 13:33:11 +04:00
|
|
|
error_propagate(errp, error);
|
2013-10-30 17:54:30 +04:00
|
|
|
goto early_err;
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-19 18:53:30 +03:00
|
|
|
if (snapshot) {
|
2015-05-08 18:49:53 +03:00
|
|
|
bdrv_flags |= BDRV_O_SNAPSHOT;
|
2015-10-19 18:53:30 +03:00
|
|
|
}
|
|
|
|
|
2016-09-15 17:53:02 +03:00
|
|
|
read_only = qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false);
|
|
|
|
|
2013-07-11 14:52:34 +04:00
|
|
|
/* init */
|
2015-11-13 16:45:42 +03:00
|
|
|
if ((!file || !*file) && !qdict_size(bs_opts)) {
|
2015-10-19 18:53:30 +03:00
|
|
|
BlockBackendRootState *blk_rs;
|
|
|
|
|
2017-01-20 19:07:26 +03:00
|
|
|
blk = blk_new(0, BLK_PERM_ALL);
|
2015-10-19 18:53:30 +03:00
|
|
|
blk_rs = blk_get_root_state(blk);
|
|
|
|
blk_rs->open_flags = bdrv_flags;
|
2016-09-15 17:53:02 +03:00
|
|
|
blk_rs->read_only = read_only;
|
2015-10-19 18:53:30 +03:00
|
|
|
blk_rs->detect_zeroes = detect_zeroes;
|
|
|
|
|
2015-02-05 21:58:14 +03:00
|
|
|
QDECREF(bs_opts);
|
|
|
|
} else {
|
|
|
|
if (file && !*file) {
|
2013-03-18 19:40:51 +04:00
|
|
|
file = NULL;
|
|
|
|
}
|
2010-06-02 20:48:27 +04:00
|
|
|
|
2015-05-08 18:49:53 +03:00
|
|
|
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
|
|
|
|
* with other callers) rather than what we want as the real defaults.
|
|
|
|
* Apply the defaults here instead. */
|
|
|
|
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
|
|
|
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
2016-09-15 17:53:02 +03:00
|
|
|
qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY,
|
|
|
|
read_only ? "on" : "off");
|
2016-03-15 16:34:37 +03:00
|
|
|
assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0);
|
2015-05-08 18:49:53 +03:00
|
|
|
|
2016-02-19 18:48:10 +03:00
|
|
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
|
|
bdrv_flags |= BDRV_O_INACTIVE;
|
|
|
|
}
|
|
|
|
|
2016-03-16 21:54:38 +03:00
|
|
|
blk = blk_new_open(file, NULL, bs_opts, bdrv_flags, errp);
|
2015-02-05 21:58:14 +03:00
|
|
|
if (!blk) {
|
|
|
|
goto err_no_bs_opts;
|
|
|
|
}
|
|
|
|
bs = blk_bs(blk);
|
2012-03-23 11:36:50 +04:00
|
|
|
|
2015-10-19 18:53:30 +03:00
|
|
|
bs->detect_zeroes = detect_zeroes;
|
2010-06-02 20:48:27 +04:00
|
|
|
|
2017-06-05 15:39:07 +03:00
|
|
|
block_acct_setup(blk_get_stats(blk), account_invalid, account_failed);
|
2015-10-28 18:33:07 +03:00
|
|
|
|
2015-11-16 12:28:38 +03:00
|
|
|
if (!parse_stats_intervals(blk_get_stats(blk), interval_list, errp)) {
|
|
|
|
blk_unref(blk);
|
|
|
|
blk = NULL;
|
|
|
|
goto err_no_bs_opts;
|
2015-10-28 18:33:07 +03:00
|
|
|
}
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
|
2016-03-22 15:00:08 +03:00
|
|
|
/* disk I/O throttling */
|
|
|
|
if (throttle_enabled(&cfg)) {
|
|
|
|
if (!throttling_group) {
|
2016-07-08 17:03:00 +03:00
|
|
|
throttling_group = id;
|
2016-03-22 15:00:08 +03:00
|
|
|
}
|
|
|
|
blk_io_limits_enable(blk, throttling_group);
|
|
|
|
blk_set_io_limits(blk, &cfg);
|
|
|
|
}
|
|
|
|
|
2016-03-15 17:39:42 +03:00
|
|
|
blk_set_enable_write_cache(blk, !writethrough);
|
2015-10-19 18:53:30 +03:00
|
|
|
blk_set_on_error(blk, on_read_error, on_write_error);
|
2013-03-15 13:35:07 +04:00
|
|
|
|
2016-07-08 17:03:00 +03:00
|
|
|
if (!monitor_add_blk(blk, id, errp)) {
|
2016-03-16 21:54:38 +03:00
|
|
|
blk_unref(blk);
|
|
|
|
blk = NULL;
|
|
|
|
goto err_no_bs_opts;
|
|
|
|
}
|
|
|
|
|
2015-02-05 21:58:14 +03:00
|
|
|
err_no_bs_opts:
|
2013-03-15 13:35:07 +04:00
|
|
|
qemu_opts_del(opts);
|
2015-11-16 12:28:38 +03:00
|
|
|
QDECREF(interval_dict);
|
|
|
|
QDECREF(interval_list);
|
2014-10-07 15:59:06 +04:00
|
|
|
return blk;
|
2011-02-08 17:12:39 +03:00
|
|
|
|
2013-10-30 17:54:30 +04:00
|
|
|
early_err:
|
|
|
|
qemu_opts_del(opts);
|
2015-11-16 12:28:38 +03:00
|
|
|
QDECREF(interval_dict);
|
|
|
|
QDECREF(interval_list);
|
2014-05-28 13:17:01 +04:00
|
|
|
err_no_opts:
|
|
|
|
QDECREF(bs_opts);
|
2011-02-08 17:12:39 +03:00
|
|
|
return NULL;
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
|
2015-10-19 18:53:32 +03:00
|
|
|
/* Takes the ownership of bs_opts */
|
|
|
|
static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
|
|
|
|
{
|
|
|
|
int bdrv_flags = 0;
|
|
|
|
|
2016-03-07 16:23:04 +03:00
|
|
|
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
|
|
|
|
* with other callers) rather than what we want as the real defaults.
|
|
|
|
* Apply the defaults here instead. */
|
|
|
|
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
|
|
|
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
2016-09-15 17:53:02 +03:00
|
|
|
qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY, "off");
|
2016-03-07 16:23:04 +03:00
|
|
|
|
2016-02-19 18:48:10 +03:00
|
|
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
|
|
bdrv_flags |= BDRV_O_INACTIVE;
|
|
|
|
}
|
|
|
|
|
2016-09-20 21:48:52 +03:00
|
|
|
return bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp);
|
2015-10-19 18:53:32 +03:00
|
|
|
}
|
|
|
|
|
2016-01-29 18:36:12 +03:00
|
|
|
void blockdev_close_all_bdrv_states(void)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs, *next_bs;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) {
|
|
|
|
AioContext *ctx = bdrv_get_aio_context(bs);
|
|
|
|
|
|
|
|
aio_context_acquire(ctx);
|
|
|
|
bdrv_unref(bs);
|
|
|
|
aio_context_release(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-16 21:54:41 +03:00
|
|
|
/* Iterates over the list of monitor-owned BlockDriverStates */
|
|
|
|
BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs ? QTAILQ_NEXT(bs, monitor_list)
|
|
|
|
: QTAILQ_FIRST(&monitor_bdrv_states);
|
|
|
|
}
|
|
|
|
|
2014-09-18 13:48:34 +04:00
|
|
|
static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to,
|
|
|
|
Error **errp)
|
2013-07-17 16:41:54 +04:00
|
|
|
{
|
|
|
|
const char *value;
|
|
|
|
|
|
|
|
value = qemu_opt_get(opts, from);
|
|
|
|
if (value) {
|
2014-09-18 13:48:34 +04:00
|
|
|
if (qemu_opt_find(opts, to)) {
|
|
|
|
error_setg(errp, "'%s' and its alias '%s' can't be used at the "
|
|
|
|
"same time", to, from);
|
|
|
|
return;
|
|
|
|
}
|
2014-09-24 09:45:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* rename all items in opts */
|
|
|
|
while ((value = qemu_opt_get(opts, from))) {
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(opts, to, value, &error_abort);
|
2013-07-17 16:41:54 +04:00
|
|
|
qemu_opt_unset(opts, from);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-28 19:00:13 +04:00
|
|
|
QemuOptsList qemu_legacy_drive_opts = {
|
|
|
|
.name = "drive",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_legacy_drive_opts.head),
|
|
|
|
.desc = {
|
|
|
|
{
|
2013-09-10 17:48:13 +04:00
|
|
|
.name = "bus",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "bus number",
|
|
|
|
},{
|
|
|
|
.name = "unit",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "unit number (i.e. lun for scsi)",
|
|
|
|
},{
|
|
|
|
.name = "index",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "index number",
|
|
|
|
},{
|
2013-08-28 19:00:13 +04:00
|
|
|
.name = "media",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "media type (disk, cdrom)",
|
2013-08-28 19:24:51 +04:00
|
|
|
},{
|
|
|
|
.name = "if",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
|
2013-09-09 18:49:49 +04:00
|
|
|
},{
|
|
|
|
.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)",
|
2013-09-13 16:09:17 +04:00
|
|
|
},{
|
|
|
|
.name = "addr",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "pci address (virtio only)",
|
2014-06-06 17:21:48 +04:00
|
|
|
},{
|
|
|
|
.name = "serial",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "disk serial number",
|
2013-12-20 22:28:14 +04:00
|
|
|
},{
|
|
|
|
.name = "file",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "file name",
|
2013-08-28 19:00:13 +04:00
|
|
|
},
|
2013-09-19 17:12:18 +04:00
|
|
|
|
|
|
|
/* Options that are passed on, but have special semantics with -drive */
|
|
|
|
{
|
2016-09-15 17:53:05 +03:00
|
|
|
.name = BDRV_OPT_READ_ONLY,
|
2013-09-19 17:12:18 +04:00
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "open drive file as read-only",
|
2014-02-09 12:52:32 +04:00
|
|
|
},{
|
|
|
|
.name = "rerror",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "read error action",
|
|
|
|
},{
|
|
|
|
.name = "werror",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "write error action",
|
2013-09-19 17:12:18 +04:00
|
|
|
},{
|
|
|
|
.name = "copy-on-read",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "copy read data from backing file into image file",
|
|
|
|
},
|
|
|
|
|
2013-08-28 19:00:13 +04:00
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2014-06-06 16:50:58 +04:00
|
|
|
DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
2013-07-17 16:41:54 +04:00
|
|
|
{
|
2013-07-18 18:31:25 +04:00
|
|
|
const char *value;
|
2014-10-07 15:59:06 +04:00
|
|
|
BlockBackend *blk;
|
2013-08-28 19:00:13 +04:00
|
|
|
DriveInfo *dinfo = NULL;
|
2013-09-10 14:01:20 +04:00
|
|
|
QDict *bs_opts;
|
2013-08-28 19:00:13 +04:00
|
|
|
QemuOpts *legacy_opts;
|
|
|
|
DriveMediaType media = MEDIA_DISK;
|
2013-08-28 19:24:51 +04:00
|
|
|
BlockInterfaceType type;
|
2013-09-09 18:49:49 +04:00
|
|
|
int cyls, heads, secs, translation;
|
2013-09-10 17:48:13 +04:00
|
|
|
int max_devs, bus_id, unit_id, index;
|
2013-09-13 16:09:17 +04:00
|
|
|
const char *devaddr;
|
2014-02-09 12:52:32 +04:00
|
|
|
const char *werror, *rerror;
|
2013-10-15 13:45:50 +04:00
|
|
|
bool read_only = false;
|
|
|
|
bool copy_on_read;
|
2014-06-06 17:21:48 +04:00
|
|
|
const char *serial;
|
2013-12-20 22:28:14 +04:00
|
|
|
const char *filename;
|
2013-08-28 19:00:13 +04:00
|
|
|
Error *local_err = NULL;
|
2014-09-24 18:37:14 +04:00
|
|
|
int i;
|
2017-05-12 13:33:49 +03:00
|
|
|
const char *deprecated[] = {
|
|
|
|
"serial", "trans", "secs", "heads", "cyls", "addr"
|
|
|
|
};
|
2013-07-18 18:31:25 +04:00
|
|
|
|
2013-07-17 16:41:54 +04:00
|
|
|
/* Change legacy command line options into QMP ones */
|
2014-09-24 18:37:14 +04:00
|
|
|
static const struct {
|
|
|
|
const char *from;
|
|
|
|
const char *to;
|
|
|
|
} opt_renames[] = {
|
|
|
|
{ "iops", "throttling.iops-total" },
|
|
|
|
{ "iops_rd", "throttling.iops-read" },
|
|
|
|
{ "iops_wr", "throttling.iops-write" },
|
2013-07-17 16:41:54 +04:00
|
|
|
|
2014-09-24 18:37:14 +04:00
|
|
|
{ "bps", "throttling.bps-total" },
|
|
|
|
{ "bps_rd", "throttling.bps-read" },
|
|
|
|
{ "bps_wr", "throttling.bps-write" },
|
2013-07-17 16:41:54 +04:00
|
|
|
|
2014-09-24 18:37:14 +04:00
|
|
|
{ "iops_max", "throttling.iops-total-max" },
|
|
|
|
{ "iops_rd_max", "throttling.iops-read-max" },
|
|
|
|
{ "iops_wr_max", "throttling.iops-write-max" },
|
2013-09-02 16:14:40 +04:00
|
|
|
|
2014-09-24 18:37:14 +04:00
|
|
|
{ "bps_max", "throttling.bps-total-max" },
|
|
|
|
{ "bps_rd_max", "throttling.bps-read-max" },
|
|
|
|
{ "bps_wr_max", "throttling.bps-write-max" },
|
2013-09-02 16:14:40 +04:00
|
|
|
|
2014-09-24 18:37:14 +04:00
|
|
|
{ "iops_size", "throttling.iops-size" },
|
2013-09-02 16:14:41 +04:00
|
|
|
|
2015-06-08 19:17:44 +03:00
|
|
|
{ "group", "throttling.group" },
|
|
|
|
|
2016-09-15 17:53:05 +03:00
|
|
|
{ "readonly", BDRV_OPT_READ_ONLY },
|
2014-09-24 18:37:14 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(opt_renames); i++) {
|
2014-09-18 13:48:34 +04:00
|
|
|
qemu_opt_rename(all_opts, opt_renames[i].from, opt_renames[i].to,
|
|
|
|
&local_err);
|
|
|
|
if (local_err) {
|
2015-02-12 15:55:05 +03:00
|
|
|
error_report_err(local_err);
|
2014-09-18 13:48:34 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-09-24 18:37:14 +04:00
|
|
|
}
|
2013-07-19 22:07:29 +04:00
|
|
|
|
2013-07-18 18:31:25 +04:00
|
|
|
value = qemu_opt_get(all_opts, "cache");
|
|
|
|
if (value) {
|
|
|
|
int flags = 0;
|
2016-03-18 17:35:51 +03:00
|
|
|
bool writethrough;
|
2013-07-18 18:31:25 +04:00
|
|
|
|
2016-03-18 17:35:51 +03:00
|
|
|
if (bdrv_parse_cache_mode(value, &flags, &writethrough) != 0) {
|
2013-07-18 18:31:25 +04:00
|
|
|
error_report("invalid cache option");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Specific options take precedence */
|
2015-04-07 17:55:00 +03:00
|
|
|
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) {
|
|
|
|
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB,
|
2016-03-18 17:35:51 +03:00
|
|
|
!writethrough, &error_abort);
|
2013-07-18 18:31:25 +04:00
|
|
|
}
|
2015-04-07 17:55:00 +03:00
|
|
|
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) {
|
|
|
|
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT,
|
2015-02-12 18:37:44 +03:00
|
|
|
!!(flags & BDRV_O_NOCACHE), &error_abort);
|
2013-07-18 18:31:25 +04:00
|
|
|
}
|
2015-04-07 17:55:00 +03:00
|
|
|
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_NO_FLUSH)) {
|
|
|
|
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_NO_FLUSH,
|
2015-02-12 18:37:44 +03:00
|
|
|
!!(flags & BDRV_O_NO_FLUSH), &error_abort);
|
2013-07-18 18:31:25 +04:00
|
|
|
}
|
|
|
|
qemu_opt_unset(all_opts, "cache");
|
|
|
|
}
|
|
|
|
|
2013-09-10 14:01:20 +04:00
|
|
|
/* Get a QDict for processing the options */
|
|
|
|
bs_opts = qdict_new();
|
|
|
|
qemu_opts_to_qdict(all_opts, bs_opts);
|
|
|
|
|
2014-01-02 06:49:17 +04:00
|
|
|
legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0,
|
|
|
|
&error_abort);
|
2013-08-28 19:00:13 +04:00
|
|
|
qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2015-02-12 15:55:05 +03:00
|
|
|
error_report_err(local_err);
|
2013-08-28 19:00:13 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2017-05-12 13:33:49 +03:00
|
|
|
/* Other deprecated options */
|
|
|
|
if (!qtest_enabled()) {
|
|
|
|
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
|
|
|
|
if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) {
|
|
|
|
error_report("'%s' is deprecated, please use the corresponding "
|
|
|
|
"option of '-device' instead", deprecated[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-28 19:00:13 +04:00
|
|
|
/* Media type */
|
|
|
|
value = qemu_opt_get(legacy_opts, "media");
|
|
|
|
if (value) {
|
|
|
|
if (!strcmp(value, "disk")) {
|
|
|
|
media = MEDIA_DISK;
|
|
|
|
} else if (!strcmp(value, "cdrom")) {
|
|
|
|
media = MEDIA_CDROM;
|
2013-10-15 13:45:50 +04:00
|
|
|
read_only = true;
|
2013-08-28 19:00:13 +04:00
|
|
|
} else {
|
|
|
|
error_report("'%s' invalid media", value);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-19 17:12:18 +04:00
|
|
|
/* copy-on-read is disabled with a warning for read-only devices */
|
2016-09-15 17:53:05 +03:00
|
|
|
read_only |= qemu_opt_get_bool(legacy_opts, BDRV_OPT_READ_ONLY, false);
|
2013-09-19 17:12:18 +04:00
|
|
|
copy_on_read = qemu_opt_get_bool(legacy_opts, "copy-on-read", false);
|
|
|
|
|
|
|
|
if (read_only && copy_on_read) {
|
2017-07-12 16:57:41 +03:00
|
|
|
warn_report("disabling copy-on-read on read-only drive");
|
2013-09-19 17:12:18 +04:00
|
|
|
copy_on_read = false;
|
|
|
|
}
|
|
|
|
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(bs_opts, BDRV_OPT_READ_ONLY, read_only ? "on" : "off");
|
|
|
|
qdict_put_str(bs_opts, "copy-on-read", copy_on_read ? "on" : "off");
|
2013-09-19 17:12:18 +04:00
|
|
|
|
2013-08-28 19:24:51 +04:00
|
|
|
/* Controller type */
|
|
|
|
value = qemu_opt_get(legacy_opts, "if");
|
|
|
|
if (value) {
|
|
|
|
for (type = 0;
|
|
|
|
type < IF_COUNT && strcmp(value, if_name[type]);
|
|
|
|
type++) {
|
|
|
|
}
|
|
|
|
if (type == IF_COUNT) {
|
|
|
|
error_report("unsupported bus type '%s'", value);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
type = block_default_type;
|
|
|
|
}
|
|
|
|
|
2013-09-09 18:49:49 +04:00
|
|
|
/* Geometry */
|
|
|
|
cyls = qemu_opt_get_number(legacy_opts, "cyls", 0);
|
|
|
|
heads = qemu_opt_get_number(legacy_opts, "heads", 0);
|
|
|
|
secs = qemu_opt_get_number(legacy_opts, "secs", 0);
|
|
|
|
|
|
|
|
if (cyls || heads || secs) {
|
|
|
|
if (cyls < 1) {
|
|
|
|
error_report("invalid physical cyls number");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (heads < 1) {
|
|
|
|
error_report("invalid physical heads number");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (secs < 1) {
|
|
|
|
error_report("invalid physical secs number");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
translation = BIOS_ATA_TRANSLATION_AUTO;
|
|
|
|
value = qemu_opt_get(legacy_opts, "trans");
|
|
|
|
if (value != NULL) {
|
|
|
|
if (!cyls) {
|
|
|
|
error_report("'%s' trans must be used with cyls, heads and secs",
|
|
|
|
value);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (!strcmp(value, "none")) {
|
|
|
|
translation = BIOS_ATA_TRANSLATION_NONE;
|
|
|
|
} else if (!strcmp(value, "lba")) {
|
|
|
|
translation = BIOS_ATA_TRANSLATION_LBA;
|
2014-02-08 14:01:54 +04:00
|
|
|
} else if (!strcmp(value, "large")) {
|
|
|
|
translation = BIOS_ATA_TRANSLATION_LARGE;
|
|
|
|
} else if (!strcmp(value, "rechs")) {
|
|
|
|
translation = BIOS_ATA_TRANSLATION_RECHS;
|
2013-09-09 18:49:49 +04:00
|
|
|
} else if (!strcmp(value, "auto")) {
|
|
|
|
translation = BIOS_ATA_TRANSLATION_AUTO;
|
|
|
|
} else {
|
|
|
|
error_report("'%s' invalid translation type", value);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (media == MEDIA_CDROM) {
|
|
|
|
if (cyls || secs || heads) {
|
|
|
|
error_report("CHS can't be set with media=cdrom");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-10 17:48:13 +04:00
|
|
|
/* Device address specified by bus/unit or index.
|
|
|
|
* If none was specified, try to find the first free one. */
|
|
|
|
bus_id = qemu_opt_get_number(legacy_opts, "bus", 0);
|
|
|
|
unit_id = qemu_opt_get_number(legacy_opts, "unit", -1);
|
|
|
|
index = qemu_opt_get_number(legacy_opts, "index", -1);
|
|
|
|
|
|
|
|
max_devs = if_max_devs[type];
|
|
|
|
|
|
|
|
if (index != -1) {
|
|
|
|
if (bus_id != 0 || unit_id != -1) {
|
|
|
|
error_report("index cannot be used with bus and unit");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
bus_id = drive_index_to_bus_id(type, index);
|
|
|
|
unit_id = drive_index_to_unit_id(type, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unit_id == -1) {
|
|
|
|
unit_id = 0;
|
|
|
|
while (drive_get(type, bus_id, unit_id) != NULL) {
|
|
|
|
unit_id++;
|
|
|
|
if (max_devs && unit_id >= max_devs) {
|
|
|
|
unit_id -= max_devs;
|
|
|
|
bus_id++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (max_devs && unit_id >= max_devs) {
|
|
|
|
error_report("unit %d too big (max is %d)", unit_id, max_devs - 1);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (drive_get(type, bus_id, unit_id) != NULL) {
|
|
|
|
error_report("drive with bus=%d, unit=%d (index=%d) exists",
|
|
|
|
bus_id, unit_id, index);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-06-06 17:21:48 +04:00
|
|
|
/* Serial number */
|
|
|
|
serial = qemu_opt_get(legacy_opts, "serial");
|
|
|
|
|
2013-09-10 17:48:13 +04:00
|
|
|
/* no id supplied -> create one */
|
|
|
|
if (qemu_opts_id(all_opts) == NULL) {
|
|
|
|
char *new_id;
|
|
|
|
const char *mediastr = "";
|
|
|
|
if (type == IF_IDE || type == IF_SCSI) {
|
|
|
|
mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
|
|
|
|
}
|
|
|
|
if (max_devs) {
|
|
|
|
new_id = g_strdup_printf("%s%i%s%i", if_name[type], bus_id,
|
|
|
|
mediastr, unit_id);
|
|
|
|
} else {
|
|
|
|
new_id = g_strdup_printf("%s%s%i", if_name[type],
|
|
|
|
mediastr, unit_id);
|
|
|
|
}
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(bs_opts, "id", new_id);
|
2013-09-10 17:48:13 +04:00
|
|
|
g_free(new_id);
|
|
|
|
}
|
|
|
|
|
2013-09-13 16:09:17 +04:00
|
|
|
/* Add virtio block device */
|
|
|
|
devaddr = qemu_opt_get(legacy_opts, "addr");
|
|
|
|
if (devaddr && type != IF_VIRTIO) {
|
|
|
|
error_report("addr is not supported by this bus type");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == IF_VIRTIO) {
|
|
|
|
QemuOpts *devopts;
|
2014-01-02 06:49:17 +04:00
|
|
|
devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
|
|
|
|
&error_abort);
|
2013-09-13 16:09:17 +04:00
|
|
|
if (arch_type == QEMU_ARCH_S390X) {
|
2015-06-17 00:06:33 +03:00
|
|
|
qemu_opt_set(devopts, "driver", "virtio-blk-ccw", &error_abort);
|
2013-09-13 16:09:17 +04:00
|
|
|
} else {
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(devopts, "driver", "virtio-blk-pci", &error_abort);
|
2013-09-13 16:09:17 +04:00
|
|
|
}
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"),
|
|
|
|
&error_abort);
|
2013-09-13 16:09:17 +04:00
|
|
|
if (devaddr) {
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(devopts, "addr", devaddr, &error_abort);
|
2013-09-13 16:09:17 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-20 22:28:14 +04:00
|
|
|
filename = qemu_opt_get(legacy_opts, "file");
|
|
|
|
|
2014-02-09 12:52:32 +04:00
|
|
|
/* Check werror/rerror compatibility with if=... */
|
|
|
|
werror = qemu_opt_get(legacy_opts, "werror");
|
|
|
|
if (werror != NULL) {
|
|
|
|
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO &&
|
|
|
|
type != IF_NONE) {
|
|
|
|
error_report("werror is not supported by this bus type");
|
|
|
|
goto fail;
|
|
|
|
}
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(bs_opts, "werror", werror);
|
2014-02-09 12:52:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
rerror = qemu_opt_get(legacy_opts, "rerror");
|
|
|
|
if (rerror != NULL) {
|
|
|
|
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI &&
|
|
|
|
type != IF_NONE) {
|
|
|
|
error_report("rerror is not supported by this bus type");
|
|
|
|
goto fail;
|
|
|
|
}
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(bs_opts, "rerror", rerror);
|
2014-02-09 12:52:32 +04:00
|
|
|
}
|
|
|
|
|
2013-09-18 17:14:47 +04:00
|
|
|
/* Actual block device init: Functionality shared with blockdev-add */
|
2014-10-07 15:59:06 +04:00
|
|
|
blk = blockdev_init(filename, bs_opts, &local_err);
|
2014-05-28 13:17:02 +04:00
|
|
|
bs_opts = NULL;
|
2014-10-07 15:59:06 +04:00
|
|
|
if (!blk) {
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2015-02-12 15:55:05 +03:00
|
|
|
error_report_err(local_err);
|
2013-09-20 13:33:11 +04:00
|
|
|
}
|
2013-09-18 17:14:47 +04:00
|
|
|
goto fail;
|
2013-09-20 13:33:11 +04:00
|
|
|
} else {
|
2014-01-30 18:07:28 +04:00
|
|
|
assert(!local_err);
|
2013-09-18 17:14:47 +04:00
|
|
|
}
|
|
|
|
|
2014-10-07 15:59:22 +04:00
|
|
|
/* Create legacy DriveInfo */
|
|
|
|
dinfo = g_malloc0(sizeof(*dinfo));
|
2013-09-10 14:01:20 +04:00
|
|
|
dinfo->opts = all_opts;
|
2013-09-18 17:14:47 +04:00
|
|
|
|
2013-09-09 18:49:49 +04:00
|
|
|
dinfo->cyls = cyls;
|
|
|
|
dinfo->heads = heads;
|
|
|
|
dinfo->secs = secs;
|
|
|
|
dinfo->trans = translation;
|
|
|
|
|
2014-02-09 12:52:32 +04:00
|
|
|
dinfo->type = type;
|
2013-09-10 17:48:13 +04:00
|
|
|
dinfo->bus = bus_id;
|
|
|
|
dinfo->unit = unit_id;
|
2013-09-13 16:09:17 +04:00
|
|
|
dinfo->devaddr = devaddr;
|
2014-06-06 17:21:48 +04:00
|
|
|
dinfo->serial = g_strdup(serial);
|
|
|
|
|
2014-10-07 15:59:22 +04:00
|
|
|
blk_set_legacy_dinfo(blk, dinfo);
|
|
|
|
|
2013-09-19 16:24:10 +04:00
|
|
|
switch(type) {
|
|
|
|
case IF_IDE:
|
|
|
|
case IF_SCSI:
|
|
|
|
case IF_XEN:
|
|
|
|
case IF_NONE:
|
|
|
|
dinfo->media_cd = media == MEDIA_CDROM;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-09-18 17:14:47 +04:00
|
|
|
fail:
|
2013-08-28 19:00:13 +04:00
|
|
|
qemu_opts_del(legacy_opts);
|
2014-05-28 13:17:02 +04:00
|
|
|
QDECREF(bs_opts);
|
2013-09-18 17:14:47 +04:00
|
|
|
return dinfo;
|
2013-07-17 16:41:54 +04:00
|
|
|
}
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
bs = bdrv_lookup_bs(name, name, errp);
|
|
|
|
if (bs == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bdrv_is_root_node(bs)) {
|
|
|
|
error_setg(errp, "Need a root block node");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bdrv_is_inserted(bs)) {
|
|
|
|
error_setg(errp, "Device has no medium");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bs;
|
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:43 +03:00
|
|
|
static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockBackend *blk;
|
|
|
|
|
|
|
|
if (!blk_name == !qdev_id) {
|
|
|
|
error_setg(errp, "Need exactly one of 'device' and 'id'");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qdev_id) {
|
|
|
|
blk = blk_by_qdev_id(qdev_id, errp);
|
|
|
|
} else {
|
|
|
|
blk = blk_by_name(blk_name);
|
|
|
|
if (blk == NULL) {
|
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
|
|
"Device '%s' not found", blk_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return blk;
|
|
|
|
}
|
|
|
|
|
hmp: Name HMP command handler functions hmp_COMMAND()
Some are called do_COMMAND() (old ones, usually), some hmp_COMMAND(),
and sometimes COMMAND pointlessly differs in spelling.
Normalize to hmp_COMMAND(), where COMMAND is exactly the command name
with '-' replaced by '_'.
Exceptions:
* do_device_add() and client_migrate_info() *not* renamed to
hmp_device_add(), hmp_client_migrate_info(), because they're also
QMP handlers. They still need to be converted to QAPI.
* do_memory_dump(), do_physical_memory_dump(), do_ioport_read(),
do_ioport_write() renamed do hmp_* instead of hmp_x(), hmp_xp(),
hmp_i(), hmp_o(), because those names are too cryptic for my taste.
* do_info_help() renamed to hmp_info_help() instead of hmp_info(),
because it only covers help.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-02-06 15:55:43 +03:00
|
|
|
void hmp_commit(Monitor *mon, const QDict *qdict)
|
2010-06-02 20:48:27 +04:00
|
|
|
{
|
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2015-03-02 14:36:48 +03:00
|
|
|
BlockBackend *blk;
|
2012-03-05 22:10:11 +04:00
|
|
|
int ret;
|
2010-06-02 20:48:27 +04:00
|
|
|
|
2010-06-02 20:55:18 +04:00
|
|
|
if (!strcmp(device, "all")) {
|
2016-03-16 21:54:32 +03:00
|
|
|
ret = blk_commit_all();
|
2010-06-02 20:55:18 +04:00
|
|
|
} else {
|
2015-11-04 20:27:23 +03:00
|
|
|
BlockDriverState *bs;
|
|
|
|
AioContext *aio_context;
|
|
|
|
|
2015-03-02 14:36:48 +03:00
|
|
|
blk = blk_by_name(device);
|
|
|
|
if (!blk) {
|
2013-01-18 21:45:35 +04:00
|
|
|
monitor_printf(mon, "Device '%s' not found\n", device);
|
2010-06-02 20:55:19 +04:00
|
|
|
return;
|
2010-06-02 20:55:18 +04:00
|
|
|
}
|
2015-10-19 18:53:29 +03:00
|
|
|
if (!blk_is_available(blk)) {
|
|
|
|
monitor_printf(mon, "Device '%s' has no medium\n", device);
|
|
|
|
return;
|
|
|
|
}
|
2015-11-04 20:27:23 +03:00
|
|
|
|
|
|
|
bs = blk_bs(blk);
|
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
ret = bdrv_commit(bs);
|
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2013-01-18 21:45:35 +04:00
|
|
|
}
|
|
|
|
if (ret < 0) {
|
|
|
|
monitor_printf(mon, "'commit' error for '%s': %s\n", device,
|
|
|
|
strerror(-ret));
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-03 19:16:50 +03:00
|
|
|
static void blockdev_do_action(TransactionAction *action, Error **errp)
|
2012-03-06 21:55:59 +04:00
|
|
|
{
|
2013-05-13 12:43:43 +04:00
|
|
|
TransactionActionList list;
|
2012-03-06 21:55:59 +04:00
|
|
|
|
2016-03-03 19:16:50 +03:00
|
|
|
list.value = action;
|
2012-03-06 21:55:59 +04:00
|
|
|
list.next = NULL;
|
2015-11-06 02:13:18 +03:00
|
|
|
qmp_transaction(&list, false, NULL, errp);
|
2012-03-06 21:55:59 +04:00
|
|
|
}
|
|
|
|
|
2014-01-24 00:31:38 +04:00
|
|
|
void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
|
|
|
|
bool has_node_name, const char *node_name,
|
|
|
|
const char *snapshot_file,
|
|
|
|
bool has_snapshot_node_name,
|
|
|
|
const char *snapshot_node_name,
|
2011-11-25 22:15:19 +04:00
|
|
|
bool has_format, const char *format,
|
2014-01-24 00:31:38 +04:00
|
|
|
bool has_mode, NewImageMode mode, Error **errp)
|
2010-12-16 15:52:16 +03:00
|
|
|
{
|
2015-10-26 15:27:14 +03:00
|
|
|
BlockdevSnapshotSync snapshot = {
|
2014-01-24 00:31:38 +04:00
|
|
|
.has_device = has_device,
|
2012-03-06 21:55:59 +04:00
|
|
|
.device = (char *) device,
|
2014-01-24 00:31:38 +04:00
|
|
|
.has_node_name = has_node_name,
|
|
|
|
.node_name = (char *) node_name,
|
2012-03-06 21:55:59 +04:00
|
|
|
.snapshot_file = (char *) snapshot_file,
|
2014-01-24 00:31:38 +04:00
|
|
|
.has_snapshot_node_name = has_snapshot_node_name,
|
|
|
|
.snapshot_node_name = (char *) snapshot_node_name,
|
2012-03-06 21:55:59 +04:00
|
|
|
.has_format = has_format,
|
|
|
|
.format = (char *) format,
|
|
|
|
.has_mode = has_mode,
|
|
|
|
.mode = mode,
|
|
|
|
};
|
2016-03-03 19:16:50 +03:00
|
|
|
TransactionAction action = {
|
|
|
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
.u.blockdev_snapshot_sync.data = &snapshot,
|
2016-03-03 19:16:50 +03:00
|
|
|
};
|
|
|
|
blockdev_do_action(&action, errp);
|
2010-12-16 15:52:16 +03:00
|
|
|
}
|
|
|
|
|
2015-10-26 15:27:16 +03:00
|
|
|
void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockdevSnapshot snapshot_data = {
|
|
|
|
.node = (char *) node,
|
|
|
|
.overlay = (char *) overlay
|
|
|
|
};
|
2016-03-03 19:16:50 +03:00
|
|
|
TransactionAction action = {
|
|
|
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
.u.blockdev_snapshot.data = &snapshot_data,
|
2016-03-03 19:16:50 +03:00
|
|
|
};
|
|
|
|
blockdev_do_action(&action, errp);
|
2015-10-26 15:27:16 +03:00
|
|
|
}
|
|
|
|
|
2013-09-11 10:04:35 +04:00
|
|
|
void qmp_blockdev_snapshot_internal_sync(const char *device,
|
|
|
|
const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockdevSnapshotInternal snapshot = {
|
|
|
|
.device = (char *) device,
|
|
|
|
.name = (char *) name
|
|
|
|
};
|
2016-03-03 19:16:50 +03:00
|
|
|
TransactionAction action = {
|
|
|
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
.u.blockdev_snapshot_internal_sync.data = &snapshot,
|
2016-03-03 19:16:50 +03:00
|
|
|
};
|
|
|
|
blockdev_do_action(&action, errp);
|
2013-09-11 10:04:35 +04:00
|
|
|
}
|
|
|
|
|
2013-09-11 10:04:36 +04:00
|
|
|
SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
|
|
|
|
bool has_id,
|
|
|
|
const char *id,
|
|
|
|
bool has_name,
|
|
|
|
const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
2015-03-02 14:36:48 +03:00
|
|
|
BlockDriverState *bs;
|
2014-11-19 17:19:42 +03:00
|
|
|
AioContext *aio_context;
|
2013-09-11 10:04:36 +04:00
|
|
|
QEMUSnapshotInfo sn;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
SnapshotInfo *info = NULL;
|
|
|
|
int ret;
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
bs = qmp_get_root_bs(device, errp);
|
|
|
|
if (!bs) {
|
2013-09-11 10:04:36 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2016-06-23 15:20:24 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2015-10-19 18:53:29 +03:00
|
|
|
aio_context_acquire(aio_context);
|
2013-09-11 10:04:36 +04:00
|
|
|
|
|
|
|
if (!has_id) {
|
|
|
|
id = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!has_name) {
|
|
|
|
name = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!id && !name) {
|
|
|
|
error_setg(errp, "Name or id must be provided");
|
2015-10-19 18:53:29 +03:00
|
|
|
goto out_aio_context;
|
2013-09-11 10:04:36 +04:00
|
|
|
}
|
|
|
|
|
2014-11-19 17:19:43 +03:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) {
|
|
|
|
goto out_aio_context;
|
|
|
|
}
|
|
|
|
|
2013-09-11 10:04:36 +04:00
|
|
|
ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2013-09-11 10:04:36 +04:00
|
|
|
error_propagate(errp, local_err);
|
2014-11-19 17:19:42 +03:00
|
|
|
goto out_aio_context;
|
2013-09-11 10:04:36 +04:00
|
|
|
}
|
|
|
|
if (!ret) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Snapshot with id '%s' and name '%s' does not exist on "
|
|
|
|
"device '%s'",
|
|
|
|
STR_OR_NULL(id), STR_OR_NULL(name), device);
|
2014-11-19 17:19:42 +03:00
|
|
|
goto out_aio_context;
|
2013-09-11 10:04:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bdrv_snapshot_delete(bs, id, name, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2013-09-11 10:04:36 +04:00
|
|
|
error_propagate(errp, local_err);
|
2014-11-19 17:19:42 +03:00
|
|
|
goto out_aio_context;
|
2013-09-11 10:04:36 +04:00
|
|
|
}
|
|
|
|
|
2014-11-19 17:19:42 +03:00
|
|
|
aio_context_release(aio_context);
|
|
|
|
|
block: Use g_new() & friends where that makes obvious sense
g_new(T, n) is neater than g_malloc(sizeof(T) * n). It's also safer,
for two reasons. One, it catches multiplication overflowing size_t.
Two, it returns T * rather than void *, which lets the compiler catch
more type errors.
Patch created with Coccinelle, with two manual changes on top:
* Add const to bdrv_iterate_format() to keep the types straight
* Convert the allocation in bdrv_drop_intermediate(), which Coccinelle
inexplicably misses
Coccinelle semantic patch:
@@
type T;
@@
-g_malloc(sizeof(T))
+g_new(T, 1)
@@
type T;
@@
-g_try_malloc(sizeof(T))
+g_try_new(T, 1)
@@
type T;
@@
-g_malloc0(sizeof(T))
+g_new0(T, 1)
@@
type T;
@@
-g_try_malloc0(sizeof(T))
+g_try_new0(T, 1)
@@
type T;
expression n;
@@
-g_malloc(sizeof(T) * (n))
+g_new(T, n)
@@
type T;
expression n;
@@
-g_try_malloc(sizeof(T) * (n))
+g_try_new(T, n)
@@
type T;
expression n;
@@
-g_malloc0(sizeof(T) * (n))
+g_new0(T, n)
@@
type T;
expression n;
@@
-g_try_malloc0(sizeof(T) * (n))
+g_try_new0(T, n)
@@
type T;
expression p, n;
@@
-g_realloc(p, sizeof(T) * (n))
+g_renew(T, p, n)
@@
type T;
expression p, n;
@@
-g_try_realloc(p, sizeof(T) * (n))
+g_try_renew(T, p, n)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2014-08-19 12:31:08 +04:00
|
|
|
info = g_new0(SnapshotInfo, 1);
|
2013-09-11 10:04:36 +04:00
|
|
|
info->id = g_strdup(sn.id_str);
|
|
|
|
info->name = g_strdup(sn.name);
|
|
|
|
info->date_nsec = sn.date_nsec;
|
|
|
|
info->date_sec = sn.date_sec;
|
|
|
|
info->vm_state_size = sn.vm_state_size;
|
|
|
|
info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000;
|
|
|
|
info->vm_clock_sec = sn.vm_clock_nsec / 1000000000;
|
|
|
|
|
|
|
|
return info;
|
2014-11-19 17:19:42 +03:00
|
|
|
|
|
|
|
out_aio_context:
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
return NULL;
|
2013-09-11 10:04:36 +04:00
|
|
|
}
|
2012-02-29 00:54:06 +04:00
|
|
|
|
2015-04-18 02:49:52 +03:00
|
|
|
/**
|
|
|
|
* block_dirty_bitmap_lookup:
|
|
|
|
* Return a dirty bitmap (if present), after validating
|
|
|
|
* the node reference and bitmap names.
|
|
|
|
*
|
|
|
|
* @node: The name of the BDS node to search for bitmaps
|
|
|
|
* @name: The name of the bitmap to search for
|
|
|
|
* @pbs: Output pointer for BDS lookup, if desired. Can be NULL.
|
|
|
|
* @paio: Output pointer for aio_context acquisition, if desired. Can be NULL.
|
|
|
|
* @errp: Output pointer for error information. Can be NULL.
|
|
|
|
*
|
|
|
|
* @return: A bitmap object on success, or NULL on failure.
|
|
|
|
*/
|
|
|
|
static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node,
|
|
|
|
const char *name,
|
|
|
|
BlockDriverState **pbs,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BdrvDirtyBitmap *bitmap;
|
|
|
|
|
|
|
|
if (!node) {
|
|
|
|
error_setg(errp, "Node cannot be NULL");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!name) {
|
|
|
|
error_setg(errp, "Bitmap name cannot be NULL");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
bs = bdrv_lookup_bs(node, node, NULL);
|
|
|
|
if (!bs) {
|
|
|
|
error_setg(errp, "Node '%s' not found", node);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bitmap = bdrv_find_dirty_bitmap(bs, name);
|
|
|
|
if (!bitmap) {
|
|
|
|
error_setg(errp, "Dirty bitmap '%s' not found", name);
|
2017-06-05 15:39:03 +03:00
|
|
|
return NULL;
|
2015-04-18 02:49:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pbs) {
|
|
|
|
*pbs = bs;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bitmap;
|
|
|
|
}
|
|
|
|
|
2014-11-21 13:48:57 +03:00
|
|
|
/* New and old BlockDriverState structs for atomic group operations */
|
2013-05-08 14:25:16 +04:00
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
typedef struct BlkActionState BlkActionState;
|
2013-05-08 14:25:16 +04:00
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
/**
|
|
|
|
* BlkActionOps:
|
|
|
|
* Table of operations that define an Action.
|
|
|
|
*
|
|
|
|
* @instance_size: Size of state struct, in bytes.
|
|
|
|
* @prepare: Prepare the work, must NOT be NULL.
|
|
|
|
* @commit: Commit the changes, can be NULL.
|
|
|
|
* @abort: Abort the changes on fail, can be NULL.
|
|
|
|
* @clean: Clean up resources after all transaction actions have called
|
|
|
|
* commit() or abort(). Can be NULL.
|
|
|
|
*
|
|
|
|
* Only prepare() may fail. In a single transaction, only one of commit() or
|
|
|
|
* abort() will be called. clean() will always be called if it is present.
|
|
|
|
*/
|
|
|
|
typedef struct BlkActionOps {
|
2013-05-08 14:25:16 +04:00
|
|
|
size_t instance_size;
|
2015-11-06 02:13:09 +03:00
|
|
|
void (*prepare)(BlkActionState *common, Error **errp);
|
|
|
|
void (*commit)(BlkActionState *common);
|
|
|
|
void (*abort)(BlkActionState *common);
|
|
|
|
void (*clean)(BlkActionState *common);
|
|
|
|
} BlkActionOps;
|
2013-05-08 14:25:16 +04:00
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
/**
|
|
|
|
* BlkActionState:
|
|
|
|
* Describes one Action's state within a Transaction.
|
|
|
|
*
|
|
|
|
* @action: QAPI-defined enum identifying which Action to perform.
|
|
|
|
* @ops: Table of ActionOps this Action can perform.
|
2015-11-06 02:13:18 +03:00
|
|
|
* @block_job_txn: Transaction which this action belongs to.
|
2015-11-06 02:13:09 +03:00
|
|
|
* @entry: List membership for all Actions in this Transaction.
|
|
|
|
*
|
|
|
|
* This structure must be arranged as first member in a subclassed type,
|
|
|
|
* assuming that the compiler will also arrange it to the same offsets as the
|
|
|
|
* base class.
|
2013-05-08 14:25:16 +04:00
|
|
|
*/
|
2015-11-06 02:13:09 +03:00
|
|
|
struct BlkActionState {
|
2013-05-13 12:43:43 +04:00
|
|
|
TransactionAction *action;
|
2015-11-06 02:13:09 +03:00
|
|
|
const BlkActionOps *ops;
|
2015-11-06 02:13:18 +03:00
|
|
|
BlockJobTxn *block_job_txn;
|
|
|
|
TransactionProperties *txn_props;
|
2015-11-06 02:13:09 +03:00
|
|
|
QSIMPLEQ_ENTRY(BlkActionState) entry;
|
2013-05-08 14:25:16 +04:00
|
|
|
};
|
|
|
|
|
2013-09-11 10:04:34 +04:00
|
|
|
/* internal snapshot private data */
|
|
|
|
typedef struct InternalSnapshotState {
|
2015-11-06 02:13:09 +03:00
|
|
|
BlkActionState common;
|
2013-09-11 10:04:34 +04:00
|
|
|
BlockDriverState *bs;
|
|
|
|
QEMUSnapshotInfo sn;
|
2015-10-23 06:08:13 +03:00
|
|
|
bool created;
|
2013-09-11 10:04:34 +04:00
|
|
|
} InternalSnapshotState;
|
|
|
|
|
2015-11-06 02:13:18 +03:00
|
|
|
|
|
|
|
static int action_check_completion_mode(BlkActionState *s, Error **errp)
|
|
|
|
{
|
|
|
|
if (s->txn_props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Action '%s' does not support Transaction property "
|
|
|
|
"completion-mode = %s",
|
2017-08-24 11:46:08 +03:00
|
|
|
TransactionActionKind_str(s->action->type),
|
|
|
|
ActionCompletionMode_str(s->txn_props->completion_mode));
|
2015-11-06 02:13:18 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void internal_snapshot_prepare(BlkActionState *common,
|
2013-09-11 10:04:34 +04:00
|
|
|
Error **errp)
|
|
|
|
{
|
2014-04-25 18:50:34 +04:00
|
|
|
Error *local_err = NULL;
|
2013-09-11 10:04:34 +04:00
|
|
|
const char *device;
|
|
|
|
const char *name;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
QEMUSnapshotInfo old_sn, *sn;
|
|
|
|
bool ret;
|
|
|
|
qemu_timeval tv;
|
|
|
|
BlockdevSnapshotInternal *internal;
|
|
|
|
InternalSnapshotState *state;
|
2017-12-06 17:45:46 +03:00
|
|
|
AioContext *aio_context;
|
2013-09-11 10:04:34 +04:00
|
|
|
int ret1;
|
|
|
|
|
2015-10-27 01:34:54 +03:00
|
|
|
g_assert(common->action->type ==
|
2013-09-11 10:04:34 +04:00
|
|
|
TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC);
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
internal = common->action->u.blockdev_snapshot_internal_sync.data;
|
2013-09-11 10:04:34 +04:00
|
|
|
state = DO_UPCAST(InternalSnapshotState, common, common);
|
|
|
|
|
|
|
|
/* 1. parse input */
|
|
|
|
device = internal->device;
|
|
|
|
name = internal->name;
|
|
|
|
|
|
|
|
/* 2. check for validation */
|
2015-11-06 02:13:18 +03:00
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
bs = qmp_get_root_bs(device, errp);
|
|
|
|
if (!bs) {
|
2013-09-11 10:04:34 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 17:45:46 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(aio_context);
|
2014-11-21 13:48:59 +03:00
|
|
|
|
2015-10-23 06:08:13 +03:00
|
|
|
state->bs = bs;
|
2017-12-06 17:45:46 +03:00
|
|
|
|
|
|
|
/* Paired with .clean() */
|
2015-10-23 06:08:13 +03:00
|
|
|
bdrv_drained_begin(bs);
|
|
|
|
|
2014-11-21 13:49:00 +03:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) {
|
2017-12-06 17:45:46 +03:00
|
|
|
goto out;
|
2014-11-21 13:49:00 +03:00
|
|
|
}
|
|
|
|
|
2013-09-11 10:04:34 +04:00
|
|
|
if (bdrv_is_read_only(bs)) {
|
2015-04-08 12:29:19 +03:00
|
|
|
error_setg(errp, "Device '%s' is read only", device);
|
2017-12-06 17:45:46 +03:00
|
|
|
goto out;
|
2013-09-11 10:04:34 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!bdrv_can_snapshot(bs)) {
|
2015-04-08 12:29:19 +03:00
|
|
|
error_setg(errp, "Block format '%s' used by device '%s' "
|
|
|
|
"does not support internal snapshots",
|
|
|
|
bs->drv->format_name, device);
|
2017-12-06 17:45:46 +03:00
|
|
|
goto out;
|
2013-09-11 10:04:34 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!strlen(name)) {
|
|
|
|
error_setg(errp, "Name is empty");
|
2017-12-06 17:45:46 +03:00
|
|
|
goto out;
|
2013-09-11 10:04:34 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check whether a snapshot with name exist */
|
2014-04-25 18:50:34 +04:00
|
|
|
ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn,
|
|
|
|
&local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2017-12-06 17:45:46 +03:00
|
|
|
goto out;
|
2013-09-11 10:04:34 +04:00
|
|
|
} else if (ret) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Snapshot with name '%s' already exists on device '%s'",
|
|
|
|
name, device);
|
2017-12-06 17:45:46 +03:00
|
|
|
goto out;
|
2013-09-11 10:04:34 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 3. take the snapshot */
|
|
|
|
sn = &state->sn;
|
|
|
|
pstrcpy(sn->name, sizeof(sn->name), name);
|
|
|
|
qemu_gettimeofday(&tv);
|
|
|
|
sn->date_sec = tv.tv_sec;
|
|
|
|
sn->date_nsec = tv.tv_usec * 1000;
|
|
|
|
sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
|
|
|
|
|
|
|
ret1 = bdrv_snapshot_create(bs, sn);
|
|
|
|
if (ret1 < 0) {
|
|
|
|
error_setg_errno(errp, -ret1,
|
|
|
|
"Failed to create snapshot '%s' on device '%s'",
|
|
|
|
name, device);
|
2017-12-06 17:45:46 +03:00
|
|
|
goto out;
|
2013-09-11 10:04:34 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 4. succeed, mark a snapshot is created */
|
2015-10-23 06:08:13 +03:00
|
|
|
state->created = true;
|
2017-12-06 17:45:46 +03:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2013-09-11 10:04:34 +04:00
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void internal_snapshot_abort(BlkActionState *common)
|
2013-09-11 10:04:34 +04:00
|
|
|
{
|
|
|
|
InternalSnapshotState *state =
|
|
|
|
DO_UPCAST(InternalSnapshotState, common, common);
|
|
|
|
BlockDriverState *bs = state->bs;
|
|
|
|
QEMUSnapshotInfo *sn = &state->sn;
|
2017-12-06 17:45:46 +03:00
|
|
|
AioContext *aio_context;
|
2013-09-11 10:04:34 +04:00
|
|
|
Error *local_error = NULL;
|
|
|
|
|
2015-10-23 06:08:13 +03:00
|
|
|
if (!state->created) {
|
2013-09-11 10:04:34 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 17:45:46 +03:00
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2013-09-11 10:04:34 +04:00
|
|
|
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
|
2015-12-18 18:35:14 +03:00
|
|
|
error_reportf_err(local_error,
|
|
|
|
"Failed to delete snapshot with id '%s' and "
|
|
|
|
"name '%s' on device '%s' in abort: ",
|
|
|
|
sn->id_str, sn->name,
|
|
|
|
bdrv_get_device_name(bs));
|
2013-09-11 10:04:34 +04:00
|
|
|
}
|
2017-12-06 17:45:46 +03:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2013-09-11 10:04:34 +04:00
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void internal_snapshot_clean(BlkActionState *common)
|
2014-11-21 13:48:59 +03:00
|
|
|
{
|
|
|
|
InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
|
|
|
|
common, common);
|
2017-12-06 17:45:46 +03:00
|
|
|
AioContext *aio_context;
|
2014-11-21 13:48:59 +03:00
|
|
|
|
2017-12-06 17:45:46 +03:00
|
|
|
if (!state->bs) {
|
|
|
|
return;
|
2014-11-21 13:48:59 +03:00
|
|
|
}
|
2017-12-06 17:45:46 +03:00
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
bdrv_drained_end(state->bs);
|
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2014-11-21 13:48:59 +03:00
|
|
|
}
|
|
|
|
|
2013-05-08 14:25:16 +04:00
|
|
|
/* external snapshot private data */
|
2013-06-24 19:13:15 +04:00
|
|
|
typedef struct ExternalSnapshotState {
|
2015-11-06 02:13:09 +03:00
|
|
|
BlkActionState common;
|
2012-02-29 00:54:06 +04:00
|
|
|
BlockDriverState *old_bs;
|
|
|
|
BlockDriverState *new_bs;
|
2017-03-02 17:26:18 +03:00
|
|
|
bool overlay_appended;
|
2013-06-24 19:13:15 +04:00
|
|
|
} ExternalSnapshotState;
|
2012-02-29 00:54:06 +04:00
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void external_snapshot_prepare(BlkActionState *common,
|
2013-05-08 14:25:12 +04:00
|
|
|
Error **errp)
|
|
|
|
{
|
2016-05-17 17:41:31 +03:00
|
|
|
int flags = 0;
|
2015-10-26 15:27:16 +03:00
|
|
|
QDict *options = NULL;
|
2013-05-08 14:25:12 +04:00
|
|
|
Error *local_err = NULL;
|
2015-10-26 15:27:16 +03:00
|
|
|
/* Device and node name of the image to generate the snapshot from */
|
2013-05-08 14:25:13 +04:00
|
|
|
const char *device;
|
2014-01-24 00:31:38 +04:00
|
|
|
const char *node_name;
|
2015-10-26 15:27:16 +03:00
|
|
|
/* Reference to the new image (for 'blockdev-snapshot') */
|
|
|
|
const char *snapshot_ref;
|
|
|
|
/* File name of the new image (for 'blockdev-snapshot-sync') */
|
2013-05-08 14:25:13 +04:00
|
|
|
const char *new_image_file;
|
2013-06-24 19:13:15 +04:00
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
2013-05-13 12:43:43 +04:00
|
|
|
TransactionAction *action = common->action;
|
2017-12-06 17:45:43 +03:00
|
|
|
AioContext *aio_context;
|
2013-05-08 14:25:12 +04:00
|
|
|
|
2015-10-26 15:27:16 +03:00
|
|
|
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
|
|
|
|
* purpose but a different set of parameters */
|
|
|
|
switch (action->type) {
|
|
|
|
case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT:
|
|
|
|
{
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
BlockdevSnapshot *s = action->u.blockdev_snapshot.data;
|
2015-10-26 15:27:16 +03:00
|
|
|
device = s->node;
|
|
|
|
node_name = s->node;
|
|
|
|
new_image_file = NULL;
|
|
|
|
snapshot_ref = s->overlay;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
|
|
|
|
{
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
|
2015-10-26 15:27:16 +03:00
|
|
|
device = s->has_device ? s->device : NULL;
|
|
|
|
node_name = s->has_node_name ? s->node_name : NULL;
|
|
|
|
new_image_file = s->snapshot_file;
|
|
|
|
snapshot_ref = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
2013-05-08 14:25:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* start processing */
|
2015-11-06 02:13:18 +03:00
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-26 15:27:16 +03:00
|
|
|
state->old_bs = bdrv_lookup_bs(device, node_name, errp);
|
|
|
|
if (!state->old_bs) {
|
2013-05-08 14:25:12 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 17:45:43 +03:00
|
|
|
aio_context = bdrv_get_aio_context(state->old_bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
/* Paired with .clean() */
|
2015-10-23 06:08:10 +03:00
|
|
|
bdrv_drained_begin(state->old_bs);
|
2014-11-21 13:48:59 +03:00
|
|
|
|
2013-06-24 19:13:15 +04:00
|
|
|
if (!bdrv_is_inserted(state->old_bs)) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2013-05-08 14:25:12 +04:00
|
|
|
}
|
|
|
|
|
2014-05-23 17:29:43 +04:00
|
|
|
if (bdrv_op_is_blocked(state->old_bs,
|
|
|
|
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2013-05-08 14:25:12 +04:00
|
|
|
}
|
|
|
|
|
2013-06-24 19:13:15 +04:00
|
|
|
if (!bdrv_is_read_only(state->old_bs)) {
|
|
|
|
if (bdrv_flush(state->old_bs)) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_IO_ERROR);
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2013-05-08 14:25:12 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-24 00:31:36 +04:00
|
|
|
if (!bdrv_is_first_non_filter(state->old_bs)) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_FEATURE_DISABLED, "snapshot");
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2013-10-02 16:33:48 +04:00
|
|
|
}
|
|
|
|
|
2015-10-26 15:27:16 +03:00
|
|
|
if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
|
2015-10-26 15:27:16 +03:00
|
|
|
const char *format = s->has_format ? s->format : "qcow2";
|
|
|
|
enum NewImageMode mode;
|
|
|
|
const char *snapshot_node_name =
|
|
|
|
s->has_snapshot_node_name ? s->snapshot_node_name : NULL;
|
2013-05-08 14:25:12 +04:00
|
|
|
|
2015-10-26 15:27:16 +03:00
|
|
|
if (node_name && !snapshot_node_name) {
|
|
|
|
error_setg(errp, "New snapshot node name missing");
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2013-05-08 14:25:12 +04:00
|
|
|
}
|
|
|
|
|
2015-10-26 15:27:16 +03:00
|
|
|
if (snapshot_node_name &&
|
|
|
|
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
|
|
|
|
error_setg(errp, "New snapshot node name already in use");
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2015-10-26 15:27:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
flags = state->old_bs->open_flags;
|
2017-07-18 03:34:21 +03:00
|
|
|
flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_COPY_ON_READ);
|
|
|
|
flags |= BDRV_O_NO_BACKING;
|
2015-10-26 15:27:16 +03:00
|
|
|
|
|
|
|
/* create new image w/backing file */
|
|
|
|
mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
|
|
|
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
2016-03-02 14:16:44 +03:00
|
|
|
int64_t size = bdrv_getlength(state->old_bs);
|
|
|
|
if (size < 0) {
|
|
|
|
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2016-03-02 14:16:44 +03:00
|
|
|
}
|
2015-10-26 15:27:16 +03:00
|
|
|
bdrv_img_create(new_image_file, format,
|
|
|
|
state->old_bs->filename,
|
|
|
|
state->old_bs->drv->format_name,
|
2017-04-21 15:27:01 +03:00
|
|
|
NULL, size, flags, false, &local_err);
|
2015-10-26 15:27:16 +03:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2015-10-26 15:27:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
options = qdict_new();
|
|
|
|
if (s->has_snapshot_node_name) {
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(options, "node-name", snapshot_node_name);
|
2015-10-26 15:27:16 +03:00
|
|
|
}
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(options, "driver", format);
|
2014-01-24 00:31:38 +04:00
|
|
|
}
|
|
|
|
|
2016-05-17 17:41:31 +03:00
|
|
|
state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags,
|
|
|
|
errp);
|
2014-02-18 21:33:05 +04:00
|
|
|
/* We will manually add the backing_hd field to the bs later */
|
2016-05-17 17:41:31 +03:00
|
|
|
if (!state->new_bs) {
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2015-10-26 15:27:16 +03:00
|
|
|
}
|
|
|
|
|
2016-03-22 20:38:44 +03:00
|
|
|
if (bdrv_has_blk(state->new_bs)) {
|
2016-09-21 15:56:09 +03:00
|
|
|
error_setg(errp, "The snapshot is already in use");
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2015-10-26 15:27:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
|
|
|
|
errp)) {
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2015-10-26 15:27:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state->new_bs->backing != NULL) {
|
|
|
|
error_setg(errp, "The snapshot already has a backing image");
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2015-11-03 13:32:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!state->new_bs->drv->supports_backing) {
|
|
|
|
error_setg(errp, "The snapshot does not support backing images");
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2017-02-20 14:46:42 +03:00
|
|
|
}
|
|
|
|
|
2017-12-06 17:45:43 +03:00
|
|
|
bdrv_set_aio_context(state->new_bs, aio_context);
|
2017-04-07 09:54:09 +03:00
|
|
|
|
2017-02-20 14:46:42 +03:00
|
|
|
/* This removes our old bs and adds the new bs. This is an operation that
|
|
|
|
* can fail, so we need to do it in .prepare; undoing it for abort is
|
|
|
|
* always possible. */
|
|
|
|
bdrv_ref(state->new_bs);
|
|
|
|
bdrv_append(state->new_bs, state->old_bs, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2017-12-06 17:45:43 +03:00
|
|
|
goto out;
|
2013-05-08 14:25:12 +04:00
|
|
|
}
|
2017-03-02 17:26:18 +03:00
|
|
|
state->overlay_appended = true;
|
2017-12-06 17:45:43 +03:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2013-05-08 14:25:12 +04:00
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void external_snapshot_commit(BlkActionState *common)
|
2013-05-08 14:25:14 +04:00
|
|
|
{
|
2013-06-24 19:13:15 +04:00
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
2017-12-06 17:45:43 +03:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->old_bs);
|
|
|
|
aio_context_acquire(aio_context);
|
2013-05-08 14:25:16 +04:00
|
|
|
|
2013-05-08 14:25:14 +04: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 */
|
2017-06-05 15:38:50 +03:00
|
|
|
if (!atomic_read(&state->old_bs->copy_on_read)) {
|
2016-02-29 15:12:26 +03:00
|
|
|
bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR,
|
|
|
|
NULL);
|
|
|
|
}
|
2017-12-06 17:45:43 +03:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2013-05-08 14:25:14 +04:00
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void external_snapshot_abort(BlkActionState *common)
|
2013-05-08 14:25:15 +04:00
|
|
|
{
|
2013-06-24 19:13:15 +04:00
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
|
|
|
if (state->new_bs) {
|
2017-03-02 17:26:18 +03:00
|
|
|
if (state->overlay_appended) {
|
2017-12-06 17:45:43 +03:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->old_bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2017-06-07 16:55:22 +03:00
|
|
|
bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd()
|
|
|
|
close state->old_bs; we need it */
|
|
|
|
bdrv_set_backing_hd(state->new_bs, NULL, &error_abort);
|
2017-03-06 18:20:51 +03:00
|
|
|
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
|
2017-06-07 16:55:22 +03:00
|
|
|
bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */
|
2017-12-06 17:45:43 +03:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2017-02-20 14:46:42 +03:00
|
|
|
}
|
2013-05-08 14:25:15 +04:00
|
|
|
}
|
2015-10-23 06:08:10 +03:00
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void external_snapshot_clean(BlkActionState *common)
|
2015-10-23 06:08:10 +03:00
|
|
|
{
|
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
2017-12-06 17:45:43 +03:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
if (!state->old_bs) {
|
|
|
|
return;
|
2014-11-21 13:48:59 +03:00
|
|
|
}
|
2017-12-06 17:45:43 +03:00
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->old_bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
bdrv_drained_end(state->old_bs);
|
|
|
|
bdrv_unref(state->new_bs);
|
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2013-05-08 14:25:15 +04: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 19:13:17 +04:00
|
|
|
typedef struct DriveBackupState {
|
2015-11-06 02:13:09 +03:00
|
|
|
BlkActionState common;
|
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 19:13:17 +04:00
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockJob *job;
|
|
|
|
} DriveBackupState;
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
2016-07-22 11:17:50 +03:00
|
|
|
Error **errp);
|
2015-11-06 02:13:17 +03:00
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void drive_backup_prepare(BlkActionState *common, Error **errp)
|
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 19:13:17 +04:00
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
2016-06-23 15:20:24 +03:00
|
|
|
BlockDriverState *bs;
|
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 19:13:17 +04:00
|
|
|
DriveBackup *backup;
|
2017-12-06 17:45:44 +03:00
|
|
|
AioContext *aio_context;
|
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 19:13:17 +04:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
2015-10-27 01:34:54 +03:00
|
|
|
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
backup = common->action->u.drive_backup.data;
|
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 19:13:17 +04:00
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
bs = qmp_get_root_bs(backup->device, errp);
|
|
|
|
if (!bs) {
|
2015-10-23 06:08:11 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 17:45:44 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
/* Paired with .clean() */
|
2016-06-23 15:20:24 +03:00
|
|
|
bdrv_drained_begin(bs);
|
2017-12-06 17:45:44 +03:00
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
state->bs = bs;
|
2014-11-21 13:48:59 +03:00
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
state->job = do_drive_backup(backup, common->block_job_txn, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
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 19:13:17 +04:00
|
|
|
error_propagate(errp, local_err);
|
2017-12-06 17:45:44 +03:00
|
|
|
goto out;
|
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 19:13:17 +04:00
|
|
|
}
|
2017-12-06 17:45:44 +03:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2016-11-08 09:50:38 +03: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 19:13:17 +04:00
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
static void drive_backup_commit(BlkActionState *common)
|
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
2017-12-06 17:45:44 +03:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
assert(state->job);
|
|
|
|
block_job_start(state->job);
|
2017-12-06 17:45:44 +03:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
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 19:13:17 +04:00
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void drive_backup_abort(BlkActionState *common)
|
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 19:13:17 +04:00
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
if (state->job) {
|
2017-12-06 17:45:44 +03:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
block_job_cancel_sync(state->job);
|
2017-12-06 17:45:44 +03:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
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 19:13:17 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void drive_backup_clean(BlkActionState *common)
|
2014-11-21 13:48:59 +03:00
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
2017-12-06 17:45:44 +03:00
|
|
|
AioContext *aio_context;
|
2014-11-21 13:48:59 +03:00
|
|
|
|
2017-12-06 17:45:44 +03:00
|
|
|
if (!state->bs) {
|
|
|
|
return;
|
2014-11-21 13:48:59 +03:00
|
|
|
}
|
2017-12-06 17:45:44 +03:00
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
bdrv_drained_end(state->bs);
|
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2014-11-21 13:48:59 +03:00
|
|
|
}
|
|
|
|
|
2014-12-18 13:37:06 +03:00
|
|
|
typedef struct BlockdevBackupState {
|
2015-11-06 02:13:09 +03:00
|
|
|
BlkActionState common;
|
2014-12-18 13:37:06 +03:00
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockJob *job;
|
|
|
|
} BlockdevBackupState;
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
|
|
|
Error **errp);
|
2015-11-06 02:13:17 +03:00
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
|
2014-12-18 13:37:06 +03:00
|
|
|
{
|
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
|
|
|
BlockdevBackup *backup;
|
2016-06-23 15:20:24 +03:00
|
|
|
BlockDriverState *bs, *target;
|
2017-12-06 17:45:45 +03:00
|
|
|
AioContext *aio_context;
|
2014-12-18 13:37:06 +03:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
2015-10-27 01:34:54 +03:00
|
|
|
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
backup = common->action->u.blockdev_backup.data;
|
2014-12-18 13:37:06 +03:00
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
bs = qmp_get_root_bs(backup->device, errp);
|
|
|
|
if (!bs) {
|
2015-10-23 06:08:12 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-02 20:22:04 +03:00
|
|
|
target = bdrv_lookup_bs(backup->target, backup->target, errp);
|
2015-10-19 18:53:29 +03:00
|
|
|
if (!target) {
|
2014-12-18 13:37:06 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 17:45:45 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
if (aio_context != bdrv_get_aio_context(target)) {
|
2014-12-18 13:37:06 +03:00
|
|
|
error_setg(errp, "Backup between two IO threads is not implemented");
|
|
|
|
return;
|
|
|
|
}
|
2017-12-06 17:45:45 +03:00
|
|
|
aio_context_acquire(aio_context);
|
2016-06-23 15:20:24 +03:00
|
|
|
state->bs = bs;
|
2017-12-06 17:45:45 +03:00
|
|
|
|
|
|
|
/* Paired with .clean() */
|
2015-10-23 06:08:12 +03:00
|
|
|
bdrv_drained_begin(state->bs);
|
2014-12-18 13:37:06 +03:00
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err);
|
2014-12-18 13:37:06 +03:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2017-12-06 17:45:45 +03:00
|
|
|
goto out;
|
2014-12-18 13:37:06 +03:00
|
|
|
}
|
2017-12-06 17:45:45 +03:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2016-11-08 09:50:38 +03:00
|
|
|
}
|
2014-12-18 13:37:06 +03:00
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
static void blockdev_backup_commit(BlkActionState *common)
|
|
|
|
{
|
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
2017-12-06 17:45:45 +03:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
assert(state->job);
|
|
|
|
block_job_start(state->job);
|
2017-12-06 17:45:45 +03:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2014-12-18 13:37:06 +03:00
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void blockdev_backup_abort(BlkActionState *common)
|
2014-12-18 13:37:06 +03:00
|
|
|
{
|
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
if (state->job) {
|
2017-12-06 17:45:45 +03:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
block_job_cancel_sync(state->job);
|
2017-12-06 17:45:45 +03:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2014-12-18 13:37:06 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void blockdev_backup_clean(BlkActionState *common)
|
2014-12-18 13:37:06 +03:00
|
|
|
{
|
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
2017-12-06 17:45:45 +03:00
|
|
|
AioContext *aio_context;
|
2014-12-18 13:37:06 +03:00
|
|
|
|
2017-12-06 17:45:45 +03:00
|
|
|
if (!state->bs) {
|
|
|
|
return;
|
2014-12-18 13:37:06 +03:00
|
|
|
}
|
2017-12-06 17:45:45 +03:00
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
bdrv_drained_end(state->bs);
|
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2014-12-18 13:37:06 +03:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:16:54 +03:00
|
|
|
typedef struct BlockDirtyBitmapState {
|
2015-11-06 02:13:09 +03:00
|
|
|
BlkActionState common;
|
2015-11-09 13:16:54 +03:00
|
|
|
BdrvDirtyBitmap *bitmap;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
HBitmap *backup;
|
|
|
|
bool prepared;
|
|
|
|
} BlockDirtyBitmapState;
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void block_dirty_bitmap_add_prepare(BlkActionState *common,
|
2015-11-09 13:16:54 +03:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
BlockDirtyBitmapAdd *action;
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
2015-11-06 02:13:18 +03:00
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
action = common->action->u.block_dirty_bitmap_add.data;
|
2015-11-09 13:16:54 +03:00
|
|
|
/* AIO context taken and released within qmp_block_dirty_bitmap_add */
|
|
|
|
qmp_block_dirty_bitmap_add(action->node, action->name,
|
|
|
|
action->has_granularity, action->granularity,
|
2017-06-28 15:05:23 +03:00
|
|
|
action->has_persistent, action->persistent,
|
2017-06-28 15:05:24 +03:00
|
|
|
action->has_autoload, action->autoload,
|
2015-11-09 13:16:54 +03:00
|
|
|
&local_err);
|
|
|
|
|
|
|
|
if (!local_err) {
|
|
|
|
state->prepared = true;
|
|
|
|
} else {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void block_dirty_bitmap_add_abort(BlkActionState *common)
|
2015-11-09 13:16:54 +03:00
|
|
|
{
|
|
|
|
BlockDirtyBitmapAdd *action;
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
action = common->action->u.block_dirty_bitmap_add.data;
|
2015-11-09 13:16:54 +03:00
|
|
|
/* Should not be able to fail: IF the bitmap was added via .prepare(),
|
|
|
|
* then the node reference and bitmap name must have been valid.
|
|
|
|
*/
|
|
|
|
if (state->prepared) {
|
|
|
|
qmp_block_dirty_bitmap_remove(action->node, action->name, &error_abort);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
2015-11-09 13:16:54 +03:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
BlockDirtyBitmap *action;
|
|
|
|
|
2015-11-06 02:13:18 +03:00
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type(). But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:
| struct q_obj_ImageInfoSpecificQCow2_wrapper {
| ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
| ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
| ImageInfoSpecificKind type;
| union { /* union tag is @type */
| void *data;
|- ImageInfoSpecificQCow2 *qcow2;
|- ImageInfoSpecificVmdk *vmdk;
|+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
| } u;
| };
Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation). Using the implicit type
also lets us get rid of the simple_union_type() hack.
Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access. The generated
qapi-visit.c code is also affected by the layout change:
|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
| }
| switch (obj->type) {
| case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
| break;
| case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
| break;
| default:
| abort();
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:37 +03:00
|
|
|
action = common->action->u.block_dirty_bitmap_clear.data;
|
2015-11-09 13:16:54 +03:00
|
|
|
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
|
|
|
action->name,
|
|
|
|
&state->bs,
|
|
|
|
errp);
|
|
|
|
if (!state->bitmap) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_dirty_bitmap_frozen(state->bitmap)) {
|
|
|
|
error_setg(errp, "Cannot modify a frozen bitmap");
|
|
|
|
return;
|
|
|
|
} else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) {
|
|
|
|
error_setg(errp, "Cannot clear a disabled bitmap");
|
|
|
|
return;
|
2017-06-28 15:05:10 +03:00
|
|
|
} else if (bdrv_dirty_bitmap_readonly(state->bitmap)) {
|
|
|
|
error_setg(errp, "Cannot clear a readonly bitmap");
|
|
|
|
return;
|
2015-11-09 13:16:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
|
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void block_dirty_bitmap_clear_abort(BlkActionState *common)
|
2015-11-09 13:16:54 +03:00
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
2017-03-16 00:28:11 +03:00
|
|
|
if (state->backup) {
|
|
|
|
bdrv_undo_clear_dirty_bitmap(state->bitmap, state->backup);
|
|
|
|
}
|
2015-11-09 13:16:54 +03:00
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void block_dirty_bitmap_clear_commit(BlkActionState *common)
|
2015-11-09 13:16:54 +03:00
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
hbitmap_free(state->backup);
|
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void abort_prepare(BlkActionState *common, Error **errp)
|
2013-06-24 19:13:18 +04:00
|
|
|
{
|
|
|
|
error_setg(errp, "Transaction aborted using Abort action");
|
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static void abort_commit(BlkActionState *common)
|
2013-06-24 19:13:18 +04:00
|
|
|
{
|
2013-07-25 20:21:28 +04:00
|
|
|
g_assert_not_reached(); /* this action never succeeds */
|
2013-06-24 19:13:18 +04:00
|
|
|
}
|
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
static const BlkActionOps actions[] = {
|
2015-10-26 15:27:16 +03:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = {
|
|
|
|
.instance_size = sizeof(ExternalSnapshotState),
|
|
|
|
.prepare = external_snapshot_prepare,
|
|
|
|
.commit = external_snapshot_commit,
|
|
|
|
.abort = external_snapshot_abort,
|
2015-11-13 16:00:24 +03:00
|
|
|
.clean = external_snapshot_clean,
|
2015-10-26 15:27:16 +03:00
|
|
|
},
|
2013-05-13 12:43:43 +04:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
|
2013-06-24 19:13:15 +04:00
|
|
|
.instance_size = sizeof(ExternalSnapshotState),
|
2013-05-08 14:25:16 +04:00
|
|
|
.prepare = external_snapshot_prepare,
|
|
|
|
.commit = external_snapshot_commit,
|
|
|
|
.abort = external_snapshot_abort,
|
2015-10-23 06:08:10 +03:00
|
|
|
.clean = external_snapshot_clean,
|
2013-05-08 14:25:16 +04: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 19:13:17 +04:00
|
|
|
[TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = {
|
|
|
|
.instance_size = sizeof(DriveBackupState),
|
|
|
|
.prepare = drive_backup_prepare,
|
2016-11-08 09:50:38 +03:00
|
|
|
.commit = drive_backup_commit,
|
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 19:13:17 +04:00
|
|
|
.abort = drive_backup_abort,
|
2014-11-21 13:48:59 +03:00
|
|
|
.clean = drive_backup_clean,
|
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 19:13:17 +04:00
|
|
|
},
|
2014-12-18 13:37:06 +03:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
|
|
|
|
.instance_size = sizeof(BlockdevBackupState),
|
|
|
|
.prepare = blockdev_backup_prepare,
|
2016-11-08 09:50:38 +03:00
|
|
|
.commit = blockdev_backup_commit,
|
2014-12-18 13:37:06 +03:00
|
|
|
.abort = blockdev_backup_abort,
|
|
|
|
.clean = blockdev_backup_clean,
|
|
|
|
},
|
2013-06-24 19:13:18 +04:00
|
|
|
[TRANSACTION_ACTION_KIND_ABORT] = {
|
2015-11-06 02:13:09 +03:00
|
|
|
.instance_size = sizeof(BlkActionState),
|
2013-06-24 19:13:18 +04:00
|
|
|
.prepare = abort_prepare,
|
|
|
|
.commit = abort_commit,
|
|
|
|
},
|
2013-09-11 10:04:34 +04:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
|
|
|
|
.instance_size = sizeof(InternalSnapshotState),
|
|
|
|
.prepare = internal_snapshot_prepare,
|
|
|
|
.abort = internal_snapshot_abort,
|
2014-11-21 13:48:59 +03:00
|
|
|
.clean = internal_snapshot_clean,
|
2013-09-11 10:04:34 +04:00
|
|
|
},
|
2015-11-09 13:16:54 +03:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD] = {
|
|
|
|
.instance_size = sizeof(BlockDirtyBitmapState),
|
|
|
|
.prepare = block_dirty_bitmap_add_prepare,
|
|
|
|
.abort = block_dirty_bitmap_add_abort,
|
|
|
|
},
|
|
|
|
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = {
|
|
|
|
.instance_size = sizeof(BlockDirtyBitmapState),
|
|
|
|
.prepare = block_dirty_bitmap_clear_prepare,
|
|
|
|
.commit = block_dirty_bitmap_clear_commit,
|
|
|
|
.abort = block_dirty_bitmap_clear_abort,
|
|
|
|
}
|
2013-05-08 14:25:16 +04:00
|
|
|
};
|
|
|
|
|
2015-11-06 02:13:18 +03:00
|
|
|
/**
|
|
|
|
* Allocate a TransactionProperties structure if necessary, and fill
|
|
|
|
* that structure with desired defaults if they are unset.
|
|
|
|
*/
|
|
|
|
static TransactionProperties *get_transaction_properties(
|
|
|
|
TransactionProperties *props)
|
|
|
|
{
|
|
|
|
if (!props) {
|
|
|
|
props = g_new0(TransactionProperties, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!props->has_completion_mode) {
|
|
|
|
props->has_completion_mode = true;
|
|
|
|
props->completion_mode = ACTION_COMPLETION_MODE_INDIVIDUAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return props;
|
|
|
|
}
|
|
|
|
|
2012-02-29 00:54:06 +04:00
|
|
|
/*
|
2014-11-21 13:48:57 +03:00
|
|
|
* 'Atomic' group operations. The operations are performed as a set, and if
|
|
|
|
* any fail then we roll back all operations in the group.
|
2012-02-29 00:54:06 +04:00
|
|
|
*/
|
2015-11-06 02:13:18 +03:00
|
|
|
void qmp_transaction(TransactionActionList *dev_list,
|
|
|
|
bool has_props,
|
|
|
|
struct TransactionProperties *props,
|
|
|
|
Error **errp)
|
2012-02-29 00:54:06 +04:00
|
|
|
{
|
2013-05-13 12:43:43 +04:00
|
|
|
TransactionActionList *dev_entry = dev_list;
|
2015-11-06 02:13:18 +03:00
|
|
|
BlockJobTxn *block_job_txn = NULL;
|
2015-11-06 02:13:09 +03:00
|
|
|
BlkActionState *state, *next;
|
2012-11-30 16:52:07 +04:00
|
|
|
Error *local_err = NULL;
|
2012-02-29 00:54:06 +04:00
|
|
|
|
2015-11-06 02:13:09 +03:00
|
|
|
QSIMPLEQ_HEAD(snap_bdrv_states, BlkActionState) snap_bdrv_states;
|
2012-02-29 00:54:06 +04:00
|
|
|
QSIMPLEQ_INIT(&snap_bdrv_states);
|
|
|
|
|
2015-11-06 02:13:18 +03:00
|
|
|
/* Does this transaction get canceled as a group on failure?
|
|
|
|
* If not, we don't really need to make a BlockJobTxn.
|
|
|
|
*/
|
|
|
|
props = get_transaction_properties(props);
|
|
|
|
if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
|
|
|
|
block_job_txn = block_job_txn_new();
|
|
|
|
}
|
|
|
|
|
2014-11-21 13:48:57 +03:00
|
|
|
/* drain all i/o before any operations */
|
2012-02-29 00:54:06 +04:00
|
|
|
bdrv_drain_all();
|
|
|
|
|
2014-11-21 13:48:57 +03:00
|
|
|
/* We don't do anything in this loop that commits us to the operations */
|
2012-02-29 00:54:06 +04:00
|
|
|
while (NULL != dev_entry) {
|
2013-05-13 12:43:43 +04:00
|
|
|
TransactionAction *dev_info = NULL;
|
2015-11-06 02:13:09 +03:00
|
|
|
const BlkActionOps *ops;
|
2012-03-06 21:55:57 +04:00
|
|
|
|
2012-02-29 00:54:06 +04:00
|
|
|
dev_info = dev_entry->value;
|
|
|
|
dev_entry = dev_entry->next;
|
|
|
|
|
2015-10-27 01:34:54 +03:00
|
|
|
assert(dev_info->type < ARRAY_SIZE(actions));
|
2013-05-08 14:25:16 +04:00
|
|
|
|
2015-10-27 01:34:54 +03:00
|
|
|
ops = &actions[dev_info->type];
|
2013-09-12 16:57:27 +04:00
|
|
|
assert(ops->instance_size > 0);
|
|
|
|
|
2013-06-24 19:13:15 +04:00
|
|
|
state = g_malloc0(ops->instance_size);
|
|
|
|
state->ops = ops;
|
|
|
|
state->action = dev_info;
|
2015-11-06 02:13:18 +03:00
|
|
|
state->block_job_txn = block_job_txn;
|
|
|
|
state->txn_props = props;
|
2013-06-24 19:13:15 +04:00
|
|
|
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
|
2012-02-29 00:54:06 +04:00
|
|
|
|
2013-06-24 19:13:15 +04:00
|
|
|
state->ops->prepare(state, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2013-05-08 14:25:16 +04:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
goto delete_and_fail;
|
2012-02-29 00:54:06 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-24 19:13:15 +04:00
|
|
|
QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) {
|
2013-06-24 19:13:16 +04:00
|
|
|
if (state->ops->commit) {
|
|
|
|
state->ops->commit(state);
|
|
|
|
}
|
2012-02-29 00:54:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* success */
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
delete_and_fail:
|
2014-11-21 13:48:57 +03:00
|
|
|
/* failure, and it is all-or-none; roll back all operations */
|
2013-06-24 19:13:15 +04:00
|
|
|
QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) {
|
|
|
|
if (state->ops->abort) {
|
|
|
|
state->ops->abort(state);
|
2013-05-08 14:25:16 +04:00
|
|
|
}
|
2012-02-29 00:54:06 +04:00
|
|
|
}
|
|
|
|
exit:
|
2013-06-24 19:13:15 +04:00
|
|
|
QSIMPLEQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) {
|
|
|
|
if (state->ops->clean) {
|
|
|
|
state->ops->clean(state);
|
2013-05-08 14:25:16 +04:00
|
|
|
}
|
2013-06-24 19:13:15 +04:00
|
|
|
g_free(state);
|
2012-02-29 00:54:06 +04:00
|
|
|
}
|
2015-11-06 02:13:18 +03:00
|
|
|
if (!has_props) {
|
|
|
|
qapi_free_TransactionProperties(props);
|
|
|
|
}
|
|
|
|
block_job_txn_unref(block_job_txn);
|
2012-02-29 00:54:06 +04:00
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:46 +03:00
|
|
|
void qmp_eject(bool has_device, const char *device,
|
|
|
|
bool has_id, const char *id,
|
|
|
|
bool has_force, bool force, Error **errp)
|
2010-06-02 20:48:27 +04:00
|
|
|
{
|
2015-10-26 23:39:12 +03:00
|
|
|
Error *local_err = NULL;
|
2016-05-19 00:53:40 +03:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!has_force) {
|
|
|
|
force = false;
|
|
|
|
}
|
2010-06-02 20:48:27 +04:00
|
|
|
|
2016-09-20 14:38:46 +03:00
|
|
|
rc = do_open_tray(has_device ? device : NULL,
|
|
|
|
has_id ? id : NULL,
|
|
|
|
force, &local_err);
|
2016-06-06 21:15:22 +03:00
|
|
|
if (rc && rc != -ENOSYS) {
|
2015-10-26 23:39:12 +03:00
|
|
|
error_propagate(errp, local_err);
|
2011-12-07 22:02:36 +04:00
|
|
|
return;
|
2011-12-07 21:47:23 +04:00
|
|
|
}
|
2016-06-06 21:15:22 +03:00
|
|
|
error_free(local_err);
|
2016-05-19 00:53:40 +03:00
|
|
|
|
2017-11-11 01:43:01 +03:00
|
|
|
blockdev_remove_medium(has_device, device, has_id, id, errp);
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
|
2014-01-24 00:31:35 +04:00
|
|
|
void qmp_block_passwd(bool has_device, const char *device,
|
|
|
|
bool has_node_name, const char *node_name,
|
|
|
|
const char *password, Error **errp)
|
2010-06-02 20:48:27 +04:00
|
|
|
{
|
2017-06-23 19:24:16 +03:00
|
|
|
error_setg(errp,
|
|
|
|
"Setting block passwords directly is no longer supported");
|
2010-06-02 20:48:27 +04:00
|
|
|
}
|
|
|
|
|
2016-06-06 21:15:22 +03:00
|
|
|
/*
|
|
|
|
* Attempt to open the tray of @device.
|
|
|
|
* If @force, ignore its tray lock.
|
|
|
|
* Else, if the tray is locked, don't open it, but ask the guest to open it.
|
|
|
|
* On error, store an error through @errp and return -errno.
|
|
|
|
* If @device does not exist, return -ENODEV.
|
|
|
|
* If it has no removable media, return -ENOTSUP.
|
|
|
|
* If it has no tray, return -ENOSYS.
|
|
|
|
* If the guest was asked to open the tray, return -EINPROGRESS.
|
|
|
|
* Else, return 0.
|
2016-05-19 00:53:40 +03:00
|
|
|
*/
|
2016-09-20 14:38:43 +03:00
|
|
|
static int do_open_tray(const char *blk_name, const char *qdev_id,
|
|
|
|
bool force, Error **errp)
|
2015-10-26 23:39:08 +03:00
|
|
|
{
|
|
|
|
BlockBackend *blk;
|
2016-09-20 14:38:43 +03:00
|
|
|
const char *device = qdev_id ?: blk_name;
|
2015-10-26 23:39:08 +03:00
|
|
|
bool locked;
|
|
|
|
|
2016-09-20 14:38:43 +03:00
|
|
|
blk = qmp_get_blk(blk_name, qdev_id, errp);
|
2015-10-26 23:39:08 +03:00
|
|
|
if (!blk) {
|
2016-05-19 00:53:40 +03:00
|
|
|
return -ENODEV;
|
2015-10-26 23:39:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!blk_dev_has_removable_media(blk)) {
|
|
|
|
error_setg(errp, "Device '%s' is not removable", device);
|
2016-05-19 00:53:40 +03:00
|
|
|
return -ENOTSUP;
|
2015-10-26 23:39:08 +03:00
|
|
|
}
|
|
|
|
|
2016-01-29 22:49:11 +03:00
|
|
|
if (!blk_dev_has_tray(blk)) {
|
2016-06-06 21:15:22 +03:00
|
|
|
error_setg(errp, "Device '%s' does not have a tray", device);
|
|
|
|
return -ENOSYS;
|
2016-01-29 22:49:11 +03:00
|
|
|
}
|
|
|
|
|
2015-10-26 23:39:08 +03:00
|
|
|
if (blk_dev_is_tray_open(blk)) {
|
2016-05-19 00:53:40 +03:00
|
|
|
return 0;
|
2015-10-26 23:39:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
locked = blk_dev_is_medium_locked(blk);
|
|
|
|
if (locked) {
|
|
|
|
blk_dev_eject_request(blk, force);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!locked || force) {
|
2017-01-24 16:21:41 +03:00
|
|
|
blk_dev_change_media_cb(blk, false, &error_abort);
|
2015-10-26 23:39:08 +03:00
|
|
|
}
|
2016-05-19 00:53:40 +03:00
|
|
|
|
|
|
|
if (locked && !force) {
|
2016-06-06 21:15:22 +03:00
|
|
|
error_setg(errp, "Device '%s' is locked and force was not specified, "
|
|
|
|
"wait for tray to open and try again", device);
|
|
|
|
return -EINPROGRESS;
|
2016-05-19 00:53:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:43 +03:00
|
|
|
void qmp_blockdev_open_tray(bool has_device, const char *device,
|
|
|
|
bool has_id, const char *id,
|
|
|
|
bool has_force, bool force,
|
2016-05-19 00:53:40 +03:00
|
|
|
Error **errp)
|
|
|
|
{
|
2016-06-06 21:15:22 +03:00
|
|
|
Error *local_err = NULL;
|
|
|
|
int rc;
|
|
|
|
|
2016-05-19 00:53:40 +03:00
|
|
|
if (!has_force) {
|
|
|
|
force = false;
|
|
|
|
}
|
2016-09-20 14:38:43 +03:00
|
|
|
rc = do_open_tray(has_device ? device : NULL,
|
|
|
|
has_id ? id : NULL,
|
|
|
|
force, &local_err);
|
2016-06-06 21:15:22 +03:00
|
|
|
if (rc && rc != -ENOSYS && rc != -EINPROGRESS) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
error_free(local_err);
|
2015-10-26 23:39:08 +03:00
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:43 +03:00
|
|
|
void qmp_blockdev_close_tray(bool has_device, const char *device,
|
|
|
|
bool has_id, const char *id,
|
|
|
|
Error **errp)
|
2015-10-26 23:39:09 +03:00
|
|
|
{
|
|
|
|
BlockBackend *blk;
|
2017-01-24 16:21:41 +03:00
|
|
|
Error *local_err = NULL;
|
2015-10-26 23:39:09 +03:00
|
|
|
|
2016-09-20 14:38:43 +03:00
|
|
|
device = has_device ? device : NULL;
|
|
|
|
id = has_id ? id : NULL;
|
|
|
|
|
|
|
|
blk = qmp_get_blk(device, id, errp);
|
2015-10-26 23:39:09 +03:00
|
|
|
if (!blk) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!blk_dev_has_removable_media(blk)) {
|
2016-09-20 14:38:43 +03:00
|
|
|
error_setg(errp, "Device '%s' is not removable", device ?: id);
|
2015-10-26 23:39:09 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-29 22:49:11 +03:00
|
|
|
if (!blk_dev_has_tray(blk)) {
|
|
|
|
/* Ignore this command on tray-less devices */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-26 23:39:09 +03:00
|
|
|
if (!blk_dev_is_tray_open(blk)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-24 16:21:41 +03:00
|
|
|
blk_dev_change_media_cb(blk, true, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
2015-10-26 23:39:09 +03:00
|
|
|
}
|
|
|
|
|
2017-11-11 01:43:01 +03:00
|
|
|
static void blockdev_remove_medium(bool has_device, const char *device,
|
|
|
|
bool has_id, const char *id, Error **errp)
|
2015-10-26 23:39:10 +03:00
|
|
|
{
|
|
|
|
BlockBackend *blk;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
AioContext *aio_context;
|
2016-09-20 14:38:45 +03:00
|
|
|
bool has_attached_device;
|
2015-10-26 23:39:10 +03:00
|
|
|
|
2016-09-20 14:38:45 +03:00
|
|
|
device = has_device ? device : NULL;
|
|
|
|
id = has_id ? id : NULL;
|
|
|
|
|
|
|
|
blk = qmp_get_blk(device, id, errp);
|
2015-10-26 23:39:10 +03:00
|
|
|
if (!blk) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For BBs without a device, we can exchange the BDS tree at will */
|
2016-09-20 14:38:45 +03:00
|
|
|
has_attached_device = blk_get_attached_dev(blk);
|
2015-10-26 23:39:10 +03:00
|
|
|
|
2016-09-20 14:38:45 +03:00
|
|
|
if (has_attached_device && !blk_dev_has_removable_media(blk)) {
|
|
|
|
error_setg(errp, "Device '%s' is not removable", device ?: id);
|
2015-10-26 23:39:10 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:45 +03:00
|
|
|
if (has_attached_device && blk_dev_has_tray(blk) &&
|
|
|
|
!blk_dev_is_tray_open(blk))
|
|
|
|
{
|
|
|
|
error_setg(errp, "Tray of device '%s' is not open", device ?: id);
|
2015-10-26 23:39:10 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bs = blk_bs(blk);
|
|
|
|
if (!bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
blk_remove_bs(blk);
|
|
|
|
|
2016-01-29 22:49:11 +03:00
|
|
|
if (!blk_dev_has_tray(blk)) {
|
|
|
|
/* For tray-less devices, blockdev-open-tray is a no-op (or may not be
|
|
|
|
* called at all); therefore, the medium needs to be ejected here.
|
|
|
|
* Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
|
|
|
|
* value passed here (i.e. false). */
|
2017-01-24 16:21:41 +03:00
|
|
|
blk_dev_change_media_cb(blk, false, &error_abort);
|
2016-01-29 22:49:11 +03:00
|
|
|
}
|
|
|
|
|
2015-10-26 23:39:10 +03:00
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
}
|
|
|
|
|
2017-11-11 01:43:02 +03:00
|
|
|
void qmp_blockdev_remove_medium(const char *id, Error **errp)
|
2017-11-11 01:43:01 +03:00
|
|
|
{
|
|
|
|
blockdev_remove_medium(false, NULL, true, id, errp);
|
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:44 +03:00
|
|
|
static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
|
2015-10-26 23:39:11 +03:00
|
|
|
BlockDriverState *bs, Error **errp)
|
|
|
|
{
|
2017-01-24 16:21:41 +03:00
|
|
|
Error *local_err = NULL;
|
2015-10-26 23:39:11 +03:00
|
|
|
bool has_device;
|
2017-01-13 21:02:32 +03:00
|
|
|
int ret;
|
2015-10-26 23:39:11 +03:00
|
|
|
|
|
|
|
/* For BBs without a device, we can exchange the BDS tree at will */
|
|
|
|
has_device = blk_get_attached_dev(blk);
|
|
|
|
|
|
|
|
if (has_device && !blk_dev_has_removable_media(blk)) {
|
2016-09-20 14:38:44 +03:00
|
|
|
error_setg(errp, "Device is not removable");
|
2015-10-26 23:39:11 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-29 22:49:11 +03:00
|
|
|
if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) {
|
2016-09-20 14:38:44 +03:00
|
|
|
error_setg(errp, "Tray of the device is not open");
|
2015-10-26 23:39:11 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blk_bs(blk)) {
|
2016-09-20 14:38:44 +03:00
|
|
|
error_setg(errp, "There already is a medium in the device");
|
2015-10-26 23:39:11 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-13 21:02:32 +03:00
|
|
|
ret = blk_insert_bs(blk, bs, errp);
|
|
|
|
if (ret < 0) {
|
|
|
|
return;
|
|
|
|
}
|
2015-10-26 23:39:11 +03:00
|
|
|
|
2016-01-29 22:49:11 +03:00
|
|
|
if (!blk_dev_has_tray(blk)) {
|
|
|
|
/* For tray-less devices, blockdev-close-tray is a no-op (or may not be
|
|
|
|
* called at all); therefore, the medium needs to be pushed into the
|
|
|
|
* slot here.
|
|
|
|
* Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
|
|
|
|
* value passed here (i.e. true). */
|
2017-01-24 16:21:41 +03:00
|
|
|
blk_dev_change_media_cb(blk, true, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
blk_remove_bs(blk);
|
|
|
|
return;
|
|
|
|
}
|
2016-01-29 22:49:11 +03:00
|
|
|
}
|
2015-10-26 23:39:11 +03:00
|
|
|
}
|
|
|
|
|
2017-11-11 01:43:01 +03:00
|
|
|
static void blockdev_insert_medium(bool has_device, const char *device,
|
|
|
|
bool has_id, const char *id,
|
|
|
|
const char *node_name, Error **errp)
|
2015-10-26 23:39:11 +03:00
|
|
|
{
|
2016-09-20 14:38:44 +03:00
|
|
|
BlockBackend *blk;
|
2015-10-26 23:39:11 +03:00
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2016-09-20 14:38:44 +03:00
|
|
|
blk = qmp_get_blk(has_device ? device : NULL,
|
|
|
|
has_id ? id : NULL,
|
|
|
|
errp);
|
|
|
|
if (!blk) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-26 23:39:11 +03:00
|
|
|
bs = bdrv_find_node(node_name);
|
|
|
|
if (!bs) {
|
|
|
|
error_setg(errp, "Node '%s' not found", node_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-22 20:38:44 +03:00
|
|
|
if (bdrv_has_blk(bs)) {
|
2016-09-21 15:56:09 +03:00
|
|
|
error_setg(errp, "Node '%s' is already in use", node_name);
|
2015-10-26 23:39:11 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:44 +03:00
|
|
|
qmp_blockdev_insert_anon_medium(blk, bs, errp);
|
2015-10-26 23:39:11 +03:00
|
|
|
}
|
|
|
|
|
2017-11-11 01:43:02 +03:00
|
|
|
void qmp_blockdev_insert_medium(const char *id, const char *node_name,
|
|
|
|
Error **errp)
|
2017-11-11 01:43:01 +03:00
|
|
|
{
|
|
|
|
blockdev_insert_medium(false, NULL, true, id, node_name, errp);
|
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:47 +03:00
|
|
|
void qmp_blockdev_change_medium(bool has_device, const char *device,
|
|
|
|
bool has_id, const char *id,
|
|
|
|
const char *filename,
|
2015-11-06 18:27:06 +03:00
|
|
|
bool has_format, const char *format,
|
2015-11-11 06:49:44 +03:00
|
|
|
bool has_read_only,
|
|
|
|
BlockdevChangeReadOnlyMode read_only,
|
2015-11-06 18:27:06 +03:00
|
|
|
Error **errp)
|
2015-10-26 23:39:13 +03:00
|
|
|
{
|
|
|
|
BlockBackend *blk;
|
|
|
|
BlockDriverState *medium_bs = NULL;
|
2016-05-17 17:41:31 +03:00
|
|
|
int bdrv_flags;
|
2016-09-12 20:08:31 +03:00
|
|
|
bool detect_zeroes;
|
2016-06-08 20:56:28 +03:00
|
|
|
int rc;
|
2015-10-26 23:39:13 +03:00
|
|
|
QDict *options = NULL;
|
|
|
|
Error *err = NULL;
|
|
|
|
|
2016-09-20 14:38:47 +03:00
|
|
|
blk = qmp_get_blk(has_device ? device : NULL,
|
|
|
|
has_id ? id : NULL,
|
|
|
|
errp);
|
2015-10-26 23:39:13 +03:00
|
|
|
if (!blk) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blk_bs(blk)) {
|
|
|
|
blk_update_root_state(blk);
|
|
|
|
}
|
|
|
|
|
|
|
|
bdrv_flags = blk_get_open_flags_from_root_state(blk);
|
2016-02-06 16:36:18 +03:00
|
|
|
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
|
|
|
|
BDRV_O_PROTOCOL);
|
2015-10-26 23:39:13 +03:00
|
|
|
|
2015-11-11 06:49:44 +03:00
|
|
|
if (!has_read_only) {
|
|
|
|
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (read_only) {
|
|
|
|
case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY:
|
|
|
|
bdrv_flags &= ~BDRV_O_RDWR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE:
|
|
|
|
bdrv_flags |= BDRV_O_RDWR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2016-09-12 20:08:31 +03:00
|
|
|
options = qdict_new();
|
|
|
|
detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
|
2016-09-12 20:08:31 +03:00
|
|
|
|
2015-11-06 18:27:06 +03:00
|
|
|
if (has_format) {
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(options, "driver", format);
|
2015-10-26 23:39:13 +03:00
|
|
|
}
|
|
|
|
|
2016-05-17 17:41:31 +03:00
|
|
|
medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
|
|
|
|
if (!medium_bs) {
|
2015-10-26 23:39:13 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:47 +03:00
|
|
|
rc = do_open_tray(has_device ? device : NULL,
|
|
|
|
has_id ? id : NULL,
|
|
|
|
false, &err);
|
2016-06-08 20:56:28 +03:00
|
|
|
if (rc && rc != -ENOSYS) {
|
2015-10-26 23:39:13 +03:00
|
|
|
error_propagate(errp, err);
|
|
|
|
goto fail;
|
|
|
|
}
|
2016-06-08 20:56:28 +03:00
|
|
|
error_free(err);
|
|
|
|
err = NULL;
|
2015-10-26 23:39:13 +03:00
|
|
|
|
2017-11-11 01:43:01 +03:00
|
|
|
blockdev_remove_medium(has_device, device, has_id, id, &err);
|
2015-10-26 23:39:13 +03:00
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:44 +03:00
|
|
|
qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
|
2015-10-26 23:39:13 +03:00
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-09-20 14:38:47 +03:00
|
|
|
qmp_blockdev_close_tray(has_device, device, has_id, id, errp);
|
2015-10-26 23:39:13 +03:00
|
|
|
|
|
|
|
fail:
|
|
|
|
/* If the medium has been inserted, the device has its own reference, so
|
|
|
|
* ours must be relinquished; and if it has not been inserted successfully,
|
|
|
|
* the reference must be relinquished anyway */
|
|
|
|
bdrv_unref(medium_bs);
|
|
|
|
}
|
|
|
|
|
2011-11-08 09:00:31 +04:00
|
|
|
/* throttling disk I/O limits */
|
2016-07-14 06:50:21 +03:00
|
|
|
void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
|
2011-11-08 09:00:31 +04:00
|
|
|
{
|
2013-09-02 16:14:39 +04:00
|
|
|
ThrottleConfig cfg;
|
2011-11-08 09:00:31 +04:00
|
|
|
BlockDriverState *bs;
|
2015-03-02 14:36:48 +03:00
|
|
|
BlockBackend *blk;
|
2014-05-14 18:22:47 +04:00
|
|
|
AioContext *aio_context;
|
2011-11-08 09:00:31 +04:00
|
|
|
|
2016-09-20 14:38:48 +03:00
|
|
|
blk = qmp_get_blk(arg->has_device ? arg->device : NULL,
|
|
|
|
arg->has_id ? arg->id : NULL,
|
|
|
|
errp);
|
2015-03-02 14:36:48 +03:00
|
|
|
if (!blk) {
|
2011-12-14 22:49:14 +04:00
|
|
|
return;
|
2011-11-08 09:00:31 +04:00
|
|
|
}
|
2015-10-19 18:53:29 +03:00
|
|
|
|
|
|
|
aio_context = blk_get_aio_context(blk);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2015-03-02 14:36:48 +03:00
|
|
|
bs = blk_bs(blk);
|
2015-10-19 18:53:29 +03:00
|
|
|
if (!bs) {
|
2016-09-20 14:38:48 +03:00
|
|
|
error_setg(errp, "Device has no medium");
|
2015-10-19 18:53:29 +03:00
|
|
|
goto out;
|
|
|
|
}
|
2011-11-08 09:00:31 +04:00
|
|
|
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2016-07-14 06:50:21 +03:00
|
|
|
cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
|
|
|
|
cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd;
|
|
|
|
cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
|
2013-09-02 16:14:39 +04:00
|
|
|
|
2016-07-14 06:50:21 +03:00
|
|
|
cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
|
|
|
|
cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd;
|
|
|
|
cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
|
2013-09-02 16:14:39 +04:00
|
|
|
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_bps_max) {
|
|
|
|
cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
|
2013-09-02 16:14:40 +04:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_bps_rd_max) {
|
|
|
|
cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
|
2013-09-02 16:14:40 +04:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_bps_wr_max) {
|
|
|
|
cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
|
2013-09-02 16:14:40 +04:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_iops_max) {
|
|
|
|
cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
|
2013-09-02 16:14:40 +04:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_iops_rd_max) {
|
|
|
|
cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
|
2013-09-02 16:14:40 +04:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_iops_wr_max) {
|
|
|
|
cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
|
2013-09-02 16:14:40 +04:00
|
|
|
}
|
2011-11-08 09:00:31 +04:00
|
|
|
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_bps_max_length) {
|
|
|
|
cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
|
2016-02-18 13:27:03 +03:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_bps_rd_max_length) {
|
|
|
|
cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
|
2016-02-18 13:27:03 +03:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_bps_wr_max_length) {
|
|
|
|
cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
|
2016-02-18 13:27:03 +03:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_iops_max_length) {
|
|
|
|
cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
|
2016-02-18 13:27:03 +03:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_iops_rd_max_length) {
|
|
|
|
cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
|
2016-02-18 13:27:03 +03:00
|
|
|
}
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_iops_wr_max_length) {
|
|
|
|
cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
|
2016-02-18 13:27:03 +03:00
|
|
|
}
|
|
|
|
|
2016-07-14 06:50:21 +03:00
|
|
|
if (arg->has_iops_size) {
|
|
|
|
cfg.op_size = arg->iops_size;
|
2013-09-02 16:14:41 +04:00
|
|
|
}
|
2013-09-02 16:14:39 +04:00
|
|
|
|
2016-02-18 13:26:59 +03:00
|
|
|
if (!throttle_is_valid(&cfg, errp)) {
|
2015-10-19 18:53:29 +03:00
|
|
|
goto out;
|
2011-11-08 09:00:31 +04:00
|
|
|
}
|
|
|
|
|
2015-06-08 19:17:44 +03:00
|
|
|
if (throttle_enabled(&cfg)) {
|
|
|
|
/* Enable I/O limits if they're not enabled yet, otherwise
|
|
|
|
* just update the throttling group. */
|
2017-08-25 16:20:23 +03:00
|
|
|
if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
|
2016-07-14 06:50:21 +03:00
|
|
|
blk_io_limits_enable(blk,
|
2016-09-20 14:38:48 +03:00
|
|
|
arg->has_group ? arg->group :
|
|
|
|
arg->has_device ? arg->device :
|
|
|
|
arg->id);
|
2016-07-14 06:50:21 +03:00
|
|
|
} else if (arg->has_group) {
|
|
|
|
blk_io_limits_update_group(blk, arg->group);
|
2015-06-08 19:17:44 +03:00
|
|
|
}
|
|
|
|
/* Set the new throttling configuration */
|
2016-03-21 15:53:52 +03:00
|
|
|
blk_set_io_limits(blk, &cfg);
|
2017-08-25 16:20:23 +03:00
|
|
|
} else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
|
2015-06-08 19:17:44 +03:00
|
|
|
/* If all throttling settings are set to 0, disable I/O limits */
|
2016-03-21 15:53:52 +03:00
|
|
|
blk_io_limits_disable(blk);
|
2011-11-08 09:00:31 +04:00
|
|
|
}
|
2014-05-14 18:22:47 +04:00
|
|
|
|
2015-10-19 18:53:29 +03:00
|
|
|
out:
|
2014-05-14 18:22:47 +04:00
|
|
|
aio_context_release(aio_context);
|
2011-11-08 09:00:31 +04:00
|
|
|
}
|
|
|
|
|
2015-04-18 02:49:52 +03:00
|
|
|
void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
|
|
|
bool has_granularity, uint32_t granularity,
|
2017-06-28 15:05:23 +03:00
|
|
|
bool has_persistent, bool persistent,
|
2017-06-28 15:05:24 +03:00
|
|
|
bool has_autoload, bool autoload,
|
2015-04-18 02:49:52 +03:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
2017-06-28 15:05:23 +03:00
|
|
|
BdrvDirtyBitmap *bitmap;
|
2015-04-18 02:49:52 +03:00
|
|
|
|
|
|
|
if (!name || name[0] == '\0') {
|
|
|
|
error_setg(errp, "Bitmap name cannot be empty");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bs = bdrv_lookup_bs(node, node, errp);
|
|
|
|
if (!bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_granularity) {
|
|
|
|
if (granularity < 512 || !is_power_of_2(granularity)) {
|
|
|
|
error_setg(errp, "Granularity must be power of 2 "
|
|
|
|
"and at least 512");
|
2017-06-05 15:39:03 +03:00
|
|
|
return;
|
2015-04-18 02:49:52 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Default to cluster size, if available: */
|
|
|
|
granularity = bdrv_get_default_bitmap_granularity(bs);
|
|
|
|
}
|
|
|
|
|
2017-06-28 15:05:23 +03:00
|
|
|
if (!has_persistent) {
|
|
|
|
persistent = false;
|
|
|
|
}
|
2017-06-28 15:05:24 +03:00
|
|
|
if (!has_autoload) {
|
|
|
|
autoload = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_autoload && !persistent) {
|
|
|
|
error_setg(errp, "Autoload flag must be used only for persistent "
|
|
|
|
"bitmaps");
|
|
|
|
return;
|
|
|
|
}
|
2017-06-28 15:05:23 +03:00
|
|
|
|
|
|
|
if (persistent &&
|
|
|
|
!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
|
2017-06-28 15:05:24 +03:00
|
|
|
if (bitmap == NULL) {
|
|
|
|
return;
|
2017-06-28 15:05:23 +03:00
|
|
|
}
|
2017-06-28 15:05:24 +03:00
|
|
|
|
|
|
|
bdrv_dirty_bitmap_set_persistance(bitmap, persistent);
|
|
|
|
bdrv_dirty_bitmap_set_autoload(bitmap, autoload);
|
2015-04-18 02:49:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BdrvDirtyBitmap *bitmap;
|
2017-06-28 15:05:29 +03:00
|
|
|
Error *local_err = NULL;
|
2015-04-18 02:49:52 +03:00
|
|
|
|
2017-06-05 15:39:03 +03:00
|
|
|
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
|
2015-04-18 02:49:52 +03:00
|
|
|
if (!bitmap || !bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
block: Add bitmap successors
A bitmap successor is an anonymous BdrvDirtyBitmap that is intended to
be created just prior to a sensitive operation (e.g. Incremental Backup)
that can either succeed or fail, but during the course of which we still
want a bitmap tracking writes.
On creating a successor, we "freeze" the parent bitmap which prevents
its deletion, enabling, anonymization, or creating a bitmap with the
same name.
On success, the parent bitmap can "abdicate" responsibility to the
successor, which will inherit its name. The successor will have been
tracking writes during the course of the backup operation. The parent
will be safely deleted.
On failure, we can "reclaim" the successor from the parent, unifying
them such that the resulting bitmap describes all writes occurring since
the last successful backup, for instance. Reclamation will thaw the
parent, but not explicitly re-enable it.
BdrvDirtyBitmap operations that target a single bitmap are protected
by assertions that the bitmap is not frozen and/or disabled.
BdrvDirtyBitmap operations that target a group of bitmaps, such as
bdrv_{set,reset}_dirty will ignore frozen/disabled drives with a
conditional instead.
Internal functions that enable/disable dirty bitmaps have assertions
added to them to prevent modifying frozen bitmaps.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 1429314609-29776-10-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2015-04-18 02:49:57 +03:00
|
|
|
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Bitmap '%s' is currently frozen and cannot be removed",
|
|
|
|
name);
|
2017-06-05 15:39:03 +03:00
|
|
|
return;
|
block: Add bitmap successors
A bitmap successor is an anonymous BdrvDirtyBitmap that is intended to
be created just prior to a sensitive operation (e.g. Incremental Backup)
that can either succeed or fail, but during the course of which we still
want a bitmap tracking writes.
On creating a successor, we "freeze" the parent bitmap which prevents
its deletion, enabling, anonymization, or creating a bitmap with the
same name.
On success, the parent bitmap can "abdicate" responsibility to the
successor, which will inherit its name. The successor will have been
tracking writes during the course of the backup operation. The parent
will be safely deleted.
On failure, we can "reclaim" the successor from the parent, unifying
them such that the resulting bitmap describes all writes occurring since
the last successful backup, for instance. Reclamation will thaw the
parent, but not explicitly re-enable it.
BdrvDirtyBitmap operations that target a single bitmap are protected
by assertions that the bitmap is not frozen and/or disabled.
BdrvDirtyBitmap operations that target a group of bitmaps, such as
bdrv_{set,reset}_dirty will ignore frozen/disabled drives with a
conditional instead.
Internal functions that enable/disable dirty bitmaps have assertions
added to them to prevent modifying frozen bitmaps.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 1429314609-29776-10-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2015-04-18 02:49:57 +03:00
|
|
|
}
|
2017-06-28 15:05:29 +03:00
|
|
|
|
|
|
|
if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
|
|
|
|
bdrv_remove_persistent_dirty_bitmap(bs, name, &local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-18 02:50:02 +03:00
|
|
|
bdrv_dirty_bitmap_make_anon(bitmap);
|
2015-04-18 02:49:52 +03:00
|
|
|
bdrv_release_dirty_bitmap(bs, bitmap);
|
|
|
|
}
|
|
|
|
|
2015-04-18 02:49:59 +03:00
|
|
|
/**
|
|
|
|
* Completely clear a bitmap, for the purposes of synchronizing a bitmap
|
|
|
|
* immediately after a full backup operation.
|
|
|
|
*/
|
|
|
|
void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BdrvDirtyBitmap *bitmap;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2017-06-05 15:39:03 +03:00
|
|
|
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
|
2015-04-18 02:49:59 +03:00
|
|
|
if (!bitmap || !bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Bitmap '%s' is currently frozen and cannot be modified",
|
|
|
|
name);
|
2017-06-05 15:39:03 +03:00
|
|
|
return;
|
2015-04-18 02:49:59 +03:00
|
|
|
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Bitmap '%s' is currently disabled and cannot be cleared",
|
|
|
|
name);
|
2017-06-05 15:39:03 +03:00
|
|
|
return;
|
2017-06-28 15:05:10 +03:00
|
|
|
} else if (bdrv_dirty_bitmap_readonly(bitmap)) {
|
|
|
|
error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name);
|
|
|
|
return;
|
2015-04-18 02:49:59 +03:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:16:54 +03:00
|
|
|
bdrv_clear_dirty_bitmap(bitmap, NULL);
|
2015-04-18 02:49:59 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 15:05:25 +03:00
|
|
|
BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
|
|
|
|
const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BdrvDirtyBitmap *bitmap;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDirtyBitmapSha256 *ret = NULL;
|
|
|
|
char *sha256;
|
|
|
|
|
|
|
|
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
|
|
|
|
if (!bitmap || !bs) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sha256 = bdrv_dirty_bitmap_sha256(bitmap, errp);
|
|
|
|
if (sha256 == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_new(BlockDirtyBitmapSha256, 1);
|
|
|
|
ret->sha256 = sha256;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-03-05 19:00:56 +03:00
|
|
|
void hmp_drive_del(Monitor *mon, const QDict *qdict)
|
2010-11-12 20:07:13 +03:00
|
|
|
{
|
|
|
|
const char *id = qdict_get_str(qdict, "id");
|
2014-10-07 15:59:05 +04:00
|
|
|
BlockBackend *blk;
|
2010-11-12 20:07:13 +03:00
|
|
|
BlockDriverState *bs;
|
2014-08-18 19:07:12 +04:00
|
|
|
AioContext *aio_context;
|
2014-05-23 17:29:43 +04:00
|
|
|
Error *local_err = NULL;
|
2010-11-12 20:07:13 +03:00
|
|
|
|
2016-02-23 19:50:37 +03:00
|
|
|
bs = bdrv_find_node(id);
|
|
|
|
if (bs) {
|
2017-03-21 19:53:28 +03:00
|
|
|
qmp_blockdev_del(id, &local_err);
|
2016-02-23 19:50:37 +03:00
|
|
|
if (local_err) {
|
|
|
|
error_report_err(local_err);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-07 15:59:05 +04:00
|
|
|
blk = blk_by_name(id);
|
|
|
|
if (!blk) {
|
2014-05-16 13:00:09 +04:00
|
|
|
error_report("Device '%s' not found", id);
|
2015-03-05 19:00:56 +03:00
|
|
|
return;
|
2010-11-12 20:07:13 +03:00
|
|
|
}
|
2014-08-18 19:07:12 +04:00
|
|
|
|
2014-10-07 15:59:22 +04:00
|
|
|
if (!blk_legacy_dinfo(blk)) {
|
2014-09-11 18:45:39 +04:00
|
|
|
error_report("Deleting device added with blockdev-add"
|
|
|
|
" is not supported");
|
2015-03-05 19:00:56 +03:00
|
|
|
return;
|
2014-09-11 18:45:39 +04:00
|
|
|
}
|
|
|
|
|
2015-10-19 18:53:29 +03:00
|
|
|
aio_context = blk_get_aio_context(blk);
|
2014-08-18 19:07:12 +04:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2015-10-19 18:53:29 +03:00
|
|
|
bs = blk_bs(blk);
|
|
|
|
if (bs) {
|
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) {
|
|
|
|
error_report_err(local_err);
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
return;
|
|
|
|
}
|
2010-11-12 20:07:13 +03:00
|
|
|
|
2016-01-29 18:36:09 +03:00
|
|
|
blk_remove_bs(blk);
|
2015-10-19 18:53:29 +03:00
|
|
|
}
|
2010-11-12 20:07:13 +03:00
|
|
|
|
2016-03-16 21:54:39 +03:00
|
|
|
/* Make the BlockBackend and the attached BlockDriverState anonymous */
|
2016-03-16 21:54:38 +03:00
|
|
|
monitor_remove_blk(blk);
|
|
|
|
|
2016-03-16 21:54:39 +03:00
|
|
|
/* If this BlockBackend has a device attached to it, its refcount will be
|
|
|
|
* decremented when the device is removed; otherwise we have to do so here.
|
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 05:51:47 +04:00
|
|
|
*/
|
2014-10-07 15:59:25 +04:00
|
|
|
if (blk_get_attached_dev(blk)) {
|
2013-06-05 12:33:14 +04:00
|
|
|
/* Further I/O must not pause the guest */
|
2015-10-19 18:53:22 +03:00
|
|
|
blk_set_on_error(blk, 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 05:51:47 +04:00
|
|
|
} else {
|
2014-10-07 15:59:09 +04:00
|
|
|
blk_unref(blk);
|
2010-11-12 20:07:13 +03:00
|
|
|
}
|
|
|
|
|
2014-08-18 19:07:12 +04:00
|
|
|
aio_context_release(aio_context);
|
2010-11-12 20:07:13 +03:00
|
|
|
}
|
2011-01-24 15:32:33 +03:00
|
|
|
|
2014-01-24 00:31:37 +04:00
|
|
|
void qmp_block_resize(bool has_device, const char *device,
|
|
|
|
bool has_node_name, const char *node_name,
|
|
|
|
int64_t size, Error **errp)
|
2011-01-24 15:32:33 +03:00
|
|
|
{
|
2014-01-24 00:31:37 +04:00
|
|
|
Error *local_err = NULL;
|
2017-02-17 13:23:33 +03:00
|
|
|
BlockBackend *blk = NULL;
|
2011-01-24 15:32:33 +03:00
|
|
|
BlockDriverState *bs;
|
2014-08-18 17:52:28 +04:00
|
|
|
AioContext *aio_context;
|
2013-05-02 17:32:55 +04:00
|
|
|
int ret;
|
2011-01-24 15:32:33 +03:00
|
|
|
|
2014-01-24 00:31:37 +04:00
|
|
|
bs = bdrv_lookup_bs(has_device ? device : NULL,
|
|
|
|
has_node_name ? node_name : NULL,
|
|
|
|
&local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2014-01-24 00:31:37 +04:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-18 17:52:28 +04:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2014-01-24 00:31:37 +04:00
|
|
|
if (!bdrv_is_first_non_filter(bs)) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_FEATURE_DISABLED, "resize");
|
2014-08-18 17:52:28 +04:00
|
|
|
goto out;
|
2011-01-24 15:32:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (size < 0) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
|
2014-08-18 17:52:28 +04:00
|
|
|
goto out;
|
2011-01-24 15:32:33 +03:00
|
|
|
}
|
|
|
|
|
2014-06-26 00:55:30 +04:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_DEVICE_IN_USE, device);
|
2014-08-18 17:52:28 +04:00
|
|
|
goto out;
|
2014-06-26 00:55:30 +04:00
|
|
|
}
|
|
|
|
|
2017-01-20 19:07:26 +03:00
|
|
|
blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
|
2017-01-13 21:02:32 +03:00
|
|
|
ret = blk_insert_bs(blk, bs, errp);
|
|
|
|
if (ret < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2017-02-17 13:23:33 +03:00
|
|
|
|
2017-05-10 20:39:45 +03:00
|
|
|
bdrv_drained_begin(bs);
|
2017-06-13 23:20:54 +03:00
|
|
|
ret = blk_truncate(blk, size, PREALLOC_MODE_OFF, errp);
|
2017-05-10 20:39:45 +03:00
|
|
|
bdrv_drained_end(bs);
|
2014-08-18 17:52:28 +04:00
|
|
|
|
|
|
|
out:
|
2017-02-17 13:23:33 +03:00
|
|
|
blk_unref(blk);
|
2014-08-18 17:52:28 +04:00
|
|
|
aio_context_release(aio_context);
|
2011-01-24 15:32:33 +03:00
|
|
|
}
|
2012-01-18 18:40:46 +04:00
|
|
|
|
2016-07-05 17:28:59 +03:00
|
|
|
void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
|
block: add backing-file option to block-stream
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block job.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-stream api, the user is able to change
the backing file of the active layer as part of the block-stream
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the active image metadata fails, then the block-stream
operation returns failure, without disrupting the guest.
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:11 +04:00
|
|
|
bool has_base, const char *base,
|
2016-10-28 10:08:19 +03:00
|
|
|
bool has_base_node, const char *base_node,
|
block: add backing-file option to block-stream
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block job.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-stream api, the user is able to change
the backing file of the active layer as part of the block-stream
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the active image metadata fails, then the block-stream
operation returns failure, without disrupting the guest.
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:11 +04:00
|
|
|
bool has_backing_file, const char *backing_file,
|
|
|
|
bool has_speed, int64_t speed,
|
2012-09-28 19:22:59 +04:00
|
|
|
bool has_on_error, BlockdevOnError on_error,
|
|
|
|
Error **errp)
|
2012-01-18 18:40:46 +04:00
|
|
|
{
|
2016-10-28 10:08:11 +03:00
|
|
|
BlockDriverState *bs, *iter;
|
2012-01-18 18:40:53 +04:00
|
|
|
BlockDriverState *base_bs = NULL;
|
2014-10-21 15:03:57 +04:00
|
|
|
AioContext *aio_context;
|
2012-04-25 19:51:00 +04:00
|
|
|
Error *local_err = NULL;
|
block: add backing-file option to block-stream
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block job.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-stream api, the user is able to change
the backing file of the active layer as part of the block-stream
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the active image metadata fails, then the block-stream
operation returns failure, without disrupting the guest.
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:11 +04:00
|
|
|
const char *base_name = NULL;
|
2012-01-18 18:40:46 +04:00
|
|
|
|
2012-09-28 19:22:59 +04:00
|
|
|
if (!has_on_error) {
|
|
|
|
on_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
|
|
}
|
|
|
|
|
2016-10-28 10:08:11 +03:00
|
|
|
bs = bdrv_lookup_bs(device, device, errp);
|
2016-06-23 15:20:24 +03:00
|
|
|
if (!bs) {
|
2012-01-18 18:40:46 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-10-21 15:03:57 +04:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-10-28 10:08:19 +03:00
|
|
|
if (has_base && has_base_node) {
|
|
|
|
error_setg(errp, "'base' and 'base-node' cannot be specified "
|
|
|
|
"at the same time");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
block: add backing-file option to block-stream
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block job.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-stream api, the user is able to change
the backing file of the active layer as part of the block-stream
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the active image metadata fails, then the block-stream
operation returns failure, without disrupting the guest.
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:11 +04:00
|
|
|
if (has_base) {
|
2012-01-18 18:40:53 +04:00
|
|
|
base_bs = bdrv_find_backing_image(bs, base);
|
|
|
|
if (base_bs == NULL) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_BASE_NOT_FOUND, base);
|
2014-10-21 15:03:57 +04:00
|
|
|
goto out;
|
2012-01-18 18:40:53 +04:00
|
|
|
}
|
2014-10-21 15:03:57 +04:00
|
|
|
assert(bdrv_get_aio_context(base_bs) == aio_context);
|
block: add backing-file option to block-stream
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block job.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-stream api, the user is able to change
the backing file of the active layer as part of the block-stream
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the active image metadata fails, then the block-stream
operation returns failure, without disrupting the guest.
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:11 +04:00
|
|
|
base_name = base;
|
2012-01-18 18:40:46 +04:00
|
|
|
}
|
|
|
|
|
2016-10-28 10:08:19 +03:00
|
|
|
if (has_base_node) {
|
|
|
|
base_bs = bdrv_lookup_bs(NULL, base_node, errp);
|
|
|
|
if (!base_bs) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (bs == base_bs || !bdrv_chain_contains(bs, base_bs)) {
|
|
|
|
error_setg(errp, "Node '%s' is not a backing image of '%s'",
|
|
|
|
base_node, device);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
assert(bdrv_get_aio_context(base_bs) == aio_context);
|
|
|
|
base_name = base_bs->filename;
|
|
|
|
}
|
|
|
|
|
2016-10-28 10:08:11 +03:00
|
|
|
/* Check for op blockers in the whole chain between bs and base */
|
|
|
|
for (iter = bs; iter && iter != base_bs; iter = backing_bs(iter)) {
|
|
|
|
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
block: add backing-file option to block-stream
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block job.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-stream api, the user is able to change
the backing file of the active layer as part of the block-stream
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the active image metadata fails, then the block-stream
operation returns failure, without disrupting the guest.
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:11 +04:00
|
|
|
/* if we are streaming the entire chain, the result will have no backing
|
|
|
|
* file, and specifying one is therefore an error */
|
|
|
|
if (base_bs == NULL && has_backing_file) {
|
|
|
|
error_setg(errp, "backing file specified, but streaming the "
|
|
|
|
"entire chain");
|
2014-10-21 15:03:57 +04:00
|
|
|
goto out;
|
block: add backing-file option to block-stream
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block job.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-stream api, the user is able to change
the backing file of the active layer as part of the block-stream
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the active image metadata fails, then the block-stream
operation returns failure, without disrupting the guest.
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* backing_file string overrides base bs filename */
|
|
|
|
base_name = has_backing_file ? backing_file : base_name;
|
|
|
|
|
2016-07-05 17:28:59 +03:00
|
|
|
stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name,
|
2016-10-27 19:06:58 +03:00
|
|
|
has_speed ? speed : 0, on_error, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2012-04-25 19:51:00 +04:00
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 15:03:57 +04:00
|
|
|
goto out;
|
2012-01-18 18:40:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_stream(bs, bs->job);
|
2014-10-21 15:03:57 +04:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2012-01-18 18:40:46 +04:00
|
|
|
}
|
2012-01-18 18:40:47 +04:00
|
|
|
|
2016-07-05 17:29:00 +03:00
|
|
|
void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
2014-06-30 17:14:15 +04:00
|
|
|
bool has_base, const char *base,
|
|
|
|
bool has_top, const char *top,
|
block: extend block-commit to accept a string for the backing file
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block commit.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-commit api, the user is able to change
the backing file of the overlay image as part of the block-commit
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the overlay image metadata fails, then the block-commit
operation returns failure, without disrupting the guest.
If the commit top is the active layer, then specifying the backing
file string will be treated as an error (there is no overlay image
to modify in that case).
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:10 +04:00
|
|
|
bool has_backing_file, const char *backing_file,
|
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 21:29:16 +04:00
|
|
|
bool has_speed, int64_t speed,
|
2017-02-20 20:10:05 +03:00
|
|
|
bool has_filter_node_name, const char *filter_node_name,
|
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 21:29:16 +04:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
2016-10-28 10:08:07 +03:00
|
|
|
BlockDriverState *iter;
|
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 21:29:16 +04:00
|
|
|
BlockDriverState *base_bs, *top_bs;
|
2014-10-21 15:03:59 +04:00
|
|
|
AioContext *aio_context;
|
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 21:29:16 +04:00
|
|
|
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 19:22:55 +04: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 21:29:16 +04:00
|
|
|
|
2014-04-10 21:36:25 +04:00
|
|
|
if (!has_speed) {
|
|
|
|
speed = 0;
|
|
|
|
}
|
2017-02-20 20:10:05 +03:00
|
|
|
if (!has_filter_node_name) {
|
|
|
|
filter_node_name = NULL;
|
|
|
|
}
|
2014-04-10 21:36:25 +04:00
|
|
|
|
2014-06-30 17:14:15 +04:00
|
|
|
/* Important Note:
|
|
|
|
* libvirt relies on the DeviceNotFound error class in order to probe for
|
|
|
|
* live commit feature versions; for this to work, we must make sure to
|
|
|
|
* perform the device lookup before any generic errors that may occur in a
|
|
|
|
* scenario in which all optional arguments are omitted. */
|
2016-06-23 15:20:24 +03:00
|
|
|
bs = qmp_get_root_bs(device, &local_err);
|
|
|
|
if (!bs) {
|
|
|
|
bs = bdrv_lookup_bs(device, device, NULL);
|
|
|
|
if (!bs) {
|
|
|
|
error_free(local_err);
|
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
|
|
"Device '%s' not found", device);
|
|
|
|
} else {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
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 21:29:16 +04:00
|
|
|
return;
|
2014-05-23 17:29:44 +04:00
|
|
|
}
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-10-21 15:03:59 +04:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2014-09-11 09:14:00 +04:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) {
|
2014-10-21 15:03:59 +04:00
|
|
|
goto out;
|
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 21:29:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* default top_bs is the active layer */
|
|
|
|
top_bs = bs;
|
|
|
|
|
2014-06-30 17:14:15 +04:00
|
|
|
if (has_top && top) {
|
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 21:29:16 +04:00
|
|
|
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");
|
2014-10-21 15:03:59 +04:00
|
|
|
goto out;
|
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 21:29:16 +04:00
|
|
|
}
|
|
|
|
|
2014-10-21 15:03:59 +04:00
|
|
|
assert(bdrv_get_aio_context(top_bs) == aio_context);
|
|
|
|
|
2012-10-16 23:49:10 +04: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) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL");
|
2014-10-21 15:03:59 +04:00
|
|
|
goto out;
|
2012-10-16 23:49:10 +04:00
|
|
|
}
|
|
|
|
|
2014-10-21 15:03:59 +04:00
|
|
|
assert(bdrv_get_aio_context(base_bs) == aio_context);
|
|
|
|
|
2016-10-28 10:08:07 +03:00
|
|
|
for (iter = top_bs; iter != backing_bs(base_bs); iter = backing_bs(iter)) {
|
|
|
|
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
|
|
|
|
goto out;
|
|
|
|
}
|
2014-09-11 09:14:00 +04:00
|
|
|
}
|
|
|
|
|
2014-06-30 17:14:15 +04:00
|
|
|
/* Do not allow attempts to commit an image into itself */
|
|
|
|
if (top_bs == base_bs) {
|
|
|
|
error_setg(errp, "cannot commit an image into itself");
|
2014-10-21 15:03:59 +04:00
|
|
|
goto out;
|
2014-06-30 17:14:15 +04:00
|
|
|
}
|
|
|
|
|
2013-12-16 10:45:31 +04:00
|
|
|
if (top_bs == bs) {
|
block: extend block-commit to accept a string for the backing file
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block commit.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-commit api, the user is able to change
the backing file of the overlay image as part of the block-commit
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the overlay image metadata fails, then the block-commit
operation returns failure, without disrupting the guest.
If the commit top is the active layer, then specifying the backing
file string will be treated as an error (there is no overlay image
to modify in that case).
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:10 +04:00
|
|
|
if (has_backing_file) {
|
|
|
|
error_setg(errp, "'backing-file' specified,"
|
|
|
|
" but 'top' is the active layer");
|
2014-10-21 15:03:59 +04:00
|
|
|
goto out;
|
block: extend block-commit to accept a string for the backing file
On some image chains, QEMU may not always be able to resolve the
filenames properly, when updating the backing file of an image
after a block commit.
For instance, certain relative pathnames may fail, or drives may
have been specified originally by file descriptor (e.g. /dev/fd/???),
or a relative protocol pathname may have been used.
In these instances, QEMU may lack the information to be able to make
the correct choice, but the user or management layer most likely does
have that knowledge.
With this extension to the block-commit api, the user is able to change
the backing file of the overlay image as part of the block-commit
operation.
This allows the change to be 'safe', in the sense that if the attempt
to write the overlay image metadata fails, then the block-commit
operation returns failure, without disrupting the guest.
If the commit top is the active layer, then specifying the backing
file string will be treated as an error (there is no overlay image
to modify in that case).
If a backing file string is not specified in the command, the backing
file string to use is determined in the same manner as it was
previously.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-25 23:40:10 +04:00
|
|
|
}
|
2016-10-27 19:06:57 +03:00
|
|
|
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
|
2017-02-20 20:10:05 +03:00
|
|
|
BLOCK_JOB_DEFAULT, speed, on_error,
|
2017-04-21 15:27:04 +03:00
|
|
|
filter_node_name, NULL, NULL, false, &local_err);
|
2013-12-16 10:45:31 +04:00
|
|
|
} else {
|
2016-10-28 10:08:07 +03:00
|
|
|
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
|
|
|
|
if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
|
|
|
|
goto out;
|
|
|
|
}
|
2016-07-05 17:29:00 +03:00
|
|
|
commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed,
|
2016-10-27 19:06:58 +03:00
|
|
|
on_error, has_backing_file ? backing_file : NULL,
|
2017-02-20 20:10:05 +03:00
|
|
|
filter_node_name, &local_err);
|
2013-12-16 10:45:31 +04: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 21:29:16 +04:00
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 15:03:59 +04:00
|
|
|
goto out;
|
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 21:29:16 +04:00
|
|
|
}
|
2014-10-21 15:03:59 +04:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
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 21:29:16 +04:00
|
|
|
}
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
|
|
|
Error **errp)
|
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 19:13:14 +04:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *target_bs;
|
2013-07-26 22:39:04 +04:00
|
|
|
BlockDriverState *source = NULL;
|
2016-11-08 09:50:38 +03:00
|
|
|
BlockJob *job = NULL;
|
2015-04-18 02:49:58 +03:00
|
|
|
BdrvDirtyBitmap *bmap = NULL;
|
2014-10-21 15:03:56 +04:00
|
|
|
AioContext *aio_context;
|
2015-08-26 20:47:48 +03:00
|
|
|
QDict *options = 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 19:13:14 +04:00
|
|
|
Error *local_err = NULL;
|
|
|
|
int flags;
|
|
|
|
int64_t size;
|
2017-05-02 19:35:53 +03:00
|
|
|
bool set_backing_hd = 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 19:13:14 +04:00
|
|
|
|
2016-07-22 11:17:50 +03:00
|
|
|
if (!backup->has_speed) {
|
|
|
|
backup->speed = 0;
|
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 19:13:14 +04:00
|
|
|
}
|
2016-07-22 11:17:50 +03:00
|
|
|
if (!backup->has_on_source_error) {
|
|
|
|
backup->on_source_error = BLOCKDEV_ON_ERROR_REPORT;
|
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 19:13:14 +04:00
|
|
|
}
|
2016-07-22 11:17:50 +03:00
|
|
|
if (!backup->has_on_target_error) {
|
|
|
|
backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT;
|
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 19:13:14 +04:00
|
|
|
}
|
2016-07-22 11:17:50 +03:00
|
|
|
if (!backup->has_mode) {
|
|
|
|
backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
|
|
|
}
|
|
|
|
if (!backup->has_job_id) {
|
|
|
|
backup->job_id = 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 19:13:14 +04:00
|
|
|
}
|
2016-07-22 11:17:52 +03:00
|
|
|
if (!backup->has_compress) {
|
|
|
|
backup->compress = 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 19:13:14 +04:00
|
|
|
|
2016-07-22 11:17:50 +03:00
|
|
|
bs = qmp_get_root_bs(backup->device, errp);
|
2016-06-23 15:20:24 +03:00
|
|
|
if (!bs) {
|
2016-11-08 09:50:38 +03:00
|
|
|
return 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 19:13:14 +04:00
|
|
|
}
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-10-21 15:03:56 +04:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-07-22 11:17:50 +03:00
|
|
|
if (!backup->has_format) {
|
|
|
|
backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ?
|
|
|
|
NULL : (char*) bs->drv->format_name;
|
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 19:13:14 +04:00
|
|
|
}
|
|
|
|
|
2014-12-18 13:37:05 +03:00
|
|
|
/* Early check to avoid creating target */
|
2014-05-23 17:29:43 +04:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
|
2014-10-21 15:03:56 +04:00
|
|
|
goto out;
|
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 19:13:14 +04:00
|
|
|
}
|
|
|
|
|
2016-03-18 19:46:45 +03:00
|
|
|
flags = bs->open_flags | BDRV_O_RDWR;
|
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 19:13:14 +04:00
|
|
|
|
2013-07-26 22:39:04 +04:00
|
|
|
/* See if we have a backing HD we can use to create our new image
|
|
|
|
* on top of. */
|
2016-07-22 11:17:50 +03:00
|
|
|
if (backup->sync == MIRROR_SYNC_MODE_TOP) {
|
2015-06-17 15:55:21 +03:00
|
|
|
source = backing_bs(bs);
|
2013-07-26 22:39:04 +04:00
|
|
|
if (!source) {
|
2016-07-22 11:17:50 +03:00
|
|
|
backup->sync = MIRROR_SYNC_MODE_FULL;
|
2013-07-26 22:39:04 +04:00
|
|
|
}
|
|
|
|
}
|
2016-07-22 11:17:50 +03:00
|
|
|
if (backup->sync == MIRROR_SYNC_MODE_NONE) {
|
2013-07-26 22:39:04 +04:00
|
|
|
source = bs;
|
2017-05-02 19:35:53 +03:00
|
|
|
flags |= BDRV_O_NO_BACKING;
|
|
|
|
set_backing_hd = true;
|
2013-07-26 22:39:04 +04:00
|
|
|
}
|
|
|
|
|
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 19:13:14 +04:00
|
|
|
size = bdrv_getlength(bs);
|
|
|
|
if (size < 0) {
|
|
|
|
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
2014-10-21 15:03:56 +04:00
|
|
|
goto out;
|
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 19:13:14 +04:00
|
|
|
}
|
|
|
|
|
2016-07-22 11:17:50 +03:00
|
|
|
if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
|
|
|
|
assert(backup->format);
|
2013-07-26 22:39:04 +04:00
|
|
|
if (source) {
|
2016-07-22 11:17:50 +03:00
|
|
|
bdrv_img_create(backup->target, backup->format, source->filename,
|
2013-07-26 22:39:04 +04:00
|
|
|
source->drv->format_name, NULL,
|
2017-04-21 15:27:01 +03:00
|
|
|
size, flags, false, &local_err);
|
2013-07-26 22:39:04 +04:00
|
|
|
} else {
|
2016-07-22 11:17:50 +03:00
|
|
|
bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL,
|
2017-04-21 15:27:01 +03:00
|
|
|
size, flags, false, &local_err);
|
2013-07-26 22:39:04 +04:00
|
|
|
}
|
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 19:13:14 +04:00
|
|
|
}
|
|
|
|
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
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 19:13:14 +04:00
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 15:03:56 +04:00
|
|
|
goto out;
|
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 19:13:14 +04:00
|
|
|
}
|
|
|
|
|
2016-07-22 11:17:50 +03:00
|
|
|
if (backup->format) {
|
2017-05-02 19:35:53 +03:00
|
|
|
if (!options) {
|
|
|
|
options = qdict_new();
|
|
|
|
}
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(options, "driver", backup->format);
|
2015-08-26 20:47:48 +03:00
|
|
|
}
|
|
|
|
|
2016-07-22 11:17:50 +03:00
|
|
|
target_bs = bdrv_open(backup->target, NULL, options, flags, errp);
|
2016-05-17 17:41:31 +03:00
|
|
|
if (!target_bs) {
|
2014-10-21 15:03:56 +04:00
|
|
|
goto out;
|
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 19:13:14 +04:00
|
|
|
}
|
|
|
|
|
2014-10-21 15:03:56 +04:00
|
|
|
bdrv_set_aio_context(target_bs, aio_context);
|
|
|
|
|
2017-05-02 19:35:53 +03:00
|
|
|
if (set_backing_hd) {
|
|
|
|
bdrv_set_backing_hd(target_bs, source, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
bdrv_unref(target_bs);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-22 11:17:50 +03:00
|
|
|
if (backup->has_bitmap) {
|
|
|
|
bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
|
2015-04-18 02:49:58 +03:00
|
|
|
if (!bmap) {
|
2016-07-22 11:17:50 +03:00
|
|
|
error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
|
2015-11-10 01:39:10 +03:00
|
|
|
bdrv_unref(target_bs);
|
2015-04-18 02:49:58 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
|
|
|
backup->sync, bmap, backup->compress,
|
|
|
|
backup->on_source_error, backup->on_target_error,
|
|
|
|
BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
|
2016-04-14 14:09:53 +03:00
|
|
|
bdrv_unref(target_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 19:13:14 +04:00
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 15:03:56 +04:00
|
|
|
goto out;
|
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 19:13:14 +04:00
|
|
|
}
|
2014-10-21 15:03:56 +04:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2016-11-08 09:50:38 +03:00
|
|
|
return job;
|
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 19:13:14 +04:00
|
|
|
}
|
|
|
|
|
2016-07-22 11:17:50 +03:00
|
|
|
void qmp_drive_backup(DriveBackup *arg, Error **errp)
|
2015-11-06 02:13:17 +03:00
|
|
|
{
|
2016-11-08 09:50:38 +03:00
|
|
|
|
|
|
|
BlockJob *job;
|
|
|
|
job = do_drive_backup(arg, NULL, errp);
|
|
|
|
if (job) {
|
|
|
|
block_job_start(job);
|
|
|
|
}
|
2015-11-06 02:13:17 +03:00
|
|
|
}
|
|
|
|
|
2014-01-24 00:31:34 +04:00
|
|
|
BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
|
|
|
|
{
|
2015-04-17 14:52:43 +03:00
|
|
|
return bdrv_named_nodes_list(errp);
|
2014-01-24 00:31:34 +04:00
|
|
|
}
|
|
|
|
|
2016-11-08 09:50:38 +03:00
|
|
|
BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
|
|
|
Error **errp)
|
2014-12-18 13:37:05 +03:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *target_bs;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
AioContext *aio_context;
|
2016-11-08 09:50:38 +03:00
|
|
|
BlockJob *job = NULL;
|
2014-12-18 13:37:05 +03:00
|
|
|
|
2016-07-22 11:17:51 +03:00
|
|
|
if (!backup->has_speed) {
|
|
|
|
backup->speed = 0;
|
2014-12-18 13:37:05 +03:00
|
|
|
}
|
2016-07-22 11:17:51 +03:00
|
|
|
if (!backup->has_on_source_error) {
|
|
|
|
backup->on_source_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
|
|
}
|
|
|
|
if (!backup->has_on_target_error) {
|
|
|
|
backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT;
|
2014-12-18 13:37:05 +03:00
|
|
|
}
|
2016-07-22 11:17:51 +03:00
|
|
|
if (!backup->has_job_id) {
|
|
|
|
backup->job_id = NULL;
|
2014-12-18 13:37:05 +03:00
|
|
|
}
|
2016-07-22 11:17:53 +03:00
|
|
|
if (!backup->has_compress) {
|
|
|
|
backup->compress = false;
|
|
|
|
}
|
2014-12-18 13:37:05 +03:00
|
|
|
|
2016-07-22 11:17:51 +03:00
|
|
|
bs = qmp_get_root_bs(backup->device, errp);
|
2016-06-23 15:20:24 +03:00
|
|
|
if (!bs) {
|
2016-11-08 09:50:38 +03:00
|
|
|
return NULL;
|
2014-12-18 13:37:05 +03:00
|
|
|
}
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-12-18 13:37:05 +03:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-07-22 11:17:51 +03:00
|
|
|
target_bs = bdrv_lookup_bs(backup->target, backup->target, errp);
|
2016-05-23 05:19:35 +03:00
|
|
|
if (!target_bs) {
|
2015-10-19 18:53:29 +03:00
|
|
|
goto out;
|
|
|
|
}
|
2014-12-18 13:37:05 +03:00
|
|
|
|
2016-05-23 05:19:36 +03:00
|
|
|
if (bdrv_get_aio_context(target_bs) != aio_context) {
|
|
|
|
if (!bdrv_has_blk(target_bs)) {
|
|
|
|
/* The target BDS is not attached, we can safely move it to another
|
|
|
|
* AioContext. */
|
|
|
|
bdrv_set_aio_context(target_bs, aio_context);
|
|
|
|
} else {
|
|
|
|
error_setg(errp, "Target is attached to a different thread from "
|
|
|
|
"source.");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2016-11-08 09:50:38 +03:00
|
|
|
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
|
|
|
backup->sync, NULL, backup->compress,
|
|
|
|
backup->on_source_error, backup->on_target_error,
|
|
|
|
BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
|
2014-12-18 13:37:05 +03:00
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2016-11-08 09:50:38 +03:00
|
|
|
return job;
|
2014-12-18 13:37:05 +03:00
|
|
|
}
|
|
|
|
|
2016-07-22 11:17:51 +03:00
|
|
|
void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
|
|
|
|
{
|
2016-11-08 09:50:38 +03:00
|
|
|
BlockJob *job;
|
|
|
|
job = do_blockdev_backup(arg, NULL, errp);
|
|
|
|
if (job) {
|
|
|
|
block_job_start(job);
|
|
|
|
}
|
2015-11-06 02:13:17 +03:00
|
|
|
}
|
|
|
|
|
2015-12-24 07:45:03 +03:00
|
|
|
/* Parameter check and block job starting for drive mirroring.
|
|
|
|
* Caller should hold @device and @target's aio context (must be the same).
|
|
|
|
**/
|
2016-07-05 17:28:57 +03:00
|
|
|
static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
2015-12-24 07:45:03 +03:00
|
|
|
BlockDriverState *target,
|
|
|
|
bool has_replaces, const char *replaces,
|
|
|
|
enum MirrorSyncMode sync,
|
block/mirror: Fix target backing BDS
Currently, we are trying to move the backing BDS from the source to the
target in bdrv_replace_in_backing_chain() which is called from
mirror_exit(). However, mirror_complete() already tries to open the
target's backing chain with a call to bdrv_open_backing_file().
First, we should only set the target's backing BDS once. Second, the
mirroring block job has a better idea of what to set it to than the
generic code in bdrv_replace_in_backing_chain() (in fact, the latter's
conditions on when to move the backing BDS from source to target are not
really correct).
Therefore, remove that code from bdrv_replace_in_backing_chain() and
leave it to mirror_complete().
Depending on what kind of mirroring is performed, we furthermore want to
use different strategies to open the target's backing chain:
- If blockdev-mirror is used, we can assume the user made sure that the
target already has the correct backing chain. In particular, we should
not try to open a backing file if the target does not have any yet.
- If drive-mirror with mode=absolute-paths is used, we can and should
reuse the already existing chain of nodes that the source BDS is in.
In case of sync=full, no backing BDS is required; with sync=top, we
just link the source's backing BDS to the target, and with sync=none,
we use the source BDS as the target's backing BDS.
We should not try to open these backing files anew because this would
lead to two BDSs existing per physical file in the backing chain, and
we would like to avoid such concurrent access.
- If drive-mirror with mode=existing is used, we have to use the
information provided in the physical image file which means opening
the target's backing chain completely anew, just as it has been done
already.
If the target's backing chain shares images with the source, this may
lead to multiple BDSs per physical image file. But since we cannot
reliably ascertain this case, there is nothing we can do about it.
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20160610185750.30956-3-mreitz@redhat.com
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-06-10 21:57:47 +03:00
|
|
|
BlockMirrorBackingMode backing_mode,
|
2015-12-24 07:45:03 +03:00
|
|
|
bool has_speed, int64_t speed,
|
|
|
|
bool has_granularity, uint32_t granularity,
|
|
|
|
bool has_buf_size, int64_t buf_size,
|
|
|
|
bool has_on_source_error,
|
|
|
|
BlockdevOnError on_source_error,
|
|
|
|
bool has_on_target_error,
|
|
|
|
BlockdevOnError on_target_error,
|
|
|
|
bool has_unmap, bool unmap,
|
2017-02-20 20:10:05 +03:00
|
|
|
bool has_filter_node_name,
|
|
|
|
const char *filter_node_name,
|
2015-12-24 07:45:03 +03:00
|
|
|
Error **errp)
|
2012-10-18 18:49:24 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
if (!has_speed) {
|
|
|
|
speed = 0;
|
|
|
|
}
|
2012-10-18 18:49:28 +04: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;
|
|
|
|
}
|
2013-01-21 20:09:46 +04:00
|
|
|
if (!has_granularity) {
|
|
|
|
granularity = 0;
|
|
|
|
}
|
2013-01-22 12:03:13 +04:00
|
|
|
if (!has_buf_size) {
|
2015-05-15 10:51:36 +03:00
|
|
|
buf_size = 0;
|
2013-01-22 12:03:13 +04:00
|
|
|
}
|
2015-06-08 08:56:08 +03:00
|
|
|
if (!has_unmap) {
|
|
|
|
unmap = true;
|
|
|
|
}
|
2017-02-20 20:10:05 +03:00
|
|
|
if (!has_filter_node_name) {
|
|
|
|
filter_node_name = NULL;
|
|
|
|
}
|
2013-01-22 12:03:13 +04:00
|
|
|
|
2013-01-21 20:09:46 +04:00
|
|
|
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
|
|
|
|
"a value in range [512B, 64MB]");
|
2013-01-21 20:09:46 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (granularity & (granularity - 1)) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
|
|
|
|
"power of 2");
|
2013-01-21 20:09:46 +04:00
|
|
|
return;
|
|
|
|
}
|
2012-10-18 18:49:24 +04:00
|
|
|
|
2015-12-24 07:45:03 +03:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
|
|
|
|
return;
|
|
|
|
}
|
2015-12-24 07:45:04 +03:00
|
|
|
if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) {
|
|
|
|
return;
|
|
|
|
}
|
2015-12-24 07:45:03 +03:00
|
|
|
|
|
|
|
if (!bs->backing && sync == MIRROR_SYNC_MODE_TOP) {
|
|
|
|
sync = MIRROR_SYNC_MODE_FULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pass the node name to replace to mirror start since it's loose coupling
|
|
|
|
* and will allow to check whether the node still exist at mirror completion
|
|
|
|
*/
|
2016-07-05 17:28:57 +03:00
|
|
|
mirror_start(job_id, bs, target,
|
2015-12-24 07:45:03 +03:00
|
|
|
has_replaces ? replaces : NULL,
|
block/mirror: Fix target backing BDS
Currently, we are trying to move the backing BDS from the source to the
target in bdrv_replace_in_backing_chain() which is called from
mirror_exit(). However, mirror_complete() already tries to open the
target's backing chain with a call to bdrv_open_backing_file().
First, we should only set the target's backing BDS once. Second, the
mirroring block job has a better idea of what to set it to than the
generic code in bdrv_replace_in_backing_chain() (in fact, the latter's
conditions on when to move the backing BDS from source to target are not
really correct).
Therefore, remove that code from bdrv_replace_in_backing_chain() and
leave it to mirror_complete().
Depending on what kind of mirroring is performed, we furthermore want to
use different strategies to open the target's backing chain:
- If blockdev-mirror is used, we can assume the user made sure that the
target already has the correct backing chain. In particular, we should
not try to open a backing file if the target does not have any yet.
- If drive-mirror with mode=absolute-paths is used, we can and should
reuse the already existing chain of nodes that the source BDS is in.
In case of sync=full, no backing BDS is required; with sync=top, we
just link the source's backing BDS to the target, and with sync=none,
we use the source BDS as the target's backing BDS.
We should not try to open these backing files anew because this would
lead to two BDSs existing per physical file in the backing chain, and
we would like to avoid such concurrent access.
- If drive-mirror with mode=existing is used, we have to use the
information provided in the physical image file which means opening
the target's backing chain completely anew, just as it has been done
already.
If the target's backing chain shares images with the source, this may
lead to multiple BDSs per physical image file. But since we cannot
reliably ascertain this case, there is nothing we can do about it.
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20160610185750.30956-3-mreitz@redhat.com
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-06-10 21:57:47 +03:00
|
|
|
speed, granularity, buf_size, sync, backing_mode,
|
2017-02-20 20:10:05 +03:00
|
|
|
on_source_error, on_target_error, unmap, filter_node_name,
|
|
|
|
errp);
|
2015-12-24 07:45:03 +03:00
|
|
|
}
|
|
|
|
|
2016-07-15 01:37:58 +03:00
|
|
|
void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
2015-12-24 07:45:03 +03:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *source, *target_bs;
|
|
|
|
AioContext *aio_context;
|
block/mirror: Fix target backing BDS
Currently, we are trying to move the backing BDS from the source to the
target in bdrv_replace_in_backing_chain() which is called from
mirror_exit(). However, mirror_complete() already tries to open the
target's backing chain with a call to bdrv_open_backing_file().
First, we should only set the target's backing BDS once. Second, the
mirroring block job has a better idea of what to set it to than the
generic code in bdrv_replace_in_backing_chain() (in fact, the latter's
conditions on when to move the backing BDS from source to target are not
really correct).
Therefore, remove that code from bdrv_replace_in_backing_chain() and
leave it to mirror_complete().
Depending on what kind of mirroring is performed, we furthermore want to
use different strategies to open the target's backing chain:
- If blockdev-mirror is used, we can assume the user made sure that the
target already has the correct backing chain. In particular, we should
not try to open a backing file if the target does not have any yet.
- If drive-mirror with mode=absolute-paths is used, we can and should
reuse the already existing chain of nodes that the source BDS is in.
In case of sync=full, no backing BDS is required; with sync=top, we
just link the source's backing BDS to the target, and with sync=none,
we use the source BDS as the target's backing BDS.
We should not try to open these backing files anew because this would
lead to two BDSs existing per physical file in the backing chain, and
we would like to avoid such concurrent access.
- If drive-mirror with mode=existing is used, we have to use the
information provided in the physical image file which means opening
the target's backing chain completely anew, just as it has been done
already.
If the target's backing chain shares images with the source, this may
lead to multiple BDSs per physical image file. But since we cannot
reliably ascertain this case, there is nothing we can do about it.
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20160610185750.30956-3-mreitz@redhat.com
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-06-10 21:57:47 +03:00
|
|
|
BlockMirrorBackingMode backing_mode;
|
2015-12-24 07:45:03 +03:00
|
|
|
Error *local_err = NULL;
|
|
|
|
QDict *options = NULL;
|
|
|
|
int flags;
|
|
|
|
int64_t size;
|
2016-07-15 01:37:58 +03:00
|
|
|
const char *format = arg->format;
|
2015-12-24 07:45:03 +03:00
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
bs = qmp_get_root_bs(arg->device, errp);
|
|
|
|
if (!bs) {
|
2012-10-18 18:49:24 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-10-21 15:03:58 +04:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-07-15 01:37:58 +03:00
|
|
|
if (!arg->has_mode) {
|
|
|
|
arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
2015-12-24 07:45:03 +03:00
|
|
|
}
|
2012-10-18 18:49:24 +04:00
|
|
|
|
2016-07-15 01:37:58 +03:00
|
|
|
if (!arg->has_format) {
|
|
|
|
format = (arg->mode == NEW_IMAGE_MODE_EXISTING
|
|
|
|
? NULL : bs->drv->format_name);
|
2012-10-18 18:49:24 +04:00
|
|
|
}
|
|
|
|
|
2016-03-18 19:46:45 +03:00
|
|
|
flags = bs->open_flags | BDRV_O_RDWR;
|
2015-06-17 15:55:21 +03:00
|
|
|
source = backing_bs(bs);
|
2016-07-15 01:37:58 +03:00
|
|
|
if (!source && arg->sync == MIRROR_SYNC_MODE_TOP) {
|
|
|
|
arg->sync = MIRROR_SYNC_MODE_FULL;
|
2012-10-18 18:49:24 +04:00
|
|
|
}
|
2016-07-15 01:37:58 +03:00
|
|
|
if (arg->sync == MIRROR_SYNC_MODE_NONE) {
|
block/drive-mirror: Reuse backing HD for sync=none
For "none" sync mode in "absolute-paths" mode, the current image should
be used as the backing file for the newly created image.
The current behavior is:
a) If the image to be mirrored has a backing file, use that (which is
wrong, since the operations recorded by "none" are applied to the
image itself, not to its backing file).
b) If the image to be mirrored lacks a backing file, the target doesn't
have one either (which is not really wrong, but not really right,
either; "none" records a set of operations executed on the image
file, therefore having no backing file to apply these operations on
seems rather pointless).
For a, this is clearly a bugfix. For b, it is still a bugfix, although
it might break existing API - but since that case crashed qemu just
three weeks ago (before 1452686495922b81d6cf43edf025c1aef15965c0), we
can safely assume there is no such API relying on that case yet.
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 1385407736-13941-2-git-send-email-mreitz@redhat.com
Signed-off-by: Anthony Liguori <aliguori@amazon.com>
2013-11-25 23:28:55 +04:00
|
|
|
source = bs;
|
|
|
|
}
|
2012-10-18 18:49:24 +04:00
|
|
|
|
2013-06-24 19:13:13 +04:00
|
|
|
size = bdrv_getlength(bs);
|
|
|
|
if (size < 0) {
|
|
|
|
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
2014-10-21 15:03:58 +04:00
|
|
|
goto out;
|
2013-06-24 19:13:13 +04:00
|
|
|
}
|
|
|
|
|
2016-07-15 01:37:58 +03:00
|
|
|
if (arg->has_replaces) {
|
2014-06-27 20:25:25 +04:00
|
|
|
BlockDriverState *to_replace_bs;
|
2014-10-21 15:03:58 +04:00
|
|
|
AioContext *replace_aio_context;
|
|
|
|
int64_t replace_size;
|
2014-06-27 20:25:25 +04:00
|
|
|
|
2016-07-15 01:37:58 +03:00
|
|
|
if (!arg->has_node_name) {
|
2014-06-27 20:25:25 +04:00
|
|
|
error_setg(errp, "a node-name must be provided when replacing a"
|
|
|
|
" named node of the graph");
|
2014-10-21 15:03:58 +04:00
|
|
|
goto out;
|
2014-06-27 20:25:25 +04:00
|
|
|
}
|
|
|
|
|
2016-07-15 01:37:58 +03:00
|
|
|
to_replace_bs = check_to_replace_node(bs, arg->replaces, &local_err);
|
2014-06-27 20:25:25 +04:00
|
|
|
|
|
|
|
if (!to_replace_bs) {
|
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 15:03:58 +04:00
|
|
|
goto out;
|
2014-06-27 20:25:25 +04:00
|
|
|
}
|
|
|
|
|
2014-10-21 15:03:58 +04:00
|
|
|
replace_aio_context = bdrv_get_aio_context(to_replace_bs);
|
|
|
|
aio_context_acquire(replace_aio_context);
|
|
|
|
replace_size = bdrv_getlength(to_replace_bs);
|
|
|
|
aio_context_release(replace_aio_context);
|
|
|
|
|
|
|
|
if (size != replace_size) {
|
2014-06-27 20:25:25 +04:00
|
|
|
error_setg(errp, "cannot replace image with a mirror image of "
|
|
|
|
"different size");
|
2014-10-21 15:03:58 +04:00
|
|
|
goto out;
|
2014-06-27 20:25:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-15 01:37:58 +03:00
|
|
|
if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
|
block/mirror: Fix target backing BDS
Currently, we are trying to move the backing BDS from the source to the
target in bdrv_replace_in_backing_chain() which is called from
mirror_exit(). However, mirror_complete() already tries to open the
target's backing chain with a call to bdrv_open_backing_file().
First, we should only set the target's backing BDS once. Second, the
mirroring block job has a better idea of what to set it to than the
generic code in bdrv_replace_in_backing_chain() (in fact, the latter's
conditions on when to move the backing BDS from source to target are not
really correct).
Therefore, remove that code from bdrv_replace_in_backing_chain() and
leave it to mirror_complete().
Depending on what kind of mirroring is performed, we furthermore want to
use different strategies to open the target's backing chain:
- If blockdev-mirror is used, we can assume the user made sure that the
target already has the correct backing chain. In particular, we should
not try to open a backing file if the target does not have any yet.
- If drive-mirror with mode=absolute-paths is used, we can and should
reuse the already existing chain of nodes that the source BDS is in.
In case of sync=full, no backing BDS is required; with sync=top, we
just link the source's backing BDS to the target, and with sync=none,
we use the source BDS as the target's backing BDS.
We should not try to open these backing files anew because this would
lead to two BDSs existing per physical file in the backing chain, and
we would like to avoid such concurrent access.
- If drive-mirror with mode=existing is used, we have to use the
information provided in the physical image file which means opening
the target's backing chain completely anew, just as it has been done
already.
If the target's backing chain shares images with the source, this may
lead to multiple BDSs per physical image file. But since we cannot
reliably ascertain this case, there is nothing we can do about it.
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20160610185750.30956-3-mreitz@redhat.com
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-06-10 21:57:47 +03:00
|
|
|
backing_mode = MIRROR_SOURCE_BACKING_CHAIN;
|
|
|
|
} else {
|
|
|
|
backing_mode = MIRROR_OPEN_BACKING_CHAIN;
|
|
|
|
}
|
|
|
|
|
2017-07-18 03:34:21 +03:00
|
|
|
/* Don't open backing image in create() */
|
|
|
|
flags |= BDRV_O_NO_BACKING;
|
|
|
|
|
2016-07-15 01:37:58 +03:00
|
|
|
if ((arg->sync == MIRROR_SYNC_MODE_FULL || !source)
|
|
|
|
&& arg->mode != NEW_IMAGE_MODE_EXISTING)
|
2013-11-06 22:50:44 +04:00
|
|
|
{
|
2012-10-18 18:49:24 +04:00
|
|
|
/* create new image w/o backing file */
|
2015-08-26 20:47:48 +03:00
|
|
|
assert(format);
|
2016-07-15 01:37:58 +03:00
|
|
|
bdrv_img_create(arg->target, format,
|
2017-04-21 15:27:01 +03:00
|
|
|
NULL, NULL, NULL, size, flags, false, &local_err);
|
2012-10-18 18:49:24 +04:00
|
|
|
} else {
|
2016-07-15 01:37:58 +03:00
|
|
|
switch (arg->mode) {
|
2012-10-18 18:49:24 +04:00
|
|
|
case NEW_IMAGE_MODE_EXISTING:
|
|
|
|
break;
|
|
|
|
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
|
|
|
|
/* create new image with backing file */
|
2016-07-15 01:37:58 +03:00
|
|
|
bdrv_img_create(arg->target, format,
|
2012-11-30 16:52:08 +04:00
|
|
|
source->filename,
|
|
|
|
source->drv->format_name,
|
2017-04-21 15:27:01 +03:00
|
|
|
NULL, size, flags, false, &local_err);
|
2012-10-18 18:49:24 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2012-11-30 16:52:08 +04:00
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 15:03:58 +04:00
|
|
|
goto out;
|
2012-10-18 18:49:24 +04:00
|
|
|
}
|
|
|
|
|
2015-08-26 20:47:48 +03:00
|
|
|
options = qdict_new();
|
2016-07-15 01:37:58 +03:00
|
|
|
if (arg->has_node_name) {
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(options, "node-name", arg->node_name);
|
2014-06-16 14:00:55 +04:00
|
|
|
}
|
2015-08-26 20:47:48 +03:00
|
|
|
if (format) {
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(options, "driver", format);
|
2015-08-26 20:47:48 +03:00
|
|
|
}
|
2014-06-16 14:00:55 +04:00
|
|
|
|
2013-01-21 20:09:43 +04:00
|
|
|
/* Mirroring takes care of copy-on-write using the source's backing
|
|
|
|
* file.
|
|
|
|
*/
|
2017-07-18 03:34:21 +03:00
|
|
|
target_bs = bdrv_open(arg->target, NULL, options, flags, errp);
|
2016-05-17 17:41:31 +03:00
|
|
|
if (!target_bs) {
|
2014-10-21 15:03:58 +04:00
|
|
|
goto out;
|
2012-10-18 18:49:24 +04:00
|
|
|
}
|
|
|
|
|
2014-10-21 15:03:58 +04:00
|
|
|
bdrv_set_aio_context(target_bs, aio_context);
|
|
|
|
|
2016-07-15 01:37:58 +03:00
|
|
|
blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs,
|
|
|
|
arg->has_replaces, arg->replaces, arg->sync,
|
|
|
|
backing_mode, arg->has_speed, arg->speed,
|
|
|
|
arg->has_granularity, arg->granularity,
|
|
|
|
arg->has_buf_size, arg->buf_size,
|
|
|
|
arg->has_on_source_error, arg->on_source_error,
|
|
|
|
arg->has_on_target_error, arg->on_target_error,
|
|
|
|
arg->has_unmap, arg->unmap,
|
2017-02-20 20:10:05 +03:00
|
|
|
false, NULL,
|
2015-12-24 07:45:03 +03:00
|
|
|
&local_err);
|
2016-04-12 17:17:41 +03:00
|
|
|
bdrv_unref(target_bs);
|
2016-06-14 00:57:56 +03:00
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 15:03:58 +04:00
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2012-10-18 18:49:24 +04:00
|
|
|
}
|
|
|
|
|
2016-07-05 17:28:57 +03:00
|
|
|
void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
|
|
|
|
const char *device, const char *target,
|
2015-12-24 07:45:05 +03:00
|
|
|
bool has_replaces, const char *replaces,
|
|
|
|
MirrorSyncMode sync,
|
|
|
|
bool has_speed, int64_t speed,
|
|
|
|
bool has_granularity, uint32_t granularity,
|
|
|
|
bool has_buf_size, int64_t buf_size,
|
|
|
|
bool has_on_source_error,
|
|
|
|
BlockdevOnError on_source_error,
|
|
|
|
bool has_on_target_error,
|
|
|
|
BlockdevOnError on_target_error,
|
2017-02-20 20:10:05 +03:00
|
|
|
bool has_filter_node_name,
|
|
|
|
const char *filter_node_name,
|
2015-12-24 07:45:05 +03:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *target_bs;
|
|
|
|
AioContext *aio_context;
|
block/mirror: Fix target backing BDS
Currently, we are trying to move the backing BDS from the source to the
target in bdrv_replace_in_backing_chain() which is called from
mirror_exit(). However, mirror_complete() already tries to open the
target's backing chain with a call to bdrv_open_backing_file().
First, we should only set the target's backing BDS once. Second, the
mirroring block job has a better idea of what to set it to than the
generic code in bdrv_replace_in_backing_chain() (in fact, the latter's
conditions on when to move the backing BDS from source to target are not
really correct).
Therefore, remove that code from bdrv_replace_in_backing_chain() and
leave it to mirror_complete().
Depending on what kind of mirroring is performed, we furthermore want to
use different strategies to open the target's backing chain:
- If blockdev-mirror is used, we can assume the user made sure that the
target already has the correct backing chain. In particular, we should
not try to open a backing file if the target does not have any yet.
- If drive-mirror with mode=absolute-paths is used, we can and should
reuse the already existing chain of nodes that the source BDS is in.
In case of sync=full, no backing BDS is required; with sync=top, we
just link the source's backing BDS to the target, and with sync=none,
we use the source BDS as the target's backing BDS.
We should not try to open these backing files anew because this would
lead to two BDSs existing per physical file in the backing chain, and
we would like to avoid such concurrent access.
- If drive-mirror with mode=existing is used, we have to use the
information provided in the physical image file which means opening
the target's backing chain completely anew, just as it has been done
already.
If the target's backing chain shares images with the source, this may
lead to multiple BDSs per physical image file. But since we cannot
reliably ascertain this case, there is nothing we can do about it.
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20160610185750.30956-3-mreitz@redhat.com
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-06-10 21:57:47 +03:00
|
|
|
BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN;
|
2015-12-24 07:45:05 +03:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
bs = qmp_get_root_bs(device, errp);
|
2015-12-24 07:45:05 +03:00
|
|
|
if (!bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
target_bs = bdrv_lookup_bs(target, target, errp);
|
|
|
|
if (!target_bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
bdrv_set_aio_context(target_bs, aio_context);
|
|
|
|
|
2016-07-05 17:28:57 +03:00
|
|
|
blockdev_mirror_common(has_job_id ? job_id : NULL, bs, target_bs,
|
block/mirror: Fix target backing BDS
Currently, we are trying to move the backing BDS from the source to the
target in bdrv_replace_in_backing_chain() which is called from
mirror_exit(). However, mirror_complete() already tries to open the
target's backing chain with a call to bdrv_open_backing_file().
First, we should only set the target's backing BDS once. Second, the
mirroring block job has a better idea of what to set it to than the
generic code in bdrv_replace_in_backing_chain() (in fact, the latter's
conditions on when to move the backing BDS from source to target are not
really correct).
Therefore, remove that code from bdrv_replace_in_backing_chain() and
leave it to mirror_complete().
Depending on what kind of mirroring is performed, we furthermore want to
use different strategies to open the target's backing chain:
- If blockdev-mirror is used, we can assume the user made sure that the
target already has the correct backing chain. In particular, we should
not try to open a backing file if the target does not have any yet.
- If drive-mirror with mode=absolute-paths is used, we can and should
reuse the already existing chain of nodes that the source BDS is in.
In case of sync=full, no backing BDS is required; with sync=top, we
just link the source's backing BDS to the target, and with sync=none,
we use the source BDS as the target's backing BDS.
We should not try to open these backing files anew because this would
lead to two BDSs existing per physical file in the backing chain, and
we would like to avoid such concurrent access.
- If drive-mirror with mode=existing is used, we have to use the
information provided in the physical image file which means opening
the target's backing chain completely anew, just as it has been done
already.
If the target's backing chain shares images with the source, this may
lead to multiple BDSs per physical image file. But since we cannot
reliably ascertain this case, there is nothing we can do about it.
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20160610185750.30956-3-mreitz@redhat.com
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-06-10 21:57:47 +03:00
|
|
|
has_replaces, replaces, sync, backing_mode,
|
2015-12-24 07:45:05 +03:00
|
|
|
has_speed, speed,
|
|
|
|
has_granularity, granularity,
|
|
|
|
has_buf_size, buf_size,
|
|
|
|
has_on_source_error, on_source_error,
|
|
|
|
has_on_target_error, on_target_error,
|
|
|
|
true, true,
|
2017-02-20 20:10:05 +03:00
|
|
|
has_filter_node_name, filter_node_name,
|
2015-12-24 07:45:05 +03:00
|
|
|
&local_err);
|
2016-06-14 00:57:56 +03:00
|
|
|
error_propagate(errp, local_err);
|
2015-12-24 07:45:05 +03:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
}
|
|
|
|
|
2016-07-05 17:28:55 +03:00
|
|
|
/* Get a block job using its ID and acquire its AioContext */
|
|
|
|
static BlockJob *find_block_job(const char *id, AioContext **aio_context,
|
2015-01-29 12:36:58 +03:00
|
|
|
Error **errp)
|
2012-01-18 18:40:47 +04:00
|
|
|
{
|
2016-07-05 17:28:55 +03:00
|
|
|
BlockJob *job;
|
2012-01-18 18:40:47 +04:00
|
|
|
|
2016-07-05 17:28:55 +03:00
|
|
|
assert(id != NULL);
|
2015-10-19 18:53:29 +03:00
|
|
|
|
2016-07-05 17:28:55 +03:00
|
|
|
*aio_context = NULL;
|
2014-10-21 15:03:50 +04:00
|
|
|
|
2016-07-05 17:28:55 +03:00
|
|
|
job = block_job_get(id);
|
2015-10-19 18:53:29 +03:00
|
|
|
|
2016-07-05 17:28:55 +03:00
|
|
|
if (!job) {
|
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
|
|
|
|
"Block job '%s' not found", id);
|
|
|
|
return NULL;
|
2012-01-18 18:40:47 +04:00
|
|
|
}
|
2014-10-21 15:03:50 +04:00
|
|
|
|
2016-07-05 17:28:55 +03:00
|
|
|
*aio_context = blk_get_aio_context(job->blk);
|
|
|
|
aio_context_acquire(*aio_context);
|
2014-10-21 15:03:50 +04:00
|
|
|
|
2016-07-05 17:28:55 +03:00
|
|
|
return job;
|
2012-01-18 18:40:47 +04:00
|
|
|
}
|
|
|
|
|
2012-04-25 19:51:02 +04:00
|
|
|
void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
|
2012-01-18 18:40:47 +04:00
|
|
|
{
|
2014-10-21 15:03:50 +04:00
|
|
|
AioContext *aio_context;
|
2015-01-29 12:36:58 +03:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-01-18 18:40:47 +04:00
|
|
|
|
|
|
|
if (!job) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-25 19:51:02 +04:00
|
|
|
block_job_set_speed(job, speed, errp);
|
2014-10-21 15:03:50 +04:00
|
|
|
aio_context_release(aio_context);
|
2012-01-18 18:40:47 +04:00
|
|
|
}
|
2012-01-18 18:40:48 +04:00
|
|
|
|
2012-09-28 19:22:51 +04:00
|
|
|
void qmp_block_job_cancel(const char *device,
|
|
|
|
bool has_force, bool force, Error **errp)
|
2012-01-18 18:40:48 +04:00
|
|
|
{
|
2014-10-21 15:03:50 +04:00
|
|
|
AioContext *aio_context;
|
2015-01-29 12:36:58 +03:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-09-28 19:22:51 +04:00
|
|
|
|
2012-01-18 18:40:48 +04:00
|
|
|
if (!job) {
|
|
|
|
return;
|
|
|
|
}
|
2014-10-21 15:03:50 +04:00
|
|
|
|
|
|
|
if (!has_force) {
|
|
|
|
force = false;
|
|
|
|
}
|
|
|
|
|
2016-10-27 19:06:59 +03:00
|
|
|
if (block_job_user_paused(job) && !force) {
|
2014-03-22 03:42:26 +04:00
|
|
|
error_setg(errp, "The block job for device '%s' is currently paused",
|
|
|
|
device);
|
2014-10-21 15:03:50 +04:00
|
|
|
goto out;
|
2012-09-28 19:22:50 +04:00
|
|
|
}
|
2012-01-18 18:40:48 +04:00
|
|
|
|
|
|
|
trace_qmp_block_job_cancel(job);
|
|
|
|
block_job_cancel(job);
|
2014-10-21 15:03:50 +04:00
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2012-01-18 18:40:48 +04:00
|
|
|
}
|
2012-01-18 18:40:49 +04:00
|
|
|
|
2012-09-28 19:22:51 +04:00
|
|
|
void qmp_block_job_pause(const char *device, Error **errp)
|
|
|
|
{
|
2014-10-21 15:03:50 +04:00
|
|
|
AioContext *aio_context;
|
2015-01-29 12:36:58 +03:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-09-28 19:22:51 +04:00
|
|
|
|
2016-10-27 19:06:59 +03:00
|
|
|
if (!job || block_job_user_paused(job)) {
|
2012-09-28 19:22:51 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_pause(job);
|
2016-10-27 19:06:59 +03:00
|
|
|
block_job_user_pause(job);
|
2014-10-21 15:03:50 +04:00
|
|
|
aio_context_release(aio_context);
|
2012-09-28 19:22:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void qmp_block_job_resume(const char *device, Error **errp)
|
|
|
|
{
|
2014-10-21 15:03:50 +04:00
|
|
|
AioContext *aio_context;
|
2015-01-29 12:36:58 +03:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-09-28 19:22:51 +04:00
|
|
|
|
2016-10-27 19:06:59 +03:00
|
|
|
if (!job || !block_job_user_paused(job)) {
|
2012-09-28 19:22:51 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_resume(job);
|
2016-10-27 19:06:59 +03:00
|
|
|
block_job_user_resume(job);
|
2014-10-21 15:03:50 +04:00
|
|
|
aio_context_release(aio_context);
|
2012-09-28 19:22:51 +04:00
|
|
|
}
|
|
|
|
|
2012-10-18 18:49:21 +04:00
|
|
|
void qmp_block_job_complete(const char *device, Error **errp)
|
|
|
|
{
|
2014-10-21 15:03:50 +04:00
|
|
|
AioContext *aio_context;
|
2015-01-29 12:36:58 +03:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-10-18 18:49:21 +04:00
|
|
|
|
|
|
|
if (!job) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_complete(job);
|
|
|
|
block_job_complete(job, errp);
|
2014-10-21 15:03:50 +04:00
|
|
|
aio_context_release(aio_context);
|
2012-10-18 18:49:21 +04:00
|
|
|
}
|
|
|
|
|
2014-07-01 11:52:16 +04:00
|
|
|
void qmp_change_backing_file(const char *device,
|
|
|
|
const char *image_node_name,
|
|
|
|
const char *backing_file,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs = NULL;
|
2014-11-19 17:19:45 +03:00
|
|
|
AioContext *aio_context;
|
2014-07-01 11:52:16 +04:00
|
|
|
BlockDriverState *image_bs = NULL;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
bool ro;
|
|
|
|
int open_flags;
|
|
|
|
int ret;
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
bs = qmp_get_root_bs(device, errp);
|
|
|
|
if (!bs) {
|
2014-07-01 11:52:16 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-23 15:20:24 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-11-19 17:19:45 +03:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2014-07-01 11:52:16 +04:00
|
|
|
image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2014-11-19 17:19:45 +03:00
|
|
|
goto out;
|
2014-07-01 11:52:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!image_bs) {
|
|
|
|
error_setg(errp, "image file not found");
|
2014-11-19 17:19:45 +03:00
|
|
|
goto out;
|
2014-07-01 11:52:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_find_base(image_bs) == image_bs) {
|
|
|
|
error_setg(errp, "not allowing backing file change on an image "
|
|
|
|
"without a backing file");
|
2014-11-19 17:19:45 +03:00
|
|
|
goto out;
|
2014-07-01 11:52:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* even though we are not necessarily operating on bs, we need it to
|
|
|
|
* determine if block ops are currently prohibited on the chain */
|
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) {
|
2014-11-19 17:19:45 +03:00
|
|
|
goto out;
|
2014-07-01 11:52:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* final sanity check */
|
|
|
|
if (!bdrv_chain_contains(bs, image_bs)) {
|
|
|
|
error_setg(errp, "'%s' and image file are not in the same chain",
|
|
|
|
device);
|
2014-11-19 17:19:45 +03:00
|
|
|
goto out;
|
2014-07-01 11:52:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if not r/w, reopen to make r/w */
|
|
|
|
open_flags = image_bs->open_flags;
|
|
|
|
ro = bdrv_is_read_only(image_bs);
|
|
|
|
|
|
|
|
if (ro) {
|
|
|
|
bdrv_reopen(image_bs, open_flags | BDRV_O_RDWR, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2014-11-19 17:19:45 +03:00
|
|
|
goto out;
|
2014-07-01 11:52:16 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = bdrv_change_backing_file(image_bs, backing_file,
|
|
|
|
image_bs->drv ? image_bs->drv->format_name : "");
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
error_setg_errno(errp, -ret, "Could not change backing file to '%s'",
|
|
|
|
backing_file);
|
|
|
|
/* don't exit here, so we can try to restore open flags if
|
|
|
|
* appropriate */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ro) {
|
|
|
|
bdrv_reopen(image_bs, open_flags, &local_err);
|
2016-06-14 00:57:56 +03:00
|
|
|
error_propagate(errp, local_err);
|
2014-07-01 11:52:16 +04:00
|
|
|
}
|
2014-11-19 17:19:45 +03:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2014-07-01 11:52:16 +04:00
|
|
|
}
|
|
|
|
|
2016-02-23 19:33:24 +03:00
|
|
|
void hmp_drive_add_node(Monitor *mon, const char *optstr)
|
|
|
|
{
|
|
|
|
QemuOpts *opts;
|
|
|
|
QDict *qdict;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false);
|
|
|
|
if (!opts) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qdict = qemu_opts_to_qdict(opts, NULL);
|
|
|
|
|
|
|
|
if (!qdict_get_try_str(qdict, "node-name")) {
|
2016-03-16 13:14:31 +03:00
|
|
|
QDECREF(qdict);
|
2016-02-23 19:33:24 +03:00
|
|
|
error_report("'node-name' needs to be specified");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockDriverState *bs = bds_tree_init(qdict, &local_err);
|
|
|
|
if (!bs) {
|
|
|
|
error_report_err(local_err);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
|
|
|
|
|
|
|
|
out:
|
|
|
|
qemu_opts_del(opts);
|
|
|
|
}
|
|
|
|
|
2013-09-23 17:26:03 +04:00
|
|
|
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
|
|
|
{
|
2015-10-19 18:53:09 +03:00
|
|
|
BlockDriverState *bs;
|
2013-09-23 17:26:03 +04:00
|
|
|
QObject *obj;
|
2016-09-30 17:45:28 +03:00
|
|
|
Visitor *v = qobject_output_visitor_new(&obj);
|
2013-09-23 17:26:03 +04:00
|
|
|
QDict *qdict;
|
block: Use JSON null instead of "" to disable backing file
BlockdevRef is an alternate of BlockdevOptions (inline definition) and
str (reference to an existing block device by name). BlockdevRef
value "" is special: "no block device should be referenced." It's
actually interpreted that way in just one place: optional member
@backing of COW formats. Semantics:
* Present means "use this block device" as backing storage
* Absent means "default to the one stored in the image"
* Except "" means "don't use backing storage at all"
The first two are perfectly normal: when the parameter is absent, it
defaults to an implied value, but the value's meaning is the same.
The third one overloads the parameter with a second meaning. The
overloading is *implicit*, i.e. it's not visible in the types. Works
here, because "" is not a value block device ID.
Pressing argument values the schema accepts, but are semantically
invalid, into service to mean "do something else entirely" is not
general, as suitable invalid values need not exist. I also find it
ugly.
To clean this up, we could add a separate flag argument to suppress
@backing, or add a distinct value to @backing. This commit implements
the latter: add JSON null to the values of @backing, deprecate "".
Because we're so close to the 2.10 freeze, implement it in the
stupidest way possible: have qmp_blockdev_add() rewrite null to ""
before anything else can see the null. Works, because BlockdevRef
occurs only within arguments of blockdev-add. The proper way to do it
would be rewriting "" to null, preferably in a cleaner way, but that
requires fixing up code to work with null. Add a TODO comment for
that.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
Acked-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
2017-07-18 09:54:00 +03:00
|
|
|
const QDictEntry *ent;
|
2013-09-23 17:26:03 +04:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
qapi: Add new visit_complete() function
Making each output visitor provide its own output collection
function was the only remaining reason for exposing visitor
sub-types to the rest of the code base. Add a polymorphic
visit_complete() function which is a no-op for input visitors,
and which populates an opaque pointer for output visitors. For
maximum type-safety, also add a parameter to the output visitor
constructors with a type-correct version of the output pointer,
and assert that the two uses match.
This approach was considered superior to either passing the
output parameter only during construction (action at a distance
during visit_free() feels awkward) or only during visit_complete()
(defeating type safety makes it easier to use incorrectly).
Most callers were function-local, and therefore a mechanical
conversion; the testsuite was a bit trickier, but the previous
cleanup patch minimized the churn here.
The visit_complete() function may be called at most once; doing
so lets us use transfer semantics rather than duplication or
ref-count semantics to get the just-built output back to the
caller, even though it means our behavior is not idempotent.
Generated code is simplified as follows for events:
|@@ -26,7 +26,7 @@ void qapi_event_send_acpi_device_ost(ACP
| QDict *qmp;
| Error *err = NULL;
| QMPEventFuncEmit emit;
|- QmpOutputVisitor *qov;
|+ QObject *obj;
| Visitor *v;
| q_obj_ACPI_DEVICE_OST_arg param = {
| info
|@@ -39,8 +39,7 @@ void qapi_event_send_acpi_device_ost(ACP
|
| qmp = qmp_event_build_dict("ACPI_DEVICE_OST");
|
|- qov = qmp_output_visitor_new();
|- v = qmp_output_get_visitor(qov);
|+ v = qmp_output_visitor_new(&obj);
|
| visit_start_struct(v, "ACPI_DEVICE_OST", NULL, 0, &err);
| if (err) {
|@@ -55,7 +54,8 @@ void qapi_event_send_acpi_device_ost(ACP
| goto out;
| }
|
|- qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov));
|+ visit_complete(v, &obj);
|+ qdict_put_obj(qmp, "data", obj);
| emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);
and for commands:
| {
| Error *err = NULL;
|- QmpOutputVisitor *qov = qmp_output_visitor_new();
| Visitor *v;
|
|- v = qmp_output_get_visitor(qov);
|+ v = qmp_output_visitor_new(ret_out);
| visit_type_AddfdInfo(v, "unused", &ret_in, &err);
|- if (err) {
|- goto out;
|+ if (!err) {
|+ visit_complete(v, ret_out);
| }
|- *ret_out = qmp_output_get_qobject(qov);
|-
|-out:
| error_propagate(errp, err);
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-13-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:43 +03:00
|
|
|
visit_type_BlockdevOptions(v, NULL, &options, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2013-09-23 17:26:03 +04:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
qapi: Add new visit_complete() function
Making each output visitor provide its own output collection
function was the only remaining reason for exposing visitor
sub-types to the rest of the code base. Add a polymorphic
visit_complete() function which is a no-op for input visitors,
and which populates an opaque pointer for output visitors. For
maximum type-safety, also add a parameter to the output visitor
constructors with a type-correct version of the output pointer,
and assert that the two uses match.
This approach was considered superior to either passing the
output parameter only during construction (action at a distance
during visit_free() feels awkward) or only during visit_complete()
(defeating type safety makes it easier to use incorrectly).
Most callers were function-local, and therefore a mechanical
conversion; the testsuite was a bit trickier, but the previous
cleanup patch minimized the churn here.
The visit_complete() function may be called at most once; doing
so lets us use transfer semantics rather than duplication or
ref-count semantics to get the just-built output back to the
caller, even though it means our behavior is not idempotent.
Generated code is simplified as follows for events:
|@@ -26,7 +26,7 @@ void qapi_event_send_acpi_device_ost(ACP
| QDict *qmp;
| Error *err = NULL;
| QMPEventFuncEmit emit;
|- QmpOutputVisitor *qov;
|+ QObject *obj;
| Visitor *v;
| q_obj_ACPI_DEVICE_OST_arg param = {
| info
|@@ -39,8 +39,7 @@ void qapi_event_send_acpi_device_ost(ACP
|
| qmp = qmp_event_build_dict("ACPI_DEVICE_OST");
|
|- qov = qmp_output_visitor_new();
|- v = qmp_output_get_visitor(qov);
|+ v = qmp_output_visitor_new(&obj);
|
| visit_start_struct(v, "ACPI_DEVICE_OST", NULL, 0, &err);
| if (err) {
|@@ -55,7 +54,8 @@ void qapi_event_send_acpi_device_ost(ACP
| goto out;
| }
|
|- qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov));
|+ visit_complete(v, &obj);
|+ qdict_put_obj(qmp, "data", obj);
| emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);
and for commands:
| {
| Error *err = NULL;
|- QmpOutputVisitor *qov = qmp_output_visitor_new();
| Visitor *v;
|
|- v = qmp_output_get_visitor(qov);
|+ v = qmp_output_visitor_new(ret_out);
| visit_type_AddfdInfo(v, "unused", &ret_in, &err);
|- if (err) {
|- goto out;
|+ if (!err) {
|+ visit_complete(v, ret_out);
| }
|- *ret_out = qmp_output_get_qobject(qov);
|-
|-out:
| error_propagate(errp, err);
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-13-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:43 +03:00
|
|
|
visit_complete(v, &obj);
|
2013-09-23 17:26:03 +04:00
|
|
|
qdict = qobject_to_qdict(obj);
|
|
|
|
|
|
|
|
qdict_flatten(qdict);
|
|
|
|
|
block: Use JSON null instead of "" to disable backing file
BlockdevRef is an alternate of BlockdevOptions (inline definition) and
str (reference to an existing block device by name). BlockdevRef
value "" is special: "no block device should be referenced." It's
actually interpreted that way in just one place: optional member
@backing of COW formats. Semantics:
* Present means "use this block device" as backing storage
* Absent means "default to the one stored in the image"
* Except "" means "don't use backing storage at all"
The first two are perfectly normal: when the parameter is absent, it
defaults to an implied value, but the value's meaning is the same.
The third one overloads the parameter with a second meaning. The
overloading is *implicit*, i.e. it's not visible in the types. Works
here, because "" is not a value block device ID.
Pressing argument values the schema accepts, but are semantically
invalid, into service to mean "do something else entirely" is not
general, as suitable invalid values need not exist. I also find it
ugly.
To clean this up, we could add a separate flag argument to suppress
@backing, or add a distinct value to @backing. This commit implements
the latter: add JSON null to the values of @backing, deprecate "".
Because we're so close to the 2.10 freeze, implement it in the
stupidest way possible: have qmp_blockdev_add() rewrite null to ""
before anything else can see the null. Works, because BlockdevRef
occurs only within arguments of blockdev-add. The proper way to do it
would be rewriting "" to null, preferably in a cleaner way, but that
requires fixing up code to work with null. Add a TODO comment for
that.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
Acked-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
2017-07-18 09:54:00 +03:00
|
|
|
/*
|
|
|
|
* Rewrite "backing": null to "backing": ""
|
|
|
|
* TODO Rewrite "" to null instead, and perhaps not even here
|
|
|
|
*/
|
|
|
|
for (ent = qdict_first(qdict); ent; ent = qdict_next(qdict, ent)) {
|
|
|
|
char *dot = strrchr(ent->key, '.');
|
|
|
|
|
|
|
|
if (!strcmp(dot ? dot + 1 : ent->key, "backing")
|
|
|
|
&& qobject_type(ent->value) == QTYPE_QNULL) {
|
|
|
|
qdict_put(qdict, ent->key, qstring_new());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-21 15:56:11 +03:00
|
|
|
if (!qdict_get_try_str(qdict, "node-name")) {
|
|
|
|
error_setg(errp, "'node-name' must be specified for the root node");
|
|
|
|
goto fail;
|
|
|
|
}
|
2016-01-29 18:36:12 +03:00
|
|
|
|
2016-09-21 15:56:11 +03:00
|
|
|
bs = bds_tree_init(qdict, errp);
|
|
|
|
if (!bs) {
|
|
|
|
goto fail;
|
2013-09-23 17:26:03 +04:00
|
|
|
}
|
|
|
|
|
2016-09-21 15:56:11 +03:00
|
|
|
QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
|
|
|
|
|
2013-09-23 17:26:03 +04:00
|
|
|
fail:
|
qapi: Add new visit_complete() function
Making each output visitor provide its own output collection
function was the only remaining reason for exposing visitor
sub-types to the rest of the code base. Add a polymorphic
visit_complete() function which is a no-op for input visitors,
and which populates an opaque pointer for output visitors. For
maximum type-safety, also add a parameter to the output visitor
constructors with a type-correct version of the output pointer,
and assert that the two uses match.
This approach was considered superior to either passing the
output parameter only during construction (action at a distance
during visit_free() feels awkward) or only during visit_complete()
(defeating type safety makes it easier to use incorrectly).
Most callers were function-local, and therefore a mechanical
conversion; the testsuite was a bit trickier, but the previous
cleanup patch minimized the churn here.
The visit_complete() function may be called at most once; doing
so lets us use transfer semantics rather than duplication or
ref-count semantics to get the just-built output back to the
caller, even though it means our behavior is not idempotent.
Generated code is simplified as follows for events:
|@@ -26,7 +26,7 @@ void qapi_event_send_acpi_device_ost(ACP
| QDict *qmp;
| Error *err = NULL;
| QMPEventFuncEmit emit;
|- QmpOutputVisitor *qov;
|+ QObject *obj;
| Visitor *v;
| q_obj_ACPI_DEVICE_OST_arg param = {
| info
|@@ -39,8 +39,7 @@ void qapi_event_send_acpi_device_ost(ACP
|
| qmp = qmp_event_build_dict("ACPI_DEVICE_OST");
|
|- qov = qmp_output_visitor_new();
|- v = qmp_output_get_visitor(qov);
|+ v = qmp_output_visitor_new(&obj);
|
| visit_start_struct(v, "ACPI_DEVICE_OST", NULL, 0, &err);
| if (err) {
|@@ -55,7 +54,8 @@ void qapi_event_send_acpi_device_ost(ACP
| goto out;
| }
|
|- qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov));
|+ visit_complete(v, &obj);
|+ qdict_put_obj(qmp, "data", obj);
| emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);
and for commands:
| {
| Error *err = NULL;
|- QmpOutputVisitor *qov = qmp_output_visitor_new();
| Visitor *v;
|
|- v = qmp_output_get_visitor(qov);
|+ v = qmp_output_visitor_new(ret_out);
| visit_type_AddfdInfo(v, "unused", &ret_in, &err);
|- if (err) {
|- goto out;
|+ if (!err) {
|+ visit_complete(v, ret_out);
| }
|- *ret_out = qmp_output_get_qobject(qov);
|-
|-out:
| error_propagate(errp, err);
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-13-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:43 +03:00
|
|
|
visit_free(v);
|
2013-09-23 17:26:03 +04:00
|
|
|
}
|
|
|
|
|
2017-03-21 19:53:28 +03:00
|
|
|
void qmp_blockdev_del(const char *node_name, Error **errp)
|
2015-11-02 17:51:55 +03:00
|
|
|
{
|
|
|
|
AioContext *aio_context;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2016-09-21 15:56:11 +03:00
|
|
|
bs = bdrv_find_node(node_name);
|
|
|
|
if (!bs) {
|
|
|
|
error_setg(errp, "Cannot find node %s", node_name);
|
2015-11-02 17:51:55 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-09-21 15:56:11 +03:00
|
|
|
if (bdrv_has_blk(bs)) {
|
|
|
|
error_setg(errp, "Node %s is in use", node_name);
|
|
|
|
return;
|
2015-11-02 17:51:55 +03:00
|
|
|
}
|
2016-09-21 15:56:11 +03:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2015-11-02 17:51:55 +03:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-09-21 15:56:11 +03:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, errp)) {
|
|
|
|
goto out;
|
|
|
|
}
|
2016-01-29 18:36:12 +03:00
|
|
|
|
2016-09-21 15:56:11 +03:00
|
|
|
if (!bs->monitor_list.tqe_prev) {
|
|
|
|
error_setg(errp, "Node %s is not owned by the monitor",
|
|
|
|
bs->node_name);
|
|
|
|
goto out;
|
2015-11-02 17:51:55 +03:00
|
|
|
}
|
|
|
|
|
2016-09-21 15:56:11 +03:00
|
|
|
if (bs->refcnt > 1) {
|
|
|
|
error_setg(errp, "Block device %s is in use",
|
|
|
|
bdrv_get_device_or_node_name(bs));
|
|
|
|
goto out;
|
2015-11-02 17:51:55 +03:00
|
|
|
}
|
|
|
|
|
2016-09-21 15:56:11 +03:00
|
|
|
QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list);
|
|
|
|
bdrv_unref(bs);
|
|
|
|
|
2015-11-02 17:51:55 +03:00
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
}
|
|
|
|
|
2016-05-10 10:36:39 +03:00
|
|
|
static BdrvChild *bdrv_find_child(BlockDriverState *parent_bs,
|
|
|
|
const char *child_name)
|
|
|
|
{
|
|
|
|
BdrvChild *child;
|
|
|
|
|
|
|
|
QLIST_FOREACH(child, &parent_bs->children, next) {
|
|
|
|
if (strcmp(child->name, child_name) == 0) {
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qmp_x_blockdev_change(const char *parent, bool has_child,
|
|
|
|
const char *child, bool has_node,
|
|
|
|
const char *node, Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *parent_bs, *new_bs = NULL;
|
|
|
|
BdrvChild *p_child;
|
|
|
|
|
|
|
|
parent_bs = bdrv_lookup_bs(parent, parent, errp);
|
|
|
|
if (!parent_bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_child == has_node) {
|
|
|
|
if (has_child) {
|
|
|
|
error_setg(errp, "The parameters child and node are in conflict");
|
|
|
|
} else {
|
|
|
|
error_setg(errp, "Either child or node must be specified");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_child) {
|
|
|
|
p_child = bdrv_find_child(parent_bs, child);
|
|
|
|
if (!p_child) {
|
|
|
|
error_setg(errp, "Node '%s' does not have child '%s'",
|
|
|
|
parent, child);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bdrv_del_child(parent_bs, p_child, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_node) {
|
|
|
|
new_bs = bdrv_find_node(node);
|
|
|
|
if (!new_bs) {
|
|
|
|
error_setg(errp, "Node '%s' not found", node);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bdrv_add_child(parent_bs, new_bs, errp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-07 15:59:10 +04:00
|
|
|
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
|
2012-01-18 18:40:49 +04:00
|
|
|
{
|
2014-10-07 15:59:10 +04:00
|
|
|
BlockJobInfoList *head = NULL, **p_next = &head;
|
2016-05-27 13:53:37 +03:00
|
|
|
BlockJob *job;
|
2012-01-18 18:40:49 +04:00
|
|
|
|
2016-05-27 13:53:37 +03:00
|
|
|
for (job = block_job_next(NULL); job; job = block_job_next(job)) {
|
2016-10-27 19:06:55 +03:00
|
|
|
BlockJobInfoList *elem;
|
|
|
|
AioContext *aio_context;
|
2014-10-21 15:03:51 +04:00
|
|
|
|
2016-10-27 19:06:55 +03:00
|
|
|
if (block_job_is_internal(job)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
elem = g_new0(BlockJobInfoList, 1);
|
|
|
|
aio_context = blk_get_aio_context(job->blk);
|
2014-10-21 15:03:51 +04:00
|
|
|
aio_context_acquire(aio_context);
|
2016-10-27 19:06:55 +03:00
|
|
|
elem->value = block_job_query(job, errp);
|
2014-10-21 15:03:51 +04:00
|
|
|
aio_context_release(aio_context);
|
2016-10-27 19:06:55 +03:00
|
|
|
if (!elem->value) {
|
|
|
|
g_free(elem);
|
|
|
|
qapi_free_BlockJobInfoList(head);
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-05-27 13:53:37 +03:00
|
|
|
*p_next = elem;
|
|
|
|
p_next = &elem->next;
|
2012-01-18 18:40:49 +04:00
|
|
|
}
|
|
|
|
|
2014-10-07 15:59:10 +04:00
|
|
|
return head;
|
2012-01-18 18:40:49 +04:00
|
|
|
}
|
2012-11-26 19:03:42 +04:00
|
|
|
|
2017-12-06 17:45:49 +03:00
|
|
|
void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
|
2017-12-07 23:13:17 +03:00
|
|
|
bool has_force, bool force, Error **errp)
|
2017-12-06 17:45:49 +03:00
|
|
|
{
|
|
|
|
AioContext *old_context;
|
|
|
|
AioContext *new_context;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
bs = bdrv_find_node(node_name);
|
|
|
|
if (!bs) {
|
|
|
|
error_setg(errp, "Cannot find node %s", node_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:13:17 +03:00
|
|
|
/* Protects against accidents. */
|
|
|
|
if (!(has_force && force) && bdrv_has_blk(bs)) {
|
|
|
|
error_setg(errp, "Node %s is associated with a BlockBackend and could "
|
|
|
|
"be in use (use force=true to override this check)",
|
|
|
|
node_name);
|
2017-12-06 17:45:49 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iothread->type == QTYPE_QSTRING) {
|
|
|
|
IOThread *obj = iothread_by_id(iothread->u.s);
|
|
|
|
if (!obj) {
|
|
|
|
error_setg(errp, "Cannot find iothread %s", iothread->u.s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_context = iothread_get_aio_context(obj);
|
|
|
|
} else {
|
|
|
|
new_context = qemu_get_aio_context();
|
|
|
|
}
|
|
|
|
|
|
|
|
old_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(old_context);
|
|
|
|
|
|
|
|
bdrv_set_aio_context(bs, new_context);
|
|
|
|
|
|
|
|
aio_context_release(old_context);
|
|
|
|
}
|
|
|
|
|
2013-03-15 13:35:07 +04:00
|
|
|
QemuOptsList qemu_common_drive_opts = {
|
2012-11-26 19:03:42 +04:00
|
|
|
.name = "drive",
|
2013-03-15 13:35:07 +04:00
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
|
2012-11-26 19:03:42 +04:00
|
|
|
.desc = {
|
|
|
|
{
|
|
|
|
.name = "snapshot",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "enable/disable snapshot mode",
|
|
|
|
},{
|
|
|
|
.name = "aio",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "host AIO implementation (threads, native)",
|
2016-03-15 17:39:42 +03:00
|
|
|
},{
|
|
|
|
.name = BDRV_OPT_CACHE_WB,
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "Enable writeback mode",
|
2012-11-26 19:03:42 +04:00
|
|
|
},{
|
|
|
|
.name = "format",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "disk format (raw, qcow2, ...)",
|
|
|
|
},{
|
|
|
|
.name = "rerror",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "read error action",
|
|
|
|
},{
|
|
|
|
.name = "werror",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "write error action",
|
|
|
|
},{
|
2016-09-15 17:53:05 +03:00
|
|
|
.name = BDRV_OPT_READ_ONLY,
|
2012-11-26 19:03:42 +04:00
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "open drive file as read-only",
|
2017-02-28 12:31:46 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
THROTTLE_OPTS,
|
|
|
|
|
|
|
|
{
|
2015-06-08 19:17:44 +03:00
|
|
|
.name = "throttling.group",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "name of the block throttling group",
|
2012-11-26 19:03:42 +04:00
|
|
|
},{
|
|
|
|
.name = "copy-on-read",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "copy read data from backing file into image file",
|
2014-05-18 02:58:19 +04:00
|
|
|
},{
|
|
|
|
.name = "detect-zeroes",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "try to optimize zero writes (off, on, unmap)",
|
2015-10-28 18:33:04 +03:00
|
|
|
},{
|
|
|
|
.name = "stats-account-invalid",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "whether to account for invalid I/O operations "
|
|
|
|
"in the statistics",
|
|
|
|
},{
|
|
|
|
.name = "stats-account-failed",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "whether to account for failed I/O operations "
|
|
|
|
"in the statistics",
|
2012-11-26 19:03:42 +04:00
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
2013-03-15 13:35:07 +04:00
|
|
|
|
|
|
|
QemuOptsList qemu_drive_opts = {
|
|
|
|
.name = "drive",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
|
|
|
|
.desc = {
|
2013-06-19 15:44:17 +04:00
|
|
|
/*
|
|
|
|
* no elements => accept any params
|
|
|
|
* validation will happen later
|
|
|
|
*/
|
2013-03-15 13:35:07 +04:00
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|