2010-06-03 00:48:27 +08: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 20:59:53 +08: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-03 00:48:27 +08:00
|
|
|
*/
|
|
|
|
|
2016-01-30 01:50:05 +08: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 19:59:04 +08:00
|
|
|
#include "sysemu/block-backend.h"
|
2012-12-18 01:20:04 +08:00
|
|
|
#include "sysemu/blockdev.h"
|
2013-02-06 00:06:20 +08:00
|
|
|
#include "hw/block/block.h"
|
2012-12-18 01:19:44 +08:00
|
|
|
#include "block/blockjob.h"
|
2018-06-15 03:14:28 +08:00
|
|
|
#include "block/qdict.h"
|
2015-06-09 00:17:44 +08:00
|
|
|
#include "block/throttle-groups.h"
|
2012-12-18 01:19:49 +08:00
|
|
|
#include "monitor/monitor.h"
|
2015-03-18 01:29:20 +08:00
|
|
|
#include "qemu/error-report.h"
|
2012-12-18 01:20:00 +08:00
|
|
|
#include "qemu/option.h"
|
2019-04-18 03:06:38 +08:00
|
|
|
#include "qemu/qemu-print.h"
|
2012-12-18 01:20:00 +08:00
|
|
|
#include "qemu/config-file.h"
|
2018-02-11 17:36:01 +08:00
|
|
|
#include "qapi/qapi-commands-block.h"
|
|
|
|
#include "qapi/qapi-commands-transaction.h"
|
|
|
|
#include "qapi/qapi-visit-block-core.h"
|
2018-02-01 19:18:39 +08:00
|
|
|
#include "qapi/qmp/qdict.h"
|
2018-02-01 19:18:36 +08:00
|
|
|
#include "qapi/qmp/qnum.h"
|
2018-02-01 19:18:35 +08:00
|
|
|
#include "qapi/qmp/qstring.h"
|
2018-02-01 19:18:31 +08:00
|
|
|
#include "qapi/error.h"
|
2015-03-18 00:22:46 +08:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2018-02-01 19:18:38 +08:00
|
|
|
#include "qapi/qmp/qlist.h"
|
2016-09-30 22:45:27 +08:00
|
|
|
#include "qapi/qobject-output-visitor.h"
|
2012-12-18 01:20:04 +08:00
|
|
|
#include "sysemu/sysemu.h"
|
2017-12-06 22:45:49 +08:00
|
|
|
#include "sysemu/iothread.h"
|
2012-12-18 01:19:44 +08:00
|
|
|
#include "block/block_int.h"
|
2017-01-26 00:14:15 +08:00
|
|
|
#include "block/trace.h"
|
2012-12-18 01:20:04 +08:00
|
|
|
#include "sysemu/arch_init.h"
|
2019-08-12 13:23:59 +08:00
|
|
|
#include "sysemu/runstate.h"
|
2020-10-04 01:13:08 +08:00
|
|
|
#include "sysemu/replay.h"
|
2016-03-21 01:16:19 +08:00
|
|
|
#include "qemu/cutils.h"
|
|
|
|
#include "qemu/help_option.h"
|
Include qemu/main-loop.h less
In my "build everything" tree, changing qemu/main-loop.h triggers a
recompile of some 5600 out of 6600 objects (not counting tests and
objects that don't depend on qemu/osdep.h). It includes block/aio.h,
which in turn includes qemu/event_notifier.h, qemu/notify.h,
qemu/processor.h, qemu/qsp.h, qemu/queue.h, qemu/thread-posix.h,
qemu/thread.h, qemu/timer.h, and a few more.
Include qemu/main-loop.h only where it's needed. Touching it now
recompiles only some 1700 objects. For block/aio.h and
qemu/event_notifier.h, these numbers drop from 5600 to 2800. For the
others, they shrink only slightly.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20190812052359.30071-21-armbru@redhat.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
2019-08-12 13:23:50 +08:00
|
|
|
#include "qemu/main-loop.h"
|
2017-02-28 17:31:46 +08:00
|
|
|
#include "qemu/throttle-options.h"
|
2010-06-03 00:48:27 +08:00
|
|
|
|
2020-03-08 17:24:40 +08:00
|
|
|
QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
|
2016-01-29 23:36:12 +08:00
|
|
|
QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
|
|
|
|
|
2020-03-08 17:24:40 +08:00
|
|
|
void bdrv_set_monitor_owned(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
|
|
|
|
}
|
|
|
|
|
2011-01-28 18:21:39 +08: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-02 02:19:25 +08:00
|
|
|
static int if_max_devs[IF_COUNT] = {
|
2011-01-28 18:21:40 +08:00
|
|
|
/*
|
|
|
|
* Do not change these numbers! They govern how drive option
|
|
|
|
* index maps to unit and bus. That mapping is ABI.
|
|
|
|
*
|
2016-04-26 18:12:43 +08:00
|
|
|
* All controllers used to implement if=T drives need to support
|
2011-01-28 18:21:40 +08: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 18:21:39 +08:00
|
|
|
};
|
|
|
|
|
2014-10-02 02:19:25 +08: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 19:59:06 +08:00
|
|
|
BlockBackend *blk;
|
2014-10-02 02:19:25 +08:00
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
if (max_devs <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-07 19:59:06 +08:00
|
|
|
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
|
|
|
dinfo = blk_legacy_dinfo(blk);
|
2014-10-02 02:19:25 +08: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 14:09:10 +08: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 19:59:18 +08:00
|
|
|
void blockdev_mark_auto_del(BlockBackend *blk)
|
2010-06-25 14:09:10 +08:00
|
|
|
{
|
2014-10-07 19:59:06 +08:00
|
|
|
DriveInfo *dinfo = blk_legacy_dinfo(blk);
|
2019-06-06 23:41:31 +08:00
|
|
|
BlockJob *job;
|
2010-06-25 14:09:10 +08:00
|
|
|
|
2014-10-07 19:59:22 +08:00
|
|
|
if (!dinfo) {
|
2013-09-18 21:14:47 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-06-06 23:41:31 +08:00
|
|
|
for (job = block_job_next(NULL); job; job = block_job_next(job)) {
|
|
|
|
if (block_job_has_bdrv(job, blk_bs(blk))) {
|
|
|
|
AioContext *aio_context = job->job.aio_context;
|
|
|
|
aio_context_acquire(aio_context);
|
2014-10-21 19:03:52 +08:00
|
|
|
|
2019-06-06 23:41:31 +08:00
|
|
|
job_cancel(&job->job, false);
|
2014-10-21 19:03:52 +08:00
|
|
|
|
2019-06-06 23:41:31 +08:00
|
|
|
aio_context_release(aio_context);
|
|
|
|
}
|
2015-10-19 23:53:29 +08:00
|
|
|
}
|
2014-10-21 19:03:52 +08:00
|
|
|
|
2014-10-07 19:59:22 +08:00
|
|
|
dinfo->auto_del = 1;
|
2010-06-25 14:09:10 +08:00
|
|
|
}
|
|
|
|
|
2014-10-07 19:59:18 +08:00
|
|
|
void blockdev_auto_del(BlockBackend *blk)
|
2010-06-25 14:09:10 +08:00
|
|
|
{
|
2014-10-07 19:59:06 +08:00
|
|
|
DriveInfo *dinfo = blk_legacy_dinfo(blk);
|
2010-06-25 14:09:10 +08:00
|
|
|
|
2010-12-09 00:05:00 +08:00
|
|
|
if (dinfo && dinfo->auto_del) {
|
2016-03-17 02:54:38 +08:00
|
|
|
monitor_remove_blk(blk);
|
2014-10-07 19:59:09 +08:00
|
|
|
blk_unref(blk);
|
2010-06-25 14:09:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-02 02:19:27 +08: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 18:21:43 +08: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 18:21:41 +08: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 19:50:26 +08:00
|
|
|
return qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
|
2011-01-28 18:21:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
2011-01-31 18:50:09 +08:00
|
|
|
const char *optstr)
|
2010-06-03 00:48:27 +08:00
|
|
|
{
|
|
|
|
QemuOpts *opts;
|
|
|
|
|
2011-01-28 18:21:41 +08:00
|
|
|
opts = drive_def(optstr);
|
2010-06-03 00:48:27 +08:00
|
|
|
if (!opts) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-01-28 18:21:41 +08:00
|
|
|
if (type != IF_DEFAULT) {
|
2015-02-13 00:52:20 +08:00
|
|
|
qemu_opt_set(opts, "if", if_name[type], &error_abort);
|
2011-01-28 18:21:41 +08:00
|
|
|
}
|
|
|
|
if (index >= 0) {
|
2015-02-13 22:50:43 +08:00
|
|
|
qemu_opt_set_number(opts, "index", index, &error_abort);
|
2011-01-28 18:21:41 +08:00
|
|
|
}
|
2010-06-03 00:48:27 +08:00
|
|
|
if (file)
|
2015-02-13 00:52:20 +08:00
|
|
|
qemu_opt_set(opts, "file", file, &error_abort);
|
2010-06-03 00:48:27 +08:00
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
|
|
|
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit)
|
|
|
|
{
|
2014-10-07 19:59:06 +08:00
|
|
|
BlockBackend *blk;
|
2010-06-03 00:48:27 +08:00
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
2014-10-07 19:59:06 +08: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-03 00:48:27 +08:00
|
|
|
return dinfo;
|
2014-10-07 19:59:06 +08:00
|
|
|
}
|
2010-06-03 00:48:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-10 00:12:13 +08:00
|
|
|
/*
|
|
|
|
* Check board claimed all -drive that are meant to be claimed.
|
|
|
|
* Fatal error if any remain unclaimed.
|
|
|
|
*/
|
2017-02-15 18:05:46 +08:00
|
|
|
void drive_check_orphaned(void)
|
2014-10-02 02:19:24 +08:00
|
|
|
{
|
2014-10-07 19:59:06 +08:00
|
|
|
BlockBackend *blk;
|
2014-10-02 02:19:24 +08:00
|
|
|
DriveInfo *dinfo;
|
2017-02-15 18:05:45 +08:00
|
|
|
Location loc;
|
2017-02-15 18:05:46 +08:00
|
|
|
bool orphans = false;
|
2014-10-02 02:19:24 +08:00
|
|
|
|
2014-10-07 19:59:06 +08:00
|
|
|
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
|
|
|
dinfo = blk_legacy_dinfo(blk);
|
2021-03-10 00:12:13 +08:00
|
|
|
/*
|
|
|
|
* Ignore default drives, because we create certain default
|
|
|
|
* drives unconditionally, then leave them unclaimed. Not the
|
|
|
|
* users fault.
|
|
|
|
* Ignore IF_VIRTIO, because it gets desugared into -device,
|
|
|
|
* so we can leave failing to -device.
|
|
|
|
* Ignore IF_NONE, because leaving unclaimed IF_NONE remains
|
|
|
|
* available for device_add is a feature.
|
|
|
|
*/
|
|
|
|
if (dinfo->is_default || dinfo->type == IF_VIRTIO
|
|
|
|
|| dinfo->type == IF_NONE) {
|
blockdev: Deprecate -drive with bogus interface type
Drives with interface types other than if=none are for onboard
devices. Unfortunately, any such drives the board doesn't pick up can
still be used with -device, like this:
$ qemu-system-x86_64 -nodefaults -display none -S -drive if=floppy,id=bogus,unit=7 -device ide-cd,drive=bogus -monitor stdio
QEMU 5.0.50 monitor - type 'help' for more information
(qemu) info block
bogus: [not inserted]
Attached to: /machine/peripheral-anon/device[0]
Removable device: not locked, tray closed
(qemu) info qtree
bus: main-system-bus
type System
[...]
bus: ide.1
type IDE
dev: ide-cd, id ""
---> drive = "bogus"
[...]
unit = 0 (0x0)
[...]
This kind of abuse has always worked. Deprecate it:
qemu-system-x86_64: -drive if=floppy,id=bogus,unit=7: warning: bogus if=floppy is deprecated, use if=none
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20200622094227.1271650-9-armbru@redhat.com>
2020-06-22 17:42:19 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!blk_get_attached_dev(blk)) {
|
2017-02-15 18:05:45 +08:00
|
|
|
loc_push_none(&loc);
|
|
|
|
qemu_opts_loc_restore(dinfo->opts);
|
2017-02-15 18:05:46 +08:00
|
|
|
error_report("machine type does not support"
|
2017-02-15 18:05:45 +08:00
|
|
|
" if=%s,bus=%d,unit=%d",
|
|
|
|
if_name[dinfo->type], dinfo->bus, dinfo->unit);
|
|
|
|
loc_pop(&loc);
|
2017-02-15 18:05:46 +08:00
|
|
|
orphans = true;
|
2014-10-02 02:19:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-15 18:05:46 +08:00
|
|
|
if (orphans) {
|
|
|
|
exit(1);
|
|
|
|
}
|
2014-10-02 02:19:24 +08:00
|
|
|
}
|
|
|
|
|
2011-01-28 18:21:44 +08: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-03 00:48:27 +08:00
|
|
|
int drive_get_max_bus(BlockInterfaceType type)
|
|
|
|
{
|
|
|
|
int max_bus;
|
2014-10-07 19:59:06 +08:00
|
|
|
BlockBackend *blk;
|
2010-06-03 00:48:27 +08:00
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
max_bus = -1;
|
2014-10-07 19:59:06 +08: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-03 00:48:27 +08:00
|
|
|
max_bus = dinfo->bus;
|
2014-10-07 19:59:06 +08:00
|
|
|
}
|
2010-06-03 00:48:27 +08:00
|
|
|
}
|
|
|
|
return max_bus;
|
|
|
|
}
|
|
|
|
|
2011-01-28 18:21:37 +08: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-03 00:48:27 +08:00
|
|
|
static void bdrv_format_print(void *opaque, const char *name)
|
|
|
|
{
|
2019-04-18 03:06:38 +08:00
|
|
|
qemu_printf(" %s", name);
|
2010-06-03 00:48:27 +08:00
|
|
|
}
|
|
|
|
|
2012-01-18 22:40:50 +08:00
|
|
|
typedef struct {
|
|
|
|
QEMUBH *bh;
|
2013-08-23 09:14:51 +08:00
|
|
|
BlockDriverState *bs;
|
|
|
|
} BDRVPutRefBH;
|
2012-01-18 22:40:50 +08:00
|
|
|
|
2013-09-20 17:33:11 +08:00
|
|
|
static int parse_block_error_action(const char *buf, bool is_read, Error **errp)
|
2010-06-03 00:48:27 +08:00
|
|
|
{
|
|
|
|
if (!strcmp(buf, "ignore")) {
|
2012-09-28 23:22:55 +08:00
|
|
|
return BLOCKDEV_ON_ERROR_IGNORE;
|
2010-06-03 00:48:27 +08:00
|
|
|
} else if (!is_read && !strcmp(buf, "enospc")) {
|
2012-09-28 23:22:55 +08:00
|
|
|
return BLOCKDEV_ON_ERROR_ENOSPC;
|
2010-06-03 00:48:27 +08:00
|
|
|
} else if (!strcmp(buf, "stop")) {
|
2012-09-28 23:22:55 +08:00
|
|
|
return BLOCKDEV_ON_ERROR_STOP;
|
2010-06-03 00:48:27 +08:00
|
|
|
} else if (!strcmp(buf, "report")) {
|
2012-09-28 23:22:55 +08:00
|
|
|
return BLOCKDEV_ON_ERROR_REPORT;
|
2010-06-03 00:48:27 +08:00
|
|
|
} else {
|
2013-09-20 17:33:11 +08:00
|
|
|
error_setg(errp, "'%s' invalid %s error action",
|
|
|
|
buf, is_read ? "read" : "write");
|
2010-06-03 00:48:27 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-16 17:28:38 +08: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;
|
2018-02-24 23:40:29 +08:00
|
|
|
const char *str = qstring_get_str(qobject_to(QString,
|
|
|
|
entry->value));
|
2015-11-16 17:28:38 +08:00
|
|
|
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-08 00:35:58 +08:00
|
|
|
case QTYPE_QNUM: {
|
2018-02-24 23:40:29 +08:00
|
|
|
int64_t length = qnum_get_int(qobject_to(QNum, entry->value));
|
2017-06-08 00:35:58 +08:00
|
|
|
|
2015-11-16 17:28:38 +08: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 23:00:13 +08:00
|
|
|
typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
|
|
|
|
|
2015-10-19 23:53:31 +08: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) {
|
2020-01-20 22:18:50 +08:00
|
|
|
if (bdrv_parse_aio(aio, bdrv_flags) < 0) {
|
|
|
|
error_setg(errp, "invalid aio option");
|
|
|
|
return;
|
2015-10-19 23:53:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* disk I/O throttling */
|
|
|
|
if (throttling_group) {
|
|
|
|
*throttling_group = qemu_opt_get(opts, "throttling.group");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (throttle_cfg) {
|
2016-02-18 18:27:00 +08:00
|
|
|
throttle_config_init(throttle_cfg);
|
2015-10-19 23:53:31 +08: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 18:27:02 +08: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 23:53:31 +08:00
|
|
|
throttle_cfg->op_size =
|
|
|
|
qemu_opt_get_number(opts, "throttling.iops-size", 0);
|
|
|
|
|
2016-02-18 18:26:59 +08:00
|
|
|
if (!throttle_is_valid(throttle_cfg, errp)) {
|
2015-10-19 23:53:31 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (detect_zeroes) {
|
|
|
|
*detect_zeroes =
|
2017-08-24 16:46:10 +08:00
|
|
|
qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup,
|
2015-10-19 23:53:31 +08: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 18:01:20 +08:00
|
|
|
/* Takes the ownership of bs_opts */
|
2014-10-07 19:59:06 +08:00
|
|
|
static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|
|
|
Error **errp)
|
2010-06-03 00:48:27 +08:00
|
|
|
{
|
|
|
|
const char *buf;
|
|
|
|
int bdrv_flags = 0;
|
|
|
|
int on_read_error, on_write_error;
|
2015-10-28 23:33:04 +08:00
|
|
|
bool account_invalid, account_failed;
|
2016-09-15 22:53:02 +08: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 19:59:04 +08:00
|
|
|
BlockBackend *blk;
|
2014-09-13 03:26:21 +08:00
|
|
|
BlockDriverState *bs;
|
2013-09-02 20:14:39 +08:00
|
|
|
ThrottleConfig cfg;
|
2010-06-03 00:48:27 +08:00
|
|
|
int snapshot = 0;
|
2013-02-13 23:53:42 +08:00
|
|
|
Error *error = NULL;
|
2013-03-15 17:35:07 +08:00
|
|
|
QemuOpts *opts;
|
2015-11-16 17:28:38 +08:00
|
|
|
QDict *interval_dict = NULL;
|
|
|
|
QList *interval_list = NULL;
|
2013-03-15 17:35:07 +08:00
|
|
|
const char *id;
|
2015-10-19 23:53:31 +08:00
|
|
|
BlockdevDetectZeroesOptions detect_zeroes =
|
|
|
|
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
|
|
|
|
const char *throttling_group = NULL;
|
2010-06-03 00:48:27 +08:00
|
|
|
|
2013-09-10 18:01:20 +08: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");
|
2020-07-08 00:05:35 +08:00
|
|
|
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, errp);
|
|
|
|
if (!opts) {
|
2014-05-28 17:17:01 +08:00
|
|
|
goto err_no_opts;
|
2013-03-15 17:35:07 +08:00
|
|
|
}
|
|
|
|
|
error: Eliminate error_propagate() with Coccinelle, part 1
When all we do with an Error we receive into a local variable is
propagating to somewhere else, we can just as well receive it there
right away. Convert
if (!foo(..., &err)) {
...
error_propagate(errp, err);
...
return ...
}
to
if (!foo(..., errp)) {
...
...
return ...
}
where nothing else needs @err. Coccinelle script:
@rule1 forall@
identifier fun, err, errp, lbl;
expression list args, args2;
binary operator op;
constant c1, c2;
symbol false;
@@
if (
(
- fun(args, &err, args2)
+ fun(args, errp, args2)
|
- !fun(args, &err, args2)
+ !fun(args, errp, args2)
|
- fun(args, &err, args2) op c1
+ fun(args, errp, args2) op c1
)
)
{
... when != err
when != lbl:
when strict
- error_propagate(errp, err);
... when != err
(
return;
|
return c2;
|
return false;
)
}
@rule2 forall@
identifier fun, err, errp, lbl;
expression list args, args2;
expression var;
binary operator op;
constant c1, c2;
symbol false;
@@
- var = fun(args, &err, args2);
+ var = fun(args, errp, args2);
... when != err
if (
(
var
|
!var
|
var op c1
)
)
{
... when != err
when != lbl:
when strict
- error_propagate(errp, err);
... when != err
(
return;
|
return c2;
|
return false;
|
return var;
)
}
@depends on rule1 || rule2@
identifier err;
@@
- Error *err = NULL;
... when != err
Not exactly elegant, I'm afraid.
The "when != lbl:" is necessary to avoid transforming
if (fun(args, &err)) {
goto out
}
...
out:
error_propagate(errp, err);
even though other paths to label out still need the error_propagate().
For an actual example, see sclp_realize().
Without the "when strict", Coccinelle transforms vfio_msix_setup(),
incorrectly. I don't know what exactly "when strict" does, only that
it helps here.
The match of return is narrower than what I want, but I can't figure
out how to express "return where the operand doesn't use @err". For
an example where it's too narrow, see vfio_intx_enable().
Silently fails to convert hw/arm/armsse.c, because Coccinelle gets
confused by ARMSSE being used both as typedef and function-like macro
there. Converted manually.
Line breaks tidied up manually. One nested declaration of @local_err
deleted manually. Preexisting unwanted blank line dropped in
hw/riscv/sifive_e.c.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200707160613.848843-35-armbru@redhat.com>
2020-07-08 00:06:02 +08:00
|
|
|
if (!qemu_opts_absorb_qdict(opts, bs_opts, errp)) {
|
2013-10-30 21:54:30 +08:00
|
|
|
goto early_err;
|
2013-03-15 17:35:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (id) {
|
|
|
|
qdict_del(bs_opts, "id");
|
|
|
|
}
|
|
|
|
|
2010-06-03 00:48:27 +08:00
|
|
|
/* extract parameters */
|
|
|
|
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
|
|
|
|
|
2015-10-28 23:33:04 +08: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 22:39:42 +08:00
|
|
|
writethrough = !qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true);
|
|
|
|
|
2016-07-08 22:03:00 +08:00
|
|
|
id = qemu_opts_id(opts);
|
|
|
|
|
2015-11-16 17:28:38 +08: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 23:33:07 +08:00
|
|
|
|
2015-10-19 23:53:31 +08: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-03 00:48:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "format")) != NULL) {
|
2012-08-02 20:45:54 +08:00
|
|
|
if (is_help_option(buf)) {
|
2019-04-18 03:06:38 +08:00
|
|
|
qemu_printf("Supported formats:");
|
2019-03-07 21:33:58 +08:00
|
|
|
bdrv_iterate_format(bdrv_format_print, NULL, false);
|
2019-04-18 03:06:38 +08:00
|
|
|
qemu_printf("\nSupported formats (read-only):");
|
2019-03-07 21:33:58 +08:00
|
|
|
bdrv_iterate_format(bdrv_format_print, NULL, true);
|
2019-04-18 03:06:38 +08:00
|
|
|
qemu_printf("\n");
|
2013-10-30 21:54:30 +08:00
|
|
|
goto early_err;
|
2010-06-03 00:48:27 +08:00
|
|
|
}
|
2013-07-09 17:09:02 +08:00
|
|
|
|
2015-02-06 02:58:14 +08:00
|
|
|
if (qdict_haskey(bs_opts, "driver")) {
|
|
|
|
error_setg(errp, "Cannot specify both 'driver' and 'format'");
|
2013-10-30 21:54:30 +08:00
|
|
|
goto early_err;
|
2013-08-08 22:45:16 +08:00
|
|
|
}
|
2017-04-28 05:58:17 +08:00
|
|
|
qdict_put_str(bs_opts, "driver", buf);
|
2010-06-03 00:48:27 +08:00
|
|
|
}
|
|
|
|
|
2012-09-28 23:22:55 +08:00
|
|
|
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
2010-06-03 00:48:27 +08:00
|
|
|
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
2013-09-20 17:33:11 +08:00
|
|
|
on_write_error = parse_block_error_action(buf, 0, &error);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (error) {
|
2013-09-20 17:33:11 +08:00
|
|
|
error_propagate(errp, error);
|
2013-10-30 21:54:30 +08:00
|
|
|
goto early_err;
|
2010-06-03 00:48:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-28 23:22:55 +08:00
|
|
|
on_read_error = BLOCKDEV_ON_ERROR_REPORT;
|
2010-06-03 00:48:27 +08:00
|
|
|
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
|
2013-09-20 17:33:11 +08:00
|
|
|
on_read_error = parse_block_error_action(buf, 1, &error);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (error) {
|
2013-09-20 17:33:11 +08:00
|
|
|
error_propagate(errp, error);
|
2013-10-30 21:54:30 +08:00
|
|
|
goto early_err;
|
2010-06-03 00:48:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-19 23:53:30 +08:00
|
|
|
if (snapshot) {
|
2015-05-08 23:49:53 +08:00
|
|
|
bdrv_flags |= BDRV_O_SNAPSHOT;
|
2015-10-19 23:53:30 +08:00
|
|
|
}
|
|
|
|
|
2016-09-15 22:53:02 +08:00
|
|
|
read_only = qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false);
|
|
|
|
|
2013-07-11 18:52:34 +08:00
|
|
|
/* init */
|
2015-11-13 21:45:42 +08:00
|
|
|
if ((!file || !*file) && !qdict_size(bs_opts)) {
|
2015-10-19 23:53:30 +08:00
|
|
|
BlockBackendRootState *blk_rs;
|
|
|
|
|
2019-04-25 20:25:10 +08:00
|
|
|
blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
|
2015-10-19 23:53:30 +08:00
|
|
|
blk_rs = blk_get_root_state(blk);
|
2021-05-27 23:40:56 +08:00
|
|
|
blk_rs->open_flags = bdrv_flags | (read_only ? 0 : BDRV_O_RDWR);
|
2015-10-19 23:53:30 +08:00
|
|
|
blk_rs->detect_zeroes = detect_zeroes;
|
|
|
|
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(bs_opts);
|
2015-02-06 02:58:14 +08:00
|
|
|
} else {
|
|
|
|
if (file && !*file) {
|
2013-03-18 23:40:51 +08:00
|
|
|
file = NULL;
|
|
|
|
}
|
2010-06-03 00:48:27 +08:00
|
|
|
|
2015-05-08 23:49:53 +08: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 22:53:02 +08:00
|
|
|
qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY,
|
|
|
|
read_only ? "on" : "off");
|
2018-10-18 00:24:30 +08:00
|
|
|
qdict_set_default_str(bs_opts, BDRV_OPT_AUTO_READ_ONLY, "on");
|
2016-03-15 21:34:37 +08:00
|
|
|
assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0);
|
2015-05-08 23:49:53 +08:00
|
|
|
|
2016-02-19 23:48:10 +08:00
|
|
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
|
|
bdrv_flags |= BDRV_O_INACTIVE;
|
|
|
|
}
|
|
|
|
|
2016-03-17 02:54:38 +08:00
|
|
|
blk = blk_new_open(file, NULL, bs_opts, bdrv_flags, errp);
|
2015-02-06 02:58:14 +08:00
|
|
|
if (!blk) {
|
|
|
|
goto err_no_bs_opts;
|
|
|
|
}
|
|
|
|
bs = blk_bs(blk);
|
2012-03-23 15:36:50 +08:00
|
|
|
|
2015-10-19 23:53:30 +08:00
|
|
|
bs->detect_zeroes = detect_zeroes;
|
2010-06-03 00:48:27 +08:00
|
|
|
|
2017-06-05 20:39:07 +08:00
|
|
|
block_acct_setup(blk_get_stats(blk), account_invalid, account_failed);
|
2015-10-28 23:33:07 +08:00
|
|
|
|
2015-11-16 17:28:38 +08: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 23:33:07 +08:00
|
|
|
}
|
2010-06-03 00:48:27 +08:00
|
|
|
}
|
|
|
|
|
2016-03-22 20:00:08 +08:00
|
|
|
/* disk I/O throttling */
|
|
|
|
if (throttle_enabled(&cfg)) {
|
|
|
|
if (!throttling_group) {
|
2016-07-08 22:03:00 +08:00
|
|
|
throttling_group = id;
|
2016-03-22 20:00:08 +08:00
|
|
|
}
|
|
|
|
blk_io_limits_enable(blk, throttling_group);
|
|
|
|
blk_set_io_limits(blk, &cfg);
|
|
|
|
}
|
|
|
|
|
2016-03-15 22:39:42 +08:00
|
|
|
blk_set_enable_write_cache(blk, !writethrough);
|
2015-10-19 23:53:30 +08:00
|
|
|
blk_set_on_error(blk, on_read_error, on_write_error);
|
2013-03-15 17:35:07 +08:00
|
|
|
|
2016-07-08 22:03:00 +08:00
|
|
|
if (!monitor_add_blk(blk, id, errp)) {
|
2016-03-17 02:54:38 +08:00
|
|
|
blk_unref(blk);
|
|
|
|
blk = NULL;
|
|
|
|
goto err_no_bs_opts;
|
|
|
|
}
|
|
|
|
|
2015-02-06 02:58:14 +08:00
|
|
|
err_no_bs_opts:
|
2013-03-15 17:35:07 +08:00
|
|
|
qemu_opts_del(opts);
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(interval_dict);
|
|
|
|
qobject_unref(interval_list);
|
2014-10-07 19:59:06 +08:00
|
|
|
return blk;
|
2011-02-08 22:12:39 +08:00
|
|
|
|
2013-10-30 21:54:30 +08:00
|
|
|
early_err:
|
|
|
|
qemu_opts_del(opts);
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(interval_dict);
|
|
|
|
qobject_unref(interval_list);
|
2014-05-28 17:17:01 +08:00
|
|
|
err_no_opts:
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(bs_opts);
|
2011-02-08 22:12:39 +08:00
|
|
|
return NULL;
|
2010-06-03 00:48:27 +08:00
|
|
|
}
|
|
|
|
|
2015-10-19 23:53:32 +08:00
|
|
|
/* Takes the ownership of bs_opts */
|
2020-03-08 17:24:40 +08:00
|
|
|
BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
|
2015-10-19 23:53:32 +08:00
|
|
|
{
|
|
|
|
int bdrv_flags = 0;
|
|
|
|
|
2016-03-07 21:23:04 +08: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 22:53:02 +08:00
|
|
|
qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY, "off");
|
2016-03-07 21:23:04 +08:00
|
|
|
|
2016-02-19 23:48:10 +08:00
|
|
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
|
|
bdrv_flags |= BDRV_O_INACTIVE;
|
|
|
|
}
|
|
|
|
|
2016-09-21 02:48:52 +08:00
|
|
|
return bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp);
|
2015-10-19 23:53:32 +08:00
|
|
|
}
|
|
|
|
|
2016-01-29 23:36:12 +08: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-17 02:54:41 +08: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);
|
|
|
|
}
|
|
|
|
|
2020-07-08 00:05:41 +08:00
|
|
|
static bool qemu_opt_rename(QemuOpts *opts, const char *from, const char *to,
|
2014-09-18 17:48:34 +08:00
|
|
|
Error **errp)
|
2013-07-17 20:41:54 +08:00
|
|
|
{
|
|
|
|
const char *value;
|
|
|
|
|
|
|
|
value = qemu_opt_get(opts, from);
|
|
|
|
if (value) {
|
2014-09-18 17:48:34 +08: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);
|
2020-07-08 00:05:41 +08:00
|
|
|
return false;
|
2014-09-18 17:48:34 +08:00
|
|
|
}
|
2014-09-24 13:45:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* rename all items in opts */
|
|
|
|
while ((value = qemu_opt_get(opts, from))) {
|
2015-02-13 00:52:20 +08:00
|
|
|
qemu_opt_set(opts, to, value, &error_abort);
|
2013-07-17 20:41:54 +08:00
|
|
|
qemu_opt_unset(opts, from);
|
|
|
|
}
|
2020-07-08 00:05:41 +08:00
|
|
|
return true;
|
2013-07-17 20:41:54 +08:00
|
|
|
}
|
|
|
|
|
2013-08-28 23:00:13 +08:00
|
|
|
QemuOptsList qemu_legacy_drive_opts = {
|
|
|
|
.name = "drive",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_legacy_drive_opts.head),
|
|
|
|
.desc = {
|
|
|
|
{
|
2013-09-10 21:48:13 +08: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 23:00:13 +08:00
|
|
|
.name = "media",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "media type (disk, cdrom)",
|
2013-08-28 23:24:51 +08:00
|
|
|
},{
|
|
|
|
.name = "if",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
|
2013-12-21 02:28:14 +08:00
|
|
|
},{
|
|
|
|
.name = "file",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "file name",
|
2013-08-28 23:00:13 +08:00
|
|
|
},
|
2013-09-19 21:12:18 +08:00
|
|
|
|
|
|
|
/* Options that are passed on, but have special semantics with -drive */
|
|
|
|
{
|
2016-09-15 22:53:05 +08:00
|
|
|
.name = BDRV_OPT_READ_ONLY,
|
2013-09-19 21:12:18 +08:00
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "open drive file as read-only",
|
2014-02-09 16:52:32 +08:00
|
|
|
},{
|
|
|
|
.name = "rerror",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "read error action",
|
|
|
|
},{
|
|
|
|
.name = "werror",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "write error action",
|
2013-09-19 21:12:18 +08:00
|
|
|
},{
|
|
|
|
.name = "copy-on-read",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "copy read data from backing file into image file",
|
|
|
|
},
|
|
|
|
|
2013-08-28 23:00:13 +08:00
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2018-10-17 16:26:57 +08:00
|
|
|
DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
|
|
|
Error **errp)
|
2013-07-17 20:41:54 +08:00
|
|
|
{
|
2013-07-18 22:31:25 +08:00
|
|
|
const char *value;
|
2014-10-07 19:59:06 +08:00
|
|
|
BlockBackend *blk;
|
2013-08-28 23:00:13 +08:00
|
|
|
DriveInfo *dinfo = NULL;
|
2013-09-10 18:01:20 +08:00
|
|
|
QDict *bs_opts;
|
2013-08-28 23:00:13 +08:00
|
|
|
QemuOpts *legacy_opts;
|
|
|
|
DriveMediaType media = MEDIA_DISK;
|
2013-08-28 23:24:51 +08:00
|
|
|
BlockInterfaceType type;
|
2013-09-10 21:48:13 +08:00
|
|
|
int max_devs, bus_id, unit_id, index;
|
2014-02-09 16:52:32 +08:00
|
|
|
const char *werror, *rerror;
|
2013-10-15 17:45:50 +08:00
|
|
|
bool read_only = false;
|
|
|
|
bool copy_on_read;
|
2013-12-21 02:28:14 +08:00
|
|
|
const char *filename;
|
2014-09-24 22:37:14 +08:00
|
|
|
int i;
|
2013-07-18 22:31:25 +08:00
|
|
|
|
2013-07-17 20:41:54 +08:00
|
|
|
/* Change legacy command line options into QMP ones */
|
2014-09-24 22:37:14 +08: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 20:41:54 +08:00
|
|
|
|
2014-09-24 22:37:14 +08:00
|
|
|
{ "bps", "throttling.bps-total" },
|
|
|
|
{ "bps_rd", "throttling.bps-read" },
|
|
|
|
{ "bps_wr", "throttling.bps-write" },
|
2013-07-17 20:41:54 +08:00
|
|
|
|
2014-09-24 22:37:14 +08:00
|
|
|
{ "iops_max", "throttling.iops-total-max" },
|
|
|
|
{ "iops_rd_max", "throttling.iops-read-max" },
|
|
|
|
{ "iops_wr_max", "throttling.iops-write-max" },
|
2013-09-02 20:14:40 +08:00
|
|
|
|
2014-09-24 22:37:14 +08:00
|
|
|
{ "bps_max", "throttling.bps-total-max" },
|
|
|
|
{ "bps_rd_max", "throttling.bps-read-max" },
|
|
|
|
{ "bps_wr_max", "throttling.bps-write-max" },
|
2013-09-02 20:14:40 +08:00
|
|
|
|
2014-09-24 22:37:14 +08:00
|
|
|
{ "iops_size", "throttling.iops-size" },
|
2013-09-02 20:14:41 +08:00
|
|
|
|
2015-06-09 00:17:44 +08:00
|
|
|
{ "group", "throttling.group" },
|
|
|
|
|
2016-09-15 22:53:05 +08:00
|
|
|
{ "readonly", BDRV_OPT_READ_ONLY },
|
2014-09-24 22:37:14 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(opt_renames); i++) {
|
qemu-option: Use returned bool to check for failure
The previous commit enables conversion of
foo(..., &err);
if (err) {
...
}
to
if (!foo(..., &err)) {
...
}
for QemuOpts functions that now return true / false on success /
error. Coccinelle script:
@@
identifier fun = {
opts_do_parse, parse_option_bool, parse_option_number,
parse_option_size, qemu_opt_parse, qemu_opt_rename, qemu_opt_set,
qemu_opt_set_bool, qemu_opt_set_number, qemu_opts_absorb_qdict,
qemu_opts_do_parse, qemu_opts_from_qdict_entry, qemu_opts_set,
qemu_opts_validate
};
expression list args, args2;
typedef Error;
Error *err;
@@
- fun(args, &err, args2);
- if (err)
+ if (!fun(args, &err, args2))
{
...
}
A few line breaks tidied up manually.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-Id: <20200707160613.848843-15-armbru@redhat.com>
[Conflict with commit 0b6786a9c1 "block/amend: refactor qcow2 amend
options" resolved by rerunning Coccinelle on master's version]
2020-07-08 00:05:42 +08:00
|
|
|
if (!qemu_opt_rename(all_opts, opt_renames[i].from,
|
error: Eliminate error_propagate() with Coccinelle, part 1
When all we do with an Error we receive into a local variable is
propagating to somewhere else, we can just as well receive it there
right away. Convert
if (!foo(..., &err)) {
...
error_propagate(errp, err);
...
return ...
}
to
if (!foo(..., errp)) {
...
...
return ...
}
where nothing else needs @err. Coccinelle script:
@rule1 forall@
identifier fun, err, errp, lbl;
expression list args, args2;
binary operator op;
constant c1, c2;
symbol false;
@@
if (
(
- fun(args, &err, args2)
+ fun(args, errp, args2)
|
- !fun(args, &err, args2)
+ !fun(args, errp, args2)
|
- fun(args, &err, args2) op c1
+ fun(args, errp, args2) op c1
)
)
{
... when != err
when != lbl:
when strict
- error_propagate(errp, err);
... when != err
(
return;
|
return c2;
|
return false;
)
}
@rule2 forall@
identifier fun, err, errp, lbl;
expression list args, args2;
expression var;
binary operator op;
constant c1, c2;
symbol false;
@@
- var = fun(args, &err, args2);
+ var = fun(args, errp, args2);
... when != err
if (
(
var
|
!var
|
var op c1
)
)
{
... when != err
when != lbl:
when strict
- error_propagate(errp, err);
... when != err
(
return;
|
return c2;
|
return false;
|
return var;
)
}
@depends on rule1 || rule2@
identifier err;
@@
- Error *err = NULL;
... when != err
Not exactly elegant, I'm afraid.
The "when != lbl:" is necessary to avoid transforming
if (fun(args, &err)) {
goto out
}
...
out:
error_propagate(errp, err);
even though other paths to label out still need the error_propagate().
For an actual example, see sclp_realize().
Without the "when strict", Coccinelle transforms vfio_msix_setup(),
incorrectly. I don't know what exactly "when strict" does, only that
it helps here.
The match of return is narrower than what I want, but I can't figure
out how to express "return where the operand doesn't use @err". For
an example where it's too narrow, see vfio_intx_enable().
Silently fails to convert hw/arm/armsse.c, because Coccinelle gets
confused by ARMSSE being used both as typedef and function-like macro
there. Converted manually.
Line breaks tidied up manually. One nested declaration of @local_err
deleted manually. Preexisting unwanted blank line dropped in
hw/riscv/sifive_e.c.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200707160613.848843-35-armbru@redhat.com>
2020-07-08 00:06:02 +08:00
|
|
|
opt_renames[i].to, errp)) {
|
2014-09-18 17:48:34 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-09-24 22:37:14 +08:00
|
|
|
}
|
2013-07-20 02:07:29 +08:00
|
|
|
|
2013-07-18 22:31:25 +08:00
|
|
|
value = qemu_opt_get(all_opts, "cache");
|
|
|
|
if (value) {
|
|
|
|
int flags = 0;
|
2016-03-18 22:35:51 +08:00
|
|
|
bool writethrough;
|
2013-07-18 22:31:25 +08:00
|
|
|
|
2016-03-18 22:35:51 +08:00
|
|
|
if (bdrv_parse_cache_mode(value, &flags, &writethrough) != 0) {
|
2018-10-17 16:26:57 +08:00
|
|
|
error_setg(errp, "invalid cache option");
|
2013-07-18 22:31:25 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Specific options take precedence */
|
2015-04-07 22:55:00 +08:00
|
|
|
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) {
|
|
|
|
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB,
|
2016-03-18 22:35:51 +08:00
|
|
|
!writethrough, &error_abort);
|
2013-07-18 22:31:25 +08:00
|
|
|
}
|
2015-04-07 22:55:00 +08:00
|
|
|
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) {
|
|
|
|
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT,
|
2015-02-12 23:37:44 +08:00
|
|
|
!!(flags & BDRV_O_NOCACHE), &error_abort);
|
2013-07-18 22:31:25 +08:00
|
|
|
}
|
2015-04-07 22:55:00 +08: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 23:37:44 +08:00
|
|
|
!!(flags & BDRV_O_NO_FLUSH), &error_abort);
|
2013-07-18 22:31:25 +08:00
|
|
|
}
|
|
|
|
qemu_opt_unset(all_opts, "cache");
|
|
|
|
}
|
|
|
|
|
2013-09-10 18:01:20 +08:00
|
|
|
/* Get a QDict for processing the options */
|
|
|
|
bs_opts = qdict_new();
|
|
|
|
qemu_opts_to_qdict(all_opts, bs_opts);
|
|
|
|
|
2014-01-02 10:49:17 +08:00
|
|
|
legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0,
|
|
|
|
&error_abort);
|
2020-07-08 00:06:03 +08:00
|
|
|
if (!qemu_opts_absorb_qdict(legacy_opts, bs_opts, errp)) {
|
2013-08-28 23:00:13 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 17:45:50 +08:00
|
|
|
read_only = true;
|
2013-08-28 23:00:13 +08:00
|
|
|
} else {
|
2018-10-17 16:26:57 +08:00
|
|
|
error_setg(errp, "'%s' invalid media", value);
|
2013-08-28 23:00:13 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-19 21:12:18 +08:00
|
|
|
/* copy-on-read is disabled with a warning for read-only devices */
|
2016-09-15 22:53:05 +08:00
|
|
|
read_only |= qemu_opt_get_bool(legacy_opts, BDRV_OPT_READ_ONLY, false);
|
2013-09-19 21:12:18 +08:00
|
|
|
copy_on_read = qemu_opt_get_bool(legacy_opts, "copy-on-read", false);
|
|
|
|
|
|
|
|
if (read_only && copy_on_read) {
|
2017-07-12 21:57:41 +08:00
|
|
|
warn_report("disabling copy-on-read on read-only drive");
|
2013-09-19 21:12:18 +08:00
|
|
|
copy_on_read = false;
|
|
|
|
}
|
|
|
|
|
2017-04-28 05:58:17 +08: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 21:12:18 +08:00
|
|
|
|
2013-08-28 23:24:51 +08: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) {
|
2018-10-17 16:26:57 +08:00
|
|
|
error_setg(errp, "unsupported bus type '%s'", value);
|
2013-08-28 23:24:51 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
type = block_default_type;
|
|
|
|
}
|
|
|
|
|
2013-09-10 21:48:13 +08: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) {
|
2018-10-17 16:26:57 +08:00
|
|
|
error_setg(errp, "index cannot be used with bus and unit");
|
2013-09-10 21:48:13 +08:00
|
|
|
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) {
|
2018-10-17 16:26:57 +08:00
|
|
|
error_setg(errp, "unit %d too big (max is %d)", unit_id, max_devs - 1);
|
2013-09-10 21:48:13 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (drive_get(type, bus_id, unit_id) != NULL) {
|
2018-10-17 16:26:57 +08:00
|
|
|
error_setg(errp, "drive with bus=%d, unit=%d (index=%d) exists",
|
|
|
|
bus_id, unit_id, index);
|
2013-09-10 21:48:13 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no id supplied -> create one */
|
|
|
|
if (qemu_opts_id(all_opts) == NULL) {
|
|
|
|
char *new_id;
|
|
|
|
const char *mediastr = "";
|
|
|
|
if (type == IF_IDE || type == IF_SCSI) {
|
|
|
|
mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
|
|
|
|
}
|
|
|
|
if (max_devs) {
|
|
|
|
new_id = g_strdup_printf("%s%i%s%i", if_name[type], bus_id,
|
|
|
|
mediastr, unit_id);
|
|
|
|
} else {
|
|
|
|
new_id = g_strdup_printf("%s%s%i", if_name[type],
|
|
|
|
mediastr, unit_id);
|
|
|
|
}
|
2017-04-28 05:58:17 +08:00
|
|
|
qdict_put_str(bs_opts, "id", new_id);
|
2013-09-10 21:48:13 +08:00
|
|
|
g_free(new_id);
|
|
|
|
}
|
|
|
|
|
2013-09-13 20:09:17 +08:00
|
|
|
/* Add virtio block device */
|
|
|
|
if (type == IF_VIRTIO) {
|
|
|
|
QemuOpts *devopts;
|
2014-01-02 10:49:17 +08:00
|
|
|
devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
|
|
|
|
&error_abort);
|
2021-03-24 00:53:04 +08:00
|
|
|
qemu_opt_set(devopts, "driver", "virtio-blk", &error_abort);
|
2015-02-13 00:52:20 +08:00
|
|
|
qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"),
|
|
|
|
&error_abort);
|
2013-09-13 20:09:17 +08:00
|
|
|
}
|
|
|
|
|
2013-12-21 02:28:14 +08:00
|
|
|
filename = qemu_opt_get(legacy_opts, "file");
|
|
|
|
|
2014-02-09 16:52:32 +08: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) {
|
2018-10-17 16:26:57 +08:00
|
|
|
error_setg(errp, "werror is not supported by this bus type");
|
2014-02-09 16:52:32 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
2017-04-28 05:58:17 +08:00
|
|
|
qdict_put_str(bs_opts, "werror", werror);
|
2014-02-09 16:52:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
rerror = qemu_opt_get(legacy_opts, "rerror");
|
|
|
|
if (rerror != NULL) {
|
|
|
|
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI &&
|
|
|
|
type != IF_NONE) {
|
2018-10-17 16:26:57 +08:00
|
|
|
error_setg(errp, "rerror is not supported by this bus type");
|
2014-02-09 16:52:32 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
2017-04-28 05:58:17 +08:00
|
|
|
qdict_put_str(bs_opts, "rerror", rerror);
|
2014-02-09 16:52:32 +08:00
|
|
|
}
|
|
|
|
|
2013-09-18 21:14:47 +08:00
|
|
|
/* Actual block device init: Functionality shared with blockdev-add */
|
2020-07-08 00:06:04 +08:00
|
|
|
blk = blockdev_init(filename, bs_opts, errp);
|
2014-05-28 17:17:02 +08:00
|
|
|
bs_opts = NULL;
|
2014-10-07 19:59:06 +08:00
|
|
|
if (!blk) {
|
2013-09-18 21:14:47 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-10-07 19:59:22 +08:00
|
|
|
/* Create legacy DriveInfo */
|
|
|
|
dinfo = g_malloc0(sizeof(*dinfo));
|
2013-09-10 18:01:20 +08:00
|
|
|
dinfo->opts = all_opts;
|
2013-09-18 21:14:47 +08:00
|
|
|
|
2014-02-09 16:52:32 +08:00
|
|
|
dinfo->type = type;
|
2013-09-10 21:48:13 +08:00
|
|
|
dinfo->bus = bus_id;
|
|
|
|
dinfo->unit = unit_id;
|
2014-06-06 21:21:48 +08:00
|
|
|
|
2014-10-07 19:59:22 +08:00
|
|
|
blk_set_legacy_dinfo(blk, dinfo);
|
|
|
|
|
2013-09-19 20:24:10 +08: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 21:14:47 +08:00
|
|
|
fail:
|
2013-08-28 23:00:13 +08:00
|
|
|
qemu_opts_del(legacy_opts);
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(bs_opts);
|
2013-09-18 21:14:47 +08:00
|
|
|
return dinfo;
|
2013-07-17 20:41:54 +08:00
|
|
|
}
|
|
|
|
|
2016-06-23 20:20:24 +08: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-03-04 00:16:50 +08:00
|
|
|
static void blockdev_do_action(TransactionAction *action, Error **errp)
|
2012-03-07 01:55:59 +08:00
|
|
|
{
|
2013-05-13 16:43:43 +08:00
|
|
|
TransactionActionList list;
|
2012-03-07 01:55:59 +08:00
|
|
|
|
2016-03-04 00:16:50 +08:00
|
|
|
list.value = action;
|
2012-03-07 01:55:59 +08:00
|
|
|
list.next = NULL;
|
2015-11-06 07:13:18 +08:00
|
|
|
qmp_transaction(&list, false, NULL, errp);
|
2012-03-07 01:55:59 +08:00
|
|
|
}
|
|
|
|
|
2014-01-24 04:31:38 +08: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-26 02:15:19 +08:00
|
|
|
bool has_format, const char *format,
|
2014-01-24 04:31:38 +08:00
|
|
|
bool has_mode, NewImageMode mode, Error **errp)
|
2010-12-16 20:52:16 +08:00
|
|
|
{
|
2015-10-26 20:27:14 +08:00
|
|
|
BlockdevSnapshotSync snapshot = {
|
2014-01-24 04:31:38 +08:00
|
|
|
.has_device = has_device,
|
2012-03-07 01:55:59 +08:00
|
|
|
.device = (char *) device,
|
2014-01-24 04:31:38 +08:00
|
|
|
.has_node_name = has_node_name,
|
|
|
|
.node_name = (char *) node_name,
|
2012-03-07 01:55:59 +08:00
|
|
|
.snapshot_file = (char *) snapshot_file,
|
2014-01-24 04:31:38 +08:00
|
|
|
.has_snapshot_node_name = has_snapshot_node_name,
|
|
|
|
.snapshot_node_name = (char *) snapshot_node_name,
|
2012-03-07 01:55:59 +08:00
|
|
|
.has_format = has_format,
|
|
|
|
.format = (char *) format,
|
|
|
|
.has_mode = has_mode,
|
|
|
|
.mode = mode,
|
|
|
|
};
|
2016-03-04 00:16:50 +08: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 06:48:37 +08:00
|
|
|
.u.blockdev_snapshot_sync.data = &snapshot,
|
2016-03-04 00:16:50 +08:00
|
|
|
};
|
|
|
|
blockdev_do_action(&action, errp);
|
2010-12-16 20:52:16 +08:00
|
|
|
}
|
|
|
|
|
2015-10-26 20:27:16 +08:00
|
|
|
void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockdevSnapshot snapshot_data = {
|
|
|
|
.node = (char *) node,
|
|
|
|
.overlay = (char *) overlay
|
|
|
|
};
|
2016-03-04 00:16:50 +08: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 06:48:37 +08:00
|
|
|
.u.blockdev_snapshot.data = &snapshot_data,
|
2016-03-04 00:16:50 +08:00
|
|
|
};
|
|
|
|
blockdev_do_action(&action, errp);
|
2015-10-26 20:27:16 +08:00
|
|
|
}
|
|
|
|
|
2013-09-11 14:04:35 +08: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-04 00:16:50 +08: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 06:48:37 +08:00
|
|
|
.u.blockdev_snapshot_internal_sync.data = &snapshot,
|
2016-03-04 00:16:50 +08:00
|
|
|
};
|
|
|
|
blockdev_do_action(&action, errp);
|
2013-09-11 14:04:35 +08:00
|
|
|
}
|
|
|
|
|
2013-09-11 14:04:36 +08: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 19:36:48 +08:00
|
|
|
BlockDriverState *bs;
|
2014-11-19 22:19:42 +08:00
|
|
|
AioContext *aio_context;
|
2013-09-11 14:04:36 +08:00
|
|
|
QEMUSnapshotInfo sn;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
SnapshotInfo *info = NULL;
|
|
|
|
int ret;
|
|
|
|
|
2016-06-23 20:20:24 +08:00
|
|
|
bs = qmp_get_root_bs(device, errp);
|
|
|
|
if (!bs) {
|
2013-09-11 14:04:36 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2016-06-23 20:20:24 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2015-10-19 23:53:29 +08:00
|
|
|
aio_context_acquire(aio_context);
|
2013-09-11 14:04:36 +08: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 23:53:29 +08:00
|
|
|
goto out_aio_context;
|
2013-09-11 14:04:36 +08:00
|
|
|
}
|
|
|
|
|
2014-11-19 22:19:43 +08:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) {
|
|
|
|
goto out_aio_context;
|
|
|
|
}
|
|
|
|
|
2013-09-11 14:04:36 +08:00
|
|
|
ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-09-11 14:04:36 +08:00
|
|
|
error_propagate(errp, local_err);
|
2014-11-19 22:19:42 +08:00
|
|
|
goto out_aio_context;
|
2013-09-11 14:04:36 +08: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 22:19:42 +08:00
|
|
|
goto out_aio_context;
|
2013-09-11 14:04:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bdrv_snapshot_delete(bs, id, name, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-09-11 14:04:36 +08:00
|
|
|
error_propagate(errp, local_err);
|
2014-11-19 22:19:42 +08:00
|
|
|
goto out_aio_context;
|
2013-09-11 14:04:36 +08:00
|
|
|
}
|
|
|
|
|
2014-11-19 22:19:42 +08: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 16:31:08 +08:00
|
|
|
info = g_new0(SnapshotInfo, 1);
|
2013-09-11 14:04:36 +08: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;
|
2020-10-04 01:13:08 +08:00
|
|
|
if (sn.icount != -1ULL) {
|
|
|
|
info->icount = sn.icount;
|
|
|
|
info->has_icount = true;
|
|
|
|
}
|
2013-09-11 14:04:36 +08:00
|
|
|
|
|
|
|
return info;
|
2014-11-19 22:19:42 +08:00
|
|
|
|
|
|
|
out_aio_context:
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
return NULL;
|
2013-09-11 14:04:36 +08:00
|
|
|
}
|
2012-02-29 04:54:06 +08:00
|
|
|
|
2014-11-21 18:48:57 +08:00
|
|
|
/* New and old BlockDriverState structs for atomic group operations */
|
2013-05-08 18:25:16 +08:00
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
typedef struct BlkActionState BlkActionState;
|
2013-05-08 18:25:16 +08:00
|
|
|
|
2015-11-06 07:13:09 +08: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 18:25:16 +08:00
|
|
|
size_t instance_size;
|
2015-11-06 07:13:09 +08:00
|
|
|
void (*prepare)(BlkActionState *common, Error **errp);
|
|
|
|
void (*commit)(BlkActionState *common);
|
|
|
|
void (*abort)(BlkActionState *common);
|
|
|
|
void (*clean)(BlkActionState *common);
|
|
|
|
} BlkActionOps;
|
2013-05-08 18:25:16 +08:00
|
|
|
|
2015-11-06 07:13:09 +08: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 07:13:18 +08:00
|
|
|
* @block_job_txn: Transaction which this action belongs to.
|
2015-11-06 07:13:09 +08: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 18:25:16 +08:00
|
|
|
*/
|
2015-11-06 07:13:09 +08:00
|
|
|
struct BlkActionState {
|
2013-05-13 16:43:43 +08:00
|
|
|
TransactionAction *action;
|
2015-11-06 07:13:09 +08:00
|
|
|
const BlkActionOps *ops;
|
2018-04-19 22:09:52 +08:00
|
|
|
JobTxn *block_job_txn;
|
2015-11-06 07:13:18 +08:00
|
|
|
TransactionProperties *txn_props;
|
2019-01-12 01:59:16 +08:00
|
|
|
QTAILQ_ENTRY(BlkActionState) entry;
|
2013-05-08 18:25:16 +08:00
|
|
|
};
|
|
|
|
|
2013-09-11 14:04:34 +08:00
|
|
|
/* internal snapshot private data */
|
|
|
|
typedef struct InternalSnapshotState {
|
2015-11-06 07:13:09 +08:00
|
|
|
BlkActionState common;
|
2013-09-11 14:04:34 +08:00
|
|
|
BlockDriverState *bs;
|
|
|
|
QEMUSnapshotInfo sn;
|
2015-10-23 11:08:13 +08:00
|
|
|
bool created;
|
2013-09-11 14:04:34 +08:00
|
|
|
} InternalSnapshotState;
|
|
|
|
|
2015-11-06 07:13:18 +08: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 16:46:08 +08:00
|
|
|
TransactionActionKind_str(s->action->type),
|
|
|
|
ActionCompletionMode_str(s->txn_props->completion_mode));
|
2015-11-06 07:13:18 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void internal_snapshot_prepare(BlkActionState *common,
|
2013-09-11 14:04:34 +08:00
|
|
|
Error **errp)
|
|
|
|
{
|
2014-04-25 22:50:34 +08:00
|
|
|
Error *local_err = NULL;
|
2013-09-11 14:04:34 +08: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 22:45:46 +08:00
|
|
|
AioContext *aio_context;
|
2013-09-11 14:04:34 +08:00
|
|
|
int ret1;
|
|
|
|
|
2015-10-27 06:34:54 +08:00
|
|
|
g_assert(common->action->type ==
|
2013-09-11 14:04:34 +08: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 06:48:37 +08:00
|
|
|
internal = common->action->u.blockdev_snapshot_internal_sync.data;
|
2013-09-11 14:04:34 +08:00
|
|
|
state = DO_UPCAST(InternalSnapshotState, common, common);
|
|
|
|
|
|
|
|
/* 1. parse input */
|
|
|
|
device = internal->device;
|
|
|
|
name = internal->name;
|
|
|
|
|
|
|
|
/* 2. check for validation */
|
2015-11-06 07:13:18 +08:00
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-23 20:20:24 +08:00
|
|
|
bs = qmp_get_root_bs(device, errp);
|
|
|
|
if (!bs) {
|
2013-09-11 14:04:34 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 22:45:46 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(aio_context);
|
2014-11-21 18:48:59 +08:00
|
|
|
|
2015-10-23 11:08:13 +08:00
|
|
|
state->bs = bs;
|
2017-12-06 22:45:46 +08:00
|
|
|
|
|
|
|
/* Paired with .clean() */
|
2015-10-23 11:08:13 +08:00
|
|
|
bdrv_drained_begin(bs);
|
|
|
|
|
2014-11-21 18:49:00 +08:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) {
|
2017-12-06 22:45:46 +08:00
|
|
|
goto out;
|
2014-11-21 18:49:00 +08:00
|
|
|
}
|
|
|
|
|
2013-09-11 14:04:34 +08:00
|
|
|
if (bdrv_is_read_only(bs)) {
|
2015-04-08 17:29:19 +08:00
|
|
|
error_setg(errp, "Device '%s' is read only", device);
|
2017-12-06 22:45:46 +08:00
|
|
|
goto out;
|
2013-09-11 14:04:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!bdrv_can_snapshot(bs)) {
|
2015-04-08 17:29:19 +08:00
|
|
|
error_setg(errp, "Block format '%s' used by device '%s' "
|
|
|
|
"does not support internal snapshots",
|
|
|
|
bs->drv->format_name, device);
|
2017-12-06 22:45:46 +08:00
|
|
|
goto out;
|
2013-09-11 14:04:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!strlen(name)) {
|
|
|
|
error_setg(errp, "Name is empty");
|
2017-12-06 22:45:46 +08:00
|
|
|
goto out;
|
2013-09-11 14:04:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check whether a snapshot with name exist */
|
2014-04-25 22:50:34 +08: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 22:45:46 +08:00
|
|
|
goto out;
|
2013-09-11 14:04:34 +08:00
|
|
|
} else if (ret) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Snapshot with name '%s' already exists on device '%s'",
|
|
|
|
name, device);
|
2017-12-06 22:45:46 +08:00
|
|
|
goto out;
|
2013-09-11 14:04:34 +08: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);
|
2020-10-04 01:13:08 +08:00
|
|
|
if (replay_mode != REPLAY_MODE_NONE) {
|
|
|
|
sn->icount = replay_get_current_icount();
|
|
|
|
} else {
|
|
|
|
sn->icount = -1ULL;
|
|
|
|
}
|
2013-09-11 14:04:34 +08:00
|
|
|
|
|
|
|
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 22:45:46 +08:00
|
|
|
goto out;
|
2013-09-11 14:04:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 4. succeed, mark a snapshot is created */
|
2015-10-23 11:08:13 +08:00
|
|
|
state->created = true;
|
2017-12-06 22:45:46 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2013-09-11 14:04:34 +08:00
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void internal_snapshot_abort(BlkActionState *common)
|
2013-09-11 14:04:34 +08:00
|
|
|
{
|
|
|
|
InternalSnapshotState *state =
|
|
|
|
DO_UPCAST(InternalSnapshotState, common, common);
|
|
|
|
BlockDriverState *bs = state->bs;
|
|
|
|
QEMUSnapshotInfo *sn = &state->sn;
|
2017-12-06 22:45:46 +08:00
|
|
|
AioContext *aio_context;
|
2013-09-11 14:04:34 +08:00
|
|
|
Error *local_error = NULL;
|
|
|
|
|
2015-10-23 11:08:13 +08:00
|
|
|
if (!state->created) {
|
2013-09-11 14:04:34 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 22:45:46 +08:00
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2013-09-11 14:04:34 +08:00
|
|
|
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
|
2015-12-18 23:35:14 +08: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 14:04:34 +08:00
|
|
|
}
|
2017-12-06 22:45:46 +08:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2013-09-11 14:04:34 +08:00
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void internal_snapshot_clean(BlkActionState *common)
|
2014-11-21 18:48:59 +08:00
|
|
|
{
|
|
|
|
InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
|
|
|
|
common, common);
|
2017-12-06 22:45:46 +08:00
|
|
|
AioContext *aio_context;
|
2014-11-21 18:48:59 +08:00
|
|
|
|
2017-12-06 22:45:46 +08:00
|
|
|
if (!state->bs) {
|
|
|
|
return;
|
2014-11-21 18:48:59 +08:00
|
|
|
}
|
2017-12-06 22:45:46 +08: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 18:48:59 +08:00
|
|
|
}
|
|
|
|
|
2013-05-08 18:25:16 +08:00
|
|
|
/* external snapshot private data */
|
2013-06-24 23:13:15 +08:00
|
|
|
typedef struct ExternalSnapshotState {
|
2015-11-06 07:13:09 +08:00
|
|
|
BlkActionState common;
|
2012-02-29 04:54:06 +08:00
|
|
|
BlockDriverState *old_bs;
|
|
|
|
BlockDriverState *new_bs;
|
2017-03-02 22:26:18 +08:00
|
|
|
bool overlay_appended;
|
2013-06-24 23:13:15 +08:00
|
|
|
} ExternalSnapshotState;
|
2012-02-29 04:54:06 +08:00
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void external_snapshot_prepare(BlkActionState *common,
|
2013-05-08 18:25:12 +08:00
|
|
|
Error **errp)
|
|
|
|
{
|
2021-02-02 20:49:44 +08:00
|
|
|
int ret;
|
2016-05-17 22:41:31 +08:00
|
|
|
int flags = 0;
|
2015-10-26 20:27:16 +08:00
|
|
|
QDict *options = NULL;
|
2013-05-08 18:25:12 +08:00
|
|
|
Error *local_err = NULL;
|
2015-10-26 20:27:16 +08:00
|
|
|
/* Device and node name of the image to generate the snapshot from */
|
2013-05-08 18:25:13 +08:00
|
|
|
const char *device;
|
2014-01-24 04:31:38 +08:00
|
|
|
const char *node_name;
|
2015-10-26 20:27:16 +08: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 18:25:13 +08:00
|
|
|
const char *new_image_file;
|
2013-06-24 23:13:15 +08:00
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
2013-05-13 16:43:43 +08:00
|
|
|
TransactionAction *action = common->action;
|
2017-12-06 22:45:43 +08:00
|
|
|
AioContext *aio_context;
|
block: Relax restrictions for blockdev-snapshot
blockdev-snapshot returned an error if the overlay was already in use,
which it defined as having any BlockBackend parent. This is in fact both
too strict (some parents can tolerate the change of visible data caused
by attaching a backing file) and too loose (some non-BlockBackend
parents may not be happy with it).
One important use case that is prevented by the too strict check is live
storage migration with blockdev-mirror. Here, the target node is
usually opened without a backing file so that the active layer is
mirrored while its backing chain can be copied in the background.
The backing chain should be attached to the mirror target node when
finalising the job, just before switching the users of the source node
to the new copy (at which point the mirror job still has a reference to
the node). drive-mirror did this automatically, but with blockdev-mirror
this is the job of the QMP client, so it needs a way to do this.
blockdev-snapshot is the obvious way, so this patch makes it work in
this scenario. The new condition is that no parent uses CONSISTENT_READ
permissions. This will ensure that the operation will still be blocked
when the node is attached to the guest device, so blockdev-snapshot
remains safe.
(For the sake of completeness, x-blockdev-reopen can be used to achieve
the same, however it is a big hammer, performs the graph change
completely unchecked and is still experimental. So even with the option
of using x-blockdev-reopen, there are reasons why blockdev-snapshot
should be able to perform this operation.)
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20200310113831.27293-3-kwolf@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Tested-by: Peter Krempa <pkrempa@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-03-10 19:38:26 +08:00
|
|
|
uint64_t perm, shared;
|
2013-05-08 18:25:12 +08:00
|
|
|
|
2015-10-26 20:27:16 +08: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 06:48:37 +08:00
|
|
|
BlockdevSnapshot *s = action->u.blockdev_snapshot.data;
|
2015-10-26 20:27:16 +08: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 06:48:37 +08:00
|
|
|
BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
|
2015-10-26 20:27:16 +08: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 18:25:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* start processing */
|
2015-11-06 07:13:18 +08:00
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-26 20:27:16 +08:00
|
|
|
state->old_bs = bdrv_lookup_bs(device, node_name, errp);
|
|
|
|
if (!state->old_bs) {
|
2013-05-08 18:25:12 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 22:45:43 +08:00
|
|
|
aio_context = bdrv_get_aio_context(state->old_bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
/* Paired with .clean() */
|
2015-10-23 11:08:10 +08:00
|
|
|
bdrv_drained_begin(state->old_bs);
|
2014-11-21 18:48:59 +08:00
|
|
|
|
2013-06-24 23:13:15 +08:00
|
|
|
if (!bdrv_is_inserted(state->old_bs)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2013-05-08 18:25:12 +08:00
|
|
|
}
|
|
|
|
|
2014-05-23 21:29:43 +08:00
|
|
|
if (bdrv_op_is_blocked(state->old_bs,
|
|
|
|
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2013-05-08 18:25:12 +08:00
|
|
|
}
|
|
|
|
|
2013-06-24 23:13:15 +08:00
|
|
|
if (!bdrv_is_read_only(state->old_bs)) {
|
|
|
|
if (bdrv_flush(state->old_bs)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_IO_ERROR);
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2013-05-08 18:25:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-26 20:27:16 +08: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 06:48:37 +08:00
|
|
|
BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
|
2015-10-26 20:27:16 +08: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 18:25:12 +08:00
|
|
|
|
2015-10-26 20:27:16 +08:00
|
|
|
if (node_name && !snapshot_node_name) {
|
2021-03-05 23:19:29 +08:00
|
|
|
error_setg(errp, "New overlay node-name missing");
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2013-05-08 18:25:12 +08:00
|
|
|
}
|
|
|
|
|
2015-10-26 20:27:16 +08:00
|
|
|
if (snapshot_node_name &&
|
|
|
|
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
|
2021-03-05 23:19:29 +08:00
|
|
|
error_setg(errp, "New overlay node-name already in use");
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2015-10-26 20:27:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
flags = state->old_bs->open_flags;
|
2017-07-18 08:34:21 +08:00
|
|
|
flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_COPY_ON_READ);
|
|
|
|
flags |= BDRV_O_NO_BACKING;
|
2015-10-26 20:27:16 +08: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 19:16:44 +08:00
|
|
|
int64_t size = bdrv_getlength(state->old_bs);
|
|
|
|
if (size < 0) {
|
|
|
|
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2016-03-02 19:16:44 +08:00
|
|
|
}
|
block: Use bdrv_refresh_filename() to pull
Before this patch, bdrv_refresh_filename() is used in a pushing manner:
Whenever the BDS graph is modified, the parents of the modified edges
are supposed to be updated (recursively upwards). However, that is
nonviable, considering that we want child changes not to concern
parents.
Also, in the long run we want a pull model anyway: Here, we would have a
bdrv_filename() function which returns a BDS's filename, freshly
constructed.
This patch is an intermediate step. It adds bdrv_refresh_filename()
calls before every place a BDS.filename value is used. The only
exceptions are protocol drivers that use their own filename, which
clearly would not profit from refreshing that filename before.
Also, bdrv_get_encrypted_filename() is removed along the way (as a user
of BDS.filename), since it is completely unused.
In turn, all of the calls to bdrv_refresh_filename() before this patch
are removed, because we no longer have to call this function on graph
changes.
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190201192935.18394-2-mreitz@redhat.com
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2019-02-02 03:29:05 +08:00
|
|
|
bdrv_refresh_filename(state->old_bs);
|
2015-10-26 20:27:16 +08:00
|
|
|
bdrv_img_create(new_image_file, format,
|
|
|
|
state->old_bs->filename,
|
|
|
|
state->old_bs->drv->format_name,
|
2017-04-21 20:27:01 +08:00
|
|
|
NULL, size, flags, false, &local_err);
|
2015-10-26 20:27:16 +08:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2015-10-26 20:27:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
options = qdict_new();
|
2018-11-02 00:30:37 +08:00
|
|
|
if (snapshot_node_name) {
|
2017-04-28 05:58:17 +08:00
|
|
|
qdict_put_str(options, "node-name", snapshot_node_name);
|
2015-10-26 20:27:16 +08:00
|
|
|
}
|
2017-04-28 05:58:17 +08:00
|
|
|
qdict_put_str(options, "driver", format);
|
2014-01-24 04:31:38 +08:00
|
|
|
}
|
|
|
|
|
2016-05-17 22:41:31 +08:00
|
|
|
state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags,
|
|
|
|
errp);
|
2014-02-19 01:33:05 +08:00
|
|
|
/* We will manually add the backing_hd field to the bs later */
|
2016-05-17 22:41:31 +08:00
|
|
|
if (!state->new_bs) {
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2015-10-26 20:27:16 +08:00
|
|
|
}
|
|
|
|
|
block: Relax restrictions for blockdev-snapshot
blockdev-snapshot returned an error if the overlay was already in use,
which it defined as having any BlockBackend parent. This is in fact both
too strict (some parents can tolerate the change of visible data caused
by attaching a backing file) and too loose (some non-BlockBackend
parents may not be happy with it).
One important use case that is prevented by the too strict check is live
storage migration with blockdev-mirror. Here, the target node is
usually opened without a backing file so that the active layer is
mirrored while its backing chain can be copied in the background.
The backing chain should be attached to the mirror target node when
finalising the job, just before switching the users of the source node
to the new copy (at which point the mirror job still has a reference to
the node). drive-mirror did this automatically, but with blockdev-mirror
this is the job of the QMP client, so it needs a way to do this.
blockdev-snapshot is the obvious way, so this patch makes it work in
this scenario. The new condition is that no parent uses CONSISTENT_READ
permissions. This will ensure that the operation will still be blocked
when the node is attached to the guest device, so blockdev-snapshot
remains safe.
(For the sake of completeness, x-blockdev-reopen can be used to achieve
the same, however it is a big hammer, performs the graph change
completely unchecked and is still experimental. So even with the option
of using x-blockdev-reopen, there are reasons why blockdev-snapshot
should be able to perform this operation.)
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20200310113831.27293-3-kwolf@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Tested-by: Peter Krempa <pkrempa@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-03-10 19:38:26 +08:00
|
|
|
/*
|
|
|
|
* Allow attaching a backing file to an overlay that's already in use only
|
|
|
|
* if the parents don't assume that they are already seeing a valid image.
|
|
|
|
* (Specifically, allow it as a mirror target, which is write-only access.)
|
|
|
|
*/
|
|
|
|
bdrv_get_cumulative_perm(state->new_bs, &perm, &shared);
|
|
|
|
if (perm & BLK_PERM_CONSISTENT_READ) {
|
2019-06-04 04:22:36 +08:00
|
|
|
error_setg(errp, "The overlay is already in use");
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2015-10-26 20:27:16 +08:00
|
|
|
}
|
|
|
|
|
2019-06-13 00:34:46 +08:00
|
|
|
if (state->new_bs->drv->is_filter) {
|
|
|
|
error_setg(errp, "Filters cannot be used as overlays");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_cow_child(state->new_bs)) {
|
2019-06-04 04:22:36 +08:00
|
|
|
error_setg(errp, "The overlay already has a backing image");
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2015-11-03 18:32:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!state->new_bs->drv->supports_backing) {
|
2019-06-04 04:22:36 +08:00
|
|
|
error_setg(errp, "The overlay does not support backing images");
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2017-02-20 19:46:42 +08:00
|
|
|
}
|
|
|
|
|
2021-02-02 20:49:44 +08:00
|
|
|
ret = bdrv_append(state->new_bs, state->old_bs, errp);
|
|
|
|
if (ret < 0) {
|
2017-12-06 22:45:43 +08:00
|
|
|
goto out;
|
2013-05-08 18:25:12 +08:00
|
|
|
}
|
2017-03-02 22:26:18 +08:00
|
|
|
state->overlay_appended = true;
|
2017-12-06 22:45:43 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2013-05-08 18:25:12 +08:00
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void external_snapshot_commit(BlkActionState *common)
|
2013-05-08 18:25:14 +08:00
|
|
|
{
|
2013-06-24 23:13:15 +08:00
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
2017-12-06 22:45:43 +08:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->old_bs);
|
|
|
|
aio_context_acquire(aio_context);
|
2013-05-08 18:25:16 +08:00
|
|
|
|
2013-05-08 18:25:14 +08: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 */
|
2020-09-23 18:56:46 +08:00
|
|
|
if (!qatomic_read(&state->old_bs->copy_on_read)) {
|
2018-11-12 22:00:39 +08:00
|
|
|
bdrv_reopen_set_read_only(state->old_bs, true, NULL);
|
2016-02-29 20:12:26 +08:00
|
|
|
}
|
2017-12-06 22:45:43 +08:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2013-05-08 18:25:14 +08:00
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void external_snapshot_abort(BlkActionState *common)
|
2013-05-08 18:25:15 +08:00
|
|
|
{
|
2013-06-24 23:13:15 +08:00
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
|
|
|
if (state->new_bs) {
|
2017-03-02 22:26:18 +08:00
|
|
|
if (state->overlay_appended) {
|
2017-12-06 22:45:43 +08:00
|
|
|
AioContext *aio_context;
|
blockdev: Return bs to the proper context on snapshot abort
external_snapshot_abort() calls to bdrv_set_backing_hd(), which
returns state->old_bs to the main AioContext, as it's intended to be
used then the BDS is going to be released. As that's not the case when
aborting an external snapshot, return it to the AioContext it was
before the call.
This issue can be triggered by issuing a transaction with two actions,
a proper blockdev-snapshot-sync and a bogus one, so the second will
trigger a transaction abort. This results in a crash with an stack
trace like this one:
#0 0x00007fa1048b28df in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007fa10489ccf5 in __GI_abort () at abort.c:79
#2 0x00007fa10489cbc9 in __assert_fail_base
(fmt=0x7fa104a03300 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=0x5572240b44d8 "bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)", file=0x557224014d30 "block.c", line=2240, function=<optimized out>) at assert.c:92
#3 0x00007fa1048aae96 in __GI___assert_fail
(assertion=assertion@entry=0x5572240b44d8 "bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)", file=file@entry=0x557224014d30 "block.c", line=line@entry=2240, function=function@entry=0x5572240b5d60 <__PRETTY_FUNCTION__.31620> "bdrv_replace_child_noperm") at assert.c:101
#4 0x0000557223e631f8 in bdrv_replace_child_noperm (child=0x557225b9c980, new_bs=new_bs@entry=0x557225c42e40) at block.c:2240
#5 0x0000557223e68be7 in bdrv_replace_node (from=0x557226951a60, to=0x557225c42e40, errp=0x5572247d6138 <error_abort>) at block.c:4196
#6 0x0000557223d069c4 in external_snapshot_abort (common=0x557225d7e170) at blockdev.c:1731
#7 0x0000557223d069c4 in external_snapshot_abort (common=0x557225d7e170) at blockdev.c:1717
#8 0x0000557223d09013 in qmp_transaction (dev_list=<optimized out>, has_props=<optimized out>, props=0x557225cc7d70, errp=errp@entry=0x7ffe704c0c98) at blockdev.c:2360
#9 0x0000557223e32085 in qmp_marshal_transaction (args=<optimized out>, ret=<optimized out>, errp=0x7ffe704c0d08) at qapi/qapi-commands-transaction.c:44
#10 0x0000557223ee798c in do_qmp_dispatch (errp=0x7ffe704c0d00, allow_oob=<optimized out>, request=<optimized out>, cmds=0x5572247d3cc0 <qmp_commands>) at qapi/qmp-dispatch.c:132
#11 0x0000557223ee798c in qmp_dispatch (cmds=0x5572247d3cc0 <qmp_commands>, request=<optimized out>, allow_oob=<optimized out>) at qapi/qmp-dispatch.c:175
#12 0x0000557223e06141 in monitor_qmp_dispatch (mon=0x557225c69ff0, req=<optimized out>) at monitor/qmp.c:120
#13 0x0000557223e0678a in monitor_qmp_bh_dispatcher (data=<optimized out>) at monitor/qmp.c:209
#14 0x0000557223f2f366 in aio_bh_call (bh=0x557225b9dc60) at util/async.c:117
#15 0x0000557223f2f366 in aio_bh_poll (ctx=ctx@entry=0x557225b9c840) at util/async.c:117
#16 0x0000557223f32754 in aio_dispatch (ctx=0x557225b9c840) at util/aio-posix.c:459
#17 0x0000557223f2f242 in aio_ctx_dispatch (source=<optimized out>, callback=<optimized out>, user_data=<optimized out>) at util/async.c:260
#18 0x00007fa10913467d in g_main_dispatch (context=0x557225c28e80) at gmain.c:3176
#19 0x00007fa10913467d in g_main_context_dispatch (context=context@entry=0x557225c28e80) at gmain.c:3829
#20 0x0000557223f31808 in glib_pollfds_poll () at util/main-loop.c:219
#21 0x0000557223f31808 in os_host_main_loop_wait (timeout=<optimized out>) at util/main-loop.c:242
#22 0x0000557223f31808 in main_loop_wait (nonblocking=<optimized out>) at util/main-loop.c:518
#23 0x0000557223d13201 in main_loop () at vl.c:1828
#24 0x0000557223bbfb82 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4504
RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1779036
Signed-off-by: Sergio Lopez <slp@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-01-08 22:31:37 +08:00
|
|
|
AioContext *tmp_context;
|
|
|
|
int ret;
|
2017-12-06 22:45:43 +08:00
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->old_bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2017-06-07 21:55:22 +08: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);
|
blockdev: Return bs to the proper context on snapshot abort
external_snapshot_abort() calls to bdrv_set_backing_hd(), which
returns state->old_bs to the main AioContext, as it's intended to be
used then the BDS is going to be released. As that's not the case when
aborting an external snapshot, return it to the AioContext it was
before the call.
This issue can be triggered by issuing a transaction with two actions,
a proper blockdev-snapshot-sync and a bogus one, so the second will
trigger a transaction abort. This results in a crash with an stack
trace like this one:
#0 0x00007fa1048b28df in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007fa10489ccf5 in __GI_abort () at abort.c:79
#2 0x00007fa10489cbc9 in __assert_fail_base
(fmt=0x7fa104a03300 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=0x5572240b44d8 "bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)", file=0x557224014d30 "block.c", line=2240, function=<optimized out>) at assert.c:92
#3 0x00007fa1048aae96 in __GI___assert_fail
(assertion=assertion@entry=0x5572240b44d8 "bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)", file=file@entry=0x557224014d30 "block.c", line=line@entry=2240, function=function@entry=0x5572240b5d60 <__PRETTY_FUNCTION__.31620> "bdrv_replace_child_noperm") at assert.c:101
#4 0x0000557223e631f8 in bdrv_replace_child_noperm (child=0x557225b9c980, new_bs=new_bs@entry=0x557225c42e40) at block.c:2240
#5 0x0000557223e68be7 in bdrv_replace_node (from=0x557226951a60, to=0x557225c42e40, errp=0x5572247d6138 <error_abort>) at block.c:4196
#6 0x0000557223d069c4 in external_snapshot_abort (common=0x557225d7e170) at blockdev.c:1731
#7 0x0000557223d069c4 in external_snapshot_abort (common=0x557225d7e170) at blockdev.c:1717
#8 0x0000557223d09013 in qmp_transaction (dev_list=<optimized out>, has_props=<optimized out>, props=0x557225cc7d70, errp=errp@entry=0x7ffe704c0c98) at blockdev.c:2360
#9 0x0000557223e32085 in qmp_marshal_transaction (args=<optimized out>, ret=<optimized out>, errp=0x7ffe704c0d08) at qapi/qapi-commands-transaction.c:44
#10 0x0000557223ee798c in do_qmp_dispatch (errp=0x7ffe704c0d00, allow_oob=<optimized out>, request=<optimized out>, cmds=0x5572247d3cc0 <qmp_commands>) at qapi/qmp-dispatch.c:132
#11 0x0000557223ee798c in qmp_dispatch (cmds=0x5572247d3cc0 <qmp_commands>, request=<optimized out>, allow_oob=<optimized out>) at qapi/qmp-dispatch.c:175
#12 0x0000557223e06141 in monitor_qmp_dispatch (mon=0x557225c69ff0, req=<optimized out>) at monitor/qmp.c:120
#13 0x0000557223e0678a in monitor_qmp_bh_dispatcher (data=<optimized out>) at monitor/qmp.c:209
#14 0x0000557223f2f366 in aio_bh_call (bh=0x557225b9dc60) at util/async.c:117
#15 0x0000557223f2f366 in aio_bh_poll (ctx=ctx@entry=0x557225b9c840) at util/async.c:117
#16 0x0000557223f32754 in aio_dispatch (ctx=0x557225b9c840) at util/aio-posix.c:459
#17 0x0000557223f2f242 in aio_ctx_dispatch (source=<optimized out>, callback=<optimized out>, user_data=<optimized out>) at util/async.c:260
#18 0x00007fa10913467d in g_main_dispatch (context=0x557225c28e80) at gmain.c:3176
#19 0x00007fa10913467d in g_main_context_dispatch (context=context@entry=0x557225c28e80) at gmain.c:3829
#20 0x0000557223f31808 in glib_pollfds_poll () at util/main-loop.c:219
#21 0x0000557223f31808 in os_host_main_loop_wait (timeout=<optimized out>) at util/main-loop.c:242
#22 0x0000557223f31808 in main_loop_wait (nonblocking=<optimized out>) at util/main-loop.c:518
#23 0x0000557223d13201 in main_loop () at vl.c:1828
#24 0x0000557223bbfb82 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4504
RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1779036
Signed-off-by: Sergio Lopez <slp@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-01-08 22:31:37 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The call to bdrv_set_backing_hd() above returns state->old_bs to
|
|
|
|
* the main AioContext. As we're still going to be using it, return
|
|
|
|
* it to the AioContext it was before.
|
|
|
|
*/
|
|
|
|
tmp_context = bdrv_get_aio_context(state->old_bs);
|
|
|
|
if (aio_context != tmp_context) {
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
aio_context_acquire(tmp_context);
|
|
|
|
|
|
|
|
ret = bdrv_try_set_aio_context(state->old_bs,
|
|
|
|
aio_context, NULL);
|
|
|
|
assert(ret == 0);
|
|
|
|
|
|
|
|
aio_context_release(tmp_context);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
}
|
|
|
|
|
2017-03-06 23:20:51 +08:00
|
|
|
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
|
2017-06-07 21:55:22 +08:00
|
|
|
bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */
|
2017-12-06 22:45:43 +08:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2017-02-20 19:46:42 +08:00
|
|
|
}
|
2013-05-08 18:25:15 +08:00
|
|
|
}
|
2015-10-23 11:08:10 +08:00
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void external_snapshot_clean(BlkActionState *common)
|
2015-10-23 11:08:10 +08:00
|
|
|
{
|
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
2017-12-06 22:45:43 +08:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
if (!state->old_bs) {
|
|
|
|
return;
|
2014-11-21 18:48:59 +08:00
|
|
|
}
|
2017-12-06 22:45:43 +08: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 18:25:15 +08: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 23:13:17 +08:00
|
|
|
typedef struct DriveBackupState {
|
2015-11-06 07:13:09 +08: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 23:13:17 +08:00
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockJob *job;
|
|
|
|
} DriveBackupState;
|
|
|
|
|
2020-01-08 22:31:32 +08:00
|
|
|
static BlockJob *do_backup_common(BackupCommon *backup,
|
|
|
|
BlockDriverState *bs,
|
|
|
|
BlockDriverState *target_bs,
|
|
|
|
AioContext *aio_context,
|
|
|
|
JobTxn *txn, Error **errp);
|
2015-11-06 07:13:17 +08:00
|
|
|
|
2015-11-06 07:13:09 +08: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 23:13:17 +08:00
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
|
|
|
DriveBackup *backup;
|
2020-01-08 22:31:32 +08:00
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *target_bs;
|
|
|
|
BlockDriverState *source = NULL;
|
2017-12-06 22:45:44 +08:00
|
|
|
AioContext *aio_context;
|
2020-01-08 22:31:34 +08:00
|
|
|
AioContext *old_context;
|
2020-01-08 22:31:32 +08:00
|
|
|
QDict *options;
|
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 23:13:17 +08:00
|
|
|
Error *local_err = NULL;
|
2020-01-08 22:31:32 +08:00
|
|
|
int flags;
|
|
|
|
int64_t size;
|
|
|
|
bool set_backing_hd = false;
|
2020-01-08 22:31:34 +08:00
|
|
|
int ret;
|
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 23:13:17 +08:00
|
|
|
|
2015-10-27 06:34:54 +08: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 06:48:37 +08: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 23:13:17 +08:00
|
|
|
|
2020-01-08 22:31:32 +08:00
|
|
|
if (!backup->has_mode) {
|
|
|
|
backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
|
|
|
}
|
|
|
|
|
2019-06-18 22:08:04 +08:00
|
|
|
bs = bdrv_lookup_bs(backup->device, backup->device, errp);
|
2016-06-23 20:20:24 +08:00
|
|
|
if (!bs) {
|
2015-10-23 11:08:11 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-08 22:31:32 +08:00
|
|
|
if (!bs->drv) {
|
|
|
|
error_setg(errp, "Device has no medium");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 22:45:44 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
/* Paired with .clean() */
|
2016-06-23 20:20:24 +08:00
|
|
|
bdrv_drained_begin(bs);
|
2017-12-06 22:45:44 +08:00
|
|
|
|
2020-01-08 22:31:32 +08:00
|
|
|
if (!backup->has_format) {
|
|
|
|
backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ?
|
|
|
|
NULL : (char *) bs->drv->format_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Early check to avoid creating target */
|
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags = bs->open_flags | BDRV_O_RDWR;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if we have a backing HD we can use to create our new image
|
|
|
|
* on top of.
|
|
|
|
*/
|
|
|
|
if (backup->sync == MIRROR_SYNC_MODE_TOP) {
|
2019-06-12 23:46:45 +08:00
|
|
|
/*
|
|
|
|
* Backup will not replace the source by the target, so none
|
|
|
|
* of the filters skipped here will be removed (in contrast to
|
|
|
|
* mirror). Therefore, we can skip all of them when looking
|
|
|
|
* for the first COW relationship.
|
|
|
|
*/
|
|
|
|
source = bdrv_cow_bs(bdrv_skip_filters(bs));
|
2020-01-08 22:31:32 +08:00
|
|
|
if (!source) {
|
|
|
|
backup->sync = MIRROR_SYNC_MODE_FULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (backup->sync == MIRROR_SYNC_MODE_NONE) {
|
|
|
|
source = bs;
|
|
|
|
flags |= BDRV_O_NO_BACKING;
|
|
|
|
set_backing_hd = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = bdrv_getlength(bs);
|
|
|
|
if (size < 0) {
|
|
|
|
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
|
|
|
|
assert(backup->format);
|
|
|
|
if (source) {
|
2019-06-12 23:46:45 +08:00
|
|
|
/* Implicit filters should not appear in the filename */
|
|
|
|
BlockDriverState *explicit_backing =
|
|
|
|
bdrv_skip_implicit_filters(source);
|
|
|
|
|
|
|
|
bdrv_refresh_filename(explicit_backing);
|
|
|
|
bdrv_img_create(backup->target, backup->format,
|
|
|
|
explicit_backing->filename,
|
|
|
|
explicit_backing->drv->format_name, NULL,
|
2020-01-08 22:31:32 +08:00
|
|
|
size, flags, false, &local_err);
|
|
|
|
} else {
|
|
|
|
bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL,
|
|
|
|
size, flags, false, &local_err);
|
|
|
|
}
|
|
|
|
}
|
2014-11-21 18:48:59 +08:00
|
|
|
|
2014-01-30 22:07:28 +08: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 23:13:17 +08:00
|
|
|
error_propagate(errp, local_err);
|
2017-12-06 22:45:44 +08: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 23:13:17 +08:00
|
|
|
}
|
2017-12-06 22:45:44 +08:00
|
|
|
|
2020-01-08 22:31:32 +08:00
|
|
|
options = qdict_new();
|
|
|
|
qdict_put_str(options, "discard", "unmap");
|
|
|
|
qdict_put_str(options, "detect-zeroes", "unmap");
|
|
|
|
if (backup->format) {
|
|
|
|
qdict_put_str(options, "driver", backup->format);
|
|
|
|
}
|
|
|
|
|
|
|
|
target_bs = bdrv_open(backup->target, NULL, options, flags, errp);
|
|
|
|
if (!target_bs) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2020-01-08 22:31:34 +08:00
|
|
|
/* Honor bdrv_try_set_aio_context() context acquisition requirements. */
|
|
|
|
old_context = bdrv_get_aio_context(target_bs);
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
aio_context_acquire(old_context);
|
|
|
|
|
|
|
|
ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
|
|
|
|
if (ret < 0) {
|
|
|
|
bdrv_unref(target_bs);
|
|
|
|
aio_context_release(old_context);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aio_context_release(old_context);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2020-01-08 22:31:32 +08:00
|
|
|
if (set_backing_hd) {
|
2021-02-02 20:49:46 +08:00
|
|
|
if (bdrv_set_backing_hd(target_bs, source, errp) < 0) {
|
2020-01-08 22:31:32 +08:00
|
|
|
goto unref;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
state->bs = bs;
|
|
|
|
|
|
|
|
state->job = do_backup_common(qapi_DriveBackup_base(backup),
|
|
|
|
bs, target_bs, aio_context,
|
|
|
|
common->block_job_txn, errp);
|
|
|
|
|
|
|
|
unref:
|
|
|
|
bdrv_unref(target_bs);
|
2017-12-06 22:45:44 +08:00
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2016-11-08 14:50:38 +08: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 23:13:17 +08:00
|
|
|
|
2016-11-08 14:50:38 +08:00
|
|
|
static void drive_backup_commit(BlkActionState *common)
|
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
2017-12-06 22:45:44 +08:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-11-08 14:50:38 +08:00
|
|
|
assert(state->job);
|
2018-04-13 23:31:02 +08:00
|
|
|
job_start(&state->job->job);
|
2017-12-06 22:45:44 +08: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 23:13:17 +08:00
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08: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 23:13:17 +08:00
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
|
|
|
|
2016-11-08 14:50:38 +08:00
|
|
|
if (state->job) {
|
2017-12-06 22:45:44 +08:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2018-04-24 22:13:52 +08:00
|
|
|
job_cancel_sync(&state->job->job);
|
2017-12-06 22:45:44 +08: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 23:13:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void drive_backup_clean(BlkActionState *common)
|
2014-11-21 18:48:59 +08:00
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
2017-12-06 22:45:44 +08:00
|
|
|
AioContext *aio_context;
|
2014-11-21 18:48:59 +08:00
|
|
|
|
2017-12-06 22:45:44 +08:00
|
|
|
if (!state->bs) {
|
|
|
|
return;
|
2014-11-21 18:48:59 +08:00
|
|
|
}
|
2017-12-06 22:45:44 +08: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 18:48:59 +08:00
|
|
|
}
|
|
|
|
|
2014-12-18 18:37:06 +08:00
|
|
|
typedef struct BlockdevBackupState {
|
2015-11-06 07:13:09 +08:00
|
|
|
BlkActionState common;
|
2014-12-18 18:37:06 +08:00
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockJob *job;
|
|
|
|
} BlockdevBackupState;
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
|
2014-12-18 18:37:06 +08:00
|
|
|
{
|
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
|
|
|
BlockdevBackup *backup;
|
2020-01-08 22:31:33 +08:00
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *target_bs;
|
2017-12-06 22:45:45 +08:00
|
|
|
AioContext *aio_context;
|
2020-01-08 22:31:34 +08:00
|
|
|
AioContext *old_context;
|
|
|
|
int ret;
|
2014-12-18 18:37:06 +08:00
|
|
|
|
2015-10-27 06:34:54 +08: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 06:48:37 +08:00
|
|
|
backup = common->action->u.blockdev_backup.data;
|
2014-12-18 18:37:06 +08:00
|
|
|
|
2018-07-03 03:46:29 +08:00
|
|
|
bs = bdrv_lookup_bs(backup->device, backup->device, errp);
|
2016-06-23 20:20:24 +08:00
|
|
|
if (!bs) {
|
2015-10-23 11:08:12 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-08 22:31:33 +08:00
|
|
|
target_bs = bdrv_lookup_bs(backup->target, backup->target, errp);
|
|
|
|
if (!target_bs) {
|
2014-12-18 18:37:06 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-08 22:31:34 +08:00
|
|
|
/* Honor bdrv_try_set_aio_context() context acquisition requirements. */
|
2017-12-06 22:45:45 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2020-01-08 22:31:34 +08:00
|
|
|
old_context = bdrv_get_aio_context(target_bs);
|
|
|
|
aio_context_acquire(old_context);
|
|
|
|
|
|
|
|
ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
|
|
|
|
if (ret < 0) {
|
|
|
|
aio_context_release(old_context);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aio_context_release(old_context);
|
2017-12-06 22:45:45 +08:00
|
|
|
aio_context_acquire(aio_context);
|
2016-06-23 20:20:24 +08:00
|
|
|
state->bs = bs;
|
2017-12-06 22:45:45 +08:00
|
|
|
|
|
|
|
/* Paired with .clean() */
|
2015-10-23 11:08:12 +08:00
|
|
|
bdrv_drained_begin(state->bs);
|
2014-12-18 18:37:06 +08:00
|
|
|
|
2020-01-08 22:31:33 +08:00
|
|
|
state->job = do_backup_common(qapi_BlockdevBackup_base(backup),
|
|
|
|
bs, target_bs, aio_context,
|
|
|
|
common->block_job_txn, errp);
|
2017-12-06 22:45:45 +08:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2016-11-08 14:50:38 +08:00
|
|
|
}
|
2014-12-18 18:37:06 +08:00
|
|
|
|
2016-11-08 14:50:38 +08:00
|
|
|
static void blockdev_backup_commit(BlkActionState *common)
|
|
|
|
{
|
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
2017-12-06 22:45:45 +08:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-11-08 14:50:38 +08:00
|
|
|
assert(state->job);
|
2018-04-13 23:31:02 +08:00
|
|
|
job_start(&state->job->job);
|
2017-12-06 22:45:45 +08:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2014-12-18 18:37:06 +08:00
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void blockdev_backup_abort(BlkActionState *common)
|
2014-12-18 18:37:06 +08:00
|
|
|
{
|
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
|
|
|
|
2016-11-08 14:50:38 +08:00
|
|
|
if (state->job) {
|
2017-12-06 22:45:45 +08:00
|
|
|
AioContext *aio_context;
|
|
|
|
|
|
|
|
aio_context = bdrv_get_aio_context(state->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2018-04-24 22:13:52 +08:00
|
|
|
job_cancel_sync(&state->job->job);
|
2017-12-06 22:45:45 +08:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2014-12-18 18:37:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void blockdev_backup_clean(BlkActionState *common)
|
2014-12-18 18:37:06 +08:00
|
|
|
{
|
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
2017-12-06 22:45:45 +08:00
|
|
|
AioContext *aio_context;
|
2014-12-18 18:37:06 +08:00
|
|
|
|
2017-12-06 22:45:45 +08:00
|
|
|
if (!state->bs) {
|
|
|
|
return;
|
2014-12-18 18:37:06 +08:00
|
|
|
}
|
2017-12-06 22:45:45 +08: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 18:37:06 +08:00
|
|
|
}
|
|
|
|
|
2015-11-09 18:16:54 +08:00
|
|
|
typedef struct BlockDirtyBitmapState {
|
2015-11-06 07:13:09 +08:00
|
|
|
BlkActionState common;
|
2015-11-09 18:16:54 +08:00
|
|
|
BdrvDirtyBitmap *bitmap;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
HBitmap *backup;
|
|
|
|
bool prepared;
|
2018-06-12 02:53:32 +08:00
|
|
|
bool was_enabled;
|
2015-11-09 18:16:54 +08:00
|
|
|
} BlockDirtyBitmapState;
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void block_dirty_bitmap_add_prepare(BlkActionState *common,
|
2015-11-09 18:16:54 +08:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
BlockDirtyBitmapAdd *action;
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
2015-11-06 07:13:18 +08: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 06:48:37 +08:00
|
|
|
action = common->action->u.block_dirty_bitmap_add.data;
|
2015-11-09 18:16:54 +08: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 20:05:23 +08:00
|
|
|
action->has_persistent, action->persistent,
|
2018-12-21 17:35:22 +08:00
|
|
|
action->has_disabled, action->disabled,
|
2015-11-09 18:16:54 +08:00
|
|
|
&local_err);
|
|
|
|
|
|
|
|
if (!local_err) {
|
|
|
|
state->prepared = true;
|
|
|
|
} else {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void block_dirty_bitmap_add_abort(BlkActionState *common)
|
2015-11-09 18:16:54 +08: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 06:48:37 +08:00
|
|
|
action = common->action->u.block_dirty_bitmap_add.data;
|
2015-11-09 18:16:54 +08: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 07:13:09 +08:00
|
|
|
static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
2015-11-09 18:16:54 +08:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
BlockDirtyBitmap *action;
|
|
|
|
|
2015-11-06 07:13:18 +08: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 06:48:37 +08:00
|
|
|
action = common->action->u.block_dirty_bitmap_clear.data;
|
2015-11-09 18:16:54 +08:00
|
|
|
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
|
|
|
action->name,
|
|
|
|
&state->bs,
|
|
|
|
errp);
|
|
|
|
if (!state->bitmap) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-13 00:05:49 +08:00
|
|
|
if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_DEFAULT, errp)) {
|
2017-06-28 20:05:10 +08:00
|
|
|
return;
|
2015-11-09 18:16:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
|
|
|
|
}
|
|
|
|
|
2018-10-30 04:23:15 +08:00
|
|
|
static void block_dirty_bitmap_restore(BlkActionState *common)
|
2015-11-09 18:16:54 +08:00
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
2017-03-16 05:28:11 +08:00
|
|
|
if (state->backup) {
|
2018-10-30 04:23:14 +08:00
|
|
|
bdrv_restore_dirty_bitmap(state->bitmap, state->backup);
|
2017-03-16 05:28:11 +08:00
|
|
|
}
|
2015-11-09 18:16:54 +08:00
|
|
|
}
|
|
|
|
|
2018-10-30 04:23:15 +08:00
|
|
|
static void block_dirty_bitmap_free_backup(BlkActionState *common)
|
2015-11-09 18:16:54 +08:00
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
hbitmap_free(state->backup);
|
|
|
|
}
|
|
|
|
|
2018-06-12 02:53:32 +08:00
|
|
|
static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmap *action;
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-21 17:35:22 +08:00
|
|
|
action = common->action->u.block_dirty_bitmap_enable.data;
|
2018-06-12 02:53:32 +08:00
|
|
|
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
|
|
|
action->name,
|
|
|
|
NULL,
|
|
|
|
errp);
|
|
|
|
if (!state->bitmap) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-13 00:05:49 +08:00
|
|
|
if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
2018-10-30 04:23:16 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-12 02:53:32 +08:00
|
|
|
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
|
|
|
|
bdrv_enable_dirty_bitmap(state->bitmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void block_dirty_bitmap_enable_abort(BlkActionState *common)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
if (!state->was_enabled) {
|
|
|
|
bdrv_disable_dirty_bitmap(state->bitmap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmap *action;
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-21 17:35:22 +08:00
|
|
|
action = common->action->u.block_dirty_bitmap_disable.data;
|
2018-06-12 02:53:32 +08:00
|
|
|
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
|
|
|
action->name,
|
|
|
|
NULL,
|
|
|
|
errp);
|
|
|
|
if (!state->bitmap) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-13 00:05:49 +08:00
|
|
|
if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
2018-10-30 04:23:16 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-12 02:53:32 +08:00
|
|
|
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
|
|
|
|
bdrv_disable_dirty_bitmap(state->bitmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void block_dirty_bitmap_disable_abort(BlkActionState *common)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
if (state->was_enabled) {
|
|
|
|
bdrv_enable_dirty_bitmap(state->bitmap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-30 04:23:15 +08:00
|
|
|
static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmapMerge *action;
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-21 17:35:22 +08:00
|
|
|
action = common->action->u.block_dirty_bitmap_merge.data;
|
2018-10-30 04:23:15 +08:00
|
|
|
|
2020-05-13 09:16:43 +08:00
|
|
|
state->bitmap = block_dirty_bitmap_merge(action->node, action->target,
|
|
|
|
action->bitmaps, &state->backup,
|
|
|
|
errp);
|
2018-10-30 04:23:15 +08:00
|
|
|
}
|
|
|
|
|
2019-07-30 04:35:54 +08:00
|
|
|
static void block_dirty_bitmap_remove_prepare(BlkActionState *common,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmap *action;
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
if (action_check_completion_mode(common, errp) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
action = common->action->u.block_dirty_bitmap_remove.data;
|
|
|
|
|
2020-05-13 09:16:43 +08:00
|
|
|
state->bitmap = block_dirty_bitmap_remove(action->node, action->name,
|
|
|
|
false, &state->bs, errp);
|
2019-07-30 04:35:54 +08:00
|
|
|
if (state->bitmap) {
|
|
|
|
bdrv_dirty_bitmap_skip_store(state->bitmap, true);
|
|
|
|
bdrv_dirty_bitmap_set_busy(state->bitmap, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void block_dirty_bitmap_remove_abort(BlkActionState *common)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
if (state->bitmap) {
|
|
|
|
bdrv_dirty_bitmap_skip_store(state->bitmap, false);
|
|
|
|
bdrv_dirty_bitmap_set_busy(state->bitmap, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void block_dirty_bitmap_remove_commit(BlkActionState *common)
|
|
|
|
{
|
|
|
|
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
|
|
|
common, common);
|
|
|
|
|
|
|
|
bdrv_dirty_bitmap_set_busy(state->bitmap, false);
|
2019-09-16 22:19:09 +08:00
|
|
|
bdrv_release_dirty_bitmap(state->bitmap);
|
2019-07-30 04:35:54 +08:00
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void abort_prepare(BlkActionState *common, Error **errp)
|
2013-06-24 23:13:18 +08:00
|
|
|
{
|
|
|
|
error_setg(errp, "Transaction aborted using Abort action");
|
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static void abort_commit(BlkActionState *common)
|
2013-06-24 23:13:18 +08:00
|
|
|
{
|
2013-07-26 00:21:28 +08:00
|
|
|
g_assert_not_reached(); /* this action never succeeds */
|
2013-06-24 23:13:18 +08:00
|
|
|
}
|
|
|
|
|
2015-11-06 07:13:09 +08:00
|
|
|
static const BlkActionOps actions[] = {
|
2015-10-26 20:27:16 +08: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 21:00:24 +08:00
|
|
|
.clean = external_snapshot_clean,
|
2015-10-26 20:27:16 +08:00
|
|
|
},
|
2013-05-13 16:43:43 +08:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
|
2013-06-24 23:13:15 +08:00
|
|
|
.instance_size = sizeof(ExternalSnapshotState),
|
2013-05-08 18:25:16 +08:00
|
|
|
.prepare = external_snapshot_prepare,
|
|
|
|
.commit = external_snapshot_commit,
|
|
|
|
.abort = external_snapshot_abort,
|
2015-10-23 11:08:10 +08:00
|
|
|
.clean = external_snapshot_clean,
|
2013-05-08 18:25:16 +08: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 23:13:17 +08:00
|
|
|
[TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = {
|
|
|
|
.instance_size = sizeof(DriveBackupState),
|
|
|
|
.prepare = drive_backup_prepare,
|
2016-11-08 14:50:38 +08: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 23:13:17 +08:00
|
|
|
.abort = drive_backup_abort,
|
2014-11-21 18:48:59 +08: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 23:13:17 +08:00
|
|
|
},
|
2014-12-18 18:37:06 +08:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
|
|
|
|
.instance_size = sizeof(BlockdevBackupState),
|
|
|
|
.prepare = blockdev_backup_prepare,
|
2016-11-08 14:50:38 +08:00
|
|
|
.commit = blockdev_backup_commit,
|
2014-12-18 18:37:06 +08:00
|
|
|
.abort = blockdev_backup_abort,
|
|
|
|
.clean = blockdev_backup_clean,
|
|
|
|
},
|
2013-06-24 23:13:18 +08:00
|
|
|
[TRANSACTION_ACTION_KIND_ABORT] = {
|
2015-11-06 07:13:09 +08:00
|
|
|
.instance_size = sizeof(BlkActionState),
|
2013-06-24 23:13:18 +08:00
|
|
|
.prepare = abort_prepare,
|
|
|
|
.commit = abort_commit,
|
|
|
|
},
|
2013-09-11 14:04:34 +08:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
|
|
|
|
.instance_size = sizeof(InternalSnapshotState),
|
|
|
|
.prepare = internal_snapshot_prepare,
|
|
|
|
.abort = internal_snapshot_abort,
|
2014-11-21 18:48:59 +08:00
|
|
|
.clean = internal_snapshot_clean,
|
2013-09-11 14:04:34 +08:00
|
|
|
},
|
2015-11-09 18:16:54 +08: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,
|
2018-10-30 04:23:15 +08:00
|
|
|
.commit = block_dirty_bitmap_free_backup,
|
|
|
|
.abort = block_dirty_bitmap_restore,
|
2018-06-12 02:53:32 +08:00
|
|
|
},
|
2018-12-21 17:35:22 +08:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE] = {
|
2018-06-12 02:53:32 +08:00
|
|
|
.instance_size = sizeof(BlockDirtyBitmapState),
|
|
|
|
.prepare = block_dirty_bitmap_enable_prepare,
|
|
|
|
.abort = block_dirty_bitmap_enable_abort,
|
|
|
|
},
|
2018-12-21 17:35:22 +08:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE] = {
|
2018-06-12 02:53:32 +08:00
|
|
|
.instance_size = sizeof(BlockDirtyBitmapState),
|
|
|
|
.prepare = block_dirty_bitmap_disable_prepare,
|
|
|
|
.abort = block_dirty_bitmap_disable_abort,
|
2018-09-06 21:02:25 +08:00
|
|
|
},
|
2018-12-21 17:35:22 +08:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE] = {
|
2018-10-30 04:23:15 +08:00
|
|
|
.instance_size = sizeof(BlockDirtyBitmapState),
|
|
|
|
.prepare = block_dirty_bitmap_merge_prepare,
|
|
|
|
.commit = block_dirty_bitmap_free_backup,
|
|
|
|
.abort = block_dirty_bitmap_restore,
|
|
|
|
},
|
2019-07-30 04:35:54 +08:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE] = {
|
|
|
|
.instance_size = sizeof(BlockDirtyBitmapState),
|
|
|
|
.prepare = block_dirty_bitmap_remove_prepare,
|
|
|
|
.commit = block_dirty_bitmap_remove_commit,
|
|
|
|
.abort = block_dirty_bitmap_remove_abort,
|
|
|
|
},
|
2018-09-06 21:02:25 +08:00
|
|
|
/* Where are transactions for MIRROR, COMMIT and STREAM?
|
|
|
|
* Although these blockjobs use transaction callbacks like the backup job,
|
|
|
|
* these jobs do not necessarily adhere to transaction semantics.
|
|
|
|
* These jobs may not fully undo all of their actions on abort, nor do they
|
|
|
|
* necessarily work in transactions with more than one job in them.
|
|
|
|
*/
|
2013-05-08 18:25:16 +08:00
|
|
|
};
|
|
|
|
|
2015-11-06 07:13:18 +08: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 04:54:06 +08:00
|
|
|
/*
|
2014-11-21 18:48:57 +08: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 04:54:06 +08:00
|
|
|
*/
|
2015-11-06 07:13:18 +08:00
|
|
|
void qmp_transaction(TransactionActionList *dev_list,
|
|
|
|
bool has_props,
|
|
|
|
struct TransactionProperties *props,
|
|
|
|
Error **errp)
|
2012-02-29 04:54:06 +08:00
|
|
|
{
|
2013-05-13 16:43:43 +08:00
|
|
|
TransactionActionList *dev_entry = dev_list;
|
2018-04-19 22:09:52 +08:00
|
|
|
JobTxn *block_job_txn = NULL;
|
2015-11-06 07:13:09 +08:00
|
|
|
BlkActionState *state, *next;
|
2012-11-30 20:52:07 +08:00
|
|
|
Error *local_err = NULL;
|
2012-02-29 04:54:06 +08:00
|
|
|
|
2019-01-12 01:59:16 +08:00
|
|
|
QTAILQ_HEAD(, BlkActionState) snap_bdrv_states;
|
|
|
|
QTAILQ_INIT(&snap_bdrv_states);
|
2012-02-29 04:54:06 +08:00
|
|
|
|
2015-11-06 07:13:18 +08:00
|
|
|
/* Does this transaction get canceled as a group on failure?
|
2018-04-19 22:09:52 +08:00
|
|
|
* If not, we don't really need to make a JobTxn.
|
2015-11-06 07:13:18 +08:00
|
|
|
*/
|
|
|
|
props = get_transaction_properties(props);
|
|
|
|
if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
|
2018-04-23 22:06:26 +08:00
|
|
|
block_job_txn = job_txn_new();
|
2015-11-06 07:13:18 +08:00
|
|
|
}
|
|
|
|
|
2014-11-21 18:48:57 +08:00
|
|
|
/* drain all i/o before any operations */
|
2012-02-29 04:54:06 +08:00
|
|
|
bdrv_drain_all();
|
|
|
|
|
2014-11-21 18:48:57 +08:00
|
|
|
/* We don't do anything in this loop that commits us to the operations */
|
2012-02-29 04:54:06 +08:00
|
|
|
while (NULL != dev_entry) {
|
2013-05-13 16:43:43 +08:00
|
|
|
TransactionAction *dev_info = NULL;
|
2015-11-06 07:13:09 +08:00
|
|
|
const BlkActionOps *ops;
|
2012-03-07 01:55:57 +08:00
|
|
|
|
2012-02-29 04:54:06 +08:00
|
|
|
dev_info = dev_entry->value;
|
|
|
|
dev_entry = dev_entry->next;
|
|
|
|
|
2015-10-27 06:34:54 +08:00
|
|
|
assert(dev_info->type < ARRAY_SIZE(actions));
|
2013-05-08 18:25:16 +08:00
|
|
|
|
2015-10-27 06:34:54 +08:00
|
|
|
ops = &actions[dev_info->type];
|
2013-09-12 20:57:27 +08:00
|
|
|
assert(ops->instance_size > 0);
|
|
|
|
|
2013-06-24 23:13:15 +08:00
|
|
|
state = g_malloc0(ops->instance_size);
|
|
|
|
state->ops = ops;
|
|
|
|
state->action = dev_info;
|
2015-11-06 07:13:18 +08:00
|
|
|
state->block_job_txn = block_job_txn;
|
|
|
|
state->txn_props = props;
|
2019-01-12 01:59:16 +08:00
|
|
|
QTAILQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
|
2012-02-29 04:54:06 +08:00
|
|
|
|
2013-06-24 23:13:15 +08:00
|
|
|
state->ops->prepare(state, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-05-08 18:25:16 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
goto delete_and_fail;
|
2012-02-29 04:54:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-12 01:59:16 +08:00
|
|
|
QTAILQ_FOREACH(state, &snap_bdrv_states, entry) {
|
2013-06-24 23:13:16 +08:00
|
|
|
if (state->ops->commit) {
|
|
|
|
state->ops->commit(state);
|
|
|
|
}
|
2012-02-29 04:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* success */
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
delete_and_fail:
|
2014-11-21 18:48:57 +08:00
|
|
|
/* failure, and it is all-or-none; roll back all operations */
|
2019-01-12 01:59:16 +08:00
|
|
|
QTAILQ_FOREACH_REVERSE(state, &snap_bdrv_states, entry) {
|
2013-06-24 23:13:15 +08:00
|
|
|
if (state->ops->abort) {
|
|
|
|
state->ops->abort(state);
|
2013-05-08 18:25:16 +08:00
|
|
|
}
|
2012-02-29 04:54:06 +08:00
|
|
|
}
|
|
|
|
exit:
|
2019-01-12 01:59:16 +08:00
|
|
|
QTAILQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) {
|
2013-06-24 23:13:15 +08:00
|
|
|
if (state->ops->clean) {
|
|
|
|
state->ops->clean(state);
|
2013-05-08 18:25:16 +08:00
|
|
|
}
|
2013-06-24 23:13:15 +08:00
|
|
|
g_free(state);
|
2012-02-29 04:54:06 +08:00
|
|
|
}
|
2015-11-06 07:13:18 +08:00
|
|
|
if (!has_props) {
|
|
|
|
qapi_free_TransactionProperties(props);
|
|
|
|
}
|
2018-04-23 22:06:26 +08:00
|
|
|
job_txn_unref(block_job_txn);
|
2012-02-29 04:54:06 +08:00
|
|
|
}
|
|
|
|
|
2017-06-28 20:05:25 +08: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;
|
|
|
|
}
|
|
|
|
|
2020-10-05 23:58:55 +08:00
|
|
|
void coroutine_fn 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 20:32:33 +08:00
|
|
|
{
|
2014-01-24 04:31:37 +08:00
|
|
|
Error *local_err = NULL;
|
2020-12-04 01:23:09 +08:00
|
|
|
BlockBackend *blk;
|
2011-01-24 20:32:33 +08:00
|
|
|
BlockDriverState *bs;
|
2020-10-05 23:58:55 +08:00
|
|
|
AioContext *old_ctx;
|
2011-01-24 20:32:33 +08:00
|
|
|
|
2014-01-24 04:31:37 +08:00
|
|
|
bs = bdrv_lookup_bs(has_device ? device : NULL,
|
|
|
|
has_node_name ? node_name : NULL,
|
|
|
|
&local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2014-01-24 04:31:37 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-01-24 20:32:33 +08:00
|
|
|
if (size < 0) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
|
2020-12-04 01:23:09 +08:00
|
|
|
return;
|
2011-01-24 20:32:33 +08:00
|
|
|
}
|
|
|
|
|
2014-06-26 04:55:30 +08:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_DEVICE_IN_USE, device);
|
2020-12-04 01:23:09 +08:00
|
|
|
return;
|
2014-06-26 04:55:30 +08:00
|
|
|
}
|
|
|
|
|
2020-04-29 03:26:46 +08:00
|
|
|
blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp);
|
|
|
|
if (!blk) {
|
2020-12-04 01:23:09 +08:00
|
|
|
return;
|
2017-01-14 02:02:32 +08:00
|
|
|
}
|
2017-02-17 18:23:33 +08:00
|
|
|
|
2020-12-04 01:23:10 +08:00
|
|
|
bdrv_co_lock(bs);
|
2017-05-11 01:39:45 +08:00
|
|
|
bdrv_drained_begin(bs);
|
2020-12-04 01:23:10 +08:00
|
|
|
bdrv_co_unlock(bs);
|
|
|
|
|
2020-10-05 23:58:55 +08:00
|
|
|
old_ctx = bdrv_co_enter(bs);
|
2020-04-29 03:26:46 +08:00
|
|
|
blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
|
2020-10-05 23:58:55 +08:00
|
|
|
bdrv_co_leave(bs, old_ctx);
|
2014-08-18 21:52:28 +08:00
|
|
|
|
2020-10-05 23:58:55 +08:00
|
|
|
bdrv_co_lock(bs);
|
2020-12-04 01:23:10 +08:00
|
|
|
bdrv_drained_end(bs);
|
2017-02-17 18:23:33 +08:00
|
|
|
blk_unref(blk);
|
2020-10-05 23:58:55 +08:00
|
|
|
bdrv_co_unlock(bs);
|
2011-01-24 20:32:33 +08:00
|
|
|
}
|
2012-01-18 22:40:46 +08:00
|
|
|
|
2016-07-05 22:28:59 +08: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-26 03:40:11 +08:00
|
|
|
bool has_base, const char *base,
|
2016-10-28 15:08:19 +08: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-26 03:40:11 +08:00
|
|
|
bool has_backing_file, const char *backing_file,
|
2020-12-16 14:17:00 +08:00
|
|
|
bool has_bottom, const char *bottom,
|
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-26 03:40:11 +08:00
|
|
|
bool has_speed, int64_t speed,
|
2012-09-28 23:22:59 +08:00
|
|
|
bool has_on_error, BlockdevOnError on_error,
|
2020-12-16 14:16:54 +08:00
|
|
|
bool has_filter_node_name, const char *filter_node_name,
|
2018-09-06 21:02:23 +08:00
|
|
|
bool has_auto_finalize, bool auto_finalize,
|
|
|
|
bool has_auto_dismiss, bool auto_dismiss,
|
2012-09-28 23:22:59 +08:00
|
|
|
Error **errp)
|
2012-01-18 22:40:46 +08:00
|
|
|
{
|
2020-12-16 14:17:00 +08:00
|
|
|
BlockDriverState *bs, *iter, *iter_end;
|
2012-01-18 22:40:53 +08:00
|
|
|
BlockDriverState *base_bs = NULL;
|
2020-12-16 14:17:00 +08:00
|
|
|
BlockDriverState *bottom_bs = NULL;
|
2014-10-21 19:03:57 +08:00
|
|
|
AioContext *aio_context;
|
2012-04-25 23:51:00 +08:00
|
|
|
Error *local_err = NULL;
|
2018-09-06 21:02:12 +08:00
|
|
|
int job_flags = JOB_DEFAULT;
|
2012-01-18 22:40:46 +08:00
|
|
|
|
2020-12-16 14:17:00 +08:00
|
|
|
if (has_base && has_base_node) {
|
|
|
|
error_setg(errp, "'base' and 'base-node' cannot be specified "
|
|
|
|
"at the same time");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_base && has_bottom) {
|
|
|
|
error_setg(errp, "'base' and 'bottom' cannot be specified "
|
|
|
|
"at the same time");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_bottom && has_base_node) {
|
|
|
|
error_setg(errp, "'bottom' and 'base-node' cannot be specified "
|
|
|
|
"at the same time");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-09-28 23:22:59 +08:00
|
|
|
if (!has_on_error) {
|
|
|
|
on_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
|
|
}
|
|
|
|
|
2016-10-28 15:08:11 +08:00
|
|
|
bs = bdrv_lookup_bs(device, device, errp);
|
2016-06-23 20:20:24 +08:00
|
|
|
if (!bs) {
|
2012-01-18 22:40:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-23 20:20:24 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-10-21 19:03:57 +08:00
|
|
|
aio_context_acquire(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-26 03:40:11 +08:00
|
|
|
if (has_base) {
|
2012-01-18 22:40:53 +08:00
|
|
|
base_bs = bdrv_find_backing_image(bs, base);
|
|
|
|
if (base_bs == NULL) {
|
2020-11-13 16:26:19 +08:00
|
|
|
error_setg(errp, "Can't find '%s' in the backing chain", base);
|
2014-10-21 19:03:57 +08:00
|
|
|
goto out;
|
2012-01-18 22:40:53 +08:00
|
|
|
}
|
2014-10-21 19:03:57 +08:00
|
|
|
assert(bdrv_get_aio_context(base_bs) == aio_context);
|
2012-01-18 22:40:46 +08:00
|
|
|
}
|
|
|
|
|
2016-10-28 15:08:19 +08: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);
|
block: Use bdrv_refresh_filename() to pull
Before this patch, bdrv_refresh_filename() is used in a pushing manner:
Whenever the BDS graph is modified, the parents of the modified edges
are supposed to be updated (recursively upwards). However, that is
nonviable, considering that we want child changes not to concern
parents.
Also, in the long run we want a pull model anyway: Here, we would have a
bdrv_filename() function which returns a BDS's filename, freshly
constructed.
This patch is an intermediate step. It adds bdrv_refresh_filename()
calls before every place a BDS.filename value is used. The only
exceptions are protocol drivers that use their own filename, which
clearly would not profit from refreshing that filename before.
Also, bdrv_get_encrypted_filename() is removed along the way (as a user
of BDS.filename), since it is completely unused.
In turn, all of the calls to bdrv_refresh_filename() before this patch
are removed, because we no longer have to call this function on graph
changes.
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190201192935.18394-2-mreitz@redhat.com
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2019-02-02 03:29:05 +08:00
|
|
|
bdrv_refresh_filename(base_bs);
|
2016-10-28 15:08:19 +08:00
|
|
|
}
|
|
|
|
|
2020-12-16 14:17:00 +08:00
|
|
|
if (has_bottom) {
|
|
|
|
bottom_bs = bdrv_lookup_bs(NULL, bottom, errp);
|
|
|
|
if (!bottom_bs) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!bottom_bs->drv) {
|
|
|
|
error_setg(errp, "Node '%s' is not open", bottom);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (bottom_bs->drv->is_filter) {
|
|
|
|
error_setg(errp, "Node '%s' is a filter, use a non-filter node "
|
|
|
|
"as 'bottom'", bottom);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!bdrv_chain_contains(bs, bottom_bs)) {
|
|
|
|
error_setg(errp, "Node '%s' is not in a chain starting from '%s'",
|
|
|
|
bottom, device);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
assert(bdrv_get_aio_context(bottom_bs) == aio_context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for op blockers in the whole chain between bs and base (or bottom)
|
|
|
|
*/
|
|
|
|
iter_end = has_bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs;
|
|
|
|
for (iter = bs; iter && iter != iter_end;
|
2019-06-12 23:48:11 +08:00
|
|
|
iter = bdrv_filter_or_cow_bs(iter))
|
|
|
|
{
|
2016-10-28 15:08:11 +08:00
|
|
|
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-26 03:40:11 +08: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 19:03:57 +08: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-26 03:40:11 +08:00
|
|
|
}
|
|
|
|
|
2018-09-06 21:02:23 +08:00
|
|
|
if (has_auto_finalize && !auto_finalize) {
|
|
|
|
job_flags |= JOB_MANUAL_FINALIZE;
|
|
|
|
}
|
|
|
|
if (has_auto_dismiss && !auto_dismiss) {
|
|
|
|
job_flags |= JOB_MANUAL_DISMISS;
|
|
|
|
}
|
|
|
|
|
2020-12-16 14:16:59 +08:00
|
|
|
stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file,
|
2020-12-16 14:17:00 +08:00
|
|
|
bottom_bs, job_flags, has_speed ? speed : 0, on_error,
|
2020-12-16 14:16:54 +08:00
|
|
|
filter_node_name, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2012-04-25 23:51:00 +08:00
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 19:03:57 +08:00
|
|
|
goto out;
|
2012-01-18 22:40:46 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 23:41:32 +08:00
|
|
|
trace_qmp_block_stream(bs);
|
2014-10-21 19:03:57 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2012-01-18 22:40:46 +08:00
|
|
|
}
|
2012-01-18 22:40:47 +08:00
|
|
|
|
2016-07-05 22:29:00 +08:00
|
|
|
void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
2017-06-27 23:18:20 +08:00
|
|
|
bool has_base_node, const char *base_node,
|
2014-06-30 21:14:15 +08:00
|
|
|
bool has_base, const char *base,
|
2017-06-27 23:18:20 +08:00
|
|
|
bool has_top_node, const char *top_node,
|
2014-06-30 21:14:15 +08:00
|
|
|
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-26 03:40:10 +08: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-28 01:29:16 +08:00
|
|
|
bool has_speed, int64_t speed,
|
2020-02-15 04:08:11 +08:00
|
|
|
bool has_on_error, BlockdevOnError on_error,
|
2017-02-21 01:10:05 +08:00
|
|
|
bool has_filter_node_name, const char *filter_node_name,
|
2018-09-06 21:02:21 +08:00
|
|
|
bool has_auto_finalize, bool auto_finalize,
|
|
|
|
bool has_auto_dismiss, bool auto_dismiss,
|
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-28 01:29:16 +08:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
2016-10-28 15:08:07 +08: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-28 01:29:16 +08:00
|
|
|
BlockDriverState *base_bs, *top_bs;
|
2014-10-21 19:03:59 +08: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-28 01:29:16 +08:00
|
|
|
Error *local_err = NULL;
|
2018-09-06 21:02:10 +08:00
|
|
|
int job_flags = JOB_DEFAULT;
|
2019-06-12 03:19:26 +08:00
|
|
|
uint64_t top_perm, top_shared;
|
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-28 01:29:16 +08:00
|
|
|
|
2014-04-11 01:36:25 +08:00
|
|
|
if (!has_speed) {
|
|
|
|
speed = 0;
|
|
|
|
}
|
2020-02-15 04:08:11 +08:00
|
|
|
if (!has_on_error) {
|
|
|
|
on_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
|
|
}
|
2017-02-21 01:10:05 +08:00
|
|
|
if (!has_filter_node_name) {
|
|
|
|
filter_node_name = NULL;
|
|
|
|
}
|
2018-09-06 21:02:21 +08:00
|
|
|
if (has_auto_finalize && !auto_finalize) {
|
|
|
|
job_flags |= JOB_MANUAL_FINALIZE;
|
|
|
|
}
|
|
|
|
if (has_auto_dismiss && !auto_dismiss) {
|
|
|
|
job_flags |= JOB_MANUAL_DISMISS;
|
|
|
|
}
|
2014-04-11 01:36:25 +08:00
|
|
|
|
2014-06-30 21:14:15 +08: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 20:20:24 +08: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-28 01:29:16 +08:00
|
|
|
return;
|
2014-05-23 21:29:44 +08:00
|
|
|
}
|
|
|
|
|
2016-06-23 20:20:24 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-10-21 19:03:59 +08:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2014-09-11 13:14:00 +08:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) {
|
2014-10-21 19:03:59 +08: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-28 01:29:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* default top_bs is the active layer */
|
|
|
|
top_bs = bs;
|
|
|
|
|
2017-06-27 23:18:20 +08:00
|
|
|
if (has_top_node && has_top) {
|
|
|
|
error_setg(errp, "'top-node' and 'top' are mutually exclusive");
|
|
|
|
goto out;
|
|
|
|
} else if (has_top_node) {
|
|
|
|
top_bs = bdrv_lookup_bs(NULL, top_node, errp);
|
|
|
|
if (top_bs == NULL) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!bdrv_chain_contains(bs, top_bs)) {
|
|
|
|
error_setg(errp, "'%s' is not in this backing file chain",
|
|
|
|
top_node);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else if (has_top && top) {
|
block: Use bdrv_refresh_filename() to pull
Before this patch, bdrv_refresh_filename() is used in a pushing manner:
Whenever the BDS graph is modified, the parents of the modified edges
are supposed to be updated (recursively upwards). However, that is
nonviable, considering that we want child changes not to concern
parents.
Also, in the long run we want a pull model anyway: Here, we would have a
bdrv_filename() function which returns a BDS's filename, freshly
constructed.
This patch is an intermediate step. It adds bdrv_refresh_filename()
calls before every place a BDS.filename value is used. The only
exceptions are protocol drivers that use their own filename, which
clearly would not profit from refreshing that filename before.
Also, bdrv_get_encrypted_filename() is removed along the way (as a user
of BDS.filename), since it is completely unused.
In turn, all of the calls to bdrv_refresh_filename() before this patch
are removed, because we no longer have to call this function on graph
changes.
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190201192935.18394-2-mreitz@redhat.com
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2019-02-02 03:29:05 +08:00
|
|
|
/* This strcmp() is just a shortcut, there is no need to
|
|
|
|
* refresh @bs's filename. If it mismatches,
|
|
|
|
* bdrv_find_backing_image() will do the refresh and may still
|
|
|
|
* return @bs. */
|
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-28 01:29:16 +08: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 19:03:59 +08: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-28 01:29:16 +08:00
|
|
|
}
|
|
|
|
|
2014-10-21 19:03:59 +08:00
|
|
|
assert(bdrv_get_aio_context(top_bs) == aio_context);
|
|
|
|
|
2017-06-27 23:18:20 +08:00
|
|
|
if (has_base_node && has_base) {
|
|
|
|
error_setg(errp, "'base-node' and 'base' are mutually exclusive");
|
|
|
|
goto out;
|
|
|
|
} else if (has_base_node) {
|
|
|
|
base_bs = bdrv_lookup_bs(NULL, base_node, errp);
|
|
|
|
if (base_bs == NULL) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!bdrv_chain_contains(top_bs, base_bs)) {
|
|
|
|
error_setg(errp, "'%s' is not in this backing file chain",
|
|
|
|
base_node);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else if (has_base && base) {
|
2012-10-17 03:49:10 +08:00
|
|
|
base_bs = bdrv_find_backing_image(top_bs, base);
|
2020-11-13 16:26:19 +08:00
|
|
|
if (base_bs == NULL) {
|
|
|
|
error_setg(errp, "Can't find '%s' in the backing chain", base);
|
|
|
|
goto out;
|
|
|
|
}
|
2012-10-17 03:49:10 +08:00
|
|
|
} else {
|
|
|
|
base_bs = bdrv_find_base(top_bs);
|
2020-11-13 16:26:19 +08:00
|
|
|
if (base_bs == NULL) {
|
|
|
|
error_setg(errp, "There is no backimg image");
|
|
|
|
goto out;
|
|
|
|
}
|
2012-10-17 03:49:10 +08:00
|
|
|
}
|
|
|
|
|
2014-10-21 19:03:59 +08:00
|
|
|
assert(bdrv_get_aio_context(base_bs) == aio_context);
|
|
|
|
|
2019-06-12 23:47:37 +08:00
|
|
|
for (iter = top_bs; iter != bdrv_filter_or_cow_bs(base_bs);
|
|
|
|
iter = bdrv_filter_or_cow_bs(iter))
|
|
|
|
{
|
2016-10-28 15:08:07 +08:00
|
|
|
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
|
|
|
|
goto out;
|
|
|
|
}
|
2014-09-11 13:14:00 +08:00
|
|
|
}
|
|
|
|
|
2014-06-30 21:14:15 +08: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 19:03:59 +08:00
|
|
|
goto out;
|
2014-06-30 21:14:15 +08:00
|
|
|
}
|
|
|
|
|
2019-06-12 03:19:26 +08:00
|
|
|
/*
|
|
|
|
* Active commit is required if and only if someone has taken a
|
|
|
|
* WRITE permission on the top node. Historically, we have always
|
|
|
|
* used active commit for top nodes, so continue that practice
|
|
|
|
* lest we possibly break clients that rely on this behavior, e.g.
|
|
|
|
* to later attach this node to a writing parent.
|
|
|
|
* (Active commit is never really wrong.)
|
|
|
|
*/
|
|
|
|
bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared);
|
|
|
|
if (top_perm & BLK_PERM_WRITE ||
|
|
|
|
bdrv_skip_filters(top_bs) == bdrv_skip_filters(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-26 03:40:10 +08:00
|
|
|
if (has_backing_file) {
|
2019-06-12 03:19:26 +08:00
|
|
|
if (bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) {
|
|
|
|
error_setg(errp, "'backing-file' specified,"
|
|
|
|
" but 'top' is the active layer");
|
|
|
|
} else {
|
|
|
|
error_setg(errp, "'backing-file' specified, but 'top' has a "
|
|
|
|
"writer on it");
|
|
|
|
}
|
2014-10-21 19:03:59 +08: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-26 03:40:10 +08:00
|
|
|
}
|
2019-06-12 03:19:26 +08:00
|
|
|
if (!has_job_id) {
|
|
|
|
/*
|
|
|
|
* Emulate here what block_job_create() does, because it
|
|
|
|
* is possible that @bs != @top_bs (the block job should
|
|
|
|
* be named after @bs, even if @top_bs is the actual
|
|
|
|
* source)
|
|
|
|
*/
|
|
|
|
job_id = bdrv_get_device_name(bs);
|
|
|
|
}
|
|
|
|
commit_active_start(job_id, top_bs, base_bs, job_flags, speed, on_error,
|
2017-04-21 20:27:04 +08:00
|
|
|
filter_node_name, NULL, NULL, false, &local_err);
|
2013-12-16 14:45:31 +08:00
|
|
|
} else {
|
2016-10-28 15:08:07 +08: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;
|
|
|
|
}
|
2018-09-06 21:02:10 +08:00
|
|
|
commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, job_flags,
|
|
|
|
speed, on_error, has_backing_file ? backing_file : NULL,
|
2017-02-21 01:10:05 +08:00
|
|
|
filter_node_name, &local_err);
|
2013-12-16 14:45:31 +08: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-28 01:29:16 +08:00
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 19:03:59 +08: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-28 01:29:16 +08:00
|
|
|
}
|
2014-10-21 19:03:59 +08: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-28 01:29:16 +08:00
|
|
|
}
|
|
|
|
|
2019-07-30 04:35:52 +08:00
|
|
|
/* Common QMP interface for drive-backup and blockdev-backup */
|
|
|
|
static BlockJob *do_backup_common(BackupCommon *backup,
|
|
|
|
BlockDriverState *bs,
|
|
|
|
BlockDriverState *target_bs,
|
|
|
|
AioContext *aio_context,
|
|
|
|
JobTxn *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 23:13:14 +08:00
|
|
|
{
|
2016-11-08 14:50:38 +08:00
|
|
|
BlockJob *job = NULL;
|
2015-04-18 07:49:58 +08:00
|
|
|
BdrvDirtyBitmap *bmap = NULL;
|
2021-01-17 05:47:00 +08:00
|
|
|
BackupPerf perf = { .max_workers = 64 };
|
2019-07-30 04:35:52 +08:00
|
|
|
int job_flags = JOB_DEFAULT;
|
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 23:13:14 +08:00
|
|
|
|
2016-07-22 16:17:50 +08: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 23:13:14 +08:00
|
|
|
}
|
2016-07-22 16:17:50 +08: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 23:13:14 +08:00
|
|
|
}
|
2016-07-22 16:17:50 +08: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 23:13:14 +08:00
|
|
|
}
|
2016-07-22 16:17:50 +08:00
|
|
|
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 23:13:14 +08:00
|
|
|
}
|
2018-03-10 16:27:44 +08:00
|
|
|
if (!backup->has_auto_finalize) {
|
|
|
|
backup->auto_finalize = true;
|
|
|
|
}
|
|
|
|
if (!backup->has_auto_dismiss) {
|
|
|
|
backup->auto_dismiss = true;
|
|
|
|
}
|
2016-07-22 16:17:52 +08: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 23:13:14 +08:00
|
|
|
|
qapi: backup: add perf.use-copy-range parameter
Experiments show, that copy_range is not always making things faster.
So, to make experimentation simpler, let's add a parameter. Some more
perf parameters will be added soon, so here is a new struct.
For now, add new backup qmp parameter with x- prefix for the following
reasons:
- We are going to add more performance parameters, some will be
related to the whole block-copy process, some only to background
copying in backup (ignored for copy-before-write operations).
- On the other hand, we are going to use block-copy interface in other
block jobs, which will need performance options as well.. And it
should be the same structure or at least somehow related.
So, there are too much unclean things about how the interface and now
we need the new options mostly for testing. Let's keep them
experimental for a while.
In do_backup_common() new x-perf parameter handled in a way to
make further options addition simpler.
We add use-copy-range with default=true, and we'll change the default
in further patch, after moving backup to use block-copy.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Message-Id: <20210116214705.822267-2-vsementsov@virtuozzo.com>
[mreitz: s/5\.2/6.0/]
Signed-off-by: Max Reitz <mreitz@redhat.com>
2021-01-17 05:46:43 +08:00
|
|
|
if (backup->x_perf) {
|
|
|
|
if (backup->x_perf->has_use_copy_range) {
|
|
|
|
perf.use_copy_range = backup->x_perf->use_copy_range;
|
|
|
|
}
|
2021-01-17 05:46:52 +08:00
|
|
|
if (backup->x_perf->has_max_workers) {
|
|
|
|
perf.max_workers = backup->x_perf->max_workers;
|
|
|
|
}
|
|
|
|
if (backup->x_perf->has_max_chunk) {
|
|
|
|
perf.max_chunk = backup->x_perf->max_chunk;
|
|
|
|
}
|
qapi: backup: add perf.use-copy-range parameter
Experiments show, that copy_range is not always making things faster.
So, to make experimentation simpler, let's add a parameter. Some more
perf parameters will be added soon, so here is a new struct.
For now, add new backup qmp parameter with x- prefix for the following
reasons:
- We are going to add more performance parameters, some will be
related to the whole block-copy process, some only to background
copying in backup (ignored for copy-before-write operations).
- On the other hand, we are going to use block-copy interface in other
block jobs, which will need performance options as well.. And it
should be the same structure or at least somehow related.
So, there are too much unclean things about how the interface and now
we need the new options mostly for testing. Let's keep them
experimental for a while.
In do_backup_common() new x-perf parameter handled in a way to
make further options addition simpler.
We add use-copy-range with default=true, and we'll change the default
in further patch, after moving backup to use block-copy.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Message-Id: <20210116214705.822267-2-vsementsov@virtuozzo.com>
[mreitz: s/5\.2/6.0/]
Signed-off-by: Max Reitz <mreitz@redhat.com>
2021-01-17 05:46:43 +08:00
|
|
|
}
|
|
|
|
|
2019-07-30 04:35:55 +08:00
|
|
|
if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) ||
|
|
|
|
(backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) {
|
|
|
|
/* done before desugaring 'incremental' to print the right message */
|
|
|
|
if (!backup->has_bitmap) {
|
|
|
|
error_setg(errp, "must provide a valid bitmap name for "
|
|
|
|
"'%s' sync mode", MirrorSyncMode_str(backup->sync));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-30 04:35:52 +08:00
|
|
|
if (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL) {
|
|
|
|
if (backup->has_bitmap_mode &&
|
|
|
|
backup->bitmap_mode != BITMAP_SYNC_MODE_ON_SUCCESS) {
|
|
|
|
error_setg(errp, "Bitmap sync mode must be '%s' "
|
|
|
|
"when using sync mode '%s'",
|
|
|
|
BitmapSyncMode_str(BITMAP_SYNC_MODE_ON_SUCCESS),
|
|
|
|
MirrorSyncMode_str(backup->sync));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
backup->has_bitmap_mode = true;
|
|
|
|
backup->sync = MIRROR_SYNC_MODE_BITMAP;
|
|
|
|
backup->bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2019-07-30 04:35:52 +08:00
|
|
|
if (backup->has_bitmap) {
|
|
|
|
bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
|
|
|
|
if (!bmap) {
|
|
|
|
error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-07-30 04:35:52 +08:00
|
|
|
if (!backup->has_bitmap_mode) {
|
|
|
|
error_setg(errp, "Bitmap sync mode must be given "
|
|
|
|
"when providing a bitmap");
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-07-30 04:35:54 +08:00
|
|
|
if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
2019-07-30 04:35:52 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2019-07-30 04:35:55 +08:00
|
|
|
|
|
|
|
/* This does not produce a useful bitmap artifact: */
|
|
|
|
if (backup->sync == MIRROR_SYNC_MODE_NONE) {
|
|
|
|
error_setg(errp, "sync mode '%s' does not produce meaningful bitmap"
|
|
|
|
" outputs", MirrorSyncMode_str(backup->sync));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the bitmap isn't used for input or output, this is useless: */
|
|
|
|
if (backup->bitmap_mode == BITMAP_SYNC_MODE_NEVER &&
|
|
|
|
backup->sync != MIRROR_SYNC_MODE_BITMAP) {
|
|
|
|
error_setg(errp, "Bitmap sync mode '%s' has no meaningful effect"
|
|
|
|
" when combined with sync mode '%s'",
|
|
|
|
BitmapSyncMode_str(backup->bitmap_mode),
|
|
|
|
MirrorSyncMode_str(backup->sync));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!backup->has_bitmap && backup->has_bitmap_mode) {
|
|
|
|
error_setg(errp, "Cannot specify bitmap sync mode without a bitmap");
|
|
|
|
return NULL;
|
2019-07-30 04:35:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!backup->auto_finalize) {
|
|
|
|
job_flags |= JOB_MANUAL_FINALIZE;
|
|
|
|
}
|
|
|
|
if (!backup->auto_dismiss) {
|
|
|
|
job_flags |= JOB_MANUAL_DISMISS;
|
|
|
|
}
|
|
|
|
|
|
|
|
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
2019-07-30 04:35:52 +08:00
|
|
|
backup->sync, bmap, backup->bitmap_mode,
|
|
|
|
backup->compress,
|
block/backup: use backup-top instead of write notifiers
Drop write notifiers and use filter node instead.
= Changes =
1. Add filter-node-name argument for backup qmp api. We have to do it
in this commit, as 257 needs to be fixed.
2. There are no more write notifiers here, so is_write_notifier
parameter is dropped from block-copy paths.
3. To sync with in-flight requests at job finish we now have drained
removing of the filter, we don't need rw-lock.
4. Block-copy is now using BdrvChildren instead of BlockBackends
5. As backup-top owns these children, we also move block-copy state
into backup-top's ownership.
= Iotest changes =
56: op-blocker doesn't shoot now, as we set it on source, but then
check on filter, when trying to start second backup.
To keep the test we instead can catch another collision: both jobs will
get 'drive0' job-id, as job-id parameter is unspecified. To prevent
interleaving with file-posix locks (as they are dependent on config)
let's use another target for second backup.
Also, it's obvious now that we'd like to drop this op-blocker at all
and add a test-case for two backups from one node (to different
destinations) actually works. But not in these series.
141: Output changed: prepatch, "Node is in use" comes from bdrv_has_blk
check inside qmp_blockdev_del. But we've dropped block-copy blk
objects, so no more blk objects on source bs (job blk is on backup-top
filter bs). New message is from op-blocker, which is the next check in
qmp_blockdev_add.
257: The test wants to emulate guest write during backup. They should
go to filter node, not to original source node, of course. Therefore we
need to specify filter node name and use it.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-id: 20191001131409.14202-6-vsementsov@virtuozzo.com
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
2019-10-01 21:14:09 +08:00
|
|
|
backup->filter_node_name,
|
qapi: backup: add perf.use-copy-range parameter
Experiments show, that copy_range is not always making things faster.
So, to make experimentation simpler, let's add a parameter. Some more
perf parameters will be added soon, so here is a new struct.
For now, add new backup qmp parameter with x- prefix for the following
reasons:
- We are going to add more performance parameters, some will be
related to the whole block-copy process, some only to background
copying in backup (ignored for copy-before-write operations).
- On the other hand, we are going to use block-copy interface in other
block jobs, which will need performance options as well.. And it
should be the same structure or at least somehow related.
So, there are too much unclean things about how the interface and now
we need the new options mostly for testing. Let's keep them
experimental for a while.
In do_backup_common() new x-perf parameter handled in a way to
make further options addition simpler.
We add use-copy-range with default=true, and we'll change the default
in further patch, after moving backup to use block-copy.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Message-Id: <20210116214705.822267-2-vsementsov@virtuozzo.com>
[mreitz: s/5\.2/6.0/]
Signed-off-by: Max Reitz <mreitz@redhat.com>
2021-01-17 05:46:43 +08:00
|
|
|
&perf,
|
2019-07-30 04:35:52 +08:00
|
|
|
backup->on_source_error,
|
|
|
|
backup->on_target_error,
|
2019-07-30 04:35:52 +08:00
|
|
|
job_flags, NULL, NULL, txn, errp);
|
|
|
|
return job;
|
|
|
|
}
|
|
|
|
|
2020-01-08 22:31:32 +08:00
|
|
|
void qmp_drive_backup(DriveBackup *backup, Error **errp)
|
2015-11-06 07:13:17 +08:00
|
|
|
{
|
2020-01-08 22:31:32 +08:00
|
|
|
TransactionAction action = {
|
|
|
|
.type = TRANSACTION_ACTION_KIND_DRIVE_BACKUP,
|
|
|
|
.u.drive_backup.data = backup,
|
|
|
|
};
|
|
|
|
blockdev_do_action(&action, errp);
|
2015-11-06 07:13:17 +08:00
|
|
|
}
|
|
|
|
|
2020-01-20 16:50:49 +08:00
|
|
|
BlockDeviceInfoList *qmp_query_named_block_nodes(bool has_flat,
|
|
|
|
bool flat,
|
|
|
|
Error **errp)
|
2014-01-24 04:31:34 +08:00
|
|
|
{
|
2020-01-20 16:50:49 +08:00
|
|
|
bool return_flat = has_flat && flat;
|
|
|
|
|
|
|
|
return bdrv_named_nodes_list(return_flat, errp);
|
2014-01-24 04:31:34 +08:00
|
|
|
}
|
|
|
|
|
2018-12-22 01:09:07 +08:00
|
|
|
XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp)
|
|
|
|
{
|
|
|
|
return bdrv_get_xdbg_block_graph(errp);
|
|
|
|
}
|
|
|
|
|
2020-01-08 22:31:33 +08:00
|
|
|
void qmp_blockdev_backup(BlockdevBackup *backup, Error **errp)
|
2014-12-18 18:37:05 +08:00
|
|
|
{
|
2020-01-08 22:31:33 +08:00
|
|
|
TransactionAction action = {
|
|
|
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP,
|
|
|
|
.u.blockdev_backup.data = backup,
|
|
|
|
};
|
|
|
|
blockdev_do_action(&action, errp);
|
2015-11-06 07:13:17 +08:00
|
|
|
}
|
|
|
|
|
2015-12-24 12:45:03 +08: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 22:28:57 +08:00
|
|
|
static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
2015-12-24 12:45:03 +08: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-11 02:57:47 +08:00
|
|
|
BlockMirrorBackingMode backing_mode,
|
2019-07-25 01:12:30 +08:00
|
|
|
bool zero_target,
|
2015-12-24 12:45:03 +08: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-21 01:10:05 +08:00
|
|
|
bool has_filter_node_name,
|
|
|
|
const char *filter_node_name,
|
2018-06-14 02:18:22 +08:00
|
|
|
bool has_copy_mode, MirrorCopyMode copy_mode,
|
2018-09-06 21:02:22 +08:00
|
|
|
bool has_auto_finalize, bool auto_finalize,
|
|
|
|
bool has_auto_dismiss, bool auto_dismiss,
|
2015-12-24 12:45:03 +08:00
|
|
|
Error **errp)
|
2012-10-18 22:49:24 +08:00
|
|
|
{
|
2019-06-12 22:27:32 +08:00
|
|
|
BlockDriverState *unfiltered_bs;
|
2018-09-06 21:02:11 +08:00
|
|
|
int job_flags = JOB_DEFAULT;
|
2012-10-18 22:49:24 +08:00
|
|
|
|
|
|
|
if (!has_speed) {
|
|
|
|
speed = 0;
|
|
|
|
}
|
2012-10-18 22:49:28 +08: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-22 00:09:46 +08:00
|
|
|
if (!has_granularity) {
|
|
|
|
granularity = 0;
|
|
|
|
}
|
2013-01-22 16:03:13 +08:00
|
|
|
if (!has_buf_size) {
|
2015-05-15 15:51:36 +08:00
|
|
|
buf_size = 0;
|
2013-01-22 16:03:13 +08:00
|
|
|
}
|
2015-06-08 13:56:08 +08:00
|
|
|
if (!has_unmap) {
|
|
|
|
unmap = true;
|
|
|
|
}
|
2017-02-21 01:10:05 +08:00
|
|
|
if (!has_filter_node_name) {
|
|
|
|
filter_node_name = NULL;
|
|
|
|
}
|
2018-06-14 02:18:22 +08:00
|
|
|
if (!has_copy_mode) {
|
|
|
|
copy_mode = MIRROR_COPY_MODE_BACKGROUND;
|
|
|
|
}
|
2018-09-06 21:02:22 +08:00
|
|
|
if (has_auto_finalize && !auto_finalize) {
|
|
|
|
job_flags |= JOB_MANUAL_FINALIZE;
|
|
|
|
}
|
|
|
|
if (has_auto_dismiss && !auto_dismiss) {
|
|
|
|
job_flags |= JOB_MANUAL_DISMISS;
|
|
|
|
}
|
2013-01-22 16:03:13 +08:00
|
|
|
|
2013-01-22 00:09:46 +08:00
|
|
|
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
|
|
|
|
"a value in range [512B, 64MB]");
|
2013-01-22 00:09:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (granularity & (granularity - 1)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
|
2020-11-13 16:26:26 +08:00
|
|
|
"a power of 2");
|
2013-01-22 00:09:46 +08:00
|
|
|
return;
|
|
|
|
}
|
2012-10-18 22:49:24 +08:00
|
|
|
|
2015-12-24 12:45:03 +08:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
|
|
|
|
return;
|
|
|
|
}
|
2015-12-24 12:45:04 +08:00
|
|
|
if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) {
|
|
|
|
return;
|
|
|
|
}
|
2015-12-24 12:45:03 +08:00
|
|
|
|
2019-06-12 22:27:32 +08:00
|
|
|
if (!bdrv_backing_chain_next(bs) && sync == MIRROR_SYNC_MODE_TOP) {
|
2015-12-24 12:45:03 +08:00
|
|
|
sync = MIRROR_SYNC_MODE_FULL;
|
|
|
|
}
|
|
|
|
|
2019-06-12 22:27:32 +08:00
|
|
|
if (!has_replaces) {
|
|
|
|
/* We want to mirror from @bs, but keep implicit filters on top */
|
|
|
|
unfiltered_bs = bdrv_skip_implicit_filters(bs);
|
|
|
|
if (unfiltered_bs != bs) {
|
|
|
|
replaces = unfiltered_bs->node_name;
|
|
|
|
has_replaces = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-14 06:53:01 +08:00
|
|
|
if (has_replaces) {
|
|
|
|
BlockDriverState *to_replace_bs;
|
|
|
|
AioContext *replace_aio_context;
|
|
|
|
int64_t bs_size, replace_size;
|
|
|
|
|
|
|
|
bs_size = bdrv_getlength(bs);
|
|
|
|
if (bs_size < 0) {
|
|
|
|
error_setg_errno(errp, -bs_size, "Failed to query device's size");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
to_replace_bs = check_to_replace_node(bs, replaces, errp);
|
|
|
|
if (!to_replace_bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (replace_size < 0) {
|
|
|
|
error_setg_errno(errp, -replace_size,
|
|
|
|
"Failed to query the replacement node's size");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (bs_size != replace_size) {
|
|
|
|
error_setg(errp, "cannot replace image with a mirror image of "
|
|
|
|
"different size");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-24 12:45:03 +08:00
|
|
|
/* 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 22:28:57 +08:00
|
|
|
mirror_start(job_id, bs, target,
|
2018-09-06 21:02:11 +08:00
|
|
|
has_replaces ? replaces : NULL, job_flags,
|
2019-07-25 01:12:30 +08:00
|
|
|
speed, granularity, buf_size, sync, backing_mode, zero_target,
|
2017-02-21 01:10:05 +08:00
|
|
|
on_source_error, on_target_error, unmap, filter_node_name,
|
2018-06-14 02:18:22 +08:00
|
|
|
copy_mode, errp);
|
2015-12-24 12:45:03 +08:00
|
|
|
}
|
|
|
|
|
2016-07-15 06:37:58 +08:00
|
|
|
void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
2015-12-24 12:45:03 +08:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
2019-06-12 22:27:32 +08:00
|
|
|
BlockDriverState *target_backing_bs, *target_bs;
|
2015-12-24 12:45:03 +08:00
|
|
|
AioContext *aio_context;
|
2020-01-08 22:31:34 +08:00
|
|
|
AioContext *old_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-11 02:57:47 +08:00
|
|
|
BlockMirrorBackingMode backing_mode;
|
2015-12-24 12:45:03 +08:00
|
|
|
Error *local_err = NULL;
|
|
|
|
QDict *options = NULL;
|
|
|
|
int flags;
|
|
|
|
int64_t size;
|
2016-07-15 06:37:58 +08:00
|
|
|
const char *format = arg->format;
|
2019-07-25 01:12:30 +08:00
|
|
|
bool zero_target;
|
2019-04-26 22:12:27 +08:00
|
|
|
int ret;
|
2015-12-24 12:45:03 +08:00
|
|
|
|
2016-06-23 20:20:24 +08:00
|
|
|
bs = qmp_get_root_bs(arg->device, errp);
|
|
|
|
if (!bs) {
|
2012-10-18 22:49:24 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-08 00:29:20 +08:00
|
|
|
/* Early check to avoid creating target */
|
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-23 20:20:24 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-10-21 19:03:58 +08:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-07-15 06:37:58 +08:00
|
|
|
if (!arg->has_mode) {
|
|
|
|
arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
2015-12-24 12:45:03 +08:00
|
|
|
}
|
2012-10-18 22:49:24 +08:00
|
|
|
|
2016-07-15 06:37:58 +08:00
|
|
|
if (!arg->has_format) {
|
|
|
|
format = (arg->mode == NEW_IMAGE_MODE_EXISTING
|
|
|
|
? NULL : bs->drv->format_name);
|
2012-10-18 22:49:24 +08:00
|
|
|
}
|
|
|
|
|
2016-03-19 00:46:45 +08:00
|
|
|
flags = bs->open_flags | BDRV_O_RDWR;
|
2019-06-12 22:27:32 +08:00
|
|
|
target_backing_bs = bdrv_cow_bs(bdrv_skip_filters(bs));
|
|
|
|
if (!target_backing_bs && arg->sync == MIRROR_SYNC_MODE_TOP) {
|
2016-07-15 06:37:58 +08:00
|
|
|
arg->sync = MIRROR_SYNC_MODE_FULL;
|
2012-10-18 22:49:24 +08:00
|
|
|
}
|
2016-07-15 06:37:58 +08:00
|
|
|
if (arg->sync == MIRROR_SYNC_MODE_NONE) {
|
2019-06-12 22:27:32 +08:00
|
|
|
target_backing_bs = bs;
|
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-26 03:28:55 +08:00
|
|
|
}
|
2012-10-18 22:49:24 +08:00
|
|
|
|
2013-06-24 23:13:13 +08:00
|
|
|
size = bdrv_getlength(bs);
|
|
|
|
if (size < 0) {
|
|
|
|
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
2014-10-21 19:03:58 +08:00
|
|
|
goto out;
|
2013-06-24 23:13:13 +08:00
|
|
|
}
|
|
|
|
|
2016-07-15 06:37:58 +08:00
|
|
|
if (arg->has_replaces) {
|
|
|
|
if (!arg->has_node_name) {
|
2014-06-28 00:25:25 +08:00
|
|
|
error_setg(errp, "a node-name must be provided when replacing a"
|
|
|
|
" named node of the graph");
|
2014-10-21 19:03:58 +08:00
|
|
|
goto out;
|
2014-06-28 00:25:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-15 06:37:58 +08: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-11 02:57:47 +08:00
|
|
|
backing_mode = MIRROR_SOURCE_BACKING_CHAIN;
|
|
|
|
} else {
|
|
|
|
backing_mode = MIRROR_OPEN_BACKING_CHAIN;
|
|
|
|
}
|
|
|
|
|
2017-07-18 08:34:21 +08:00
|
|
|
/* Don't open backing image in create() */
|
|
|
|
flags |= BDRV_O_NO_BACKING;
|
|
|
|
|
2019-06-12 22:27:32 +08:00
|
|
|
if ((arg->sync == MIRROR_SYNC_MODE_FULL || !target_backing_bs)
|
2016-07-15 06:37:58 +08:00
|
|
|
&& arg->mode != NEW_IMAGE_MODE_EXISTING)
|
2013-11-07 02:50:44 +08:00
|
|
|
{
|
2012-10-18 22:49:24 +08:00
|
|
|
/* create new image w/o backing file */
|
2015-08-27 01:47:48 +08:00
|
|
|
assert(format);
|
2016-07-15 06:37:58 +08:00
|
|
|
bdrv_img_create(arg->target, format,
|
2017-04-21 20:27:01 +08:00
|
|
|
NULL, NULL, NULL, size, flags, false, &local_err);
|
2012-10-18 22:49:24 +08:00
|
|
|
} else {
|
2019-06-12 22:27:32 +08:00
|
|
|
/* Implicit filters should not appear in the filename */
|
|
|
|
BlockDriverState *explicit_backing =
|
|
|
|
bdrv_skip_implicit_filters(target_backing_bs);
|
|
|
|
|
2016-07-15 06:37:58 +08:00
|
|
|
switch (arg->mode) {
|
2012-10-18 22:49:24 +08:00
|
|
|
case NEW_IMAGE_MODE_EXISTING:
|
|
|
|
break;
|
|
|
|
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
|
|
|
|
/* create new image with backing file */
|
2019-06-12 22:27:32 +08:00
|
|
|
bdrv_refresh_filename(explicit_backing);
|
2016-07-15 06:37:58 +08:00
|
|
|
bdrv_img_create(arg->target, format,
|
2019-06-12 22:27:32 +08:00
|
|
|
explicit_backing->filename,
|
|
|
|
explicit_backing->drv->format_name,
|
2017-04-21 20:27:01 +08:00
|
|
|
NULL, size, flags, false, &local_err);
|
2012-10-18 22:49:24 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2012-11-30 20:52:08 +08:00
|
|
|
error_propagate(errp, local_err);
|
2014-10-21 19:03:58 +08:00
|
|
|
goto out;
|
2012-10-18 22:49:24 +08:00
|
|
|
}
|
|
|
|
|
2015-08-27 01:47:48 +08:00
|
|
|
options = qdict_new();
|
2016-07-15 06:37:58 +08:00
|
|
|
if (arg->has_node_name) {
|
2017-04-28 05:58:17 +08:00
|
|
|
qdict_put_str(options, "node-name", arg->node_name);
|
2014-06-16 18:00:55 +08:00
|
|
|
}
|
2015-08-27 01:47:48 +08:00
|
|
|
if (format) {
|
2017-04-28 05:58:17 +08:00
|
|
|
qdict_put_str(options, "driver", format);
|
2015-08-27 01:47:48 +08:00
|
|
|
}
|
2014-06-16 18:00:55 +08:00
|
|
|
|
2013-01-22 00:09:43 +08:00
|
|
|
/* Mirroring takes care of copy-on-write using the source's backing
|
|
|
|
* file.
|
|
|
|
*/
|
2017-07-18 08:34:21 +08:00
|
|
|
target_bs = bdrv_open(arg->target, NULL, options, flags, errp);
|
2016-05-17 22:41:31 +08:00
|
|
|
if (!target_bs) {
|
2014-10-21 19:03:58 +08:00
|
|
|
goto out;
|
2012-10-18 22:49:24 +08:00
|
|
|
}
|
|
|
|
|
2019-07-25 01:12:30 +08:00
|
|
|
zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL &&
|
|
|
|
(arg->mode == NEW_IMAGE_MODE_EXISTING ||
|
|
|
|
!bdrv_has_zero_init(target_bs)));
|
|
|
|
|
2020-01-08 22:31:34 +08:00
|
|
|
|
|
|
|
/* Honor bdrv_try_set_aio_context() context acquisition requirements. */
|
|
|
|
old_context = bdrv_get_aio_context(target_bs);
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
aio_context_acquire(old_context);
|
|
|
|
|
2019-04-26 22:12:27 +08:00
|
|
|
ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
|
|
|
|
if (ret < 0) {
|
|
|
|
bdrv_unref(target_bs);
|
2020-01-08 22:31:34 +08:00
|
|
|
aio_context_release(old_context);
|
|
|
|
return;
|
2019-04-26 22:12:27 +08:00
|
|
|
}
|
2014-10-21 19:03:58 +08:00
|
|
|
|
2020-01-08 22:31:34 +08:00
|
|
|
aio_context_release(old_context);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-07-15 06:37:58 +08:00
|
|
|
blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs,
|
|
|
|
arg->has_replaces, arg->replaces, arg->sync,
|
2019-07-25 01:12:30 +08:00
|
|
|
backing_mode, zero_target,
|
|
|
|
arg->has_speed, arg->speed,
|
2016-07-15 06:37:58 +08:00
|
|
|
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-21 01:10:05 +08:00
|
|
|
false, NULL,
|
2018-06-14 02:18:22 +08:00
|
|
|
arg->has_copy_mode, arg->copy_mode,
|
2018-09-06 21:02:22 +08:00
|
|
|
arg->has_auto_finalize, arg->auto_finalize,
|
|
|
|
arg->has_auto_dismiss, arg->auto_dismiss,
|
2020-07-08 00:06:04 +08:00
|
|
|
errp);
|
2016-04-12 22:17:41 +08:00
|
|
|
bdrv_unref(target_bs);
|
2014-10-21 19:03:58 +08:00
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2012-10-18 22:49:24 +08:00
|
|
|
}
|
|
|
|
|
2016-07-05 22:28:57 +08:00
|
|
|
void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
|
|
|
|
const char *device, const char *target,
|
2015-12-24 12:45:05 +08: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-21 01:10:05 +08:00
|
|
|
bool has_filter_node_name,
|
|
|
|
const char *filter_node_name,
|
2018-06-14 02:18:22 +08:00
|
|
|
bool has_copy_mode, MirrorCopyMode copy_mode,
|
2018-09-06 21:02:22 +08:00
|
|
|
bool has_auto_finalize, bool auto_finalize,
|
|
|
|
bool has_auto_dismiss, bool auto_dismiss,
|
2015-12-24 12:45:05 +08:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *target_bs;
|
|
|
|
AioContext *aio_context;
|
2020-01-08 22:31:34 +08:00
|
|
|
AioContext *old_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-11 02:57:47 +08:00
|
|
|
BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN;
|
2019-07-25 01:12:30 +08:00
|
|
|
bool zero_target;
|
2019-04-26 22:12:27 +08:00
|
|
|
int ret;
|
2015-12-24 12:45:05 +08:00
|
|
|
|
2016-06-23 20:20:24 +08:00
|
|
|
bs = qmp_get_root_bs(device, errp);
|
2015-12-24 12:45:05 +08:00
|
|
|
if (!bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
target_bs = bdrv_lookup_bs(target, target, errp);
|
|
|
|
if (!target_bs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-25 01:12:30 +08:00
|
|
|
zero_target = (sync == MIRROR_SYNC_MODE_FULL);
|
|
|
|
|
2020-01-08 22:31:34 +08:00
|
|
|
/* Honor bdrv_try_set_aio_context() context acquisition requirements. */
|
|
|
|
old_context = bdrv_get_aio_context(target_bs);
|
2015-12-24 12:45:05 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2020-01-08 22:31:34 +08:00
|
|
|
aio_context_acquire(old_context);
|
2015-12-24 12:45:05 +08:00
|
|
|
|
2019-04-26 22:12:27 +08:00
|
|
|
ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
|
2020-01-08 22:31:34 +08:00
|
|
|
|
|
|
|
aio_context_release(old_context);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2019-04-26 22:12:27 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2015-12-24 12:45:05 +08:00
|
|
|
|
2016-07-05 22:28:57 +08: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-11 02:57:47 +08:00
|
|
|
has_replaces, replaces, sync, backing_mode,
|
2019-07-25 01:12:30 +08:00
|
|
|
zero_target, has_speed, speed,
|
2015-12-24 12:45:05 +08:00
|
|
|
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-21 01:10:05 +08:00
|
|
|
has_filter_node_name, filter_node_name,
|
2018-06-14 02:18:22 +08:00
|
|
|
has_copy_mode, copy_mode,
|
2018-09-06 21:02:22 +08:00
|
|
|
has_auto_finalize, auto_finalize,
|
|
|
|
has_auto_dismiss, auto_dismiss,
|
2020-07-08 00:06:04 +08:00
|
|
|
errp);
|
2019-04-26 22:12:27 +08:00
|
|
|
out:
|
2015-12-24 12:45:05 +08:00
|
|
|
aio_context_release(aio_context);
|
|
|
|
}
|
|
|
|
|
2016-07-05 22:28:55 +08: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 17:36:58 +08:00
|
|
|
Error **errp)
|
2012-01-18 22:40:47 +08:00
|
|
|
{
|
2016-07-05 22:28:55 +08:00
|
|
|
BlockJob *job;
|
2012-01-18 22:40:47 +08:00
|
|
|
|
2016-07-05 22:28:55 +08:00
|
|
|
assert(id != NULL);
|
2015-10-19 23:53:29 +08:00
|
|
|
|
2016-07-05 22:28:55 +08:00
|
|
|
*aio_context = NULL;
|
2014-10-21 19:03:50 +08:00
|
|
|
|
2016-07-05 22:28:55 +08:00
|
|
|
job = block_job_get(id);
|
2015-10-19 23:53:29 +08:00
|
|
|
|
2016-07-05 22:28:55 +08:00
|
|
|
if (!job) {
|
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
|
|
|
|
"Block job '%s' not found", id);
|
|
|
|
return NULL;
|
2012-01-18 22:40:47 +08:00
|
|
|
}
|
2014-10-21 19:03:50 +08:00
|
|
|
|
2016-07-05 22:28:55 +08:00
|
|
|
*aio_context = blk_get_aio_context(job->blk);
|
|
|
|
aio_context_acquire(*aio_context);
|
2014-10-21 19:03:50 +08:00
|
|
|
|
2016-07-05 22:28:55 +08:00
|
|
|
return job;
|
2012-01-18 22:40:47 +08:00
|
|
|
}
|
|
|
|
|
2012-04-25 23:51:02 +08:00
|
|
|
void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
|
2012-01-18 22:40:47 +08:00
|
|
|
{
|
2014-10-21 19:03:50 +08:00
|
|
|
AioContext *aio_context;
|
2015-01-29 17:36:58 +08:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-01-18 22:40:47 +08:00
|
|
|
|
|
|
|
if (!job) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-25 23:51:02 +08:00
|
|
|
block_job_set_speed(job, speed, errp);
|
2014-10-21 19:03:50 +08:00
|
|
|
aio_context_release(aio_context);
|
2012-01-18 22:40:47 +08:00
|
|
|
}
|
2012-01-18 22:40:48 +08:00
|
|
|
|
2012-09-28 23:22:51 +08:00
|
|
|
void qmp_block_job_cancel(const char *device,
|
|
|
|
bool has_force, bool force, Error **errp)
|
2012-01-18 22:40:48 +08:00
|
|
|
{
|
2014-10-21 19:03:50 +08:00
|
|
|
AioContext *aio_context;
|
2015-01-29 17:36:58 +08:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-09-28 23:22:51 +08:00
|
|
|
|
2012-01-18 22:40:48 +08:00
|
|
|
if (!job) {
|
|
|
|
return;
|
|
|
|
}
|
2014-10-21 19:03:50 +08:00
|
|
|
|
|
|
|
if (!has_force) {
|
|
|
|
force = false;
|
|
|
|
}
|
|
|
|
|
2018-04-18 23:10:26 +08:00
|
|
|
if (job_user_paused(&job->job) && !force) {
|
2014-03-22 07:42:26 +08:00
|
|
|
error_setg(errp, "The block job for device '%s' is currently paused",
|
|
|
|
device);
|
2014-10-21 19:03:50 +08:00
|
|
|
goto out;
|
2012-09-28 23:22:50 +08:00
|
|
|
}
|
2012-01-18 22:40:48 +08:00
|
|
|
|
|
|
|
trace_qmp_block_job_cancel(job);
|
2018-04-24 22:13:52 +08:00
|
|
|
job_user_cancel(&job->job, force, errp);
|
2014-10-21 19:03:50 +08:00
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2012-01-18 22:40:48 +08:00
|
|
|
}
|
2012-01-18 22:40:49 +08:00
|
|
|
|
2012-09-28 23:22:51 +08:00
|
|
|
void qmp_block_job_pause(const char *device, Error **errp)
|
|
|
|
{
|
2014-10-21 19:03:50 +08:00
|
|
|
AioContext *aio_context;
|
2015-01-29 17:36:58 +08:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-09-28 23:22:51 +08:00
|
|
|
|
blockjobs: add block_job_verb permission table
Which commands ("verbs") are appropriate for jobs in which state is
also somewhat burdensome to keep track of.
As of this commit, it looks rather useless, but begins to look more
interesting the more states we add to the STM table.
A recurring theme is that no verb will apply to an 'undefined' job.
Further, it's not presently possible to restrict the "pause" or "resume"
verbs any more than they are in this commit because of the asynchronous
nature of how jobs enter the PAUSED state; justifications for some
seemingly erroneous applications are given below.
=====
Verbs
=====
Cancel: Any state except undefined.
Pause: Any state except undefined;
'created': Requests that the job pauses as it starts.
'running': Normal usage. (PAUSED)
'paused': The job may be paused for internal reasons,
but the user may wish to force an indefinite
user-pause, so this is allowed.
'ready': Normal usage. (STANDBY)
'standby': Same logic as above.
Resume: Any state except undefined;
'created': Will lift a user's pause-on-start request.
'running': Will lift a pause request before it takes effect.
'paused': Normal usage.
'ready': Will lift a pause request before it takes effect.
'standby': Normal usage.
Set-speed: Any state except undefined, though ready may not be meaningful.
Complete: Only a 'ready' job may accept a complete request.
=======
Changes
=======
(1)
To facilitate "nice" error checking, all five major block-job verb
interfaces in blockjob.c now support an errp parameter:
- block_job_user_cancel is added as a new interface.
- block_job_user_pause gains an errp paramter
- block_job_user_resume gains an errp parameter
- block_job_set_speed already had an errp parameter.
- block_job_complete already had an errp parameter.
(2)
block-job-pause and block-job-resume will no longer no-op when trying
to pause an already paused job, or trying to resume a job that isn't
paused. These functions will now report that they did not perform the
action requested because it was not possible.
iotests have been adjusted to address this new behavior.
(3)
block-job-complete doesn't worry about checking !block_job_started,
because the permission table guards against this.
(4)
test-bdrv-drain's job implementation needs to announce that it is
'ready' now, in order to be completed.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2018-03-10 16:27:32 +08:00
|
|
|
if (!job) {
|
2012-09-28 23:22:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_pause(job);
|
2018-04-18 23:10:26 +08:00
|
|
|
job_user_pause(&job->job, errp);
|
2014-10-21 19:03:50 +08:00
|
|
|
aio_context_release(aio_context);
|
2012-09-28 23:22:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void qmp_block_job_resume(const char *device, Error **errp)
|
|
|
|
{
|
2014-10-21 19:03:50 +08:00
|
|
|
AioContext *aio_context;
|
2015-01-29 17:36:58 +08:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-09-28 23:22:51 +08:00
|
|
|
|
blockjobs: add block_job_verb permission table
Which commands ("verbs") are appropriate for jobs in which state is
also somewhat burdensome to keep track of.
As of this commit, it looks rather useless, but begins to look more
interesting the more states we add to the STM table.
A recurring theme is that no verb will apply to an 'undefined' job.
Further, it's not presently possible to restrict the "pause" or "resume"
verbs any more than they are in this commit because of the asynchronous
nature of how jobs enter the PAUSED state; justifications for some
seemingly erroneous applications are given below.
=====
Verbs
=====
Cancel: Any state except undefined.
Pause: Any state except undefined;
'created': Requests that the job pauses as it starts.
'running': Normal usage. (PAUSED)
'paused': The job may be paused for internal reasons,
but the user may wish to force an indefinite
user-pause, so this is allowed.
'ready': Normal usage. (STANDBY)
'standby': Same logic as above.
Resume: Any state except undefined;
'created': Will lift a user's pause-on-start request.
'running': Will lift a pause request before it takes effect.
'paused': Normal usage.
'ready': Will lift a pause request before it takes effect.
'standby': Normal usage.
Set-speed: Any state except undefined, though ready may not be meaningful.
Complete: Only a 'ready' job may accept a complete request.
=======
Changes
=======
(1)
To facilitate "nice" error checking, all five major block-job verb
interfaces in blockjob.c now support an errp parameter:
- block_job_user_cancel is added as a new interface.
- block_job_user_pause gains an errp paramter
- block_job_user_resume gains an errp parameter
- block_job_set_speed already had an errp parameter.
- block_job_complete already had an errp parameter.
(2)
block-job-pause and block-job-resume will no longer no-op when trying
to pause an already paused job, or trying to resume a job that isn't
paused. These functions will now report that they did not perform the
action requested because it was not possible.
iotests have been adjusted to address this new behavior.
(3)
block-job-complete doesn't worry about checking !block_job_started,
because the permission table guards against this.
(4)
test-bdrv-drain's job implementation needs to announce that it is
'ready' now, in order to be completed.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2018-03-10 16:27:32 +08:00
|
|
|
if (!job) {
|
2012-09-28 23:22:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_resume(job);
|
2018-04-18 23:10:26 +08:00
|
|
|
job_user_resume(&job->job, errp);
|
2014-10-21 19:03:50 +08:00
|
|
|
aio_context_release(aio_context);
|
2012-09-28 23:22:51 +08:00
|
|
|
}
|
|
|
|
|
2012-10-18 22:49:21 +08:00
|
|
|
void qmp_block_job_complete(const char *device, Error **errp)
|
|
|
|
{
|
2014-10-21 19:03:50 +08:00
|
|
|
AioContext *aio_context;
|
2015-01-29 17:36:58 +08:00
|
|
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
2012-10-18 22:49:21 +08:00
|
|
|
|
|
|
|
if (!job) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_complete(job);
|
2018-04-23 18:24:16 +08:00
|
|
|
job_complete(&job->job, errp);
|
2014-10-21 19:03:50 +08:00
|
|
|
aio_context_release(aio_context);
|
2012-10-18 22:49:21 +08:00
|
|
|
}
|
|
|
|
|
2018-03-10 16:27:43 +08:00
|
|
|
void qmp_block_job_finalize(const char *id, Error **errp)
|
|
|
|
{
|
|
|
|
AioContext *aio_context;
|
|
|
|
BlockJob *job = find_block_job(id, &aio_context, errp);
|
|
|
|
|
|
|
|
if (!job) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_finalize(job);
|
job: take each job's lock individually in job_txn_apply
All callers of job_txn_apply hold a single job's lock, but different
jobs within a transaction can have different contexts, thus we need to
lock each one individually before applying the callback function.
Similar to job_completed_txn_abort this also requires releasing the
caller's context before and reacquiring it after to avoid recursive
locks which might break AIO_WAIT_WHILE in the callback. This is safe, since
existing code would already have to take this into account, lest
job_completed_txn_abort might have broken.
This also brings to light a different issue: When a callback function in
job_txn_apply moves it's job to a different AIO context, callers will
try to release the wrong lock (now that we re-acquire the lock
correctly, previously it would just continue with the old lock, leaving
the job unlocked for the rest of the return path). Fix this by not caching
the job's context.
This is only necessary for qmp_block_job_finalize, qmp_job_finalize and
job_exit, since everyone else calls through job_exit.
One test needed adapting, since it calls job_finalize directly, so it
manually needs to acquire the correct context.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
Message-Id: <20200407115651.69472-2-s.reiter@proxmox.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-04-07 19:56:49 +08:00
|
|
|
job_ref(&job->job);
|
2018-04-23 22:06:26 +08:00
|
|
|
job_finalize(&job->job, errp);
|
job: take each job's lock individually in job_txn_apply
All callers of job_txn_apply hold a single job's lock, but different
jobs within a transaction can have different contexts, thus we need to
lock each one individually before applying the callback function.
Similar to job_completed_txn_abort this also requires releasing the
caller's context before and reacquiring it after to avoid recursive
locks which might break AIO_WAIT_WHILE in the callback. This is safe, since
existing code would already have to take this into account, lest
job_completed_txn_abort might have broken.
This also brings to light a different issue: When a callback function in
job_txn_apply moves it's job to a different AIO context, callers will
try to release the wrong lock (now that we re-acquire the lock
correctly, previously it would just continue with the old lock, leaving
the job unlocked for the rest of the return path). Fix this by not caching
the job's context.
This is only necessary for qmp_block_job_finalize, qmp_job_finalize and
job_exit, since everyone else calls through job_exit.
One test needed adapting, since it calls job_finalize directly, so it
manually needs to acquire the correct context.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
Message-Id: <20200407115651.69472-2-s.reiter@proxmox.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-04-07 19:56:49 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Job's context might have changed via job_finalize (and job_txn_apply
|
|
|
|
* automatically acquires the new one), so make sure we release the correct
|
|
|
|
* one.
|
|
|
|
*/
|
|
|
|
aio_context = blk_get_aio_context(job->blk);
|
|
|
|
job_unref(&job->job);
|
2018-03-10 16:27:43 +08:00
|
|
|
aio_context_release(aio_context);
|
|
|
|
}
|
|
|
|
|
2018-03-10 16:27:36 +08:00
|
|
|
void qmp_block_job_dismiss(const char *id, Error **errp)
|
|
|
|
{
|
|
|
|
AioContext *aio_context;
|
2018-04-24 23:10:12 +08:00
|
|
|
BlockJob *bjob = find_block_job(id, &aio_context, errp);
|
|
|
|
Job *job;
|
2018-03-10 16:27:36 +08:00
|
|
|
|
2018-04-24 23:10:12 +08:00
|
|
|
if (!bjob) {
|
2018-03-10 16:27:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-24 23:10:12 +08:00
|
|
|
trace_qmp_block_job_dismiss(bjob);
|
|
|
|
job = &bjob->job;
|
|
|
|
job_dismiss(&job, errp);
|
2018-03-10 16:27:36 +08:00
|
|
|
aio_context_release(aio_context);
|
|
|
|
}
|
|
|
|
|
2014-07-01 15:52:16 +08: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 22:19:45 +08:00
|
|
|
AioContext *aio_context;
|
2014-07-01 15:52:16 +08:00
|
|
|
BlockDriverState *image_bs = NULL;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
bool ro;
|
|
|
|
int ret;
|
|
|
|
|
2016-06-23 20:20:24 +08:00
|
|
|
bs = qmp_get_root_bs(device, errp);
|
|
|
|
if (!bs) {
|
2014-07-01 15:52:16 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-23 20:20:24 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2014-11-19 22:19:45 +08:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2014-07-01 15:52:16 +08:00
|
|
|
image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2014-11-19 22:19:45 +08:00
|
|
|
goto out;
|
2014-07-01 15:52:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!image_bs) {
|
|
|
|
error_setg(errp, "image file not found");
|
2014-11-19 22:19:45 +08:00
|
|
|
goto out;
|
2014-07-01 15:52:16 +08: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 22:19:45 +08:00
|
|
|
goto out;
|
2014-07-01 15:52:16 +08: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 22:19:45 +08:00
|
|
|
goto out;
|
2014-07-01 15:52:16 +08: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 22:19:45 +08:00
|
|
|
goto out;
|
2014-07-01 15:52:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if not r/w, reopen to make r/w */
|
|
|
|
ro = bdrv_is_read_only(image_bs);
|
|
|
|
|
|
|
|
if (ro) {
|
2018-11-12 22:00:38 +08:00
|
|
|
if (bdrv_reopen_set_read_only(image_bs, false, errp) != 0) {
|
2014-11-19 22:19:45 +08:00
|
|
|
goto out;
|
2014-07-01 15:52:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = bdrv_change_backing_file(image_bs, backing_file,
|
2020-07-07 04:39:53 +08:00
|
|
|
image_bs->drv ? image_bs->drv->format_name : "",
|
|
|
|
false);
|
2014-07-01 15:52:16 +08:00
|
|
|
|
|
|
|
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) {
|
2020-07-08 00:06:04 +08:00
|
|
|
bdrv_reopen_set_read_only(image_bs, true, errp);
|
2014-07-01 15:52:16 +08:00
|
|
|
}
|
2014-11-19 22:19:45 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
2014-07-01 15:52:16 +08:00
|
|
|
}
|
|
|
|
|
2013-09-23 21:26:03 +08:00
|
|
|
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
|
|
|
{
|
2015-10-19 23:53:09 +08:00
|
|
|
BlockDriverState *bs;
|
2013-09-23 21:26:03 +08:00
|
|
|
QObject *obj;
|
2016-09-30 22:45:28 +08:00
|
|
|
Visitor *v = qobject_output_visitor_new(&obj);
|
2013-09-23 21:26:03 +08:00
|
|
|
QDict *qdict;
|
|
|
|
|
2020-04-24 16:43:35 +08:00
|
|
|
visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
|
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-10 00:48:43 +08:00
|
|
|
visit_complete(v, &obj);
|
2018-02-24 23:40:29 +08:00
|
|
|
qdict = qobject_to(QDict, obj);
|
2013-09-23 21:26:03 +08:00
|
|
|
|
|
|
|
qdict_flatten(qdict);
|
|
|
|
|
2016-09-21 20:56:11 +08: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 23:36:12 +08:00
|
|
|
|
2016-09-21 20:56:11 +08:00
|
|
|
bs = bds_tree_init(qdict, errp);
|
|
|
|
if (!bs) {
|
|
|
|
goto fail;
|
2013-09-23 21:26:03 +08:00
|
|
|
}
|
|
|
|
|
2020-03-08 17:24:40 +08:00
|
|
|
bdrv_set_monitor_owned(bs);
|
2016-09-21 20:56:11 +08:00
|
|
|
|
2013-09-23 21:26:03 +08: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-10 00:48:43 +08:00
|
|
|
visit_free(v);
|
2013-09-23 21:26:03 +08:00
|
|
|
}
|
|
|
|
|
2019-03-13 00:48:51 +08:00
|
|
|
void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
AioContext *ctx;
|
|
|
|
QObject *obj;
|
|
|
|
Visitor *v = qobject_output_visitor_new(&obj);
|
|
|
|
BlockReopenQueue *queue;
|
|
|
|
QDict *qdict;
|
|
|
|
|
|
|
|
/* Check for the selected node name */
|
|
|
|
if (!options->has_node_name) {
|
2021-03-05 23:19:29 +08:00
|
|
|
error_setg(errp, "node-name not specified");
|
2019-03-13 00:48:51 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
bs = bdrv_find_node(options->node_name);
|
|
|
|
if (!bs) {
|
2021-03-05 23:19:29 +08:00
|
|
|
error_setg(errp, "Failed to find node with node-name='%s'",
|
|
|
|
options->node_name);
|
2019-03-13 00:48:51 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Put all options in a QDict and flatten it */
|
2020-04-24 16:43:35 +08:00
|
|
|
visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
|
2019-03-13 00:48:51 +08:00
|
|
|
visit_complete(v, &obj);
|
|
|
|
qdict = qobject_to(QDict, obj);
|
|
|
|
|
|
|
|
qdict_flatten(qdict);
|
|
|
|
|
|
|
|
/* Perform the reopen operation */
|
|
|
|
ctx = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(ctx);
|
|
|
|
bdrv_subtree_drained_begin(bs);
|
|
|
|
queue = bdrv_reopen_queue(NULL, bs, qdict, false);
|
|
|
|
bdrv_reopen_multiple(queue, errp);
|
|
|
|
bdrv_subtree_drained_end(bs);
|
|
|
|
aio_context_release(ctx);
|
|
|
|
|
|
|
|
fail:
|
|
|
|
visit_free(v);
|
|
|
|
}
|
|
|
|
|
2017-03-22 00:53:28 +08:00
|
|
|
void qmp_blockdev_del(const char *node_name, Error **errp)
|
2015-11-02 22:51:55 +08:00
|
|
|
{
|
|
|
|
AioContext *aio_context;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2016-09-21 20:56:11 +08:00
|
|
|
bs = bdrv_find_node(node_name);
|
|
|
|
if (!bs) {
|
2021-03-05 23:19:29 +08:00
|
|
|
error_setg(errp, "Failed to find node with node-name='%s'", node_name);
|
2015-11-02 22:51:55 +08:00
|
|
|
return;
|
|
|
|
}
|
2016-09-21 20:56:11 +08:00
|
|
|
if (bdrv_has_blk(bs)) {
|
|
|
|
error_setg(errp, "Node %s is in use", node_name);
|
|
|
|
return;
|
2015-11-02 22:51:55 +08:00
|
|
|
}
|
2016-09-21 20:56:11 +08:00
|
|
|
aio_context = bdrv_get_aio_context(bs);
|
2015-11-02 22:51:55 +08:00
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
2016-09-21 20:56:11 +08:00
|
|
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, errp)) {
|
|
|
|
goto out;
|
|
|
|
}
|
2016-01-29 23:36:12 +08:00
|
|
|
|
2018-12-07 06:00:09 +08:00
|
|
|
if (!QTAILQ_IN_USE(bs, monitor_list)) {
|
2016-09-21 20:56:11 +08:00
|
|
|
error_setg(errp, "Node %s is not owned by the monitor",
|
|
|
|
bs->node_name);
|
|
|
|
goto out;
|
2015-11-02 22:51:55 +08:00
|
|
|
}
|
|
|
|
|
2016-09-21 20:56:11 +08: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 22:51:55 +08:00
|
|
|
}
|
|
|
|
|
2016-09-21 20:56:11 +08:00
|
|
|
QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list);
|
|
|
|
bdrv_unref(bs);
|
|
|
|
|
2015-11-02 22:51:55 +08:00
|
|
|
out:
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
}
|
|
|
|
|
2016-05-10 15:36:39 +08: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 19:59:10 +08:00
|
|
|
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
|
2012-01-18 22:40:49 +08:00
|
|
|
{
|
2021-01-14 06:10:12 +08:00
|
|
|
BlockJobInfoList *head = NULL, **tail = &head;
|
2016-05-27 18:53:37 +08:00
|
|
|
BlockJob *job;
|
2012-01-18 22:40:49 +08:00
|
|
|
|
2016-05-27 18:53:37 +08:00
|
|
|
for (job = block_job_next(NULL); job; job = block_job_next(job)) {
|
2021-01-14 06:10:12 +08:00
|
|
|
BlockJobInfo *value;
|
2016-10-28 00:06:55 +08:00
|
|
|
AioContext *aio_context;
|
2014-10-21 19:03:51 +08:00
|
|
|
|
2016-10-28 00:06:55 +08:00
|
|
|
if (block_job_is_internal(job)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
aio_context = blk_get_aio_context(job->blk);
|
2014-10-21 19:03:51 +08:00
|
|
|
aio_context_acquire(aio_context);
|
2021-01-14 06:10:12 +08:00
|
|
|
value = block_job_query(job, errp);
|
2014-10-21 19:03:51 +08:00
|
|
|
aio_context_release(aio_context);
|
2021-01-14 06:10:12 +08:00
|
|
|
if (!value) {
|
2016-10-28 00:06:55 +08:00
|
|
|
qapi_free_BlockJobInfoList(head);
|
|
|
|
return NULL;
|
|
|
|
}
|
2021-01-14 06:10:12 +08:00
|
|
|
QAPI_LIST_APPEND(tail, value);
|
2012-01-18 22:40:49 +08:00
|
|
|
}
|
|
|
|
|
2014-10-07 19:59:10 +08:00
|
|
|
return head;
|
2012-01-18 22:40:49 +08:00
|
|
|
}
|
2012-11-26 23:03:42 +08:00
|
|
|
|
2017-12-06 22:45:49 +08:00
|
|
|
void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
|
2017-12-08 04:13:17 +08:00
|
|
|
bool has_force, bool force, Error **errp)
|
2017-12-06 22:45:49 +08:00
|
|
|
{
|
|
|
|
AioContext *old_context;
|
|
|
|
AioContext *new_context;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
bs = bdrv_find_node(node_name);
|
|
|
|
if (!bs) {
|
2021-03-05 23:19:29 +08:00
|
|
|
error_setg(errp, "Failed to find node with node-name='%s'", node_name);
|
2017-12-06 22:45:49 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-08 04:13:17 +08: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 22:45:49 +08: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);
|
|
|
|
|
2019-04-26 22:12:27 +08:00
|
|
|
bdrv_try_set_aio_context(bs, new_context, errp);
|
2017-12-06 22:45:49 +08:00
|
|
|
|
|
|
|
aio_context_release(old_context);
|
|
|
|
}
|
|
|
|
|
2013-03-15 17:35:07 +08:00
|
|
|
QemuOptsList qemu_common_drive_opts = {
|
2012-11-26 23:03:42 +08:00
|
|
|
.name = "drive",
|
2013-03-15 17:35:07 +08:00
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
|
2012-11-26 23:03:42 +08:00
|
|
|
.desc = {
|
|
|
|
{
|
|
|
|
.name = "snapshot",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "enable/disable snapshot mode",
|
|
|
|
},{
|
|
|
|
.name = "aio",
|
|
|
|
.type = QEMU_OPT_STRING,
|
2020-01-20 22:18:50 +08:00
|
|
|
.help = "host AIO implementation (threads, native, io_uring)",
|
2016-03-15 22:39:42 +08:00
|
|
|
},{
|
|
|
|
.name = BDRV_OPT_CACHE_WB,
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "Enable writeback mode",
|
2012-11-26 23:03:42 +08: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 22:53:05 +08:00
|
|
|
.name = BDRV_OPT_READ_ONLY,
|
2012-11-26 23:03:42 +08:00
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "open drive file as read-only",
|
2017-02-28 17:31:46 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
THROTTLE_OPTS,
|
|
|
|
|
|
|
|
{
|
2015-06-09 00:17:44 +08:00
|
|
|
.name = "throttling.group",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "name of the block throttling group",
|
2012-11-26 23:03:42 +08:00
|
|
|
},{
|
|
|
|
.name = "copy-on-read",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "copy read data from backing file into image file",
|
2014-05-18 06:58:19 +08:00
|
|
|
},{
|
|
|
|
.name = "detect-zeroes",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "try to optimize zero writes (off, on, unmap)",
|
2015-10-28 23:33:04 +08: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 23:03:42 +08:00
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
2013-03-15 17:35:07 +08:00
|
|
|
|
|
|
|
QemuOptsList qemu_drive_opts = {
|
|
|
|
.name = "drive",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
|
|
|
|
.desc = {
|
2013-06-19 19:44:17 +08:00
|
|
|
/*
|
|
|
|
* no elements => accept any params
|
|
|
|
* validation will happen later
|
|
|
|
*/
|
2013-03-15 17:35:07 +08:00
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|