Block layer core and image format patches
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJVevYFAAoJEH8JsnLIjy/W3jEP/0hiQ3rCRZ/he8s5maTdT+TR YSeHkB5rKpz0Uopn1DMn1QrIbUVzX7dyb+uf9zQ0/xRQIzf6k8uxqU/NWrdoF3NK qx91dGWedwnG+TEBIMbcR7nMrw4dP6kH7uPz/VWMXDHVLz0HIcD95qhKgs0mSY6J dWqex6ACjXM68zJU5IioagU9evV80WZE1S8z7zfixxtTBx5hCaTVbwalkaCxcrXw PbZle55rjI8B10+OzgBw0fq10nias+NTndU9CwNBboxmEtAjq8/mQ663vcWlmiFo 9a/hkda27Z5ut/0Tqk1v4uLHauylp++rrAabPBAuCFMKes6cdkddP15Q/r52aJ29 5meodQtbet1rGrM+Aq4vuSuWId71PGypEI/3URDdNfYFNISoeLLsk4lcQUu7VrDD sRX3Jt8SI3nkIgOnhPyi7NDPmafxFt8yRt5vM8MyR5ynF8NS/2hiAc3wqnbXGjUj a5GqDCefb1yM0R5HvksuFFt3OnXlKJQ3J+ksXNUJf9DSAZPauqWD696pcTeg8wyy 3PIGkczgUuKTVfFWd3THZxJLAo7ZuqvXBHHV8o1SeMBDxwh4FhTd8Kjvm3rUNFfl VDox4qwZ1AcxLrxgqazKU7sD9iWBDHURRcpOoUsBys7oxQZnhcmMp1fRlEkTOyrD HNiSNByqBrtkfeVzHlSe =QgYk -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer core and image format patches # gpg: Signature made Fri Jun 12 16:08:53 2015 BST using RSA key ID C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" * remotes/kevin/tags/for-upstream: (25 commits) block: Fix reopen flag inheritance block: Add BlockDriverState.inherits_from block: Add list of children to BlockDriverState queue.h: Add QLIST_FIX_HEAD_PTR() block: Drain requests before swapping nodes in bdrv_swap() block: Move flag inheritance to bdrv_open_inherit() block: Use QemuOpts in bdrv_open_common() block: Use macro for cache option names vmdk: Use bdrv_open_image() quorum: Use bdrv_open_image() check-qdict: Test cases for new functions qdict: Add qdict_{set,copy}_default() qdict: Add qdict_array_entries() iotests: Add tests for overriding BDRV_O_PROTOCOL block: driver should override flags in bdrv_open() block: Change bitmap truncate conditional to assertion block: record new size in bdrv_dirty_bitmap_truncate raw-posix: Fix .bdrv_co_get_block_status() for unaligned image size vmdk: Use vmdk_find_index_in_cluster everywhere vmdk: Fix index_in_cluster calculation in vmdk_co_get_block_status ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
f3e3b083d4
239
block.c
239
block.c
@ -80,6 +80,12 @@ static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states =
|
||||
static QLIST_HEAD(, BlockDriver) bdrv_drivers =
|
||||
QLIST_HEAD_INITIALIZER(bdrv_drivers);
|
||||
|
||||
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
BlockDriverState *parent,
|
||||
const BdrvChildRole *child_role,
|
||||
BlockDriver *drv, Error **errp);
|
||||
|
||||
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
|
||||
/* If non-zero, use only whitelisted block drivers */
|
||||
static int use_bdrv_whitelist;
|
||||
@ -683,8 +689,8 @@ static int bdrv_temp_snapshot_flags(int flags)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->file should get, based on the given flags for
|
||||
* the parent BDS
|
||||
* Returns the flags that bs->file should get if a protocol driver is expected,
|
||||
* based on the given flags for the parent BDS
|
||||
*/
|
||||
static int bdrv_inherited_flags(int flags)
|
||||
{
|
||||
@ -701,6 +707,25 @@ static int bdrv_inherited_flags(int flags)
|
||||
return flags;
|
||||
}
|
||||
|
||||
const BdrvChildRole child_file = {
|
||||
.inherit_flags = bdrv_inherited_flags,
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->file should get if the use of formats (and not
|
||||
* only protocols) is permitted for it, based on the given flags for the parent
|
||||
* BDS
|
||||
*/
|
||||
static int bdrv_inherited_fmt_flags(int parent_flags)
|
||||
{
|
||||
int flags = child_file.inherit_flags(parent_flags);
|
||||
return flags & ~BDRV_O_PROTOCOL;
|
||||
}
|
||||
|
||||
const BdrvChildRole child_format = {
|
||||
.inherit_flags = bdrv_inherited_fmt_flags,
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->backing_hd should get, based on the given flags
|
||||
* for the parent BDS
|
||||
@ -716,6 +741,10 @@ static int bdrv_backing_flags(int flags)
|
||||
return flags;
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_backing = {
|
||||
.inherit_flags = bdrv_backing_flags,
|
||||
};
|
||||
|
||||
static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
{
|
||||
int open_flags = flags | BDRV_O_CACHE_WB;
|
||||
@ -768,6 +797,19 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
|
||||
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
|
||||
}
|
||||
|
||||
static QemuOptsList bdrv_runtime_opts = {
|
||||
.name = "bdrv_common",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "node-name",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Node name of the block device node",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Common part for opening disk images and files
|
||||
*
|
||||
@ -779,6 +821,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
int ret, open_flags;
|
||||
const char *filename;
|
||||
const char *node_name = NULL;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(drv != NULL);
|
||||
@ -799,23 +842,22 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
|
||||
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
|
||||
|
||||
node_name = qdict_get_try_str(options, "node-name");
|
||||
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
node_name = qemu_opt_get(opts, "node-name");
|
||||
bdrv_assign_node_name(bs, node_name, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
qdict_del(options, "node-name");
|
||||
|
||||
/* bdrv_open() with directly using a protocol as drv. This layer is already
|
||||
* opened, so assign it to bs (while file becomes a closed BlockDriverState)
|
||||
* and return immediately. */
|
||||
if (file != NULL && drv->bdrv_file_open) {
|
||||
bdrv_swap(file, bs);
|
||||
return 0;
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
bs->open_flags = flags;
|
||||
bs->guest_block_size = 512;
|
||||
bs->request_alignment = 512;
|
||||
bs->zero_beyond_eof = true;
|
||||
@ -828,7 +870,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
? "Driver '%s' can only be used for read-only devices"
|
||||
: "Driver '%s' is not whitelisted",
|
||||
drv->format_name);
|
||||
return -ENOTSUP;
|
||||
ret = -ENOTSUP;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
|
||||
@ -837,7 +880,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
bdrv_enable_copy_on_read(bs);
|
||||
} else {
|
||||
error_setg(errp, "Can't use copy-on-read on read-only device");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
}
|
||||
|
||||
@ -903,6 +947,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
assert(bdrv_opt_mem_align(bs) != 0);
|
||||
assert(bdrv_min_mem_align(bs) != 0);
|
||||
assert((bs->request_alignment != 0) || bs->sg);
|
||||
|
||||
qemu_opts_del(opts);
|
||||
return 0;
|
||||
|
||||
free_and_fail:
|
||||
@ -910,6 +956,8 @@ free_and_fail:
|
||||
g_free(bs->opaque);
|
||||
bs->opaque = NULL;
|
||||
bs->drv = NULL;
|
||||
fail_opts:
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -943,14 +991,17 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||
/*
|
||||
* Fills in default options for opening images and converts the legacy
|
||||
* filename/flags pair to option QDict entries.
|
||||
* The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a
|
||||
* block driver has been specified explicitly.
|
||||
*/
|
||||
static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
|
||||
BlockDriver *drv, Error **errp)
|
||||
static int bdrv_fill_options(QDict **options, const char **pfilename,
|
||||
int *flags, BlockDriver *drv, Error **errp)
|
||||
{
|
||||
const char *filename = *pfilename;
|
||||
const char *drvname;
|
||||
bool protocol = flags & BDRV_O_PROTOCOL;
|
||||
bool protocol = *flags & BDRV_O_PROTOCOL;
|
||||
bool parse_filename = false;
|
||||
BlockDriver *tmp_drv;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Parse json: pseudo-protocol */
|
||||
@ -968,6 +1019,24 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
|
||||
*pfilename = filename = NULL;
|
||||
}
|
||||
|
||||
drvname = qdict_get_try_str(*options, "driver");
|
||||
|
||||
/* If the user has explicitly specified the driver, this choice should
|
||||
* override the BDRV_O_PROTOCOL flag */
|
||||
tmp_drv = drv;
|
||||
if (!tmp_drv && drvname) {
|
||||
tmp_drv = bdrv_find_format(drvname);
|
||||
}
|
||||
if (tmp_drv) {
|
||||
protocol = tmp_drv->bdrv_file_open;
|
||||
}
|
||||
|
||||
if (protocol) {
|
||||
*flags |= BDRV_O_PROTOCOL;
|
||||
} else {
|
||||
*flags &= ~BDRV_O_PROTOCOL;
|
||||
}
|
||||
|
||||
/* Fetch the file name from the options QDict if necessary */
|
||||
if (protocol && filename) {
|
||||
if (!qdict_haskey(*options, "filename")) {
|
||||
@ -982,7 +1051,6 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
|
||||
|
||||
/* Find the right block driver */
|
||||
filename = qdict_get_try_str(*options, "filename");
|
||||
drvname = qdict_get_try_str(*options, "driver");
|
||||
|
||||
if (drv) {
|
||||
if (drvname) {
|
||||
@ -1119,9 +1187,10 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
||||
}
|
||||
|
||||
assert(bs->backing_hd == NULL);
|
||||
ret = bdrv_open(&backing_hd,
|
||||
*backing_filename ? backing_filename : NULL, NULL, options,
|
||||
bdrv_backing_flags(bs->open_flags), NULL, &local_err);
|
||||
ret = bdrv_open_inherit(&backing_hd,
|
||||
*backing_filename ? backing_filename : NULL,
|
||||
NULL, options, 0, bs, &child_backing,
|
||||
NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
bdrv_unref(backing_hd);
|
||||
backing_hd = NULL;
|
||||
@ -1155,7 +1224,8 @@ free_exit:
|
||||
* To conform with the behavior of bdrv_open(), *pbs has to be NULL.
|
||||
*/
|
||||
int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
||||
QDict *options, const char *bdref_key, int flags,
|
||||
QDict *options, const char *bdref_key,
|
||||
BlockDriverState* parent, const BdrvChildRole *child_role,
|
||||
bool allow_none, Error **errp)
|
||||
{
|
||||
QDict *image_options;
|
||||
@ -1183,7 +1253,8 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = bdrv_open(pbs, filename, reference, image_options, flags, NULL, errp);
|
||||
ret = bdrv_open_inherit(pbs, filename, reference, image_options, 0,
|
||||
parent, child_role, NULL, errp);
|
||||
|
||||
done:
|
||||
qdict_del(options, bdref_key);
|
||||
@ -1255,6 +1326,19 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bdrv_attach_child(BlockDriverState *parent_bs,
|
||||
BlockDriverState *child_bs,
|
||||
const BdrvChildRole *child_role)
|
||||
{
|
||||
BdrvChild *child = g_new(BdrvChild, 1);
|
||||
*child = (BdrvChild) {
|
||||
.bs = child_bs,
|
||||
.role = child_role,
|
||||
};
|
||||
|
||||
QLIST_INSERT_HEAD(&parent_bs->children, child, next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens a disk image (raw, qcow2, vmdk, ...)
|
||||
*
|
||||
@ -1270,9 +1354,11 @@ out:
|
||||
* should be opened. If specified, neither options nor a filename may be given,
|
||||
* nor can an existing BDS be reused (that is, *pbs has to be NULL).
|
||||
*/
|
||||
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
BlockDriver *drv, Error **errp)
|
||||
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
BlockDriverState *parent,
|
||||
const BdrvChildRole *child_role,
|
||||
BlockDriver *drv, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
BlockDriverState *file = NULL, *bs;
|
||||
@ -1281,6 +1367,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
int snapshot_flags = 0;
|
||||
|
||||
assert(pbs);
|
||||
assert(!child_role || !flags);
|
||||
assert(!child_role == !parent);
|
||||
|
||||
if (reference) {
|
||||
bool options_non_empty = options ? qdict_size(options) : false;
|
||||
@ -1303,6 +1391,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
return -ENODEV;
|
||||
}
|
||||
bdrv_ref(bs);
|
||||
if (child_role) {
|
||||
bdrv_attach_child(parent, bs, child_role);
|
||||
}
|
||||
*pbs = bs;
|
||||
return 0;
|
||||
}
|
||||
@ -1318,7 +1409,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
options = qdict_new();
|
||||
}
|
||||
|
||||
ret = bdrv_fill_options(&options, &filename, flags, drv, &local_err);
|
||||
if (child_role) {
|
||||
bs->inherits_from = parent;
|
||||
flags = child_role->inherit_flags(parent->open_flags);
|
||||
}
|
||||
|
||||
ret = bdrv_fill_options(&options, &filename, &flags, drv, &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
@ -1337,12 +1433,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
}
|
||||
|
||||
assert(drvname || !(flags & BDRV_O_PROTOCOL));
|
||||
if (drv && !drv->bdrv_file_open) {
|
||||
/* If the user explicitly wants a format driver here, we'll need to add
|
||||
* another layer for the protocol in bs->file */
|
||||
flags &= ~BDRV_O_PROTOCOL;
|
||||
}
|
||||
|
||||
bs->open_flags = flags;
|
||||
bs->options = options;
|
||||
options = qdict_clone_shallow(options);
|
||||
|
||||
@ -1357,9 +1449,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
}
|
||||
|
||||
assert(file == NULL);
|
||||
bs->open_flags = flags;
|
||||
ret = bdrv_open_image(&file, filename, options, "file",
|
||||
bdrv_inherited_flags(flags),
|
||||
true, &local_err);
|
||||
bs, &child_file, true, &local_err);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@ -1378,6 +1470,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* BDRV_O_PROTOCOL must be set iff a protocol BDS is about to be created */
|
||||
assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->bdrv_file_open);
|
||||
/* file must be NULL if a protocol BDS is about to be created
|
||||
* (the inverse results in an error message from bdrv_open_common()) */
|
||||
assert(!(flags & BDRV_O_PROTOCOL) || !file);
|
||||
|
||||
/* Open the image */
|
||||
ret = bdrv_open_common(bs, file, options, flags, drv, &local_err);
|
||||
if (ret < 0) {
|
||||
@ -1440,6 +1538,10 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
goto close_and_fail;
|
||||
}
|
||||
|
||||
if (child_role) {
|
||||
bdrv_attach_child(parent, bs, child_role);
|
||||
}
|
||||
|
||||
QDECREF(options);
|
||||
*pbs = bs;
|
||||
return 0;
|
||||
@ -1476,6 +1578,14 @@ close_and_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
BlockDriver *drv, Error **errp)
|
||||
{
|
||||
return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL,
|
||||
NULL, drv, errp);
|
||||
}
|
||||
|
||||
typedef struct BlockReopenQueueEntry {
|
||||
bool prepared;
|
||||
BDRVReopenState state;
|
||||
@ -1506,6 +1616,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
assert(bs != NULL);
|
||||
|
||||
BlockReopenQueueEntry *bs_entry;
|
||||
BdrvChild *child;
|
||||
|
||||
if (bs_queue == NULL) {
|
||||
bs_queue = g_new0(BlockReopenQueue, 1);
|
||||
QSIMPLEQ_INIT(bs_queue);
|
||||
@ -1514,8 +1626,15 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
/* bdrv_open() masks this flag out */
|
||||
flags &= ~BDRV_O_PROTOCOL;
|
||||
|
||||
if (bs->file) {
|
||||
bdrv_reopen_queue(bs_queue, bs->file, bdrv_inherited_flags(flags));
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
int child_flags;
|
||||
|
||||
if (child->bs->inherits_from != bs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
child_flags = child->role->inherit_flags(flags);
|
||||
bdrv_reopen_queue(bs_queue, child->bs, child_flags);
|
||||
}
|
||||
|
||||
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
||||
@ -1726,6 +1845,16 @@ void bdrv_close(BlockDriverState *bs)
|
||||
notifier_list_notify(&bs->close_notifiers, bs);
|
||||
|
||||
if (bs->drv) {
|
||||
BdrvChild *child, *next;
|
||||
|
||||
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
|
||||
if (child->bs->inherits_from == bs) {
|
||||
child->bs->inherits_from = NULL;
|
||||
}
|
||||
QLIST_REMOVE(child, next);
|
||||
g_free(child);
|
||||
}
|
||||
|
||||
if (bs->backing_hd) {
|
||||
BlockDriverState *backing_hd = bs->backing_hd;
|
||||
bdrv_set_backing_hd(bs, NULL);
|
||||
@ -1876,6 +2005,10 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
||||
void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
||||
{
|
||||
BlockDriverState tmp;
|
||||
BdrvChild *child;
|
||||
|
||||
bdrv_drain(bs_new);
|
||||
bdrv_drain(bs_old);
|
||||
|
||||
/* The code needs to swap the node_name but simply swapping node_list won't
|
||||
* work so first remove the nodes from the graph list, do the swap then
|
||||
@ -1935,6 +2068,30 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
||||
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_old, node_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update lh_first.le_prev for non-empty lists.
|
||||
*
|
||||
* The head of the op blocker list doesn't change because it is moved back
|
||||
* in bdrv_move_feature_fields().
|
||||
*/
|
||||
assert(QLIST_EMPTY(&bs_old->tracked_requests));
|
||||
assert(QLIST_EMPTY(&bs_new->tracked_requests));
|
||||
|
||||
QLIST_FIX_HEAD_PTR(&bs_new->children, next);
|
||||
QLIST_FIX_HEAD_PTR(&bs_old->children, next);
|
||||
|
||||
/* Update references in bs->opaque and children */
|
||||
QLIST_FOREACH(child, &bs_old->children, next) {
|
||||
if (child->bs->inherits_from == bs_new) {
|
||||
child->bs->inherits_from = bs_old;
|
||||
}
|
||||
}
|
||||
QLIST_FOREACH(child, &bs_new->children, next) {
|
||||
if (child->bs->inherits_from == bs_old) {
|
||||
child->bs->inherits_from = bs_new;
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_rebind(bs_new);
|
||||
bdrv_rebind(bs_old);
|
||||
}
|
||||
@ -1957,6 +2114,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
|
||||
/* The contents of 'tmp' will become bs_top, as we are
|
||||
* swapping bs_new and bs_top contents. */
|
||||
bdrv_set_backing_hd(bs_top, bs_new);
|
||||
bdrv_attach_child(bs_top, bs_new, &child_backing);
|
||||
}
|
||||
|
||||
static void bdrv_delete(BlockDriverState *bs)
|
||||
@ -3242,10 +3400,9 @@ static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
||||
uint64_t size = bdrv_nb_sectors(bs);
|
||||
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
continue;
|
||||
}
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
hbitmap_truncate(bitmap->bitmap, size);
|
||||
bitmap->size = size;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -429,7 +429,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Open the backing file */
|
||||
assert(bs->file == NULL);
|
||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
|
||||
flags | BDRV_O_PROTOCOL, false, &local_err);
|
||||
bs, &child_file, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
|
@ -125,7 +125,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Open the raw file */
|
||||
assert(bs->file == NULL);
|
||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
|
||||
"raw", flags | BDRV_O_PROTOCOL, false, &local_err);
|
||||
"raw", bs, &child_file, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
@ -134,7 +134,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Open the test file */
|
||||
assert(s->test_file == NULL);
|
||||
ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
|
||||
"test", flags, false, &local_err);
|
||||
"test", bs, &child_format, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
s->test_file = NULL;
|
||||
|
@ -483,9 +483,11 @@ static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
|
||||
[QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2,
|
||||
};
|
||||
|
||||
static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size,
|
||||
static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
uint64_t *l2_cache_size,
|
||||
uint64_t *refcount_cache_size, Error **errp)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t combined_cache_size;
|
||||
bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
|
||||
|
||||
@ -525,7 +527,9 @@ static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size,
|
||||
}
|
||||
} else {
|
||||
if (!l2_cache_size_set && !refcount_cache_size_set) {
|
||||
*l2_cache_size = DEFAULT_L2_CACHE_BYTE_SIZE;
|
||||
*l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE,
|
||||
(uint64_t)DEFAULT_L2_CACHE_CLUSTERS
|
||||
* s->cluster_size);
|
||||
*refcount_cache_size = *l2_cache_size
|
||||
/ DEFAULT_L2_REFCOUNT_SIZE_RATIO;
|
||||
} else if (!l2_cache_size_set) {
|
||||
@ -803,7 +807,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
read_cache_sizes(opts, &l2_cache_size, &refcount_cache_size, &local_err);
|
||||
read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
|
@ -62,11 +62,14 @@
|
||||
#define MIN_CLUSTER_BITS 9
|
||||
#define MAX_CLUSTER_BITS 21
|
||||
|
||||
#define MIN_L2_CACHE_SIZE 1 /* cluster */
|
||||
/* Must be at least 2 to cover COW */
|
||||
#define MIN_L2_CACHE_SIZE 2 /* clusters */
|
||||
|
||||
/* Must be at least 4 to cover all cases of refcount table growth */
|
||||
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
|
||||
|
||||
/* Whichever is more */
|
||||
#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
|
||||
#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
|
||||
|
||||
/* The refblock cache needs only a fourth of the L2 cache size to cover as many
|
||||
|
@ -866,25 +866,18 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *opts = NULL;
|
||||
bool *opened;
|
||||
QDict *sub = NULL;
|
||||
QList *list = NULL;
|
||||
const QListEntry *lentry;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
qdict_flatten(options);
|
||||
qdict_extract_subqdict(options, &sub, "children.");
|
||||
qdict_array_split(sub, &list);
|
||||
|
||||
if (qdict_size(sub)) {
|
||||
error_setg(&local_err, "Invalid option children.%s",
|
||||
qdict_first(sub)->key);
|
||||
/* count how many different children are present */
|
||||
s->num_children = qdict_array_entries(options, "children.");
|
||||
if (s->num_children < 0) {
|
||||
error_setg(&local_err, "Option children is not a valid array");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* count how many different children are present */
|
||||
s->num_children = qlist_size(list);
|
||||
if (s->num_children < 2) {
|
||||
error_setg(&local_err,
|
||||
"Number of provided children must be greater than 1");
|
||||
@ -937,37 +930,17 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->bs = g_new0(BlockDriverState *, s->num_children);
|
||||
opened = g_new0(bool, s->num_children);
|
||||
|
||||
for (i = 0, lentry = qlist_first(list); lentry;
|
||||
lentry = qlist_next(lentry), i++) {
|
||||
QDict *d;
|
||||
QString *string;
|
||||
|
||||
switch (qobject_type(lentry->value))
|
||||
{
|
||||
/* List of options */
|
||||
case QTYPE_QDICT:
|
||||
d = qobject_to_qdict(lentry->value);
|
||||
QINCREF(d);
|
||||
ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL,
|
||||
&local_err);
|
||||
break;
|
||||
|
||||
/* QMP reference */
|
||||
case QTYPE_QSTRING:
|
||||
string = qobject_to_qstring(lentry->value);
|
||||
ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL,
|
||||
flags, NULL, &local_err);
|
||||
break;
|
||||
|
||||
default:
|
||||
error_setg(&local_err, "Specification of child block device %i "
|
||||
"is invalid", i);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
char indexstr[32];
|
||||
ret = snprintf(indexstr, 32, "children.%d", i);
|
||||
assert(ret < 32);
|
||||
|
||||
ret = bdrv_open_image(&s->bs[i], NULL, options, indexstr, bs,
|
||||
&child_format, false, &local_err);
|
||||
if (ret < 0) {
|
||||
goto close_exit;
|
||||
}
|
||||
|
||||
opened[i] = true;
|
||||
}
|
||||
|
||||
@ -990,8 +963,6 @@ exit:
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
QDECREF(list);
|
||||
QDECREF(sub);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
84
block/vmdk.c
84
block/vmdk.c
@ -321,37 +321,13 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Queue extents, if any, for reopen() */
|
||||
/* We have nothing to do for VMDK reopen, stubs just return success */
|
||||
static int vmdk_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
BDRVVmdkState *s;
|
||||
int ret = -1;
|
||||
int i;
|
||||
VmdkExtent *e;
|
||||
|
||||
assert(state != NULL);
|
||||
assert(state->bs != NULL);
|
||||
|
||||
if (queue == NULL) {
|
||||
error_setg(errp, "No reopen queue for VMDK extents");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
s = state->bs->opaque;
|
||||
|
||||
assert(s != NULL);
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
e = &s->extents[i];
|
||||
if (e->file != state->bs->file) {
|
||||
bdrv_reopen_queue(queue, e->file, state->flags);
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_parent_open(BlockDriverState *bs)
|
||||
@ -543,7 +519,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
Error **errp);
|
||||
QDict *options, Error **errp);
|
||||
|
||||
static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
|
||||
Error **errp)
|
||||
@ -582,7 +558,7 @@ static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
|
||||
|
||||
static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
BlockDriverState *file,
|
||||
int flags, Error **errp)
|
||||
int flags, QDict *options, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
uint32_t magic;
|
||||
@ -606,7 +582,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
if (!buf) {
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, errp);
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, options, errp);
|
||||
g_free(buf);
|
||||
return ret;
|
||||
}
|
||||
@ -763,7 +739,7 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
|
||||
/* Open an extent file and append to bs array */
|
||||
static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
BlockDriverState *file, int flags,
|
||||
char *buf, Error **errp)
|
||||
char *buf, QDict *options, Error **errp)
|
||||
{
|
||||
uint32_t magic;
|
||||
|
||||
@ -773,7 +749,7 @@ static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
return vmdk_open_vmfs_sparse(bs, file, flags, errp);
|
||||
break;
|
||||
case VMDK4_MAGIC:
|
||||
return vmdk_open_vmdk4(bs, file, flags, errp);
|
||||
return vmdk_open_vmdk4(bs, file, flags, options, errp);
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Image not in VMDK format");
|
||||
@ -783,7 +759,8 @@ static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
const char *desc_file_path, Error **errp)
|
||||
const char *desc_file_path, QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
int matches;
|
||||
@ -797,6 +774,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
BlockDriverState *extent_file;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
VmdkExtent *extent;
|
||||
char extent_opt_prefix[32];
|
||||
|
||||
while (*p) {
|
||||
/* parse extent line in one of below formats:
|
||||
@ -846,8 +824,12 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
extent_path = g_malloc0(PATH_MAX);
|
||||
path_combine(extent_path, PATH_MAX, desc_file_path, fname);
|
||||
extent_file = NULL;
|
||||
ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
|
||||
bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);
|
||||
|
||||
ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents);
|
||||
assert(ret < 32);
|
||||
|
||||
ret = bdrv_open_image(&extent_file, extent_path, options,
|
||||
extent_opt_prefix, bs, &child_file, false, errp);
|
||||
g_free(extent_path);
|
||||
if (ret) {
|
||||
return ret;
|
||||
@ -870,7 +852,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
if (!buf) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp);
|
||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf,
|
||||
options, errp);
|
||||
}
|
||||
g_free(buf);
|
||||
if (ret) {
|
||||
@ -898,7 +881,7 @@ next_line:
|
||||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
Error **errp)
|
||||
QDict *options, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
char ct[128];
|
||||
@ -920,7 +903,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
}
|
||||
s->create_type = g_strdup(ct);
|
||||
s->desc_offset = 0;
|
||||
ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, errp);
|
||||
ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, options, errp);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@ -942,11 +925,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
switch (magic) {
|
||||
case VMDK3_MAGIC:
|
||||
case VMDK4_MAGIC:
|
||||
ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp);
|
||||
ret = vmdk_open_sparse(bs, bs->file, flags, buf, options, errp);
|
||||
s->desc_offset = 0x200;
|
||||
break;
|
||||
default:
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, errp);
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, options, errp);
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
@ -1248,6 +1231,17 @@ static VmdkExtent *find_extent(BDRVVmdkState *s,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent,
|
||||
int64_t sector_num)
|
||||
{
|
||||
uint64_t index_in_cluster, extent_begin_sector, extent_relative_sector_num;
|
||||
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
return index_in_cluster;
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
@ -1285,7 +1279,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
break;
|
||||
}
|
||||
|
||||
index_in_cluster = sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
@ -1413,7 +1407,6 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
uint64_t n, index_in_cluster;
|
||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||
VmdkExtent *extent = NULL;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
@ -1425,9 +1418,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
ret = get_cluster_offset(bs, extent, NULL,
|
||||
sector_num << 9, false, &cluster_offset,
|
||||
0, 0);
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
@ -1489,7 +1480,6 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
VmdkExtent *extent = NULL;
|
||||
int ret;
|
||||
int64_t index_in_cluster, n;
|
||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||
uint64_t cluster_offset;
|
||||
VmdkMetaData m_data;
|
||||
|
||||
@ -1505,9 +1495,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
if (!extent) {
|
||||
return -EIO;
|
||||
}
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
|
24
blockdev.c
24
blockdev.c
@ -393,13 +393,13 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu_opt_get_bool(opts, "cache.writeback", true)) {
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) {
|
||||
bdrv_flags |= BDRV_O_CACHE_WB;
|
||||
}
|
||||
if (qemu_opt_get_bool(opts, "cache.direct", false)) {
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
|
||||
bdrv_flags |= BDRV_O_NOCACHE;
|
||||
}
|
||||
if (qemu_opt_get_bool(opts, "cache.no-flush", false)) {
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
|
||||
bdrv_flags |= BDRV_O_NO_FLUSH;
|
||||
}
|
||||
|
||||
@ -742,16 +742,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
}
|
||||
|
||||
/* Specific options take precedence */
|
||||
if (!qemu_opt_get(all_opts, "cache.writeback")) {
|
||||
qemu_opt_set_bool(all_opts, "cache.writeback",
|
||||
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) {
|
||||
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB,
|
||||
!!(flags & BDRV_O_CACHE_WB), &error_abort);
|
||||
}
|
||||
if (!qemu_opt_get(all_opts, "cache.direct")) {
|
||||
qemu_opt_set_bool(all_opts, "cache.direct",
|
||||
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) {
|
||||
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT,
|
||||
!!(flags & BDRV_O_NOCACHE), &error_abort);
|
||||
}
|
||||
if (!qemu_opt_get(all_opts, "cache.no-flush")) {
|
||||
qemu_opt_set_bool(all_opts, "cache.no-flush",
|
||||
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_NO_FLUSH)) {
|
||||
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_NO_FLUSH,
|
||||
!!(flags & BDRV_O_NO_FLUSH), &error_abort);
|
||||
}
|
||||
qemu_opt_unset(all_opts, "cache");
|
||||
@ -3121,15 +3121,15 @@ QemuOptsList qemu_common_drive_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "discard operation (ignore/off, unmap/on)",
|
||||
},{
|
||||
.name = "cache.writeback",
|
||||
.name = BDRV_OPT_CACHE_WB,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables writeback mode for any caches",
|
||||
},{
|
||||
.name = "cache.direct",
|
||||
.name = BDRV_OPT_CACHE_DIRECT,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables use of O_DIRECT (bypass the host page cache)",
|
||||
},{
|
||||
.name = "cache.no-flush",
|
||||
.name = BDRV_OPT_CACHE_NO_FLUSH,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "ignore any flush requests for the device",
|
||||
},{
|
||||
|
@ -12,6 +12,7 @@
|
||||
/* block.c */
|
||||
typedef struct BlockDriver BlockDriver;
|
||||
typedef struct BlockJob BlockJob;
|
||||
typedef struct BdrvChildRole BdrvChildRole;
|
||||
|
||||
typedef struct BlockDriverInfo {
|
||||
/* in bytes, 0 if irrelevant */
|
||||
@ -90,6 +91,14 @@ typedef struct HDGeometry {
|
||||
|
||||
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
|
||||
|
||||
|
||||
/* Option names of options parsed by the block layer */
|
||||
|
||||
#define BDRV_OPT_CACHE_WB "cache.writeback"
|
||||
#define BDRV_OPT_CACHE_DIRECT "cache.direct"
|
||||
#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush"
|
||||
|
||||
|
||||
#define BDRV_SECTOR_BITS 9
|
||||
#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
|
||||
#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1)
|
||||
@ -196,7 +205,8 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
|
||||
int bdrv_parse_cache_flags(const char *mode, int *flags);
|
||||
int bdrv_parse_discard_flags(const char *mode, int *flags);
|
||||
int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
||||
QDict *options, const char *bdref_key, int flags,
|
||||
QDict *options, const char *bdref_key,
|
||||
BlockDriverState* parent, const BdrvChildRole *child_role,
|
||||
bool allow_none, Error **errp);
|
||||
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd);
|
||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
|
||||
|
@ -330,6 +330,19 @@ typedef struct BdrvAioNotifier {
|
||||
QLIST_ENTRY(BdrvAioNotifier) list;
|
||||
} BdrvAioNotifier;
|
||||
|
||||
struct BdrvChildRole {
|
||||
int (*inherit_flags)(int parent_flags);
|
||||
};
|
||||
|
||||
extern const BdrvChildRole child_file;
|
||||
extern const BdrvChildRole child_format;
|
||||
|
||||
typedef struct BdrvChild {
|
||||
BlockDriverState *bs;
|
||||
const BdrvChildRole *role;
|
||||
QLIST_ENTRY(BdrvChild) next;
|
||||
} BdrvChild;
|
||||
|
||||
/*
|
||||
* Note: the function bdrv_append() copies and swaps contents of
|
||||
* BlockDriverStates, so if you add new fields to this struct, please
|
||||
@ -429,6 +442,12 @@ struct BlockDriverState {
|
||||
/* long-running background operation */
|
||||
BlockJob *job;
|
||||
|
||||
/* The node that this node inherited default options from (and a reopen on
|
||||
* which can affect this node by changing these defaults). This is always a
|
||||
* parent node of this node. */
|
||||
BlockDriverState *inherits_from;
|
||||
QLIST_HEAD(, BdrvChild) children;
|
||||
|
||||
QDict *options;
|
||||
BlockdevDetectZeroesOptions detect_zeroes;
|
||||
|
||||
|
@ -65,11 +65,15 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key,
|
||||
int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value);
|
||||
const char *qdict_get_try_str(const QDict *qdict, const char *key);
|
||||
|
||||
void qdict_copy_default(QDict *dst, QDict *src, const char *key);
|
||||
void qdict_set_default_str(QDict *dst, const char *key, const char *val);
|
||||
|
||||
QDict *qdict_clone_shallow(const QDict *src);
|
||||
void qdict_flatten(QDict *qdict);
|
||||
|
||||
void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
|
||||
void qdict_array_split(QDict *src, QList **dst);
|
||||
int qdict_array_entries(QDict *src, const char *subqdict);
|
||||
|
||||
void qdict_join(QDict *dest, QDict *src, bool overwrite);
|
||||
|
||||
|
@ -117,6 +117,12 @@ struct { \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QLIST_FIX_HEAD_PTR(head, field) do { \
|
||||
if ((head)->lh_first != NULL) { \
|
||||
(head)->lh_first->field.le_prev = &(head)->lh_first; \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QLIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
|
111
qobject/qdict.c
111
qobject/qdict.c
@ -477,6 +477,39 @@ static void qdict_destroy_obj(QObject *obj)
|
||||
g_free(qdict);
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the
|
||||
* value of 'key' in 'src' is copied there (and the refcount increased
|
||||
* accordingly).
|
||||
*/
|
||||
void qdict_copy_default(QDict *dst, QDict *src, const char *key)
|
||||
{
|
||||
QObject *val;
|
||||
|
||||
if (qdict_haskey(dst, key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
val = qdict_get(src, key);
|
||||
if (val) {
|
||||
qobject_incref(val);
|
||||
qdict_put_obj(dst, key, val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a
|
||||
* new QString initialised by 'val' is put there.
|
||||
*/
|
||||
void qdict_set_default_str(QDict *dst, const char *key, const char *val)
|
||||
{
|
||||
if (qdict_haskey(dst, key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
qdict_put(dst, key, qstring_from_str(val));
|
||||
}
|
||||
|
||||
static void qdict_flatten_qdict(QDict *qdict, QDict *target,
|
||||
const char *prefix);
|
||||
|
||||
@ -597,17 +630,21 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start)
|
||||
}
|
||||
}
|
||||
|
||||
static bool qdict_has_prefixed_entries(const QDict *src, const char *start)
|
||||
static int qdict_count_prefixed_entries(const QDict *src, const char *start)
|
||||
{
|
||||
const QDictEntry *entry;
|
||||
int count = 0;
|
||||
|
||||
for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) {
|
||||
if (strstart(entry->key, start, NULL)) {
|
||||
return true;
|
||||
if (count == INT_MAX) {
|
||||
return -ERANGE;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -646,7 +683,8 @@ void qdict_array_split(QDict *src, QList **dst)
|
||||
snprintf_ret = snprintf(prefix, 32, "%u.", i);
|
||||
assert(snprintf_ret < 32);
|
||||
|
||||
is_subqdict = qdict_has_prefixed_entries(src, prefix);
|
||||
/* Overflow is the same as positive non-zero results */
|
||||
is_subqdict = qdict_count_prefixed_entries(src, prefix);
|
||||
|
||||
// There may be either a single subordinate object (named "%u") or
|
||||
// multiple objects (each with a key prefixed "%u."), but not both.
|
||||
@ -666,6 +704,71 @@ void qdict_array_split(QDict *src, QList **dst)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_array_entries(): Returns the number of direct array entries if the
|
||||
* sub-QDict of src specified by the prefix in subqdict (or src itself for
|
||||
* prefix == "") is valid as an array, i.e. the length of the created list if
|
||||
* the sub-QDict would become empty after calling qdict_array_split() on it. If
|
||||
* the array is not valid, -EINVAL is returned.
|
||||
*/
|
||||
int qdict_array_entries(QDict *src, const char *subqdict)
|
||||
{
|
||||
const QDictEntry *entry;
|
||||
unsigned i;
|
||||
unsigned entries = 0;
|
||||
size_t subqdict_len = strlen(subqdict);
|
||||
|
||||
assert(!subqdict_len || subqdict[subqdict_len - 1] == '.');
|
||||
|
||||
/* qdict_array_split() loops until UINT_MAX, but as we want to return
|
||||
* negative errors, we only have a signed return value here. Any additional
|
||||
* entries will lead to -EINVAL. */
|
||||
for (i = 0; i < INT_MAX; i++) {
|
||||
QObject *subqobj;
|
||||
int subqdict_entries;
|
||||
size_t slen = 32 + subqdict_len;
|
||||
char indexstr[slen], prefix[slen];
|
||||
size_t snprintf_ret;
|
||||
|
||||
snprintf_ret = snprintf(indexstr, slen, "%s%u", subqdict, i);
|
||||
assert(snprintf_ret < slen);
|
||||
|
||||
subqobj = qdict_get(src, indexstr);
|
||||
|
||||
snprintf_ret = snprintf(prefix, slen, "%s%u.", subqdict, i);
|
||||
assert(snprintf_ret < slen);
|
||||
|
||||
subqdict_entries = qdict_count_prefixed_entries(src, prefix);
|
||||
if (subqdict_entries < 0) {
|
||||
return subqdict_entries;
|
||||
}
|
||||
|
||||
/* There may be either a single subordinate object (named "%u") or
|
||||
* multiple objects (each with a key prefixed "%u."), but not both. */
|
||||
if (subqobj && subqdict_entries) {
|
||||
return -EINVAL;
|
||||
} else if (!subqobj && !subqdict_entries) {
|
||||
break;
|
||||
}
|
||||
|
||||
entries += subqdict_entries ? subqdict_entries : 1;
|
||||
}
|
||||
|
||||
/* Consider everything handled that isn't part of the given sub-QDict */
|
||||
for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) {
|
||||
if (!strstart(qdict_entry_key(entry), subqdict, NULL)) {
|
||||
entries++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Anything left in the sub-QDict that wasn't handled? */
|
||||
if (qdict_size(src) != entries) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_join(): Absorb the src QDict into the dest QDict, that is, move all
|
||||
* elements from src to dest.
|
||||
|
@ -152,6 +152,28 @@ static void qdict_get_try_str_test(void)
|
||||
QDECREF(tests_dict);
|
||||
}
|
||||
|
||||
static void qdict_defaults_test(void)
|
||||
{
|
||||
QDict *dict, *copy;
|
||||
|
||||
dict = qdict_new();
|
||||
copy = qdict_new();
|
||||
|
||||
qdict_set_default_str(dict, "foo", "abc");
|
||||
qdict_set_default_str(dict, "foo", "def");
|
||||
g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc");
|
||||
qdict_set_default_str(dict, "bar", "ghi");
|
||||
|
||||
qdict_copy_default(copy, dict, "foo");
|
||||
g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc");
|
||||
qdict_set_default_str(copy, "bar", "xyz");
|
||||
qdict_copy_default(copy, dict, "bar");
|
||||
g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz");
|
||||
|
||||
QDECREF(copy);
|
||||
QDECREF(dict);
|
||||
}
|
||||
|
||||
static void qdict_haskey_not_test(void)
|
||||
{
|
||||
QDict *tests_dict = qdict_new();
|
||||
@ -444,6 +466,49 @@ static void qdict_array_split_test(void)
|
||||
QDECREF(test_dict);
|
||||
}
|
||||
|
||||
static void qdict_array_entries_test(void)
|
||||
{
|
||||
QDict *dict = qdict_new();
|
||||
|
||||
g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
|
||||
|
||||
qdict_put(dict, "bar", qint_from_int(0));
|
||||
qdict_put(dict, "baz.0", qint_from_int(0));
|
||||
g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
|
||||
|
||||
qdict_put(dict, "foo.1", qint_from_int(0));
|
||||
g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
|
||||
qdict_put(dict, "foo.0", qint_from_int(0));
|
||||
g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2);
|
||||
qdict_put(dict, "foo.bar", qint_from_int(0));
|
||||
g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
|
||||
qdict_del(dict, "foo.bar");
|
||||
|
||||
qdict_put(dict, "foo.2.a", qint_from_int(0));
|
||||
qdict_put(dict, "foo.2.b", qint_from_int(0));
|
||||
qdict_put(dict, "foo.2.c", qint_from_int(0));
|
||||
g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3);
|
||||
g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
|
||||
|
||||
QDECREF(dict);
|
||||
|
||||
dict = qdict_new();
|
||||
qdict_put(dict, "1", qint_from_int(0));
|
||||
g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
|
||||
qdict_put(dict, "0", qint_from_int(0));
|
||||
g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2);
|
||||
qdict_put(dict, "bar", qint_from_int(0));
|
||||
g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
|
||||
qdict_del(dict, "bar");
|
||||
|
||||
qdict_put(dict, "2.a", qint_from_int(0));
|
||||
qdict_put(dict, "2.b", qint_from_int(0));
|
||||
qdict_put(dict, "2.c", qint_from_int(0));
|
||||
g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3);
|
||||
|
||||
QDECREF(dict);
|
||||
}
|
||||
|
||||
static void qdict_join_test(void)
|
||||
{
|
||||
QDict *dict1, *dict2;
|
||||
@ -663,6 +728,7 @@ int main(int argc, char **argv)
|
||||
g_test_add_func("/public/get_try_int", qdict_get_try_int_test);
|
||||
g_test_add_func("/public/get_str", qdict_get_str_test);
|
||||
g_test_add_func("/public/get_try_str", qdict_get_try_str_test);
|
||||
g_test_add_func("/public/defaults", qdict_defaults_test);
|
||||
g_test_add_func("/public/haskey_not", qdict_haskey_not_test);
|
||||
g_test_add_func("/public/haskey", qdict_haskey_test);
|
||||
g_test_add_func("/public/del", qdict_del_test);
|
||||
@ -670,6 +736,7 @@ int main(int argc, char **argv)
|
||||
g_test_add_func("/public/iterapi", qdict_iterapi_test);
|
||||
g_test_add_func("/public/flatten", qdict_flatten_test);
|
||||
g_test_add_func("/public/array_split", qdict_array_split_test);
|
||||
g_test_add_func("/public/array_entries", qdict_array_entries_test);
|
||||
g_test_add_func("/public/join", qdict_join_test);
|
||||
|
||||
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
|
||||
|
@ -194,7 +194,6 @@ echo === Specifying the protocol layer ===
|
||||
echo
|
||||
|
||||
run_qemu -drive file="$TEST_IMG",file.driver=file
|
||||
run_qemu -drive file="$TEST_IMG",file.driver=qcow2
|
||||
|
||||
echo
|
||||
echo === Leaving out required options ===
|
||||
|
@ -253,9 +253,6 @@ Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename'
|
||||
|
||||
|
||||
=== Leaving out required options ===
|
||||
|
||||
|
@ -93,6 +93,16 @@ $QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \
|
||||
-c 'read -P 42 0 64k' \
|
||||
| _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo '=== Testing minimal L2 cache and COW ==='
|
||||
echo
|
||||
|
||||
$QEMU_IMG snapshot -c foo "$TEST_IMG"
|
||||
# This requires a COW operation, which accesses two L2 tables simultaneously
|
||||
# (COW source and destination), so there must be enough space in the cache to
|
||||
# place both tables there (and qemu should not crash)
|
||||
$QEMU_IO -c "open -o cache-size=0 $TEST_IMG" -c 'write 0 64k' | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo '*** done'
|
||||
rm -f $seq.full
|
||||
|
@ -26,4 +26,9 @@ read 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
=== Testing minimal L2 cache and COW ===
|
||||
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
||||
|
60
tests/qemu-iotests/119
Executable file
60
tests/qemu-iotests/119
Executable file
@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# NBD test case for overriding BDRV_O_PROTOCOL by explicitly specifying
|
||||
# a driver
|
||||
#
|
||||
# Copyright (C) 2015 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# creator
|
||||
owner=mreitz@redhat.com
|
||||
|
||||
seq="$(basename $0)"
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here="$PWD"
|
||||
tmp=/tmp/$$
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt raw
|
||||
_supported_proto nbd
|
||||
_supported_os Linux
|
||||
|
||||
_make_test_img 64M
|
||||
# This should not crash
|
||||
echo "{'execute': 'qmp_capabilities'}
|
||||
{'execute': 'human-monitor-command',
|
||||
'arguments': {'command-line': 'qemu-io drv \"read -P 0 0 64k\"'}}
|
||||
{'execute': 'quit'}" \
|
||||
| $QEMU -drive id=drv,if=none,file="$TEST_IMG",driver=nbd \
|
||||
-qmp stdio -nodefaults \
|
||||
| _filter_qmp | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo
|
||||
echo '*** done'
|
||||
rm -f $seq.full
|
||||
status=0
|
11
tests/qemu-iotests/119.out
Normal file
11
tests/qemu-iotests/119.out
Normal file
@ -0,0 +1,11 @@
|
||||
QA output created by 119
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
QMP_VERSION
|
||||
{"return": {}}
|
||||
read 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
{"return": ""}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||
|
||||
*** done
|
65
tests/qemu-iotests/120
Executable file
65
tests/qemu-iotests/120
Executable file
@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Non-NBD test cases for overriding BDRV_O_PROTOCOL by explicitly
|
||||
# specifying a driver
|
||||
#
|
||||
# Copyright (C) 2015 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# creator
|
||||
owner=mreitz@redhat.com
|
||||
|
||||
seq="$(basename $0)"
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here="$PWD"
|
||||
tmp=/tmp/$$
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt generic
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
_make_test_img 64M
|
||||
|
||||
echo "{'execute': 'qmp_capabilities'}
|
||||
{'execute': 'human-monitor-command',
|
||||
'arguments': {'command-line': 'qemu-io drv \"write -P 42 0 64k\"'}}
|
||||
{'execute': 'quit'}" \
|
||||
| $QEMU -qmp stdio -nodefaults \
|
||||
-drive id=drv,if=none,file="$TEST_IMG",driver=raw,file.driver=$IMGFMT \
|
||||
| _filter_qmp | _filter_qemu_io
|
||||
$QEMU_IO -c 'read -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
$QEMU_IO_PROG -c 'read -P 42 0 64k' \
|
||||
"json:{'driver': 'raw', 'file': {'driver': '$IMGFMT', 'file': {'filename': '$TEST_IMG'}}}" \
|
||||
| _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo
|
||||
echo '*** done'
|
||||
rm -f $seq.full
|
||||
status=0
|
15
tests/qemu-iotests/120.out
Normal file
15
tests/qemu-iotests/120.out
Normal file
@ -0,0 +1,15 @@
|
||||
QA output created by 120
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
QMP_VERSION
|
||||
{"return": {}}
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
{"return": ""}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||
read 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
*** done
|
@ -125,7 +125,7 @@ class TestIncrementalBackup(iotests.QMPTestCase):
|
||||
|
||||
event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
|
||||
match={'data': {'device': kwargs['device']}})
|
||||
self.assertIsNotNone(event)
|
||||
self.assertNotEqual(event, None)
|
||||
|
||||
try:
|
||||
failure = self.dictpath(event, 'data/error')
|
||||
|
@ -29,6 +29,7 @@ tmp=/tmp/$$
|
||||
status=1 # failure is the default!
|
||||
|
||||
devname="eiodev$$"
|
||||
sudo=""
|
||||
|
||||
_setup_eiodev()
|
||||
{
|
||||
@ -37,6 +38,7 @@ _setup_eiodev()
|
||||
echo "0 $((1024 * 1024 * 1024 / 512)) error" | \
|
||||
$cmd dmsetup create "$devname" 2>/dev/null
|
||||
if [ "$?" -eq 0 ]; then
|
||||
sudo="$cmd"
|
||||
return
|
||||
fi
|
||||
done
|
||||
@ -74,7 +76,7 @@ TEST_IMG="/dev/mapper/$devname"
|
||||
echo
|
||||
echo "== reading from error device =="
|
||||
# Opening image should succeed but the read operation should fail
|
||||
$QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io
|
||||
$sudo $QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
|
@ -121,6 +121,8 @@
|
||||
114 rw auto quick
|
||||
115 rw auto
|
||||
116 rw auto quick
|
||||
119 rw auto quick
|
||||
120 rw auto quick
|
||||
121 rw auto
|
||||
122 rw auto
|
||||
123 rw auto quick
|
||||
|
Loading…
Reference in New Issue
Block a user