Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches

# gpg: Signature made Wed 11 Nov 2015 16:03:19 GMT using RSA key ID C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"

* remotes/kevin/tags/for-upstream: (41 commits)
  iotests: Check for quorum support in test 139
  qcow2: Fix qcow2_get_cluster_offset() for zero clusters
  iotests: Add tests for the x-blockdev-del command
  block: Add 'x-blockdev-del' QMP command
  block: Add blk_get_refcnt()
  mirror: block all operations on the target image during the job
  qemu-iotests: fix -valgrind option for check
  qemu-iotests: fix cleanup of background processes
  qemu-io: Correct error messages
  qemu-io: Check for trailing chars
  qemu-io: fix cvtnum lval types
  block: test 'blockdev-snapshot' using a file BDS as the overlay
  block: Remove inner quotation marks in iotest 085
  block: Disallow snapshots if the overlay doesn't support backing files
  throttle: Use bs->throttle_state instead of bs->io_limits_enabled
  throttle: Check for pending requests in throttle_group_unregister_bs()
  qemu-img: add check for zero-length job len
  qcow2: avoid misaligned 64bit bswap
  qemu-iotests: Test the reopening of overlay_bs in 'block-commit'
  commit: reopen overlay_bs before base
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2015-11-11 16:43:19 +00:00
38 changed files with 2651 additions and 336 deletions
+14 -8
View File
@@ -73,8 +73,7 @@ struct BdrvDirtyBitmap {
#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
QTAILQ_HEAD_INITIALIZER(bdrv_states);
struct BdrvStates bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states);
static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states =
QTAILQ_HEAD_INITIALIZER(graph_bdrv_states);
@@ -1394,6 +1393,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
BlockDriverState *bs;
BlockDriver *drv = NULL;
const char *drvname;
const char *backing;
Error *local_err = NULL;
int snapshot_flags = 0;
@@ -1461,6 +1461,12 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
assert(drvname || !(flags & BDRV_O_PROTOCOL));
backing = qdict_get_try_str(options, "backing");
if (backing && *backing == '\0') {
flags |= BDRV_O_NO_BACKING;
qdict_del(options, "backing");
}
bs->open_flags = flags;
bs->options = options;
options = qdict_clone_shallow(options);
@@ -1901,7 +1907,7 @@ void bdrv_close(BlockDriverState *bs)
}
/* Disable I/O limits and drain all pending throttled requests */
if (bs->io_limits_enabled) {
if (bs->throttle_state) {
bdrv_io_limits_disable(bs);
}
@@ -2683,12 +2689,12 @@ BlockDriverState *bdrv_lookup_bs(const char *device,
blk = blk_by_name(device);
if (blk) {
if (!blk_bs(blk)) {
bs = blk_bs(blk);
if (!bs) {
error_setg(errp, "Device '%s' has no medium", device);
return NULL;
}
return blk_bs(blk);
return bs;
}
}
@@ -3706,7 +3712,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
baf->detach_aio_context(baf->opaque);
}
if (bs->io_limits_enabled) {
if (bs->throttle_state) {
throttle_timers_detach_aio_context(&bs->throttle_timers);
}
if (bs->drv->bdrv_detach_aio_context) {
@@ -3742,7 +3748,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
if (bs->drv->bdrv_attach_aio_context) {
bs->drv->bdrv_attach_aio_context(bs, new_context);
}
if (bs->io_limits_enabled) {
if (bs->throttle_state) {
throttle_timers_attach_aio_context(&bs->throttle_timers, new_context);
}
+51 -10
View File
@@ -189,6 +189,11 @@ static void drive_info_del(DriveInfo *dinfo)
g_free(dinfo);
}
int blk_get_refcnt(BlockBackend *blk)
{
return blk ? blk->refcnt : 0;
}
/*
* Increment @blk's reference count.
* @blk must not be null.
@@ -333,6 +338,18 @@ void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk)
}
}
/*
* Disassociates the currently associated BlockDriverState from @blk.
*/
void blk_remove_bs(BlockBackend *blk)
{
blk_update_root_state(blk);
blk->bs->blk = NULL;
bdrv_unref(blk->bs);
blk->bs = NULL;
}
/*
* Associates a new BlockDriverState with @blk.
*/
@@ -417,18 +434,15 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
void blk_dev_change_media_cb(BlockBackend *blk, bool load)
{
if (blk->dev_ops && blk->dev_ops->change_media_cb) {
bool tray_was_closed = !blk_dev_is_tray_open(blk);
bool tray_was_open, tray_is_open;
tray_was_open = blk_dev_is_tray_open(blk);
blk->dev_ops->change_media_cb(blk->dev_opaque, load);
if (tray_was_closed) {
/* tray open */
qapi_event_send_device_tray_moved(blk_name(blk),
true, &error_abort);
}
if (load) {
/* tray close */
qapi_event_send_device_tray_moved(blk_name(blk),
false, &error_abort);
tray_is_open = blk_dev_is_tray_open(blk);
if (tray_was_open != tray_is_open) {
qapi_event_send_device_tray_moved(blk_name(blk), tray_is_open,
&error_abort);
}
}
}
@@ -1227,6 +1241,33 @@ void blk_update_root_state(BlockBackend *blk)
}
}
/*
* Applies the information in the root state to the given BlockDriverState. This
* does not include the flags which have to be specified for bdrv_open(), use
* blk_get_open_flags_from_root_state() to inquire them.
*/
void blk_apply_root_state(BlockBackend *blk, BlockDriverState *bs)
{
bs->detect_zeroes = blk->root_state.detect_zeroes;
if (blk->root_state.throttle_group) {
bdrv_io_limits_enable(bs, blk->root_state.throttle_group);
}
}
/*
* Returns the flags to be used for bdrv_open() of a BlockDriverState which is
* supposed to inherit the root state.
*/
int blk_get_open_flags_from_root_state(BlockBackend *blk)
{
int bs_flags;
bs_flags = blk->root_state.read_only ? 0 : BDRV_O_RDWR;
bs_flags |= blk->root_state.open_flags & ~BDRV_O_RDWR;
return bs_flags;
}
BlockBackendRootState *blk_get_root_state(BlockBackend *blk)
{
return &blk->root_state;
+4 -4
View File
@@ -236,14 +236,14 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
orig_overlay_flags = bdrv_get_flags(overlay_bs);
/* convert base & overlay_bs to r/w, if necessary */
if (!(orig_base_flags & BDRV_O_RDWR)) {
reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
orig_base_flags | BDRV_O_RDWR);
}
if (!(orig_overlay_flags & BDRV_O_RDWR)) {
reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, NULL,
orig_overlay_flags | BDRV_O_RDWR);
}
if (!(orig_base_flags & BDRV_O_RDWR)) {
reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
orig_base_flags | BDRV_O_RDWR);
}
if (reopen_queue) {
bdrv_reopen_multiple(reopen_queue, &local_err);
if (local_err != NULL) {
+4
View File
@@ -384,6 +384,7 @@ static void mirror_exit(BlockJob *job, void *opaque)
aio_context_release(replace_aio_context);
}
g_free(s->replaces);
bdrv_op_unblock_all(s->target, s->common.blocker);
bdrv_unref(s->target);
block_job_completed(&s->common, data->ret);
g_free(data);
@@ -744,6 +745,9 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
block_job_release(bs);
return;
}
bdrv_op_block_all(s->target, s->common.blocker);
bdrv_set_enable_write_cache(s->target, true);
if (s->target->blk) {
blk_set_on_error(s->target->blk, on_target_error, on_target_error);
+1 -1
View File
@@ -64,7 +64,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
info->backing_file_depth = bdrv_get_backing_file_depth(bs);
info->detect_zeroes = bs->detect_zeroes;
if (bs->io_limits_enabled) {
if (bs->throttle_state) {
ThrottleConfig cfg;
throttle_group_get_config(bs, &cfg);
+9 -6
View File
@@ -312,7 +312,7 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
if (!offset)
return 0;
assert(qcow2_get_cluster_type(first_entry) != QCOW2_CLUSTER_COMPRESSED);
assert(qcow2_get_cluster_type(first_entry) == QCOW2_CLUSTER_NORMAL);
for (i = 0; i < nb_clusters; i++) {
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
@@ -324,14 +324,16 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
return i;
}
static int count_contiguous_free_clusters(int nb_clusters, uint64_t *l2_table)
static int count_contiguous_clusters_by_type(int nb_clusters,
uint64_t *l2_table,
int wanted_type)
{
int i;
for (i = 0; i < nb_clusters; i++) {
int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
if (type != QCOW2_CLUSTER_UNALLOCATED) {
if (type != wanted_type) {
break;
}
}
@@ -554,13 +556,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
ret = -EIO;
goto fail;
}
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
&l2_table[l2_index], QCOW_OFLAG_ZERO);
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
QCOW2_CLUSTER_ZERO);
*cluster_offset = 0;
break;
case QCOW2_CLUSTER_UNALLOCATED:
/* how many empty clusters ? */
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
QCOW2_CLUSTER_UNALLOCATED);
*cluster_offset = 0;
break;
case QCOW2_CLUSTER_NORMAL:
+7 -4
View File
@@ -560,13 +560,16 @@ static int alloc_refcount_block(BlockDriverState *bs,
}
/* Hook up the new refcount table in the qcow2 header */
uint8_t data[12];
cpu_to_be64w((uint64_t*)data, table_offset);
cpu_to_be32w((uint32_t*)(data + 8), table_clusters);
struct QEMU_PACKED {
uint64_t d64;
uint32_t d32;
} data;
cpu_to_be64w(&data.d64, table_offset);
cpu_to_be32w(&data.d32, table_clusters);
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE);
ret = bdrv_pwrite_sync(bs->file->bs,
offsetof(QCowHeader, refcount_table_offset),
data, sizeof(data));
&data, sizeof(data));
if (ret < 0) {
goto fail_table;
}
+7
View File
@@ -437,6 +437,9 @@ void throttle_group_register_bs(BlockDriverState *bs, const char *groupname)
* list, destroying the timers and setting the throttle_state pointer
* to NULL.
*
* The BlockDriverState must not have pending throttled requests, so
* the caller has to drain them first.
*
* The group will be destroyed if it's empty after this operation.
*
* @bs: the BlockDriverState to remove
@@ -446,6 +449,10 @@ void throttle_group_unregister_bs(BlockDriverState *bs)
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
int i;
assert(bs->pending_reqs[0] == 0 && bs->pending_reqs[1] == 0);
assert(qemu_co_queue_empty(&bs->throttled_reqs[0]));
assert(qemu_co_queue_empty(&bs->throttled_reqs[1]));
qemu_mutex_lock(&tg->lock);
for (i = 0; i < 2; i++) {
if (tg->tokens[i] == bs) {
+419 -158
View File
File diff suppressed because it is too large Load Diff
+17 -3
View File
@@ -194,8 +194,8 @@ ETEXI
{
.name = "change",
.args_type = "device:B,target:F,arg:s?",
.params = "device filename [format]",
.args_type = "device:B,target:F,arg:s?,read-only-mode:s?",
.params = "device filename [format [read-only-mode]]",
.help = "change a removable medium, optional format",
.mhandler.cmd = hmp_change,
},
@@ -206,7 +206,7 @@ STEXI
Change the configuration of a device.
@table @option
@item change @var{diskdevice} @var{filename} [@var{format}]
@item change @var{diskdevice} @var{filename} [@var{format} [@var{read-only-mode}]]
Change the medium for a removable disk device to point to @var{filename}. eg
@example
@@ -215,6 +215,20 @@ Change the medium for a removable disk device to point to @var{filename}. eg
@var{format} is optional.
@var{read-only-mode} may be used to change the read-only status of the device.
It accepts the following values:
@table @var
@item retain
Retains the current status; this is the default.
@item read-only
Makes the device read-only.
@item read-write
Makes the device writable.
@end table
@item change vnc @var{display},@var{options}
Change the configuration of the VNC server. The valid syntax for @var{display}
and @var{options} are described at @ref{sec_invocation}. eg
+35 -12
View File
@@ -27,6 +27,7 @@
#include "qapi/opts-visitor.h"
#include "qapi/qmp/qerror.h"
#include "qapi/string-output-visitor.h"
#include "qapi/util.h"
#include "qapi-visit.h"
#include "ui/console.h"
#include "block/qapi.h"
@@ -1343,24 +1344,46 @@ void hmp_change(Monitor *mon, const QDict *qdict)
const char *device = qdict_get_str(qdict, "device");
const char *target = qdict_get_str(qdict, "target");
const char *arg = qdict_get_try_str(qdict, "arg");
const char *read_only = qdict_get_try_str(qdict, "read-only-mode");
BlockdevChangeReadOnlyMode read_only_mode = 0;
Error *err = NULL;
if (strcmp(device, "vnc") == 0 &&
(strcmp(target, "passwd") == 0 ||
strcmp(target, "password") == 0)) {
if (!arg) {
monitor_read_password(mon, hmp_change_read_arg, NULL);
if (strcmp(device, "vnc") == 0) {
if (read_only) {
monitor_printf(mon,
"Parameter 'read-only-mode' is invalid for VNC\n");
return;
}
if (strcmp(target, "passwd") == 0 ||
strcmp(target, "password") == 0) {
if (!arg) {
monitor_read_password(mon, hmp_change_read_arg, NULL);
return;
}
}
qmp_change("vnc", target, !!arg, arg, &err);
} else {
if (read_only) {
read_only_mode =
qapi_enum_parse(BlockdevChangeReadOnlyMode_lookup,
read_only, BLOCKDEV_CHANGE_READ_ONLY_MODE_MAX,
BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, &err);
if (err) {
hmp_handle_error(mon, &err);
return;
}
}
qmp_blockdev_change_medium(device, target, !!arg, arg,
!!read_only, read_only_mode, &err);
if (err &&
error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) {
error_free(err);
monitor_read_block_device_key(mon, device, NULL, NULL);
return;
}
}
qmp_change(device, target, !!arg, arg, &err);
if (err &&
error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) {
error_free(err);
monitor_read_block_device_key(mon, device, NULL, NULL);
return;
}
hmp_handle_error(mon, &err);
}
+6 -1
View File
@@ -390,7 +390,10 @@ struct BlockDriverState {
/* number of in-flight serialising requests */
unsigned int serialising_in_flight;
/* I/O throttling */
/* I/O throttling.
* throttle_state tells us if this BDS has I/O limits configured.
* io_limits_enabled tells us if they are currently being
* enforced, but it can be temporarily set to false */
CoQueue throttled_reqs[2];
bool io_limits_enabled;
/* The following fields are protected by the ThrottleGroup lock.
@@ -473,6 +476,8 @@ extern BlockDriver bdrv_file;
extern BlockDriver bdrv_raw;
extern BlockDriver bdrv_qcow2;
extern QTAILQ_HEAD(BdrvStates, BlockDriverState) bdrv_states;
/**
* bdrv_setup_io_funcs:
*
+4
View File
@@ -65,6 +65,7 @@ BlockBackend *blk_new_with_bs(const char *name, Error **errp);
BlockBackend *blk_new_open(const char *name, const char *filename,
const char *reference, QDict *options, int flags,
Error **errp);
int blk_get_refcnt(BlockBackend *blk);
void blk_ref(BlockBackend *blk);
void blk_unref(BlockBackend *blk);
const char *blk_name(BlockBackend *blk);
@@ -72,6 +73,7 @@ BlockBackend *blk_by_name(const char *name);
BlockBackend *blk_next(BlockBackend *blk);
BlockDriverState *blk_bs(BlockBackend *blk);
void blk_remove_bs(BlockBackend *blk);
void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs);
void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk);
@@ -166,6 +168,8 @@ void blk_io_unplug(BlockBackend *blk);
BlockAcctStats *blk_get_stats(BlockBackend *blk);
BlockBackendRootState *blk_get_root_state(BlockBackend *blk);
void blk_update_root_state(BlockBackend *blk);
void blk_apply_root_state(BlockBackend *blk, BlockDriverState *bs);
int blk_get_open_flags_from_root_state(BlockBackend *blk);
void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
BlockCompletionFunc *cb, void *opaque);
-2
View File
@@ -63,8 +63,6 @@ DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type);
/* device-hotplug */
void qmp_change_blockdev(const char *device, const char *filename,
const char *format, Error **errp);
void hmp_commit(Monitor *mon, const QDict *qdict);
void hmp_drive_del(Monitor *mon, const QDict *qdict);
#endif
+7 -3
View File
@@ -1545,10 +1545,12 @@
# abort since 1.6
# blockdev-snapshot-internal-sync since 1.7
# blockdev-backup since 2.3
# blockdev-snapshot since 2.5
##
{ 'union': 'TransactionAction',
'data': {
'blockdev-snapshot-sync': 'BlockdevSnapshot',
'blockdev-snapshot': 'BlockdevSnapshot',
'blockdev-snapshot-sync': 'BlockdevSnapshotSync',
'drive-backup': 'DriveBackup',
'blockdev-backup': 'BlockdevBackup',
'abort': 'Abort',
@@ -1856,8 +1858,10 @@
# device's password. The behavior of reads and writes to the block
# device between when these calls are executed is undefined.
#
# Notes: It is strongly recommended that this interface is not used especially
# for changing block devices.
# Notes: This interface is deprecated, and it is strongly recommended that you
# avoid using it. For changing block devices, use
# blockdev-change-medium; for changing VNC parameters, use
# change-vnc-password.
#
# Since: 0.14.0
##
+187 -5
View File
@@ -682,7 +682,7 @@
'data': [ 'existing', 'absolute-paths' ] }
##
# @BlockdevSnapshot
# @BlockdevSnapshotSync
#
# Either @device or @node-name must be set but not both.
#
@@ -699,11 +699,26 @@
# @mode: #optional whether and how QEMU should create a new image, default is
# 'absolute-paths'.
##
{ 'struct': 'BlockdevSnapshot',
{ 'struct': 'BlockdevSnapshotSync',
'data': { '*device': 'str', '*node-name': 'str',
'snapshot-file': 'str', '*snapshot-node-name': 'str',
'*format': 'str', '*mode': 'NewImageMode' } }
##
# @BlockdevSnapshot
#
# @node: device or node name that will have a snapshot created.
#
# @overlay: reference to the existing block device that will become
# the overlay of @node, as part of creating the snapshot.
# It must not have a current backing file (this can be
# achieved by passing "backing": "" to blockdev-add).
#
# Since 2.5
##
{ 'struct': 'BlockdevSnapshot',
'data': { 'node': 'str', 'overlay': 'str' } }
##
# @DriveBackup
#
@@ -790,7 +805,7 @@
#
# Generates a synchronous snapshot of a block device.
#
# For the arguments, see the documentation of BlockdevSnapshot.
# For the arguments, see the documentation of BlockdevSnapshotSync.
#
# Returns: nothing on success
# If @device is not a valid block device, DeviceNotFound
@@ -798,6 +813,19 @@
# Since 0.14.0
##
{ 'command': 'blockdev-snapshot-sync',
'data': 'BlockdevSnapshotSync' }
##
# @blockdev-snapshot
#
# Generates a snapshot of a block device.
#
# For the arguments, see the documentation of BlockdevSnapshot.
#
# Since 2.5
##
{ 'command': 'blockdev-snapshot',
'data': 'BlockdevSnapshot' }
##
@@ -1867,8 +1895,8 @@
# level and no BlockBackend will be created.
#
# This command is still a work in progress. It doesn't support all
# block drivers, it lacks a matching blockdev-del, and more. Stay
# away from it unless you want to help with its development.
# block drivers among other things. Stay away from it unless you want
# to help with its development.
#
# @options: block device options for the new device
#
@@ -1876,6 +1904,160 @@
##
{ 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
##
# @x-blockdev-del:
#
# Deletes a block device that has been added using blockdev-add.
# The selected device can be either a block backend or a graph node.
#
# In the former case the backend will be destroyed, along with its
# inserted medium if there's any. The command will fail if the backend
# or its medium are in use.
#
# In the latter case the node will be destroyed. The command will fail
# if the node is attached to a block backend or is otherwise being
# used.
#
# One of @id or @node-name must be specified, but not both.
#
# This command is still a work in progress and is considered
# experimental. Stay away from it unless you want to help with its
# development.
#
# @id: #optional Name of the block backend device to delete.
#
# @node-name: #optional Name of the graph node to delete.
#
# Since: 2.5
##
{ 'command': 'x-blockdev-del', 'data': { '*id': 'str', '*node-name': 'str' } }
##
# @blockdev-open-tray:
#
# Opens a block device's tray. If there is a block driver state tree inserted as
# a medium, it will become inaccessible to the guest (but it will remain
# associated to the block device, so closing the tray will make it accessible
# again).
#
# If the tray was already open before, this will be a no-op.
#
# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in
# which no such event will be generated, these include:
# - if the guest has locked the tray, @force is false and the guest does not
# respond to the eject request
# - if the BlockBackend denoted by @device does not have a guest device attached
# to it
# - if the guest device does not have an actual tray and is empty, for instance
# for floppy disk drives
#
# @device: block device name
#
# @force: #optional if false (the default), an eject request will be sent to
# the guest if it has locked the tray (and the tray will not be opened
# immediately); if true, the tray will be opened regardless of whether
# it is locked
#
# Since: 2.5
##
{ 'command': 'blockdev-open-tray',
'data': { 'device': 'str',
'*force': 'bool' } }
##
# @blockdev-close-tray:
#
# Closes a block device's tray. If there is a block driver state tree associated
# with the block device (which is currently ejected), that tree will be loaded
# as the medium.
#
# If the tray was already closed before, this will be a no-op.
#
# @device: block device name
#
# Since: 2.5
##
{ 'command': 'blockdev-close-tray',
'data': { 'device': 'str' } }
##
# @blockdev-remove-medium:
#
# Removes a medium (a block driver state tree) from a block device. That block
# device's tray must currently be open (unless there is no attached guest
# device).
#
# If the tray is open and there is no medium inserted, this will be a no-op.
#
# @device: block device name
#
# Since: 2.5
##
{ 'command': 'blockdev-remove-medium',
'data': { 'device': 'str' } }
##
# @blockdev-insert-medium:
#
# Inserts a medium (a block driver state tree) into a block device. That block
# device's tray must currently be open (unless there is no attached guest
# device) and there must be no medium inserted already.
#
# @device: block device name
#
# @node-name: name of a node in the block driver state graph
#
# Since: 2.5
##
{ 'command': 'blockdev-insert-medium',
'data': { 'device': 'str',
'node-name': 'str'} }
##
# @BlockdevChangeReadOnlyMode:
#
# Specifies the new read-only mode of a block device subject to the
# @blockdev-change-medium command.
#
# @retain: Retains the current read-only mode
#
# @read-only: Makes the device read-only
#
# @read-write: Makes the device writable
#
# Since: 2.3
##
{ 'enum': 'BlockdevChangeReadOnlyMode',
'data': ['retain', 'read-only', 'read-write'] }
##
# @blockdev-change-medium:
#
# Changes the medium inserted into a block device by ejecting the current medium
# and loading a new image file which is inserted as the new medium (this command
# combines blockdev-open-tray, blockdev-remove-medium, blockdev-insert-medium
# and blockdev-close-tray).
#
# @device: block device name
#
# @filename: filename of the new image to be loaded
#
# @format: #optional, format to open the new image with (defaults to
# the probed format)
#
# @read-only-mode: #optional, change the read-only mode of the device; defaults
# to 'retain'
#
# Since: 2.5
##
{ 'command': 'blockdev-change-medium',
'data': { 'device': 'str',
'filename': 'str',
'*format': 'str',
'*read-only-mode': 'BlockdevChangeReadOnlyMode' } }
##
# @BlockErrorAction
+2 -1
View File
@@ -656,7 +656,8 @@ static void run_block_job(BlockJob *job, Error **errp)
do {
aio_poll(aio_context, true);
qemu_progress_print((float)job->offset / job->len * 100.f, 0);
qemu_progress_print(job->len ?
((float)job->offset / job->len * 100.f) : 0.0f, 0);
} while (!job->ready);
block_job_complete_sync(job, errp);
+131 -56
View File
@@ -136,7 +136,29 @@ static char **breakline(char *input, int *count)
static int64_t cvtnum(const char *s)
{
char *end;
return qemu_strtosz_suffix(s, &end, QEMU_STRTOSZ_DEFSUFFIX_B);
int64_t ret;
ret = qemu_strtosz_suffix(s, &end, QEMU_STRTOSZ_DEFSUFFIX_B);
if (*end != '\0') {
/* Detritus at the end of the string */
return -EINVAL;
}
return ret;
}
static void print_cvtnum_err(int64_t rc, const char *arg)
{
switch (rc) {
case -EINVAL:
printf("Parsing error: non-numeric argument,"
" or extraneous/unrecognized suffix -- %s\n", arg);
break;
case -ERANGE:
printf("Parsing error: argument too large -- %s\n", arg);
break;
default:
printf("Parsing error: %s\n", arg);
}
}
#define EXABYTES(x) ((long long)(x) << 60)
@@ -294,9 +316,10 @@ static void qemu_io_free(void *p)
qemu_vfree(p);
}
static void dump_buffer(const void *buffer, int64_t offset, int len)
static void dump_buffer(const void *buffer, int64_t offset, int64_t len)
{
int i, j;
uint64_t i;
int j;
const uint8_t *p;
for (i = 0, p = buffer; i < len; i += 16) {
@@ -319,7 +342,7 @@ static void dump_buffer(const void *buffer, int64_t offset, int len)
}
static void print_report(const char *op, struct timeval *t, int64_t offset,
int count, int total, int cnt, int Cflag)
int64_t count, int64_t total, int cnt, int Cflag)
{
char s1[64], s2[64], ts[64];
@@ -327,12 +350,12 @@ static void print_report(const char *op, struct timeval *t, int64_t offset,
if (!Cflag) {
cvtstr((double)total, s1, sizeof(s1));
cvtstr(tdiv((double)total, *t), s2, sizeof(s2));
printf("%s %d/%d bytes at offset %" PRId64 "\n",
printf("%s %"PRId64"/%"PRId64" bytes at offset %" PRId64 "\n",
op, total, count, offset);
printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n",
s1, cnt, ts, s2, tdiv((double)cnt, *t));
} else {/* bytes,ops,time,bytes/sec,ops/sec */
printf("%d,%d,%s,%.3f,%.3f\n",
printf("%"PRId64",%d,%s,%.3f,%.3f\n",
total, cnt, ts,
tdiv((double)total, *t),
tdiv((double)cnt, *t));
@@ -359,13 +382,13 @@ create_iovec(BlockBackend *blk, QEMUIOVector *qiov, char **argv, int nr_iov,
len = cvtnum(arg);
if (len < 0) {
printf("non-numeric length argument -- %s\n", arg);
print_cvtnum_err(len, arg);
goto fail;
}
/* should be SIZE_T_MAX, but that doesn't exist */
if (len > INT_MAX) {
printf("too large length argument -- %s\n", arg);
printf("Argument '%s' exceeds maximum size %d\n", arg, INT_MAX);
goto fail;
}
@@ -393,11 +416,15 @@ fail:
return buf;
}
static int do_read(BlockBackend *blk, char *buf, int64_t offset, int count,
int *total)
static int do_read(BlockBackend *blk, char *buf, int64_t offset, int64_t count,
int64_t *total)
{
int ret;
if (count >> 9 > INT_MAX) {
return -ERANGE;
}
ret = blk_read(blk, offset >> 9, (uint8_t *)buf, count >> 9);
if (ret < 0) {
return ret;
@@ -406,11 +433,15 @@ static int do_read(BlockBackend *blk, char *buf, int64_t offset, int count,
return 1;
}
static int do_write(BlockBackend *blk, char *buf, int64_t offset, int count,
int *total)
static int do_write(BlockBackend *blk, char *buf, int64_t offset, int64_t count,
int64_t *total)
{
int ret;
if (count >> 9 > INT_MAX) {
return -ERANGE;
}
ret = blk_write(blk, offset >> 9, (uint8_t *)buf, count >> 9);
if (ret < 0) {
return ret;
@@ -419,9 +450,13 @@ static int do_write(BlockBackend *blk, char *buf, int64_t offset, int count,
return 1;
}
static int do_pread(BlockBackend *blk, char *buf, int64_t offset, int count,
int *total)
static int do_pread(BlockBackend *blk, char *buf, int64_t offset,
int64_t count, int64_t *total)
{
if (count > INT_MAX) {
return -ERANGE;
}
*total = blk_pread(blk, offset, (uint8_t *)buf, count);
if (*total < 0) {
return *total;
@@ -429,9 +464,13 @@ static int do_pread(BlockBackend *blk, char *buf, int64_t offset, int count,
return 1;
}
static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, int count,
int *total)
static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset,
int64_t count, int64_t *total)
{
if (count > INT_MAX) {
return -ERANGE;
}
*total = blk_pwrite(blk, offset, (uint8_t *)buf, count);
if (*total < 0) {
return *total;
@@ -442,8 +481,8 @@ static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, int count,
typedef struct {
BlockBackend *blk;
int64_t offset;
int count;
int *total;
int64_t count;
int64_t *total;
int ret;
bool done;
} CoWriteZeroes;
@@ -463,8 +502,8 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque)
*data->total = data->count;
}
static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int count,
int *total)
static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int64_t count,
int64_t *total)
{
Coroutine *co;
CoWriteZeroes data = {
@@ -475,6 +514,10 @@ static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int count,
.done = false,
};
if (count >> BDRV_SECTOR_BITS > INT_MAX) {
return -ERANGE;
}
co = qemu_coroutine_create(co_write_zeroes_entry);
qemu_coroutine_enter(co, &data);
while (!data.done) {
@@ -488,10 +531,14 @@ static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int count,
}
static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset,
int count, int *total)
int64_t count, int64_t *total)
{
int ret;
if (count >> 9 > INT_MAX) {
return -ERANGE;
}
ret = blk_write_compressed(blk, offset >> 9, (uint8_t *)buf, count >> 9);
if (ret < 0) {
return ret;
@@ -501,8 +548,12 @@ static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset,
}
static int do_load_vmstate(BlockBackend *blk, char *buf, int64_t offset,
int count, int *total)
int64_t count, int64_t *total)
{
if (count > INT_MAX) {
return -ERANGE;
}
*total = blk_load_vmstate(blk, (uint8_t *)buf, offset, count);
if (*total < 0) {
return *total;
@@ -511,8 +562,12 @@ static int do_load_vmstate(BlockBackend *blk, char *buf, int64_t offset,
}
static int do_save_vmstate(BlockBackend *blk, char *buf, int64_t offset,
int count, int *total)
int64_t count, int64_t *total)
{
if (count > INT_MAX) {
return -ERANGE;
}
*total = blk_save_vmstate(blk, (uint8_t *)buf, offset, count);
if (*total < 0) {
return *total;
@@ -642,10 +697,11 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
int c, cnt;
char *buf;
int64_t offset;
int count;
int64_t count;
/* Some compilers get confused and warn if this is not initialized. */
int total = 0;
int pattern = 0, pattern_offset = 0, pattern_count = 0;
int64_t total = 0;
int pattern = 0;
int64_t pattern_offset = 0, pattern_count = 0;
while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != -1) {
switch (c) {
@@ -659,7 +715,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
lflag = 1;
pattern_count = cvtnum(optarg);
if (pattern_count < 0) {
printf("non-numeric length argument -- %s\n", optarg);
print_cvtnum_err(pattern_count, optarg);
return 0;
}
break;
@@ -680,7 +736,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
sflag = 1;
pattern_offset = cvtnum(optarg);
if (pattern_offset < 0) {
printf("non-numeric length argument -- %s\n", optarg);
print_cvtnum_err(pattern_offset, optarg);
return 0;
}
break;
@@ -703,14 +759,18 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
offset = cvtnum(argv[optind]);
if (offset < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(offset, argv[optind]);
return 0;
}
optind++;
count = cvtnum(argv[optind]);
if (count < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(count, argv[optind]);
return 0;
} else if (count > SIZE_MAX) {
printf("length cannot exceed %" PRIu64 ", given %s\n",
(uint64_t) SIZE_MAX, argv[optind]);
return 0;
}
@@ -734,7 +794,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
return 0;
}
if (count & 0x1ff) {
printf("count %d is not sector aligned\n",
printf("count %"PRId64" is not sector aligned\n",
count);
return 0;
}
@@ -762,7 +822,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
memset(cmp_buf, pattern, pattern_count);
if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) {
printf("Pattern verification failed at offset %"
PRId64 ", %d bytes\n",
PRId64 ", %"PRId64" bytes\n",
offset + pattern_offset, pattern_count);
}
g_free(cmp_buf);
@@ -861,7 +921,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
offset = cvtnum(argv[optind]);
if (offset < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(offset, argv[optind]);
return 0;
}
optind++;
@@ -957,9 +1017,9 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
int c, cnt;
char *buf = NULL;
int64_t offset;
int count;
int64_t count;
/* Some compilers get confused and warn if this is not initialized. */
int total = 0;
int64_t total = 0;
int pattern = 0xcd;
while ((c = getopt(argc, argv, "bcCpP:qz")) != -1) {
@@ -1010,14 +1070,18 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
offset = cvtnum(argv[optind]);
if (offset < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(offset, argv[optind]);
return 0;
}
optind++;
count = cvtnum(argv[optind]);
if (count < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(count, argv[optind]);
return 0;
} else if (count > SIZE_MAX) {
printf("length cannot exceed %" PRIu64 ", given %s\n",
(uint64_t) SIZE_MAX, argv[optind]);
return 0;
}
@@ -1029,7 +1093,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
}
if (count & 0x1ff) {
printf("count %d is not sector aligned\n",
printf("count %"PRId64" is not sector aligned\n",
count);
return 0;
}
@@ -1142,7 +1206,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv)
offset = cvtnum(argv[optind]);
if (offset < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(offset, argv[optind]);
return 0;
}
optind++;
@@ -1269,7 +1333,7 @@ static int multiwrite_f(BlockBackend *blk, int argc, char **argv)
/* Read the offset of the request */
offset = cvtnum(argv[optind]);
if (offset < 0) {
printf("non-numeric offset argument -- %s\n", argv[optind]);
print_cvtnum_err(offset, argv[optind]);
goto out;
}
optind++;
@@ -1496,7 +1560,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv)
ctx->offset = cvtnum(argv[optind]);
if (ctx->offset < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(ctx->offset, argv[optind]);
g_free(ctx);
return 0;
}
@@ -1591,7 +1655,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
ctx->offset = cvtnum(argv[optind]);
if (ctx->offset < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(ctx->offset, argv[optind]);
g_free(ctx);
return 0;
}
@@ -1651,7 +1715,7 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv)
offset = cvtnum(argv[1]);
if (offset < 0) {
printf("non-numeric truncate argument -- %s\n", argv[1]);
print_cvtnum_err(offset, argv[1]);
return 0;
}
@@ -1777,8 +1841,7 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
struct timeval t1, t2;
int Cflag = 0, qflag = 0;
int c, ret;
int64_t offset;
int count;
int64_t offset, count;
while ((c = getopt(argc, argv, "Cq")) != -1) {
switch (c) {
@@ -1799,14 +1862,19 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
offset = cvtnum(argv[optind]);
if (offset < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(offset, argv[optind]);
return 0;
}
optind++;
count = cvtnum(argv[optind]);
if (count < 0) {
printf("non-numeric length argument -- %s\n", argv[optind]);
print_cvtnum_err(count, argv[optind]);
return 0;
} else if (count >> BDRV_SECTOR_BITS > INT_MAX) {
printf("length cannot exceed %"PRIu64", given %s\n",
(uint64_t)INT_MAX << BDRV_SECTOR_BITS,
argv[optind]);
return 0;
}
@@ -1833,15 +1901,14 @@ out:
static int alloc_f(BlockBackend *blk, int argc, char **argv)
{
BlockDriverState *bs = blk_bs(blk);
int64_t offset, sector_num;
int nb_sectors, remaining;
int64_t offset, sector_num, nb_sectors, remaining;
char s1[64];
int num, sum_alloc;
int ret;
int num, ret;
int64_t sum_alloc;
offset = cvtnum(argv[1]);
if (offset < 0) {
printf("non-numeric offset argument -- %s\n", argv[1]);
print_cvtnum_err(offset, argv[1]);
return 0;
} else if (offset & 0x1ff) {
printf("offset %" PRId64 " is not sector aligned\n",
@@ -1852,7 +1919,11 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
if (argc == 3) {
nb_sectors = cvtnum(argv[2]);
if (nb_sectors < 0) {
printf("non-numeric length argument -- %s\n", argv[2]);
print_cvtnum_err(nb_sectors, argv[2]);
return 0;
} else if (nb_sectors > INT_MAX) {
printf("length argument cannot exceed %d, given %s\n",
INT_MAX, argv[2]);
return 0;
}
} else {
@@ -1881,7 +1952,7 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
cvtstr(offset, s1, sizeof(s1));
printf("%d/%d sectors allocated at offset %s\n",
printf("%"PRId64"/%"PRId64" sectors allocated at offset %s\n",
sum_alloc, nb_sectors, s1);
return 0;
}
@@ -2191,9 +2262,13 @@ static const cmdinfo_t sigraise_cmd = {
static int sigraise_f(BlockBackend *blk, int argc, char **argv)
{
int sig = cvtnum(argv[1]);
int64_t sig = cvtnum(argv[1]);
if (sig < 0) {
printf("non-numeric signal number argument -- %s\n", argv[1]);
print_cvtnum_err(sig, argv[1]);
return 0;
} else if (sig > NSIG) {
printf("signal argument '%s' is too large to be a valid signal\n",
argv[1]);
return 0;
}
+315 -2
View File
@@ -1492,6 +1492,44 @@ Example:
"format": "qcow2" } }
<- { "return": {} }
EQMP
{
.name = "blockdev-snapshot",
.args_type = "node:s,overlay:s",
.mhandler.cmd_new = qmp_marshal_blockdev_snapshot,
},
SQMP
blockdev-snapshot
-----------------
Since 2.5
Create a snapshot, by installing 'node' as the backing image of
'overlay'. Additionally, if 'node' is associated with a block
device, the block device changes to using 'overlay' as its new active
image.
Arguments:
- "node": device that will have a snapshot created (json-string)
- "overlay": device that will have 'node' as its backing image (json-string)
Example:
-> { "execute": "blockdev-add",
"arguments": { "options": { "driver": "qcow2",
"node-name": "node1534",
"file": { "driver": "file",
"filename": "hd1.qcow2" },
"backing": "" } } }
<- { "return": {} }
-> { "execute": "blockdev-snapshot", "arguments": { "node": "ide-hd0",
"overlay": "node1534" } }
<- { "return": {} }
EQMP
{
@@ -3908,8 +3946,8 @@ blockdev-add
Add a block device.
This command is still a work in progress. It doesn't support all
block drivers, it lacks a matching blockdev-del, and more. Stay away
from it unless you want to help with its development.
block drivers among other things. Stay away from it unless you want
to help with its development.
Arguments:
@@ -3952,6 +3990,228 @@ Example (2):
<- { "return": {} }
EQMP
{
.name = "x-blockdev-del",
.args_type = "id:s?,node-name:s?",
.mhandler.cmd_new = qmp_marshal_x_blockdev_del,
},
SQMP
x-blockdev-del
------------
Since 2.5
Deletes a block device thas has been added using blockdev-add.
The selected device can be either a block backend or a graph node.
In the former case the backend will be destroyed, along with its
inserted medium if there's any. The command will fail if the backend
or its medium are in use.
In the latter case the node will be destroyed. The command will fail
if the node is attached to a block backend or is otherwise being
used.
One of "id" or "node-name" must be specified, but not both.
This command is still a work in progress and is considered
experimental. Stay away from it unless you want to help with its
development.
Arguments:
- "id": Name of the block backend device to delete (json-string, optional)
- "node-name": Name of the graph node to delete (json-string, optional)
Example:
-> { "execute": "blockdev-add",
"arguments": {
"options": {
"driver": "qcow2",
"id": "drive0",
"file": {
"driver": "file",
"filename": "test.qcow2"
}
}
}
}
<- { "return": {} }
-> { "execute": "x-blockdev-del",
"arguments": { "id": "drive0" }
}
<- { "return": {} }
EQMP
{
.name = "blockdev-open-tray",
.args_type = "device:s,force:b?",
.mhandler.cmd_new = qmp_marshal_blockdev_open_tray,
},
SQMP
blockdev-open-tray
------------------
Opens a block device's tray. If there is a block driver state tree inserted as a
medium, it will become inaccessible to the guest (but it will remain associated
to the block device, so closing the tray will make it accessible again).
If the tray was already open before, this will be a no-op.
Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in
which no such event will be generated, these include:
- if the guest has locked the tray, @force is false and the guest does not
respond to the eject request
- if the BlockBackend denoted by @device does not have a guest device attached
to it
- if the guest device does not have an actual tray and is empty, for instance
for floppy disk drives
Arguments:
- "device": block device name (json-string)
- "force": if false (the default), an eject request will be sent to the guest if
it has locked the tray (and the tray will not be opened immediately);
if true, the tray will be opened regardless of whether it is locked
(json-bool, optional)
Example:
-> { "execute": "blockdev-open-tray",
"arguments": { "device": "ide1-cd0" } }
<- { "timestamp": { "seconds": 1418751016,
"microseconds": 716996 },
"event": "DEVICE_TRAY_MOVED",
"data": { "device": "ide1-cd0",
"tray-open": true } }
<- { "return": {} }
EQMP
{
.name = "blockdev-close-tray",
.args_type = "device:s",
.mhandler.cmd_new = qmp_marshal_blockdev_close_tray,
},
SQMP
blockdev-close-tray
-------------------
Closes a block device's tray. If there is a block driver state tree associated
with the block device (which is currently ejected), that tree will be loaded as
the medium.
If the tray was already closed before, this will be a no-op.
Arguments:
- "device": block device name (json-string)
Example:
-> { "execute": "blockdev-close-tray",
"arguments": { "device": "ide1-cd0" } }
<- { "timestamp": { "seconds": 1418751345,
"microseconds": 272147 },
"event": "DEVICE_TRAY_MOVED",
"data": { "device": "ide1-cd0",
"tray-open": false } }
<- { "return": {} }
EQMP
{
.name = "blockdev-remove-medium",
.args_type = "device:s",
.mhandler.cmd_new = qmp_marshal_blockdev_remove_medium,
},
SQMP
blockdev-remove-medium
----------------------
Removes a medium (a block driver state tree) from a block device. That block
device's tray must currently be open (unless there is no attached guest device).
If the tray is open and there is no medium inserted, this will be a no-op.
Arguments:
- "device": block device name (json-string)
Example:
-> { "execute": "blockdev-remove-medium",
"arguments": { "device": "ide1-cd0" } }
<- { "error": { "class": "GenericError",
"desc": "Tray of device 'ide1-cd0' is not open" } }
-> { "execute": "blockdev-open-tray",
"arguments": { "device": "ide1-cd0" } }
<- { "timestamp": { "seconds": 1418751627,
"microseconds": 549958 },
"event": "DEVICE_TRAY_MOVED",
"data": { "device": "ide1-cd0",
"tray-open": true } }
<- { "return": {} }
-> { "execute": "blockdev-remove-medium",
"arguments": { "device": "ide1-cd0" } }
<- { "return": {} }
EQMP
{
.name = "blockdev-insert-medium",
.args_type = "device:s,node-name:s",
.mhandler.cmd_new = qmp_marshal_blockdev_insert_medium,
},
SQMP
blockdev-insert-medium
----------------------
Inserts a medium (a block driver state tree) into a block device. That block
device's tray must currently be open (unless there is no attached guest device)
and there must be no medium inserted already.
Arguments:
- "device": block device name (json-string)
- "node-name": root node of the BDS tree to insert into the block device
Example:
-> { "execute": "blockdev-add",
"arguments": { "options": { "node-name": "node0",
"driver": "raw",
"file": { "driver": "file",
"filename": "fedora.iso" } } } }
<- { "return": {} }
-> { "execute": "blockdev-insert-medium",
"arguments": { "device": "ide1-cd0",
"node-name": "node0" } }
<- { "return": {} }
EQMP
{
@@ -4014,6 +4274,59 @@ Example:
}
} } ] }
EQMP
{
.name = "blockdev-change-medium",
.args_type = "device:B,filename:F,format:s?,read-only-mode:s?",
.mhandler.cmd_new = qmp_marshal_blockdev_change_medium,
},
SQMP
blockdev-change-medium
----------------------
Changes the medium inserted into a block device by ejecting the current medium
and loading a new image file which is inserted as the new medium.
Arguments:
- "device": device name (json-string)
- "filename": filename of the new image (json-string)
- "format": format of the new image (json-string, optional)
- "read-only-mode": new read-only mode (json-string, optional)
- Possible values: "retain" (default), "read-only", "read-write"
Examples:
1. Change a removable medium
-> { "execute": "blockdev-change-medium",
"arguments": { "device": "ide1-cd0",
"filename": "/srv/images/Fedora-12-x86_64-DVD.iso",
"format": "raw" } }
<- { "return": {} }
2. Load a read-only medium into a writable drive
-> { "execute": "blockdev-change-medium",
"arguments": { "device": "isa-fd0",
"filename": "/srv/images/ro.img",
"format": "raw",
"read-only-mode": "retain" } }
<- { "error":
{ "class": "GenericError",
"desc": "Could not open '/srv/images/ro.img': Permission denied" } }
-> { "execute": "blockdev-change-medium",
"arguments": { "device": "isa-fd0",
"filename": "/srv/images/ro.img",
"format": "raw",
"read-only-mode": "read-only" } }
<- { "return": {} }
EQMP
{
+2 -1
View File
@@ -414,7 +414,8 @@ void qmp_change(const char *device, const char *target,
if (strcmp(device, "vnc") == 0) {
qmp_change_vnc(target, has_arg, arg, errp);
} else {
qmp_change_blockdev(device, target, arg, errp);
qmp_blockdev_change_medium(device, target, has_arg, arg, false, 0,
errp);
}
}

Some files were not shown because too many files have changed in this diff Show More