mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+17
-3
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
{
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user