Block layer patches
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJZFHXJAAoJEH8JsnLIjy/WvdsP/3mTGDZJO4M0HXWBWYQBwdGE EuMyGxn8Y0IUIBNx8sogAY9yQ/aQUh1b3bLWg7MAvZvQpi2S448Ak/2agaRFTWXJ vVv1nP0uigfgElSZZKTyj2SyhghrGkBou98hyYOd05YAApXmxoBKvTgC1di0Ec5K 86Mc91Cr3udh4Q5w5Ss2IxiN+fNoHhHdOOm8gkWM4iQl6fmDJ8lBBxoiU+K2uXX+ /flAVRgogFglKb0EAq8eSdy4qFRyp1G43yzOJKNiM3BuYeXpipnyO5mY3ynrEv5Y 3213hJ/rriY15xHcKtkPxEqd32iX2qCRGFXWMMiq4r527JG/B40SEJQaxfHu33U2 Et3+0MxoDHFseXE2sSjs8c0DKQiU5/hf7RkzqOMPMNluIX4ykrSyOdKzj4EFEowr GGLobm4Hr/rv8YDhRhllyKZ6Xzrjihy5WH5T9+HVfOlf35AxjJGZYqfCouhCiP15 0Nxz3qWfDH/U4zvhY+PIfQ51kvGl02oMZF6uu48kvzwdbIKgnImKg5HbG5R85mYQ BFqCjaEWjSUWxrqbZ/uRBUqavCLJm9C3mEWLCzGBmXNNs4Th8kXftuo1br+QHtr5 aT7xoxUJs/nmBoCKYhlCSrxjqCa0clenvNeiO4Q44UxWmOFQi9SM5myeSuCIaa+S qnYHZLZ74gVZpQaoPkjE =SA0O -----END PGP SIGNATURE----- Merge remote-tracking branch 'kwolf/tags/for-upstream' into staging Block layer patches # gpg: Signature made Thu 11 May 2017 10:31:37 AM EDT # gpg: using RSA key 0x7F09B272C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * kwolf/tags/for-upstream: (58 commits) MAINTAINERS: Add qemu-progress to the block layer qcow2: Discard/zero clusters by byte count qcow2: Assert that cluster operations are aligned qcow2: Optimize write zero of unaligned tail cluster iotests: Add test 179 to cover write zeroes with unmap iotests: Improve _filter_qemu_img_map qcow2: Optimize zero_single_l2() to minimize L2 churn qcow2: Make distinction between zero cluster types obvious qcow2: Name typedef for cluster type qcow2: Correctly report status of preallocated zero clusters block: Update comments on BDRV_BLOCK_* meanings qcow2: Use consistent switch indentation qcow2: Nicer variable names in qcow2_update_snapshot_refcount() tests: Add coverage for recent block geometry fixes blkdebug: Add ability to override unmap geometries blkdebug: Simplify override logic blkdebug: Add pass-through write_zero and discard support blkdebug: Refactor error injection blkdebug: Sanity check block layer guarantees qemu-io: Switch 'map' output to byte-based reporting ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
3753e255da
@ -1170,6 +1170,7 @@ F: include/block/
|
||||
F: qemu-img*
|
||||
F: qemu-io*
|
||||
F: tests/qemu-iotests/
|
||||
F: util/qemu-progress.c
|
||||
T: git git://repo.or.cz/qemu/kevin.git block
|
||||
|
||||
Block I/O path
|
||||
|
127
block.c
127
block.c
@ -192,11 +192,20 @@ void path_combine(char *dest, int dest_size,
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns whether the image file is opened as read-only. Note that this can
|
||||
* return false and writing to the image file is still not possible because the
|
||||
* image is inactivated. */
|
||||
bool bdrv_is_read_only(BlockDriverState *bs)
|
||||
{
|
||||
return bs->read_only;
|
||||
}
|
||||
|
||||
/* Returns whether the image file can be written to right now */
|
||||
bool bdrv_is_writable(BlockDriverState *bs)
|
||||
{
|
||||
return !bdrv_is_read_only(bs) && !(bs->open_flags & BDRV_O_INACTIVE);
|
||||
}
|
||||
|
||||
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
|
||||
{
|
||||
/* Do not set read_only if copy_on_read is enabled */
|
||||
@ -762,6 +771,13 @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
|
||||
bdrv_drained_end(bs);
|
||||
}
|
||||
|
||||
static int bdrv_child_cb_inactivate(BdrvChild *child)
|
||||
{
|
||||
BlockDriverState *bs = child->opaque;
|
||||
assert(bs->open_flags & BDRV_O_INACTIVE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the options and flags that a temporary snapshot should get, based on
|
||||
* the originally requested flags (the originally requested image will have
|
||||
@ -800,6 +816,7 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options,
|
||||
* the parent. */
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE);
|
||||
|
||||
/* Inherit the read-only option from the parent if it's not set */
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
|
||||
@ -821,6 +838,7 @@ const BdrvChildRole child_file = {
|
||||
.inherit_options = bdrv_inherited_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.inactivate = bdrv_child_cb_inactivate,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -842,6 +860,7 @@ const BdrvChildRole child_format = {
|
||||
.inherit_options = bdrv_inherited_fmt_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.inactivate = bdrv_child_cb_inactivate,
|
||||
};
|
||||
|
||||
static void bdrv_backing_attach(BdrvChild *c)
|
||||
@ -908,6 +927,7 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
|
||||
* which is only applied on the top level (BlockBackend) */
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE);
|
||||
|
||||
/* backing files always opened read-only */
|
||||
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on");
|
||||
@ -926,6 +946,7 @@ const BdrvChildRole child_backing = {
|
||||
.inherit_options = bdrv_backing_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.inactivate = bdrv_child_cb_inactivate,
|
||||
};
|
||||
|
||||
static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
@ -1150,6 +1171,11 @@ QemuOptsList bdrv_runtime_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "discard operation (ignore/off, unmap/on)",
|
||||
},
|
||||
{
|
||||
.name = BDRV_OPT_FORCE_SHARE,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "always accept other writers (default: off)",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@ -1189,6 +1215,16 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
||||
drv = bdrv_find_format(driver_name);
|
||||
assert(drv != NULL);
|
||||
|
||||
bs->force_share = qemu_opt_get_bool(opts, BDRV_OPT_FORCE_SHARE, false);
|
||||
|
||||
if (bs->force_share && (bs->open_flags & BDRV_O_RDWR)) {
|
||||
error_setg(errp,
|
||||
BDRV_OPT_FORCE_SHARE
|
||||
"=on can only be used with read-only images");
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
if (file != NULL) {
|
||||
filename = blk_bs(file)->filename;
|
||||
} else {
|
||||
@ -1448,6 +1484,22 @@ static int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
|
||||
static void bdrv_child_abort_perm_update(BdrvChild *c);
|
||||
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
|
||||
|
||||
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
|
||||
BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
uint64_t parent_perm, uint64_t parent_shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
if (bs->drv && bs->drv->bdrv_child_perm) {
|
||||
bs->drv->bdrv_child_perm(bs, c, role,
|
||||
parent_perm, parent_shared,
|
||||
nperm, nshared);
|
||||
}
|
||||
if (child_bs && child_bs->force_share) {
|
||||
*nshared = BLK_PERM_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether permissions on this node can be changed in a way that
|
||||
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
|
||||
@ -1467,7 +1519,7 @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
|
||||
|
||||
/* Write permissions never work with read-only images */
|
||||
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
|
||||
bdrv_is_read_only(bs))
|
||||
!bdrv_is_writable(bs))
|
||||
{
|
||||
error_setg(errp, "Block node is read-only");
|
||||
return -EPERM;
|
||||
@ -1492,9 +1544,9 @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
|
||||
/* Check all children */
|
||||
QLIST_FOREACH(c, &bs->children, next) {
|
||||
uint64_t cur_perm, cur_shared;
|
||||
drv->bdrv_child_perm(bs, c, c->role,
|
||||
cumulative_perms, cumulative_shared_perms,
|
||||
&cur_perm, &cur_shared);
|
||||
bdrv_child_perm(bs, c->bs, c, c->role,
|
||||
cumulative_perms, cumulative_shared_perms,
|
||||
&cur_perm, &cur_shared);
|
||||
ret = bdrv_child_check_perm(c, cur_perm, cur_shared, ignore_children,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
@ -1554,9 +1606,9 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
|
||||
/* Update all children */
|
||||
QLIST_FOREACH(c, &bs->children, next) {
|
||||
uint64_t cur_perm, cur_shared;
|
||||
drv->bdrv_child_perm(bs, c, c->role,
|
||||
cumulative_perms, cumulative_shared_perms,
|
||||
&cur_perm, &cur_shared);
|
||||
bdrv_child_perm(bs, c->bs, c, c->role,
|
||||
cumulative_perms, cumulative_shared_perms,
|
||||
&cur_perm, &cur_shared);
|
||||
bdrv_child_set_perm(c, cur_perm, cur_shared);
|
||||
}
|
||||
}
|
||||
@ -1586,7 +1638,7 @@ static char *bdrv_child_user_desc(BdrvChild *c)
|
||||
return g_strdup("another user");
|
||||
}
|
||||
|
||||
static char *bdrv_perm_names(uint64_t perm)
|
||||
char *bdrv_perm_names(uint64_t perm)
|
||||
{
|
||||
struct perm_name {
|
||||
uint64_t perm;
|
||||
@ -1752,7 +1804,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
||||
bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
|
||||
|
||||
/* Format drivers may touch metadata even if the guest doesn't write */
|
||||
if (!bdrv_is_read_only(bs)) {
|
||||
if (bdrv_is_writable(bs)) {
|
||||
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
||||
}
|
||||
|
||||
@ -1778,6 +1830,10 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
||||
BLK_PERM_WRITE_UNCHANGED;
|
||||
}
|
||||
|
||||
if (bs->open_flags & BDRV_O_INACTIVE) {
|
||||
shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
||||
}
|
||||
|
||||
*nperm = perm;
|
||||
*nshared = shared;
|
||||
}
|
||||
@ -1891,8 +1947,8 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
|
||||
|
||||
assert(parent_bs->drv);
|
||||
assert(bdrv_get_aio_context(parent_bs) == bdrv_get_aio_context(child_bs));
|
||||
parent_bs->drv->bdrv_child_perm(parent_bs, NULL, child_role,
|
||||
perm, shared_perm, &perm, &shared_perm);
|
||||
bdrv_child_perm(parent_bs, child_bs, NULL, child_role,
|
||||
perm, shared_perm, &perm, &shared_perm);
|
||||
|
||||
child = bdrv_root_attach_child(child_bs, child_name, child_role,
|
||||
perm, shared_perm, parent_bs, errp);
|
||||
@ -3916,7 +3972,8 @@ void bdrv_init_with_whitelist(void)
|
||||
|
||||
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BdrvChild *child;
|
||||
BdrvChild *child, *parent;
|
||||
uint64_t perm, shared_perm;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
@ -3952,6 +4009,26 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
error_setg_errno(errp, -ret, "Could not refresh total sector count");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update permissions, they may differ for inactive nodes */
|
||||
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||
ret = bdrv_check_perm(bs, perm, shared_perm, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
bdrv_set_perm(bs, perm, shared_perm);
|
||||
|
||||
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
||||
if (parent->role->activate) {
|
||||
parent->role->activate(parent, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_invalidate_cache_all(Error **errp)
|
||||
@ -3976,7 +4053,7 @@ void bdrv_invalidate_cache_all(Error **errp)
|
||||
static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
||||
bool setting_flag)
|
||||
{
|
||||
BdrvChild *child;
|
||||
BdrvChild *child, *parent;
|
||||
int ret;
|
||||
|
||||
if (!setting_flag && bs->drv->bdrv_inactivate) {
|
||||
@ -3986,6 +4063,27 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
if (setting_flag) {
|
||||
uint64_t perm, shared_perm;
|
||||
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
|
||||
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
||||
if (parent->role->inactivate) {
|
||||
ret = parent->role->inactivate(parent);
|
||||
if (ret < 0) {
|
||||
bs->open_flags &= ~BDRV_O_INACTIVE;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update permissions, they may differ for inactive nodes */
|
||||
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||
bdrv_check_perm(bs, perm, shared_perm, NULL, &error_abort);
|
||||
bdrv_set_perm(bs, perm, shared_perm);
|
||||
}
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
ret = bdrv_inactivate_recurse(child->bs, setting_flag);
|
||||
if (ret < 0) {
|
||||
@ -3993,9 +4091,6 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
if (setting_flag) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
266
block/blkdebug.c
266
block/blkdebug.c
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Block protocol for I/O error injection
|
||||
*
|
||||
* Copyright (C) 2016-2017 Red Hat, Inc.
|
||||
* Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@ -37,7 +38,12 @@
|
||||
typedef struct BDRVBlkdebugState {
|
||||
int state;
|
||||
int new_state;
|
||||
int align;
|
||||
uint64_t align;
|
||||
uint64_t max_transfer;
|
||||
uint64_t opt_write_zero;
|
||||
uint64_t max_write_zero;
|
||||
uint64_t opt_discard;
|
||||
uint64_t max_discard;
|
||||
|
||||
/* For blkdebug_refresh_filename() */
|
||||
char *config_file;
|
||||
@ -342,6 +348,31 @@ static QemuOptsList runtime_opts = {
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Required alignment in bytes",
|
||||
},
|
||||
{
|
||||
.name = "max-transfer",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Maximum transfer size in bytes",
|
||||
},
|
||||
{
|
||||
.name = "opt-write-zero",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Optimum write zero alignment in bytes",
|
||||
},
|
||||
{
|
||||
.name = "max-write-zero",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Maximum write zero size in bytes",
|
||||
},
|
||||
{
|
||||
.name = "opt-discard",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Optimum discard alignment in bytes",
|
||||
},
|
||||
{
|
||||
.name = "max-discard",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Maximum discard size in bytes",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@ -352,8 +383,8 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
uint64_t align;
|
||||
int ret;
|
||||
uint64_t align;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
@ -382,19 +413,69 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set request alignment */
|
||||
align = qemu_opt_get_size(opts, "align", 0);
|
||||
if (align < INT_MAX && is_power_of_2(align)) {
|
||||
s->align = align;
|
||||
} else if (align) {
|
||||
error_setg(errp, "Invalid alignment");
|
||||
ret = -EINVAL;
|
||||
bs->supported_write_flags = BDRV_REQ_FUA &
|
||||
bs->file->bs->supported_write_flags;
|
||||
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
||||
bs->file->bs->supported_zero_flags;
|
||||
ret = -EINVAL;
|
||||
|
||||
/* Set alignment overrides */
|
||||
s->align = qemu_opt_get_size(opts, "align", 0);
|
||||
if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) {
|
||||
error_setg(errp, "Cannot meet constraints with align %" PRIu64,
|
||||
s->align);
|
||||
goto out;
|
||||
}
|
||||
align = MAX(s->align, bs->file->bs->bl.request_alignment);
|
||||
|
||||
s->max_transfer = qemu_opt_get_size(opts, "max-transfer", 0);
|
||||
if (s->max_transfer &&
|
||||
(s->max_transfer >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->max_transfer, align))) {
|
||||
error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64,
|
||||
s->max_transfer);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero", 0);
|
||||
if (s->opt_write_zero &&
|
||||
(s->opt_write_zero >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->opt_write_zero, align))) {
|
||||
error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64,
|
||||
s->opt_write_zero);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero", 0);
|
||||
if (s->max_write_zero &&
|
||||
(s->max_write_zero >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->max_write_zero,
|
||||
MAX(s->opt_write_zero, align)))) {
|
||||
error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64,
|
||||
s->max_write_zero);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->opt_discard = qemu_opt_get_size(opts, "opt-discard", 0);
|
||||
if (s->opt_discard &&
|
||||
(s->opt_discard >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->opt_discard, align))) {
|
||||
error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64,
|
||||
s->opt_discard);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->max_discard = qemu_opt_get_size(opts, "max-discard", 0);
|
||||
if (s->max_discard &&
|
||||
(s->max_discard >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->max_discard,
|
||||
MAX(s->opt_discard, align)))) {
|
||||
error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64,
|
||||
s->max_discard);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (ret < 0) {
|
||||
g_free(s->config_file);
|
||||
@ -403,11 +484,30 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int inject_error(BlockDriverState *bs, BlkdebugRule *rule)
|
||||
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
int error = rule->options.inject.error;
|
||||
bool immediately = rule->options.inject.immediately;
|
||||
BlkdebugRule *rule = NULL;
|
||||
int error;
|
||||
bool immediately;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
if (inject_offset == -1 ||
|
||||
(bytes && inject_offset >= offset &&
|
||||
inject_offset < offset + bytes))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rule || !rule->options.inject.error) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
immediately = rule->options.inject.immediately;
|
||||
error = rule->options.inject.error;
|
||||
|
||||
if (rule->options.inject.once) {
|
||||
QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
|
||||
@ -426,21 +526,18 @@ static int coroutine_fn
|
||||
blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
int err;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
if (inject_offset == -1 ||
|
||||
(inject_offset >= offset && inject_offset < offset + bytes))
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* Sanity check block layer guarantees */
|
||||
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
|
||||
if (bs->bl.max_transfer) {
|
||||
assert(bytes <= bs->bl.max_transfer);
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, rule);
|
||||
err = rule_check(bs, offset, bytes);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||
@ -450,21 +547,18 @@ static int coroutine_fn
|
||||
blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
int err;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
if (inject_offset == -1 ||
|
||||
(inject_offset >= offset && inject_offset < offset + bytes))
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* Sanity check block layer guarantees */
|
||||
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
|
||||
if (bs->bl.max_transfer) {
|
||||
assert(bytes <= bs->bl.max_transfer);
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, rule);
|
||||
err = rule_check(bs, offset, bytes);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||
@ -472,22 +566,81 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
|
||||
static int blkdebug_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
int err = rule_check(bs, 0, 0);
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
if (rule->options.inject.offset == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, rule);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_flush(bs->file->bs);
|
||||
}
|
||||
|
||||
static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int count,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
uint32_t align = MAX(bs->bl.request_alignment,
|
||||
bs->bl.pwrite_zeroes_alignment);
|
||||
int err;
|
||||
|
||||
/* Only pass through requests that are larger than requested
|
||||
* preferred alignment (so that we test the fallback to writes on
|
||||
* unaligned portions), and check that the block layer never hands
|
||||
* us anything unaligned that crosses an alignment boundary. */
|
||||
if (count < align) {
|
||||
assert(QEMU_IS_ALIGNED(offset, align) ||
|
||||
QEMU_IS_ALIGNED(offset + count, align) ||
|
||||
DIV_ROUND_UP(offset, align) ==
|
||||
DIV_ROUND_UP(offset + count, align));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
assert(QEMU_IS_ALIGNED(offset, align));
|
||||
assert(QEMU_IS_ALIGNED(count, align));
|
||||
if (bs->bl.max_pwrite_zeroes) {
|
||||
assert(count <= bs->bl.max_pwrite_zeroes);
|
||||
}
|
||||
|
||||
err = rule_check(bs, offset, count);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_pwrite_zeroes(bs->file, offset, count, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int count)
|
||||
{
|
||||
uint32_t align = bs->bl.pdiscard_alignment;
|
||||
int err;
|
||||
|
||||
/* Only pass through requests that are larger than requested
|
||||
* minimum alignment, and ensure that unaligned requests do not
|
||||
* cross optimum discard boundaries. */
|
||||
if (count < bs->bl.request_alignment) {
|
||||
assert(QEMU_IS_ALIGNED(offset, align) ||
|
||||
QEMU_IS_ALIGNED(offset + count, align) ||
|
||||
DIV_ROUND_UP(offset, align) ==
|
||||
DIV_ROUND_UP(offset + count, align));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||
assert(QEMU_IS_ALIGNED(count, bs->bl.request_alignment));
|
||||
if (align && count >= align) {
|
||||
assert(QEMU_IS_ALIGNED(offset, align));
|
||||
assert(QEMU_IS_ALIGNED(count, align));
|
||||
}
|
||||
if (bs->bl.max_pdiscard) {
|
||||
assert(count <= bs->bl.max_pdiscard);
|
||||
}
|
||||
|
||||
err = rule_check(bs, offset, count);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_pdiscard(bs->file->bs, offset, count);
|
||||
}
|
||||
|
||||
static void blkdebug_close(BlockDriverState *bs)
|
||||
{
|
||||
@ -715,6 +868,21 @@ static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
if (s->align) {
|
||||
bs->bl.request_alignment = s->align;
|
||||
}
|
||||
if (s->max_transfer) {
|
||||
bs->bl.max_transfer = s->max_transfer;
|
||||
}
|
||||
if (s->opt_write_zero) {
|
||||
bs->bl.pwrite_zeroes_alignment = s->opt_write_zero;
|
||||
}
|
||||
if (s->max_write_zero) {
|
||||
bs->bl.max_pwrite_zeroes = s->max_write_zero;
|
||||
}
|
||||
if (s->opt_discard) {
|
||||
bs->bl.pdiscard_alignment = s->opt_discard;
|
||||
}
|
||||
if (s->max_discard) {
|
||||
bs->bl.max_pdiscard = s->max_discard;
|
||||
}
|
||||
}
|
||||
|
||||
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
@ -742,6 +910,8 @@ static BlockDriver bdrv_blkdebug = {
|
||||
.bdrv_co_preadv = blkdebug_co_preadv,
|
||||
.bdrv_co_pwritev = blkdebug_co_pwritev,
|
||||
.bdrv_co_flush_to_disk = blkdebug_co_flush,
|
||||
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = blkdebug_co_pdiscard,
|
||||
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
|
||||
|
@ -130,6 +130,56 @@ static const char *blk_root_get_name(BdrvChild *child)
|
||||
return blk_name(child->opaque);
|
||||
}
|
||||
|
||||
/*
|
||||
* Notifies the user of the BlockBackend that migration has completed. qdev
|
||||
* devices can tighten their permissions in response (specifically revoke
|
||||
* shared write permissions that we needed for storage migration).
|
||||
*
|
||||
* If an error is returned, the VM cannot be allowed to be resumed.
|
||||
*/
|
||||
static void blk_root_activate(BdrvChild *child, Error **errp)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!blk->disable_perm) {
|
||||
return;
|
||||
}
|
||||
|
||||
blk->disable_perm = false;
|
||||
|
||||
blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
blk->disable_perm = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int blk_root_inactivate(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
|
||||
if (blk->disable_perm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only inactivate BlockBackends for guest devices (which are inactive at
|
||||
* this point because the VM is stopped) and unattached monitor-owned
|
||||
* BlockBackends. If there is still any other user like a block job, then
|
||||
* we simply can't inactivate the image. */
|
||||
if (!blk->dev && !blk->name[0]) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
blk->disable_perm = true;
|
||||
if (blk->root) {
|
||||
bdrv_child_try_set_perm(blk->root, 0, BLK_PERM_ALL, &error_abort);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_root = {
|
||||
.inherit_options = blk_root_inherit_options,
|
||||
|
||||
@ -140,6 +190,9 @@ static const BdrvChildRole child_root = {
|
||||
|
||||
.drained_begin = blk_root_drained_begin,
|
||||
.drained_end = blk_root_drained_end,
|
||||
|
||||
.activate = blk_root_activate,
|
||||
.inactivate = blk_root_inactivate,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -601,34 +654,6 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm)
|
||||
*shared_perm = blk->shared_perm;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notifies the user of all BlockBackends that migration has completed. qdev
|
||||
* devices can tighten their permissions in response (specifically revoke
|
||||
* shared write permissions that we needed for storage migration).
|
||||
*
|
||||
* If an error is returned, the VM cannot be allowed to be resumed.
|
||||
*/
|
||||
void blk_resume_after_migration(Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
Error *local_err = NULL;
|
||||
|
||||
for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
|
||||
if (!blk->disable_perm) {
|
||||
continue;
|
||||
}
|
||||
|
||||
blk->disable_perm = false;
|
||||
|
||||
blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
blk->disable_perm = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int blk_do_attach_dev(BlockBackend *blk, void *dev)
|
||||
{
|
||||
if (blk->dev) {
|
||||
|
@ -129,12 +129,23 @@ do { \
|
||||
|
||||
#define MAX_BLOCKSIZE 4096
|
||||
|
||||
/* Posix file locking bytes. Libvirt takes byte 0, we start from higher bytes,
|
||||
* leaving a few more bytes for its future use. */
|
||||
#define RAW_LOCK_PERM_BASE 100
|
||||
#define RAW_LOCK_SHARED_BASE 200
|
||||
|
||||
typedef struct BDRVRawState {
|
||||
int fd;
|
||||
int lock_fd;
|
||||
bool use_lock;
|
||||
int type;
|
||||
int open_flags;
|
||||
size_t buf_align;
|
||||
|
||||
/* The current permissions. */
|
||||
uint64_t perm;
|
||||
uint64_t shared_perm;
|
||||
|
||||
#ifdef CONFIG_XFS
|
||||
bool is_xfs:1;
|
||||
#endif
|
||||
@ -392,6 +403,11 @@ static QemuOptsList raw_runtime_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "host AIO implementation (threads, native)",
|
||||
},
|
||||
{
|
||||
.name = "locking",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "file locking mode (on/off/auto, default: auto)",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@ -406,6 +422,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
BlockdevAioOptions aio, aio_default;
|
||||
int fd, ret;
|
||||
struct stat st;
|
||||
OnOffAuto locking;
|
||||
|
||||
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
@ -435,6 +452,37 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
}
|
||||
s->use_linux_aio = (aio == BLOCKDEV_AIO_OPTIONS_NATIVE);
|
||||
|
||||
locking = qapi_enum_parse(OnOffAuto_lookup, qemu_opt_get(opts, "locking"),
|
||||
ON_OFF_AUTO__MAX, ON_OFF_AUTO_AUTO, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
switch (locking) {
|
||||
case ON_OFF_AUTO_ON:
|
||||
s->use_lock = true;
|
||||
#ifndef F_OFD_SETLK
|
||||
fprintf(stderr,
|
||||
"File lock requested but OFD locking syscall is unavailable, "
|
||||
"falling back to POSIX file locks.\n"
|
||||
"Due to the implementation, locks can be lost unexpectedly.\n");
|
||||
#endif
|
||||
break;
|
||||
case ON_OFF_AUTO_OFF:
|
||||
s->use_lock = false;
|
||||
break;
|
||||
case ON_OFF_AUTO_AUTO:
|
||||
#ifdef F_OFD_SETLK
|
||||
s->use_lock = true;
|
||||
#else
|
||||
s->use_lock = false;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
s->open_flags = open_flags;
|
||||
raw_parse_flags(bdrv_flags, &s->open_flags);
|
||||
|
||||
@ -450,6 +498,21 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
}
|
||||
s->fd = fd;
|
||||
|
||||
s->lock_fd = -1;
|
||||
if (s->use_lock) {
|
||||
fd = qemu_open(filename, s->open_flags);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
error_setg_errno(errp, errno, "Could not open '%s' for locking",
|
||||
filename);
|
||||
qemu_close(s->fd);
|
||||
goto fail;
|
||||
}
|
||||
s->lock_fd = fd;
|
||||
}
|
||||
s->perm = 0;
|
||||
s->shared_perm = BLK_PERM_ALL;
|
||||
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
/* Currently Linux does AIO only for files opened with O_DIRECT */
|
||||
if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) {
|
||||
@ -537,6 +600,161 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return raw_open_common(bs, options, flags, 0, errp);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
RAW_PL_PREPARE,
|
||||
RAW_PL_COMMIT,
|
||||
RAW_PL_ABORT,
|
||||
} RawPermLockOp;
|
||||
|
||||
#define PERM_FOREACH(i) \
|
||||
for ((i) = 0; (1ULL << (i)) <= BLK_PERM_ALL; i++)
|
||||
|
||||
/* Lock bytes indicated by @perm_lock_bits and @shared_perm_lock_bits in the
|
||||
* file; if @unlock == true, also unlock the unneeded bytes.
|
||||
* @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
|
||||
*/
|
||||
static int raw_apply_lock_bytes(BDRVRawState *s,
|
||||
uint64_t perm_lock_bits,
|
||||
uint64_t shared_perm_lock_bits,
|
||||
bool unlock, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
PERM_FOREACH(i) {
|
||||
int off = RAW_LOCK_PERM_BASE + i;
|
||||
if (perm_lock_bits & (1ULL << i)) {
|
||||
ret = qemu_lock_fd(s->lock_fd, off, 1, false);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to lock byte %d", off);
|
||||
return ret;
|
||||
}
|
||||
} else if (unlock) {
|
||||
ret = qemu_unlock_fd(s->lock_fd, off, 1);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to unlock byte %d", off);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
PERM_FOREACH(i) {
|
||||
int off = RAW_LOCK_SHARED_BASE + i;
|
||||
if (shared_perm_lock_bits & (1ULL << i)) {
|
||||
ret = qemu_lock_fd(s->lock_fd, off, 1, false);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to lock byte %d", off);
|
||||
return ret;
|
||||
}
|
||||
} else if (unlock) {
|
||||
ret = qemu_unlock_fd(s->lock_fd, off, 1);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to unlock byte %d", off);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */
|
||||
static int raw_check_lock_bytes(BDRVRawState *s,
|
||||
uint64_t perm, uint64_t shared_perm,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
PERM_FOREACH(i) {
|
||||
int off = RAW_LOCK_SHARED_BASE + i;
|
||||
uint64_t p = 1ULL << i;
|
||||
if (perm & p) {
|
||||
ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
|
||||
if (ret) {
|
||||
char *perm_name = bdrv_perm_names(p);
|
||||
error_setg(errp,
|
||||
"Failed to get \"%s\" lock",
|
||||
perm_name);
|
||||
g_free(perm_name);
|
||||
error_append_hint(errp,
|
||||
"Is another process using the image?\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
PERM_FOREACH(i) {
|
||||
int off = RAW_LOCK_PERM_BASE + i;
|
||||
uint64_t p = 1ULL << i;
|
||||
if (!(shared_perm & p)) {
|
||||
ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
|
||||
if (ret) {
|
||||
char *perm_name = bdrv_perm_names(p);
|
||||
error_setg(errp,
|
||||
"Failed to get shared \"%s\" lock",
|
||||
perm_name);
|
||||
g_free(perm_name);
|
||||
error_append_hint(errp,
|
||||
"Is another process using the image?\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_handle_perm_lock(BlockDriverState *bs,
|
||||
RawPermLockOp op,
|
||||
uint64_t new_perm, uint64_t new_shared,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret = 0;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!s->use_lock) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bdrv_get_flags(bs) & BDRV_O_INACTIVE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(s->lock_fd > 0);
|
||||
|
||||
switch (op) {
|
||||
case RAW_PL_PREPARE:
|
||||
ret = raw_apply_lock_bytes(s, s->perm | new_perm,
|
||||
~s->shared_perm | ~new_shared,
|
||||
false, errp);
|
||||
if (!ret) {
|
||||
ret = raw_check_lock_bytes(s, new_perm, new_shared, errp);
|
||||
if (!ret) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
op = RAW_PL_ABORT;
|
||||
/* fall through to unlock bytes. */
|
||||
case RAW_PL_ABORT:
|
||||
raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err);
|
||||
if (local_err) {
|
||||
/* Theoretically the above call only unlocks bytes and it cannot
|
||||
* fail. Something weird happened, report it.
|
||||
*/
|
||||
error_report_err(local_err);
|
||||
}
|
||||
break;
|
||||
case RAW_PL_COMMIT:
|
||||
raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err);
|
||||
if (local_err) {
|
||||
/* Theoretically the above call only unlocks bytes and it cannot
|
||||
* fail. Something weird happened, report it.
|
||||
*/
|
||||
error_report_err(local_err);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
@ -1405,6 +1623,10 @@ static void raw_close(BlockDriverState *bs)
|
||||
qemu_close(s->fd);
|
||||
s->fd = -1;
|
||||
}
|
||||
if (s->lock_fd >= 0) {
|
||||
qemu_close(s->lock_fd);
|
||||
s->lock_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
|
||||
@ -1949,6 +2171,25 @@ static QemuOptsList raw_create_opts = {
|
||||
}
|
||||
};
|
||||
|
||||
static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
|
||||
Error **errp)
|
||||
{
|
||||
return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
|
||||
}
|
||||
|
||||
static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL);
|
||||
s->perm = perm;
|
||||
s->shared_perm = shared;
|
||||
}
|
||||
|
||||
static void raw_abort_perm_update(BlockDriverState *bs)
|
||||
{
|
||||
raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_file = {
|
||||
.format_name = "file",
|
||||
.protocol_name = "file",
|
||||
@ -1979,7 +2220,9 @@ BlockDriver bdrv_file = {
|
||||
.bdrv_get_info = raw_get_info,
|
||||
.bdrv_get_allocated_file_size
|
||||
= raw_get_allocated_file_size,
|
||||
|
||||
.bdrv_check_perm = raw_check_perm,
|
||||
.bdrv_set_perm = raw_set_perm,
|
||||
.bdrv_abort_perm_update = raw_abort_perm_update,
|
||||
.create_opts = &raw_create_opts,
|
||||
};
|
||||
|
||||
@ -2438,6 +2681,9 @@ static BlockDriver bdrv_host_device = {
|
||||
.bdrv_get_info = raw_get_info,
|
||||
.bdrv_get_allocated_file_size
|
||||
= raw_get_allocated_file_size,
|
||||
.bdrv_check_perm = raw_check_perm,
|
||||
.bdrv_set_perm = raw_set_perm,
|
||||
.bdrv_abort_perm_update = raw_abort_perm_update,
|
||||
.bdrv_probe_blocksizes = hdev_probe_blocksizes,
|
||||
.bdrv_probe_geometry = hdev_probe_geometry,
|
||||
|
||||
|
@ -344,6 +344,11 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (qdict_get_try_bool(options, "locking", false)) {
|
||||
error_setg(errp, "locking=on is not supported on Windows");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
filename = qemu_opt_get(opts, "filename");
|
||||
|
||||
use_aio = get_aio_option(opts, flags, &local_err);
|
||||
|
@ -309,14 +309,19 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
||||
uint64_t *l2_table, uint64_t stop_flags)
|
||||
{
|
||||
int i;
|
||||
QCow2ClusterType first_cluster_type;
|
||||
uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED;
|
||||
uint64_t first_entry = be64_to_cpu(l2_table[0]);
|
||||
uint64_t offset = first_entry & mask;
|
||||
|
||||
if (!offset)
|
||||
if (!offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(qcow2_get_cluster_type(first_entry) == QCOW2_CLUSTER_NORMAL);
|
||||
/* must be allocated */
|
||||
first_cluster_type = qcow2_get_cluster_type(first_entry);
|
||||
assert(first_cluster_type == QCOW2_CLUSTER_NORMAL ||
|
||||
first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC);
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
|
||||
@ -328,14 +333,21 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
||||
return i;
|
||||
}
|
||||
|
||||
static int count_contiguous_clusters_by_type(int nb_clusters,
|
||||
uint64_t *l2_table,
|
||||
int wanted_type)
|
||||
/*
|
||||
* Checks how many consecutive unallocated clusters in a given L2
|
||||
* table have the same cluster type.
|
||||
*/
|
||||
static int count_contiguous_clusters_unallocated(int nb_clusters,
|
||||
uint64_t *l2_table,
|
||||
QCow2ClusterType wanted_type)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(wanted_type == QCOW2_CLUSTER_ZERO_PLAIN ||
|
||||
wanted_type == QCOW2_CLUSTER_UNALLOCATED);
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
|
||||
uint64_t entry = be64_to_cpu(l2_table[i]);
|
||||
QCow2ClusterType type = qcow2_get_cluster_type(entry);
|
||||
|
||||
if (type != wanted_type) {
|
||||
break;
|
||||
@ -487,6 +499,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int l1_bits, c;
|
||||
unsigned int offset_in_cluster;
|
||||
uint64_t bytes_available, bytes_needed, nb_clusters;
|
||||
QCow2ClusterType type;
|
||||
int ret;
|
||||
|
||||
offset_in_cluster = offset_into_cluster(s, offset);
|
||||
@ -509,13 +522,13 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
l1_index = offset >> l1_bits;
|
||||
if (l1_index >= s->l1_size) {
|
||||
ret = QCOW2_CLUSTER_UNALLOCATED;
|
||||
type = QCOW2_CLUSTER_UNALLOCATED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
||||
if (!l2_offset) {
|
||||
ret = QCOW2_CLUSTER_UNALLOCATED;
|
||||
type = QCOW2_CLUSTER_UNALLOCATED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -544,38 +557,37 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
* true */
|
||||
assert(nb_clusters <= INT_MAX);
|
||||
|
||||
ret = qcow2_get_cluster_type(*cluster_offset);
|
||||
switch (ret) {
|
||||
type = qcow2_get_cluster_type(*cluster_offset);
|
||||
if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN ||
|
||||
type == QCOW2_CLUSTER_ZERO_ALLOC)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
|
||||
" in pre-v3 image (L2 offset: %#" PRIx64
|
||||
", L2 index: %#x)", l2_offset, l2_index);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
switch (type) {
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
/* Compressed clusters can only be processed one by one */
|
||||
c = 1;
|
||||
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (s->qcow_version < 3) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
|
||||
" in pre-v3 image (L2 offset: %#" PRIx64
|
||||
", L2 index: %#x)", l2_offset, l2_index);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
|
||||
QCOW2_CLUSTER_ZERO);
|
||||
*cluster_offset = 0;
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
/* how many empty clusters ? */
|
||||
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
|
||||
QCOW2_CLUSTER_UNALLOCATED);
|
||||
c = count_contiguous_clusters_unallocated(nb_clusters,
|
||||
&l2_table[l2_index], type);
|
||||
*cluster_offset = 0;
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
/* how many allocated clusters ? */
|
||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
||||
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
||||
*cluster_offset &= L2E_OFFSET_MASK;
|
||||
if (offset_into_cluster(s, *cluster_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset %#"
|
||||
qcow2_signal_corruption(bs, true, -1, -1,
|
||||
"Cluster allocation offset %#"
|
||||
PRIx64 " unaligned (L2 offset: %#" PRIx64
|
||||
", L2 index: %#x)", *cluster_offset,
|
||||
l2_offset, l2_index);
|
||||
@ -602,7 +614,7 @@ out:
|
||||
assert(bytes_available - offset_in_cluster <= UINT_MAX);
|
||||
*bytes = bytes_available - offset_in_cluster;
|
||||
|
||||
return ret;
|
||||
return type;
|
||||
|
||||
fail:
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
|
||||
@ -835,7 +847,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
* Don't discard clusters that reach a refcount of 0 (e.g. compressed
|
||||
* clusters), the next write will reuse them anyway.
|
||||
*/
|
||||
if (j != 0) {
|
||||
if (!m->keep_old_clusters && j != 0) {
|
||||
for (i = 0; i < j; i++) {
|
||||
qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
@ -860,7 +872,7 @@ static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters,
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]);
|
||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
|
||||
switch(cluster_type) {
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
@ -870,7 +882,8 @@ static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters,
|
||||
break;
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
@ -1132,8 +1145,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t entry;
|
||||
uint64_t nb_clusters;
|
||||
int ret;
|
||||
bool keep_old_clusters = false;
|
||||
|
||||
uint64_t alloc_cluster_offset;
|
||||
uint64_t alloc_cluster_offset = 0;
|
||||
|
||||
trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset,
|
||||
*bytes);
|
||||
@ -1170,31 +1184,54 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||
* wrong with our code. */
|
||||
assert(nb_clusters > 0);
|
||||
|
||||
if (qcow2_get_cluster_type(entry) == QCOW2_CLUSTER_ZERO_ALLOC &&
|
||||
(entry & QCOW_OFLAG_COPIED) &&
|
||||
(!*host_offset ||
|
||||
start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK)))
|
||||
{
|
||||
/* Try to reuse preallocated zero clusters; contiguous normal clusters
|
||||
* would be fine, too, but count_cow_clusters() above has limited
|
||||
* nb_clusters already to a range of COW clusters */
|
||||
int preallocated_nb_clusters =
|
||||
count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], QCOW_OFLAG_COPIED);
|
||||
assert(preallocated_nb_clusters > 0);
|
||||
|
||||
nb_clusters = preallocated_nb_clusters;
|
||||
alloc_cluster_offset = entry & L2E_OFFSET_MASK;
|
||||
|
||||
/* We want to reuse these clusters, so qcow2_alloc_cluster_link_l2()
|
||||
* should not free them. */
|
||||
keep_old_clusters = true;
|
||||
}
|
||||
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
|
||||
|
||||
/* Allocate, if necessary at a given offset in the image file */
|
||||
alloc_cluster_offset = start_of_cluster(s, *host_offset);
|
||||
ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
|
||||
&nb_clusters);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Can't extend contiguous allocation */
|
||||
if (nb_clusters == 0) {
|
||||
*bytes = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* !*host_offset would overwrite the image header and is reserved for "no
|
||||
* host offset preferred". If 0 was a valid host offset, it'd trigger the
|
||||
* following overlap check; do that now to avoid having an invalid value in
|
||||
* *host_offset. */
|
||||
if (!alloc_cluster_offset) {
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
|
||||
nb_clusters * s->cluster_size);
|
||||
assert(ret < 0);
|
||||
goto fail;
|
||||
/* Allocate, if necessary at a given offset in the image file */
|
||||
alloc_cluster_offset = start_of_cluster(s, *host_offset);
|
||||
ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
|
||||
&nb_clusters);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Can't extend contiguous allocation */
|
||||
if (nb_clusters == 0) {
|
||||
*bytes = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* !*host_offset would overwrite the image header and is reserved for
|
||||
* "no host offset preferred". If 0 was a valid host offset, it'd
|
||||
* trigger the following overlap check; do that now to avoid having an
|
||||
* invalid value in *host_offset. */
|
||||
if (!alloc_cluster_offset) {
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
|
||||
nb_clusters * s->cluster_size);
|
||||
assert(ret < 0);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1225,6 +1262,8 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||
.offset = start_of_cluster(s, guest_offset),
|
||||
.nb_clusters = nb_clusters,
|
||||
|
||||
.keep_old_clusters = keep_old_clusters,
|
||||
|
||||
.cow_start = {
|
||||
.offset = 0,
|
||||
.nb_bytes = offset_into_cluster(s, guest_offset),
|
||||
@ -1472,24 +1511,25 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
* but rather fall through to the backing file.
|
||||
*/
|
||||
switch (qcow2_get_cluster_type(old_l2_entry)) {
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
if (full_discard || !bs->backing) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
if (full_discard || !bs->backing) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (!full_discard) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
if (!full_discard) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
/* First remove L2 entries */
|
||||
@ -1509,35 +1549,36 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
return nb_clusters;
|
||||
}
|
||||
|
||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_sectors, enum qcow2_discard_type type, bool full_discard)
|
||||
int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, enum qcow2_discard_type type,
|
||||
bool full_discard)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t end_offset;
|
||||
uint64_t end_offset = offset + bytes;
|
||||
uint64_t nb_clusters;
|
||||
int64_t cleared;
|
||||
int ret;
|
||||
|
||||
end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS);
|
||||
|
||||
/* The caller must cluster-align start; round end down except at EOF */
|
||||
/* Caller must pass aligned values, except at image end */
|
||||
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||
if (end_offset != bs->total_sectors * BDRV_SECTOR_SIZE) {
|
||||
end_offset = start_of_cluster(s, end_offset);
|
||||
}
|
||||
assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
|
||||
end_offset == bs->total_sectors << BDRV_SECTOR_BITS);
|
||||
|
||||
nb_clusters = size_to_clusters(s, end_offset - offset);
|
||||
nb_clusters = size_to_clusters(s, bytes);
|
||||
|
||||
s->cache_discards = true;
|
||||
|
||||
/* Each L2 table is handled by its own loop iteration */
|
||||
while (nb_clusters > 0) {
|
||||
ret = discard_single_l2(bs, offset, nb_clusters, type, full_discard);
|
||||
if (ret < 0) {
|
||||
cleared = discard_single_l2(bs, offset, nb_clusters, type,
|
||||
full_discard);
|
||||
if (cleared < 0) {
|
||||
ret = cleared;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nb_clusters -= ret;
|
||||
offset += (ret * s->cluster_size);
|
||||
nb_clusters -= cleared;
|
||||
offset += (cleared * s->cluster_size);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -1561,6 +1602,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
int l2_index;
|
||||
int ret;
|
||||
int i;
|
||||
bool unmap = !!(flags & BDRV_REQ_MAY_UNMAP);
|
||||
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
|
||||
if (ret < 0) {
|
||||
@ -1573,12 +1615,22 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
uint64_t old_offset;
|
||||
QCow2ClusterType cluster_type;
|
||||
|
||||
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||
|
||||
/* Update L2 entries */
|
||||
/*
|
||||
* Minimize L2 changes if the cluster already reads back as
|
||||
* zeroes with correct allocation.
|
||||
*/
|
||||
cluster_type = qcow2_get_cluster_type(old_offset);
|
||||
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN ||
|
||||
(cluster_type == QCOW2_CLUSTER_ZERO_ALLOC && !unmap)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
|
||||
if (old_offset & QCOW_OFLAG_COMPRESSED || flags & BDRV_REQ_MAY_UNMAP) {
|
||||
if (cluster_type == QCOW2_CLUSTER_COMPRESSED || unmap) {
|
||||
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
|
||||
qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
|
||||
} else {
|
||||
@ -1591,31 +1643,39 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
return nb_clusters;
|
||||
}
|
||||
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors,
|
||||
int flags)
|
||||
int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, int flags)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t end_offset = offset + bytes;
|
||||
uint64_t nb_clusters;
|
||||
int64_t cleared;
|
||||
int ret;
|
||||
|
||||
/* Caller must pass aligned values, except at image end */
|
||||
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
|
||||
end_offset == bs->total_sectors << BDRV_SECTOR_BITS);
|
||||
|
||||
/* The zero flag is only supported by version 3 and newer */
|
||||
if (s->qcow_version < 3) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Each L2 table is handled by its own loop iteration */
|
||||
nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS);
|
||||
nb_clusters = size_to_clusters(s, bytes);
|
||||
|
||||
s->cache_discards = true;
|
||||
|
||||
while (nb_clusters > 0) {
|
||||
ret = zero_single_l2(bs, offset, nb_clusters, flags);
|
||||
if (ret < 0) {
|
||||
cleared = zero_single_l2(bs, offset, nb_clusters, flags);
|
||||
if (cleared < 0) {
|
||||
ret = cleared;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nb_clusters -= ret;
|
||||
offset += (ret * s->cluster_size);
|
||||
nb_clusters -= cleared;
|
||||
offset += (cleared * s->cluster_size);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -1699,14 +1759,14 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
for (j = 0; j < s->l2_size; j++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
||||
int64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
bool preallocated = offset != 0;
|
||||
QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
|
||||
if (cluster_type != QCOW2_CLUSTER_ZERO) {
|
||||
if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN &&
|
||||
cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!preallocated) {
|
||||
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
|
||||
if (!bs->backing) {
|
||||
/* not backed; therefore we can simply deallocate the
|
||||
* cluster */
|
||||
@ -1741,7 +1801,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
"%#" PRIx64 " unaligned (L2 offset: %#"
|
||||
PRIx64 ", L2 index: %#x)", offset,
|
||||
l2_offset, j);
|
||||
if (!preallocated) {
|
||||
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
QCOW2_DISCARD_ALWAYS);
|
||||
}
|
||||
@ -1751,7 +1811,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
if (!preallocated) {
|
||||
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
QCOW2_DISCARD_ALWAYS);
|
||||
}
|
||||
@ -1760,7 +1820,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
|
||||
ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0);
|
||||
if (ret < 0) {
|
||||
if (!preallocated) {
|
||||
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
QCOW2_DISCARD_ALWAYS);
|
||||
}
|
||||
|
@ -1028,18 +1028,17 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (l2_entry & L2E_OFFSET_MASK) {
|
||||
if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, false, -1, -1,
|
||||
"Cannot free unaligned cluster %#llx",
|
||||
l2_entry & L2E_OFFSET_MASK);
|
||||
} else {
|
||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||
nb_clusters << s->cluster_bits, type);
|
||||
}
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, false, -1, -1,
|
||||
"Cannot free unaligned cluster %#llx",
|
||||
l2_entry & L2E_OFFSET_MASK);
|
||||
} else {
|
||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||
nb_clusters << s->cluster_bits, type);
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
break;
|
||||
default:
|
||||
@ -1059,9 +1058,9 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
int64_t l1_table_offset, int l1_size, int addend)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;
|
||||
uint64_t *l1_table, *l2_table, l2_offset, entry, l1_size2, refcount;
|
||||
bool l1_allocated = false;
|
||||
int64_t old_offset, old_l2_offset;
|
||||
int64_t old_entry, old_l2_offset;
|
||||
int i, j, l1_modified = 0, nb_csectors;
|
||||
int ret;
|
||||
|
||||
@ -1089,15 +1088,16 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for(i = 0;i < l1_size; i++)
|
||||
for (i = 0; i < l1_size; i++) {
|
||||
be64_to_cpus(&l1_table[i]);
|
||||
}
|
||||
} else {
|
||||
assert(l1_size == s->l1_size);
|
||||
l1_table = s->l1_table;
|
||||
l1_allocated = false;
|
||||
}
|
||||
|
||||
for(i = 0; i < l1_size; i++) {
|
||||
for (i = 0; i < l1_size; i++) {
|
||||
l2_offset = l1_table[i];
|
||||
if (l2_offset) {
|
||||
old_l2_offset = l2_offset;
|
||||
@ -1117,81 +1117,79 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for(j = 0; j < s->l2_size; j++) {
|
||||
for (j = 0; j < s->l2_size; j++) {
|
||||
uint64_t cluster_index;
|
||||
uint64_t offset;
|
||||
|
||||
offset = be64_to_cpu(l2_table[j]);
|
||||
old_offset = offset;
|
||||
offset &= ~QCOW_OFLAG_COPIED;
|
||||
entry = be64_to_cpu(l2_table[j]);
|
||||
old_entry = entry;
|
||||
entry &= ~QCOW_OFLAG_COPIED;
|
||||
offset = entry & L2E_OFFSET_MASK;
|
||||
|
||||
switch (qcow2_get_cluster_type(offset)) {
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
nb_csectors = ((offset >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
if (addend != 0) {
|
||||
ret = update_refcount(bs,
|
||||
(offset & s->cluster_offset_mask) & ~511,
|
||||
switch (qcow2_get_cluster_type(entry)) {
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
nb_csectors = ((entry >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
if (addend != 0) {
|
||||
ret = update_refcount(bs,
|
||||
(entry & s->cluster_offset_mask) & ~511,
|
||||
nb_csectors * 512, abs(addend), addend < 0,
|
||||
QCOW2_DISCARD_SNAPSHOT);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
/* compressed clusters are never modified */
|
||||
refcount = 2;
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Data "
|
||||
"cluster offset %#llx "
|
||||
"unaligned (L2 offset: %#"
|
||||
PRIx64 ", L2 index: %#x)",
|
||||
offset & L2E_OFFSET_MASK,
|
||||
l2_offset, j);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
|
||||
if (!cluster_index) {
|
||||
/* unallocated */
|
||||
refcount = 0;
|
||||
break;
|
||||
}
|
||||
if (addend != 0) {
|
||||
ret = qcow2_update_cluster_refcount(bs,
|
||||
cluster_index, abs(addend), addend < 0,
|
||||
QCOW2_DISCARD_SNAPSHOT);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_get_refcount(bs, cluster_index, &refcount);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* compressed clusters are never modified */
|
||||
refcount = 2;
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
refcount = 0;
|
||||
break;
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Cluster "
|
||||
"allocation offset %#" PRIx64
|
||||
" unaligned (L2 offset: %#"
|
||||
PRIx64 ", L2 index: %#x)",
|
||||
offset, l2_offset, j);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
default:
|
||||
abort();
|
||||
cluster_index = offset >> s->cluster_bits;
|
||||
assert(cluster_index);
|
||||
if (addend != 0) {
|
||||
ret = qcow2_update_cluster_refcount(bs,
|
||||
cluster_index, abs(addend), addend < 0,
|
||||
QCOW2_DISCARD_SNAPSHOT);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_get_refcount(bs, cluster_index, &refcount);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
refcount = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
if (refcount == 1) {
|
||||
offset |= QCOW_OFLAG_COPIED;
|
||||
entry |= QCOW_OFLAG_COPIED;
|
||||
}
|
||||
if (offset != old_offset) {
|
||||
if (entry != old_entry) {
|
||||
if (addend > 0) {
|
||||
qcow2_cache_set_dependency(bs, s->l2_table_cache,
|
||||
s->refcount_block_cache);
|
||||
}
|
||||
l2_table[j] = cpu_to_be64(offset);
|
||||
l2_table[j] = cpu_to_be64(entry);
|
||||
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache,
|
||||
l2_table);
|
||||
}
|
||||
@ -1441,12 +1439,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if ((l2_entry & L2E_OFFSET_MASK) == 0) {
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
{
|
||||
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||
@ -1476,6 +1469,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
break;
|
||||
}
|
||||
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
break;
|
||||
|
||||
@ -1638,10 +1632,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
for (j = 0; j < s->l2_size; j++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
||||
uint64_t data_offset = l2_entry & L2E_OFFSET_MASK;
|
||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
|
||||
if ((cluster_type == QCOW2_CLUSTER_NORMAL) ||
|
||||
((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) {
|
||||
if (cluster_type == QCOW2_CLUSTER_NORMAL ||
|
||||
cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||
ret = qcow2_get_refcount(bs,
|
||||
data_offset >> s->cluster_bits,
|
||||
&refcount);
|
||||
|
@ -440,10 +440,9 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
|
||||
/* The VM state isn't needed any more in the active L1 table; in fact, it
|
||||
* hurts by causing expensive COW for the next snapshot. */
|
||||
qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
|
||||
align_offset(sn->vm_state_size, s->cluster_size)
|
||||
>> BDRV_SECTOR_BITS,
|
||||
QCOW2_DISCARD_NEVER, false);
|
||||
qcow2_cluster_discard(bs, qcow2_vm_state_offset(s),
|
||||
align_offset(sn->vm_state_size, s->cluster_size),
|
||||
QCOW2_DISCARD_NEVER, false);
|
||||
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
|
@ -1385,7 +1385,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
||||
*file = bs->file->bs;
|
||||
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
|
||||
}
|
||||
if (ret == QCOW2_CLUSTER_ZERO) {
|
||||
if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||
status |= BDRV_BLOCK_ZERO;
|
||||
} else if (ret != QCOW2_CLUSTER_UNALLOCATED) {
|
||||
status |= BDRV_BLOCK_DATA;
|
||||
@ -1482,7 +1482,8 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
|
||||
break;
|
||||
|
||||
@ -2139,7 +2140,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
* too, as long as the bulk is allocated here). Therefore, using
|
||||
* floating point arithmetic is fine. */
|
||||
int64_t meta_size = 0;
|
||||
uint64_t nreftablee, nrefblocke, nl1e, nl2e;
|
||||
uint64_t nreftablee, nrefblocke, nl1e, nl2e, refblock_count;
|
||||
int64_t aligned_total_size = align_offset(total_size, cluster_size);
|
||||
int refblock_bits, refblock_size;
|
||||
/* refcount entry size in bytes */
|
||||
@ -2182,11 +2183,12 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
nrefblocke = (aligned_total_size + meta_size + cluster_size)
|
||||
/ (cluster_size - rces - rces * sizeof(uint64_t)
|
||||
/ cluster_size);
|
||||
meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size;
|
||||
refblock_count = DIV_ROUND_UP(nrefblocke, refblock_size);
|
||||
meta_size += refblock_count * cluster_size;
|
||||
|
||||
/* total size of refcount tables */
|
||||
nreftablee = nrefblocke / refblock_size;
|
||||
nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t));
|
||||
nreftablee = align_offset(refblock_count,
|
||||
cluster_size / sizeof(uint64_t));
|
||||
meta_size += nreftablee * sizeof(uint64_t);
|
||||
|
||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
|
||||
@ -2449,6 +2451,10 @@ static bool is_zero_sectors(BlockDriverState *bs, int64_t start,
|
||||
BlockDriverState *file;
|
||||
int64_t res;
|
||||
|
||||
if (start + count > bs->total_sectors) {
|
||||
count = bs->total_sectors - start;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
return true;
|
||||
}
|
||||
@ -2467,6 +2473,9 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
uint32_t tail = (offset + count) % s->cluster_size;
|
||||
|
||||
trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, count);
|
||||
if (offset + count == bs->total_sectors * BDRV_SECTOR_SIZE) {
|
||||
tail = 0;
|
||||
}
|
||||
|
||||
if (head || tail) {
|
||||
int64_t cl_start = (offset - head) >> BDRV_SECTOR_BITS;
|
||||
@ -2490,7 +2499,9 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
count = s->cluster_size;
|
||||
nr = s->cluster_size;
|
||||
ret = qcow2_get_cluster_offset(bs, offset, &nr, &off);
|
||||
if (ret != QCOW2_CLUSTER_UNALLOCATED && ret != QCOW2_CLUSTER_ZERO) {
|
||||
if (ret != QCOW2_CLUSTER_UNALLOCATED &&
|
||||
ret != QCOW2_CLUSTER_ZERO_PLAIN &&
|
||||
ret != QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@ -2501,7 +2512,7 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, count);
|
||||
|
||||
/* Whatever is left can use real zero clusters */
|
||||
ret = qcow2_zero_clusters(bs, offset, count >> BDRV_SECTOR_BITS, flags);
|
||||
ret = qcow2_cluster_zeroize(bs, offset, count, flags);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
return ret;
|
||||
@ -2524,8 +2535,8 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_discard_clusters(bs, offset, count >> BDRV_SECTOR_BITS,
|
||||
QCOW2_DISCARD_REQUEST, false);
|
||||
ret = qcow2_cluster_discard(bs, offset, count, QCOW2_DISCARD_REQUEST,
|
||||
false);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
@ -2832,9 +2843,8 @@ fail:
|
||||
static int qcow2_make_empty(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t start_sector;
|
||||
int sector_step = (QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size) /
|
||||
BDRV_SECTOR_SIZE);
|
||||
uint64_t offset, end_offset;
|
||||
int step = QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size);
|
||||
int l1_clusters, ret = 0;
|
||||
|
||||
l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t));
|
||||
@ -2851,18 +2861,15 @@ static int qcow2_make_empty(BlockDriverState *bs)
|
||||
|
||||
/* This fallback code simply discards every active cluster; this is slow,
|
||||
* but works in all cases */
|
||||
for (start_sector = 0; start_sector < bs->total_sectors;
|
||||
start_sector += sector_step)
|
||||
{
|
||||
end_offset = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
for (offset = 0; offset < end_offset; offset += step) {
|
||||
/* As this function is generally used after committing an external
|
||||
* snapshot, QCOW2_DISCARD_SNAPSHOT seems appropriate. Also, the
|
||||
* default action for this kind of discard is to pass the discard,
|
||||
* which will ideally result in an actually smaller image file, as
|
||||
* is probably desired. */
|
||||
ret = qcow2_discard_clusters(bs, start_sector * BDRV_SECTOR_SIZE,
|
||||
MIN(sector_step,
|
||||
bs->total_sectors - start_sector),
|
||||
QCOW2_DISCARD_SNAPSHOT, true);
|
||||
ret = qcow2_cluster_discard(bs, offset, MIN(step, end_offset - offset),
|
||||
QCOW2_DISCARD_SNAPSHOT, true);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
|
@ -322,6 +322,9 @@ typedef struct QCowL2Meta
|
||||
/** Number of newly allocated clusters */
|
||||
int nb_clusters;
|
||||
|
||||
/** Do not free the old clusters */
|
||||
bool keep_old_clusters;
|
||||
|
||||
/**
|
||||
* Requests that overlap with this allocation and wait to be restarted
|
||||
* when the allocating request has completed.
|
||||
@ -346,12 +349,13 @@ typedef struct QCowL2Meta
|
||||
QLIST_ENTRY(QCowL2Meta) next_in_flight;
|
||||
} QCowL2Meta;
|
||||
|
||||
enum {
|
||||
typedef enum QCow2ClusterType {
|
||||
QCOW2_CLUSTER_UNALLOCATED,
|
||||
QCOW2_CLUSTER_ZERO_PLAIN,
|
||||
QCOW2_CLUSTER_ZERO_ALLOC,
|
||||
QCOW2_CLUSTER_NORMAL,
|
||||
QCOW2_CLUSTER_COMPRESSED,
|
||||
QCOW2_CLUSTER_ZERO
|
||||
};
|
||||
} QCow2ClusterType;
|
||||
|
||||
typedef enum QCow2MetadataOverlap {
|
||||
QCOW2_OL_MAIN_HEADER_BITNR = 0,
|
||||
@ -440,12 +444,15 @@ static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s)
|
||||
return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
|
||||
}
|
||||
|
||||
static inline int qcow2_get_cluster_type(uint64_t l2_entry)
|
||||
static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry)
|
||||
{
|
||||
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
|
||||
return QCOW2_CLUSTER_COMPRESSED;
|
||||
} else if (l2_entry & QCOW_OFLAG_ZERO) {
|
||||
return QCOW2_CLUSTER_ZERO;
|
||||
if (l2_entry & L2E_OFFSET_MASK) {
|
||||
return QCOW2_CLUSTER_ZERO_ALLOC;
|
||||
}
|
||||
return QCOW2_CLUSTER_ZERO_PLAIN;
|
||||
} else if (!(l2_entry & L2E_OFFSET_MASK)) {
|
||||
return QCOW2_CLUSTER_UNALLOCATED;
|
||||
} else {
|
||||
@ -544,10 +551,11 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
int compressed_size);
|
||||
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
|
||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_sectors, enum qcow2_discard_type type, bool full_discard);
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors,
|
||||
int flags);
|
||||
int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, enum qcow2_discard_type type,
|
||||
bool full_discard);
|
||||
int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, int flags);
|
||||
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
|
20
blockdev.c
20
blockdev.c
@ -2923,10 +2923,9 @@ void qmp_block_resize(bool has_device, const char *device,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* complete all in-flight operations before resizing the device */
|
||||
bdrv_drain_all();
|
||||
|
||||
bdrv_drained_begin(bs);
|
||||
ret = blk_truncate(blk, size, errp);
|
||||
bdrv_drained_end(bs);
|
||||
|
||||
out:
|
||||
blk_unref(blk);
|
||||
@ -3151,6 +3150,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
Error *local_err = NULL;
|
||||
int flags;
|
||||
int64_t size;
|
||||
bool set_backing_hd = false;
|
||||
|
||||
if (!backup->has_speed) {
|
||||
backup->speed = 0;
|
||||
@ -3201,6 +3201,8 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
}
|
||||
if (backup->sync == MIRROR_SYNC_MODE_NONE) {
|
||||
source = bs;
|
||||
flags |= BDRV_O_NO_BACKING;
|
||||
set_backing_hd = true;
|
||||
}
|
||||
|
||||
size = bdrv_getlength(bs);
|
||||
@ -3227,7 +3229,9 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
}
|
||||
|
||||
if (backup->format) {
|
||||
options = qdict_new();
|
||||
if (!options) {
|
||||
options = qdict_new();
|
||||
}
|
||||
qdict_put_str(options, "driver", backup->format);
|
||||
}
|
||||
|
||||
@ -3238,6 +3242,14 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
|
||||
bdrv_set_aio_context(target_bs, aio_context);
|
||||
|
||||
if (set_backing_hd) {
|
||||
bdrv_set_backing_hd(target_bs, source, &local_err);
|
||||
if (local_err) {
|
||||
bdrv_unref(target_bs);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (backup->has_bitmap) {
|
||||
bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
|
||||
if (!bmap) {
|
||||
|
@ -227,6 +227,29 @@ static uint16_t nvme_flush(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
|
||||
return NVME_NO_COMPLETE;
|
||||
}
|
||||
|
||||
static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
|
||||
NvmeRequest *req)
|
||||
{
|
||||
NvmeRwCmd *rw = (NvmeRwCmd *)cmd;
|
||||
const uint8_t lba_index = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas);
|
||||
const uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds;
|
||||
uint64_t slba = le64_to_cpu(rw->slba);
|
||||
uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
|
||||
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
|
||||
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
|
||||
|
||||
if (slba + nlb > ns->id_ns.nsze) {
|
||||
return NVME_LBA_RANGE | NVME_DNR;
|
||||
}
|
||||
|
||||
req->has_sg = false;
|
||||
block_acct_start(blk_get_stats(n->conf.blk), &req->acct, 0,
|
||||
BLOCK_ACCT_WRITE);
|
||||
req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, aio_slba, aio_nlb,
|
||||
BDRV_REQ_MAY_UNMAP, nvme_rw_cb, req);
|
||||
return NVME_NO_COMPLETE;
|
||||
}
|
||||
|
||||
static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
|
||||
NvmeRequest *req)
|
||||
{
|
||||
@ -279,6 +302,8 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
||||
switch (cmd->opcode) {
|
||||
case NVME_CMD_FLUSH:
|
||||
return nvme_flush(n, ns, cmd, req);
|
||||
case NVME_CMD_WRITE_ZEROS:
|
||||
return nvme_write_zeros(n, ns, cmd, req);
|
||||
case NVME_CMD_WRITE:
|
||||
case NVME_CMD_READ:
|
||||
return nvme_rw(n, ns, cmd, req);
|
||||
@ -895,6 +920,7 @@ static int nvme_init(PCIDevice *pci_dev)
|
||||
id->sqes = (0x6 << 4) | 0x6;
|
||||
id->cqes = (0x4 << 4) | 0x4;
|
||||
id->nn = cpu_to_le32(n->num_namespaces);
|
||||
id->oncs = cpu_to_le16(NVME_ONCS_WRITE_ZEROS);
|
||||
id->psd[0].mp = cpu_to_le16(0x9c4);
|
||||
id->psd[0].enlat = cpu_to_le32(0x10);
|
||||
id->psd[0].exlat = cpu_to_le32(0x4);
|
||||
|
@ -179,6 +179,7 @@ enum NvmeIoCommands {
|
||||
NVME_CMD_READ = 0x02,
|
||||
NVME_CMD_WRITE_UNCOR = 0x04,
|
||||
NVME_CMD_COMPARE = 0x05,
|
||||
NVME_CMD_WRITE_ZEROS = 0x08,
|
||||
NVME_CMD_DSM = 0x09,
|
||||
};
|
||||
|
||||
|
@ -109,6 +109,7 @@ typedef struct HDGeometry {
|
||||
#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush"
|
||||
#define BDRV_OPT_READ_ONLY "read-only"
|
||||
#define BDRV_OPT_DISCARD "discard"
|
||||
#define BDRV_OPT_FORCE_SHARE "force-share"
|
||||
|
||||
|
||||
#define BDRV_SECTOR_BITS 9
|
||||
@ -120,29 +121,32 @@ typedef struct HDGeometry {
|
||||
#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS)
|
||||
|
||||
/*
|
||||
* Allocation status flags
|
||||
* BDRV_BLOCK_DATA: data is read from a file returned by bdrv_get_block_status.
|
||||
* BDRV_BLOCK_ZERO: sectors read as zero
|
||||
* BDRV_BLOCK_OFFSET_VALID: sector stored as raw data in a file returned by
|
||||
* bdrv_get_block_status.
|
||||
* Allocation status flags for bdrv_get_block_status() and friends.
|
||||
*
|
||||
* Public flags:
|
||||
* BDRV_BLOCK_DATA: allocation for data at offset is tied to this layer
|
||||
* BDRV_BLOCK_ZERO: offset reads as zero
|
||||
* BDRV_BLOCK_OFFSET_VALID: an associated offset exists for accessing raw data
|
||||
* BDRV_BLOCK_ALLOCATED: the content of the block is determined by this
|
||||
* layer (as opposed to the backing file)
|
||||
* BDRV_BLOCK_RAW: used internally to indicate that the request
|
||||
* was answered by the raw driver and that one
|
||||
* should look in bs->file directly.
|
||||
* layer (short for DATA || ZERO), set by block layer
|
||||
*
|
||||
* If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 represent the offset in
|
||||
* bs->file where sector data can be read from as raw data.
|
||||
* Internal flag:
|
||||
* BDRV_BLOCK_RAW: used internally to indicate that the request was
|
||||
* answered by a passthrough driver such as raw and that the
|
||||
* block layer should recompute the answer from bs->file.
|
||||
*
|
||||
* DATA == 0 && ZERO == 0 means that data is read from backing_hd if present.
|
||||
* If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 (BDRV_BLOCK_OFFSET_MASK)
|
||||
* represent the offset in the returned BDS that is allocated for the
|
||||
* corresponding raw data; however, whether that offset actually contains
|
||||
* data also depends on BDRV_BLOCK_DATA and BDRV_BLOCK_ZERO, as follows:
|
||||
*
|
||||
* DATA ZERO OFFSET_VALID
|
||||
* t t t sectors read as zero, bs->file is zero at offset
|
||||
* t f t sectors read as valid from bs->file at offset
|
||||
* f t t sectors preallocated, read as zero, bs->file not
|
||||
* t t t sectors read as zero, returned file is zero at offset
|
||||
* t f t sectors read as valid from file at offset
|
||||
* f t t sectors preallocated, read as zero, returned file not
|
||||
* necessarily zero at offset
|
||||
* f f t sectors preallocated but read from backing_hd,
|
||||
* bs->file contains garbage at offset
|
||||
* returned file contains garbage at offset
|
||||
* t t f sectors preallocated, read as zero, unknown offset
|
||||
* t f f sectors read from unknown file or offset
|
||||
* f t f not allocated or unknown offset, read as zero
|
||||
@ -224,6 +228,8 @@ enum {
|
||||
BLK_PERM_ALL = 0x1f,
|
||||
};
|
||||
|
||||
char *bdrv_perm_names(uint64_t perm);
|
||||
|
||||
/* disk I/O throttling */
|
||||
void bdrv_init(void);
|
||||
void bdrv_init_with_whitelist(void);
|
||||
@ -366,8 +372,6 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp);
|
||||
void bdrv_invalidate_cache_all(Error **errp);
|
||||
int bdrv_inactivate_all(void);
|
||||
|
||||
void blk_resume_after_migration(Error **errp);
|
||||
|
||||
/* Ensure contents are flushed to disk. */
|
||||
int bdrv_flush(BlockDriverState *bs);
|
||||
int coroutine_fn bdrv_co_flush(BlockDriverState *bs);
|
||||
@ -434,6 +438,7 @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
||||
int64_t sector_num, int nb_sectors, int *pnum);
|
||||
|
||||
bool bdrv_is_read_only(BlockDriverState *bs);
|
||||
bool bdrv_is_writable(BlockDriverState *bs);
|
||||
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
|
||||
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
|
||||
bool bdrv_is_sg(BlockDriverState *bs);
|
||||
|
@ -165,6 +165,13 @@ struct BlockDriver {
|
||||
int64_t offset, int count, BdrvRequestFlags flags);
|
||||
int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs,
|
||||
int64_t offset, int count);
|
||||
|
||||
/*
|
||||
* Building block for bdrv_block_status[_above]. The driver should
|
||||
* answer only according to the current layer, and should not
|
||||
* set BDRV_BLOCK_ALLOCATED, but may set BDRV_BLOCK_RAW. See block.h
|
||||
* for the meaning of _DATA, _ZERO, and _OFFSET_VALID.
|
||||
*/
|
||||
int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum,
|
||||
BlockDriverState **file);
|
||||
@ -473,6 +480,12 @@ struct BdrvChildRole {
|
||||
void (*drained_begin)(BdrvChild *child);
|
||||
void (*drained_end)(BdrvChild *child);
|
||||
|
||||
/* Notifies the parent that the child has been activated/inactivated (e.g.
|
||||
* when migration is completing) and it can start/stop requesting
|
||||
* permissions and doing I/O on it. */
|
||||
void (*activate)(BdrvChild *child, Error **errp);
|
||||
int (*inactivate)(BdrvChild *child);
|
||||
|
||||
void (*attach)(BdrvChild *child);
|
||||
void (*detach)(BdrvChild *child);
|
||||
};
|
||||
@ -518,6 +531,7 @@ struct BlockDriverState {
|
||||
bool valid_key; /* if true, a valid encryption key has been set */
|
||||
bool sg; /* if true, the device is a /dev/sg* */
|
||||
bool probed; /* if true, format was probed rather than specified */
|
||||
bool force_share; /* if true, always allow all shared permissions */
|
||||
|
||||
BlockDriver *drv; /* NULL means no media */
|
||||
void *opaque;
|
||||
|
@ -341,6 +341,9 @@ int qemu_close(int fd);
|
||||
#ifndef _WIN32
|
||||
int qemu_dup(int fd);
|
||||
#endif
|
||||
int qemu_lock_fd(int fd, int64_t start, int64_t len, bool exclusive);
|
||||
int qemu_unlock_fd(int fd, int64_t start, int64_t len);
|
||||
int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive);
|
||||
|
||||
#if defined(__HAIKU__) && defined(__i386__)
|
||||
#define FMT_pid "%ld"
|
||||
|
@ -338,20 +338,11 @@ static void process_incoming_migration_bh(void *opaque)
|
||||
Error *local_err = NULL;
|
||||
MigrationIncomingState *mis = opaque;
|
||||
|
||||
/* Make sure all file formats flush their mutable metadata */
|
||||
/* Make sure all file formats flush their mutable metadata.
|
||||
* If we get an error here, just don't restart the VM yet. */
|
||||
bdrv_invalidate_cache_all(&local_err);
|
||||
if (local_err) {
|
||||
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
||||
MIGRATION_STATUS_FAILED);
|
||||
error_report_err(local_err);
|
||||
migrate_decompress_threads_join();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* If we get an error here, just don't restart the VM yet. */
|
||||
blk_resume_after_migration(&local_err);
|
||||
if (local_err) {
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
autostart = false;
|
||||
}
|
||||
|
@ -1612,16 +1612,11 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
|
||||
|
||||
qemu_announce_self();
|
||||
|
||||
/* Make sure all file formats flush their mutable metadata */
|
||||
/* Make sure all file formats flush their mutable metadata.
|
||||
* If we get an error here, just don't restart the VM yet. */
|
||||
bdrv_invalidate_cache_all(&local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
|
||||
/* If we get an error here, just don't restart the VM yet. */
|
||||
blk_resume_after_migration(&local_err);
|
||||
if (local_err) {
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
autostart = false;
|
||||
}
|
||||
|
@ -2127,11 +2127,15 @@
|
||||
#
|
||||
# @filename: path to the image file
|
||||
# @aio: AIO backend (default: threads) (since: 2.8)
|
||||
# @locking: whether to enable file locking. If set to 'auto', only enable
|
||||
# when Open File Descriptor (OFD) locking API is available
|
||||
# (default: auto, since 2.10)
|
||||
#
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'struct': 'BlockdevOptionsFile',
|
||||
'data': { 'filename': 'str',
|
||||
'*locking': 'OnOffAuto',
|
||||
'*aio': 'BlockdevAioOptions' } }
|
||||
|
||||
##
|
||||
@ -2430,8 +2434,33 @@
|
||||
#
|
||||
# @config: filename of the configuration file
|
||||
#
|
||||
# @align: required alignment for requests in bytes,
|
||||
# must be power of 2, or 0 for default
|
||||
# @align: required alignment for requests in bytes, must be
|
||||
# positive power of 2, or 0 for default
|
||||
#
|
||||
# @max-transfer: maximum size for I/O transfers in bytes, must be
|
||||
# positive multiple of @align and of the underlying
|
||||
# file's request alignment (but need not be a power of
|
||||
# 2), or 0 for default (since 2.10)
|
||||
#
|
||||
# @opt-write-zero: preferred alignment for write zero requests in bytes,
|
||||
# must be positive multiple of @align and of the
|
||||
# underlying file's request alignment (but need not be a
|
||||
# power of 2), or 0 for default (since 2.10)
|
||||
#
|
||||
# @max-write-zero: maximum size for write zero requests in bytes, must be
|
||||
# positive multiple of @align, of @opt-write-zero, and of
|
||||
# the underlying file's request alignment (but need not
|
||||
# be a power of 2), or 0 for default (since 2.10)
|
||||
#
|
||||
# @opt-discard: preferred alignment for discard requests in bytes, must
|
||||
# be positive multiple of @align and of the underlying
|
||||
# file's request alignment (but need not be a power of
|
||||
# 2), or 0 for default (since 2.10)
|
||||
#
|
||||
# @max-discard: maximum size for discard requests in bytes, must be
|
||||
# positive multiple of @align, of @opt-discard, and of
|
||||
# the underlying file's request alignment (but need not
|
||||
# be a power of 2), or 0 for default (since 2.10)
|
||||
#
|
||||
# @inject-error: array of error injection descriptions
|
||||
#
|
||||
@ -2442,7 +2471,9 @@
|
||||
{ 'struct': 'BlockdevOptionsBlkdebug',
|
||||
'data': { 'image': 'BlockdevRef',
|
||||
'*config': 'str',
|
||||
'*align': 'int',
|
||||
'*align': 'int', '*max-transfer': 'int32',
|
||||
'*opt-write-zero': 'int32', '*max-write-zero': 'int32',
|
||||
'*opt-discard': 'int32', '*max-discard': 'int32',
|
||||
'*inject-error': ['BlkdebugInjectErrorOptions'],
|
||||
'*set-state': ['BlkdebugSetStateOptions'] } }
|
||||
|
||||
@ -2898,6 +2929,8 @@
|
||||
# (default: false)
|
||||
# @detect-zeroes: detect and optimize zero writes (Since 2.1)
|
||||
# (default: off)
|
||||
# @force-share: force share all permission on added nodes.
|
||||
# Requires read-only=true. (Since 2.10)
|
||||
#
|
||||
# Remaining options are determined by the block driver.
|
||||
#
|
||||
@ -2909,6 +2942,7 @@
|
||||
'*discard': 'BlockdevDiscardOptions',
|
||||
'*cache': 'BlockdevCacheOptions',
|
||||
'*read-only': 'bool',
|
||||
'*force-share': 'bool',
|
||||
'*detect-zeroes': 'BlockdevDetectZeroesOptions' },
|
||||
'discriminator': 'driver',
|
||||
'data': {
|
||||
|
@ -10,15 +10,15 @@ STEXI
|
||||
ETEXI
|
||||
|
||||
DEF("bench", img_bench,
|
||||
"bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-w] filename")
|
||||
"bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-w] [-U] filename")
|
||||
STEXI
|
||||
@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] @var{filename}
|
||||
@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] [-U] @var{filename}
|
||||
ETEXI
|
||||
|
||||
DEF("check", img_check,
|
||||
"check [-q] [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] filename")
|
||||
"check [-q] [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename")
|
||||
STEXI
|
||||
@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename}
|
||||
@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename}
|
||||
ETEXI
|
||||
|
||||
DEF("create", img_create,
|
||||
@ -34,45 +34,45 @@ STEXI
|
||||
ETEXI
|
||||
|
||||
DEF("compare", img_compare,
|
||||
"compare [--object objectdef] [--image-opts] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] filename1 filename2")
|
||||
"compare [--object objectdef] [--image-opts] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] [-U] filename1 filename2")
|
||||
STEXI
|
||||
@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] @var{filename1} @var{filename2}
|
||||
@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] [-U] @var{filename1} @var{filename2}
|
||||
ETEXI
|
||||
|
||||
DEF("convert", img_convert,
|
||||
"convert [--object objectdef] [--image-opts] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
|
||||
"convert [--object objectdef] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
|
||||
STEXI
|
||||
@item convert [--object @var{objectdef}] [--image-opts] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
@item convert [--object @var{objectdef}] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
ETEXI
|
||||
|
||||
DEF("dd", img_dd,
|
||||
"dd [--image-opts] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output")
|
||||
"dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output")
|
||||
STEXI
|
||||
@item dd [--image-opts] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
|
||||
@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
|
||||
ETEXI
|
||||
|
||||
DEF("info", img_info,
|
||||
"info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] filename")
|
||||
"info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] [-U] filename")
|
||||
STEXI
|
||||
@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
|
||||
@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] [-U] @var{filename}
|
||||
ETEXI
|
||||
|
||||
DEF("map", img_map,
|
||||
"map [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] filename")
|
||||
"map [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-U] filename")
|
||||
STEXI
|
||||
@item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
|
||||
@item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [-U] @var{filename}
|
||||
ETEXI
|
||||
|
||||
DEF("snapshot", img_snapshot,
|
||||
"snapshot [--object objectdef] [--image-opts] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
|
||||
"snapshot [--object objectdef] [--image-opts] [-U] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
|
||||
STEXI
|
||||
@item snapshot [--object @var{objectdef}] [--image-opts] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
|
||||
@item snapshot [--object @var{objectdef}] [--image-opts] [-U] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
|
||||
ETEXI
|
||||
|
||||
DEF("rebase", img_rebase,
|
||||
"rebase [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
|
||||
"rebase [--object objectdef] [--image-opts] [-U] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
|
||||
STEXI
|
||||
@item rebase [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
|
||||
@item rebase [--object @var{objectdef}] [--image-opts] [-U] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
|
||||
ETEXI
|
||||
|
||||
DEF("resize", img_resize,
|
||||
|
179
qemu-img.c
179
qemu-img.c
@ -28,6 +28,7 @@
|
||||
#include "qapi/qobject-output-visitor.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/option.h"
|
||||
@ -283,12 +284,20 @@ static int img_open_password(BlockBackend *blk, const char *filename,
|
||||
|
||||
static BlockBackend *img_open_opts(const char *optstr,
|
||||
QemuOpts *opts, int flags, bool writethrough,
|
||||
bool quiet)
|
||||
bool quiet, bool force_share)
|
||||
{
|
||||
QDict *options;
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *blk;
|
||||
options = qemu_opts_to_qdict(opts, NULL);
|
||||
if (force_share) {
|
||||
if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE)
|
||||
&& !qdict_get_bool(options, BDRV_OPT_FORCE_SHARE)) {
|
||||
error_report("--force-share/-U conflicts with image options");
|
||||
return NULL;
|
||||
}
|
||||
qdict_put(options, BDRV_OPT_FORCE_SHARE, qbool_from_bool(true));
|
||||
}
|
||||
blk = blk_new_open(NULL, NULL, options, flags, &local_err);
|
||||
if (!blk) {
|
||||
error_reportf_err(local_err, "Could not open '%s': ", optstr);
|
||||
@ -305,17 +314,20 @@ static BlockBackend *img_open_opts(const char *optstr,
|
||||
|
||||
static BlockBackend *img_open_file(const char *filename,
|
||||
const char *fmt, int flags,
|
||||
bool writethrough, bool quiet)
|
||||
bool writethrough, bool quiet,
|
||||
bool force_share)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
Error *local_err = NULL;
|
||||
QDict *options = NULL;
|
||||
QDict *options = qdict_new();
|
||||
|
||||
if (fmt) {
|
||||
options = qdict_new();
|
||||
qdict_put_str(options, "driver", fmt);
|
||||
}
|
||||
|
||||
if (force_share) {
|
||||
qdict_put(options, BDRV_OPT_FORCE_SHARE, qbool_from_bool(true));
|
||||
}
|
||||
blk = blk_new_open(filename, NULL, options, flags, &local_err);
|
||||
if (!blk) {
|
||||
error_reportf_err(local_err, "Could not open '%s': ", filename);
|
||||
@ -334,7 +346,7 @@ static BlockBackend *img_open_file(const char *filename,
|
||||
static BlockBackend *img_open(bool image_opts,
|
||||
const char *filename,
|
||||
const char *fmt, int flags, bool writethrough,
|
||||
bool quiet)
|
||||
bool quiet, bool force_share)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
if (image_opts) {
|
||||
@ -348,9 +360,11 @@ static BlockBackend *img_open(bool image_opts,
|
||||
if (!opts) {
|
||||
return NULL;
|
||||
}
|
||||
blk = img_open_opts(filename, opts, flags, writethrough, quiet);
|
||||
blk = img_open_opts(filename, opts, flags, writethrough, quiet,
|
||||
force_share);
|
||||
} else {
|
||||
blk = img_open_file(filename, fmt, flags, writethrough, quiet);
|
||||
blk = img_open_file(filename, fmt, flags, writethrough, quiet,
|
||||
force_share);
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
@ -650,6 +664,7 @@ static int img_check(int argc, char **argv)
|
||||
ImageCheck *check;
|
||||
bool quiet = false;
|
||||
bool image_opts = false;
|
||||
bool force_share = false;
|
||||
|
||||
fmt = NULL;
|
||||
output = NULL;
|
||||
@ -664,9 +679,10 @@ static int img_check(int argc, char **argv)
|
||||
{"output", required_argument, 0, OPTION_OUTPUT},
|
||||
{"object", required_argument, 0, OPTION_OBJECT},
|
||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||
{"force-share", no_argument, 0, 'U'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":hf:r:T:q",
|
||||
c = getopt_long(argc, argv, ":hf:r:T:qU",
|
||||
long_options, &option_index);
|
||||
if (c == -1) {
|
||||
break;
|
||||
@ -705,6 +721,9 @@ static int img_check(int argc, char **argv)
|
||||
case 'q':
|
||||
quiet = true;
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
case OPTION_OBJECT: {
|
||||
QemuOpts *opts;
|
||||
opts = qemu_opts_parse_noisily(&qemu_object_opts,
|
||||
@ -744,7 +763,8 @@ static int img_check(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
|
||||
force_share);
|
||||
if (!blk) {
|
||||
return 1;
|
||||
}
|
||||
@ -947,7 +967,8 @@ static int img_commit(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
|
||||
false);
|
||||
if (!blk) {
|
||||
return 1;
|
||||
}
|
||||
@ -1206,6 +1227,7 @@ static int img_compare(int argc, char **argv)
|
||||
int c, pnum;
|
||||
uint64_t progress_base;
|
||||
bool image_opts = false;
|
||||
bool force_share = false;
|
||||
|
||||
cache = BDRV_DEFAULT_CACHE;
|
||||
for (;;) {
|
||||
@ -1213,9 +1235,10 @@ static int img_compare(int argc, char **argv)
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"object", required_argument, 0, OPTION_OBJECT},
|
||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||
{"force-share", no_argument, 0, 'U'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":hf:F:T:pqs",
|
||||
c = getopt_long(argc, argv, ":hf:F:T:pqsU",
|
||||
long_options, NULL);
|
||||
if (c == -1) {
|
||||
break;
|
||||
@ -1248,6 +1271,9 @@ static int img_compare(int argc, char **argv)
|
||||
case 's':
|
||||
strict = true;
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
case OPTION_OBJECT: {
|
||||
QemuOpts *opts;
|
||||
opts = qemu_opts_parse_noisily(&qemu_object_opts,
|
||||
@ -1293,13 +1319,15 @@ static int img_compare(int argc, char **argv)
|
||||
goto out3;
|
||||
}
|
||||
|
||||
blk1 = img_open(image_opts, filename1, fmt1, flags, writethrough, quiet);
|
||||
blk1 = img_open(image_opts, filename1, fmt1, flags, writethrough, quiet,
|
||||
force_share);
|
||||
if (!blk1) {
|
||||
ret = 2;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
blk2 = img_open(image_opts, filename2, fmt2, flags, writethrough, quiet);
|
||||
blk2 = img_open(image_opts, filename2, fmt2, flags, writethrough, quiet,
|
||||
force_share);
|
||||
if (!blk2) {
|
||||
ret = 2;
|
||||
goto out2;
|
||||
@ -1733,13 +1761,13 @@ static void coroutine_fn convert_co_do_copy(void *opaque)
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
n = convert_iteration_sectors(s, s->sector_num);
|
||||
if (n < 0) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
s->ret = n;
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
/* save current sector and allocation status to local variables */
|
||||
sector_num = s->sector_num;
|
||||
@ -1764,7 +1792,6 @@ static void coroutine_fn convert_co_do_copy(void *opaque)
|
||||
error_report("error while reading sector %" PRId64
|
||||
": %s", sector_num, strerror(-ret));
|
||||
s->ret = ret;
|
||||
goto out;
|
||||
}
|
||||
} else if (!s->min_sparse && status == BLK_ZERO) {
|
||||
status = BLK_DATA;
|
||||
@ -1773,22 +1800,20 @@ static void coroutine_fn convert_co_do_copy(void *opaque)
|
||||
|
||||
if (s->wr_in_order) {
|
||||
/* keep writes in order */
|
||||
while (s->wr_offs != sector_num) {
|
||||
if (s->ret != -EINPROGRESS) {
|
||||
goto out;
|
||||
}
|
||||
while (s->wr_offs != sector_num && s->ret == -EINPROGRESS) {
|
||||
s->wait_sector_num[index] = sector_num;
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
s->wait_sector_num[index] = -1;
|
||||
}
|
||||
|
||||
ret = convert_co_write(s, sector_num, n, buf, status);
|
||||
if (ret < 0) {
|
||||
error_report("error while writing sector %" PRId64
|
||||
": %s", sector_num, strerror(-ret));
|
||||
s->ret = ret;
|
||||
goto out;
|
||||
if (s->ret == -EINPROGRESS) {
|
||||
ret = convert_co_write(s, sector_num, n, buf, status);
|
||||
if (ret < 0) {
|
||||
error_report("error while writing sector %" PRId64
|
||||
": %s", sector_num, strerror(-ret));
|
||||
s->ret = ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->wr_in_order) {
|
||||
@ -1809,7 +1834,6 @@ static void coroutine_fn convert_co_do_copy(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
qemu_vfree(buf);
|
||||
s->co[index] = NULL;
|
||||
s->running_coroutines--;
|
||||
@ -1871,7 +1895,7 @@ static int convert_do_copy(ImgConvertState *s)
|
||||
qemu_coroutine_enter(s->co[i]);
|
||||
}
|
||||
|
||||
while (s->ret == -EINPROGRESS) {
|
||||
while (s->running_coroutines) {
|
||||
main_loop_wait(false);
|
||||
}
|
||||
|
||||
@ -1902,6 +1926,7 @@ static int img_convert(int argc, char **argv)
|
||||
bool writethrough, src_writethrough, quiet = false, image_opts = false,
|
||||
skip_create = false, progress = false;
|
||||
int64_t ret = -EINVAL;
|
||||
bool force_share = false;
|
||||
|
||||
ImgConvertState s = (ImgConvertState) {
|
||||
/* Need at least 4k of zeros for sparse detection */
|
||||
@ -1916,9 +1941,10 @@ static int img_convert(int argc, char **argv)
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"object", required_argument, 0, OPTION_OBJECT},
|
||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||
{"force-share", no_argument, 0, 'U'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":hf:O:B:ce6o:s:l:S:pt:T:qnm:W",
|
||||
c = getopt_long(argc, argv, ":hf:O:B:ce6o:s:l:S:pt:T:qnm:WU",
|
||||
long_options, NULL);
|
||||
if (c == -1) {
|
||||
break;
|
||||
@ -2021,6 +2047,9 @@ static int img_convert(int argc, char **argv)
|
||||
case 'W':
|
||||
s.wr_in_order = false;
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
case OPTION_OBJECT: {
|
||||
QemuOpts *object_opts;
|
||||
object_opts = qemu_opts_parse_noisily(&qemu_object_opts,
|
||||
@ -2080,7 +2109,8 @@ static int img_convert(int argc, char **argv)
|
||||
|
||||
for (bs_i = 0; bs_i < s.src_num; bs_i++) {
|
||||
s.src[bs_i] = img_open(image_opts, argv[optind + bs_i],
|
||||
fmt, src_flags, src_writethrough, quiet);
|
||||
fmt, src_flags, src_writethrough, quiet,
|
||||
force_share);
|
||||
if (!s.src[bs_i]) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
@ -2233,7 +2263,8 @@ static int img_convert(int argc, char **argv)
|
||||
* the bdrv_create() call which takes different params.
|
||||
* Not critical right now, so fix can wait...
|
||||
*/
|
||||
s.target = img_open_file(out_filename, out_fmt, flags, writethrough, quiet);
|
||||
s.target = img_open_file(out_filename, out_fmt, flags, writethrough, quiet,
|
||||
false);
|
||||
if (!s.target) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
@ -2384,7 +2415,7 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
|
||||
static ImageInfoList *collect_image_info_list(bool image_opts,
|
||||
const char *filename,
|
||||
const char *fmt,
|
||||
bool chain)
|
||||
bool chain, bool force_share)
|
||||
{
|
||||
ImageInfoList *head = NULL;
|
||||
ImageInfoList **last = &head;
|
||||
@ -2407,7 +2438,8 @@ static ImageInfoList *collect_image_info_list(bool image_opts,
|
||||
g_hash_table_insert(filenames, (gpointer)filename, NULL);
|
||||
|
||||
blk = img_open(image_opts, filename, fmt,
|
||||
BDRV_O_NO_BACKING | BDRV_O_NO_IO, false, false);
|
||||
BDRV_O_NO_BACKING | BDRV_O_NO_IO, false, false,
|
||||
force_share);
|
||||
if (!blk) {
|
||||
goto err;
|
||||
}
|
||||
@ -2459,6 +2491,7 @@ static int img_info(int argc, char **argv)
|
||||
const char *filename, *fmt, *output;
|
||||
ImageInfoList *list;
|
||||
bool image_opts = false;
|
||||
bool force_share = false;
|
||||
|
||||
fmt = NULL;
|
||||
output = NULL;
|
||||
@ -2471,9 +2504,10 @@ static int img_info(int argc, char **argv)
|
||||
{"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN},
|
||||
{"object", required_argument, 0, OPTION_OBJECT},
|
||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||
{"force-share", no_argument, 0, 'U'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":f:h",
|
||||
c = getopt_long(argc, argv, ":f:hU",
|
||||
long_options, &option_index);
|
||||
if (c == -1) {
|
||||
break;
|
||||
@ -2491,6 +2525,9 @@ static int img_info(int argc, char **argv)
|
||||
case 'f':
|
||||
fmt = optarg;
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
case OPTION_OUTPUT:
|
||||
output = optarg;
|
||||
break;
|
||||
@ -2530,7 +2567,8 @@ static int img_info(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
list = collect_image_info_list(image_opts, filename, fmt, chain);
|
||||
list = collect_image_info_list(image_opts, filename, fmt, chain,
|
||||
force_share);
|
||||
if (!list) {
|
||||
return 1;
|
||||
}
|
||||
@ -2676,6 +2714,7 @@ static int img_map(int argc, char **argv)
|
||||
MapEntry curr = { .length = 0 }, next;
|
||||
int ret = 0;
|
||||
bool image_opts = false;
|
||||
bool force_share = false;
|
||||
|
||||
fmt = NULL;
|
||||
output = NULL;
|
||||
@ -2687,9 +2726,10 @@ static int img_map(int argc, char **argv)
|
||||
{"output", required_argument, 0, OPTION_OUTPUT},
|
||||
{"object", required_argument, 0, OPTION_OBJECT},
|
||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||
{"force-share", no_argument, 0, 'U'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":f:h",
|
||||
c = getopt_long(argc, argv, ":f:hU",
|
||||
long_options, &option_index);
|
||||
if (c == -1) {
|
||||
break;
|
||||
@ -2707,6 +2747,9 @@ static int img_map(int argc, char **argv)
|
||||
case 'f':
|
||||
fmt = optarg;
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
case OPTION_OUTPUT:
|
||||
output = optarg;
|
||||
break;
|
||||
@ -2743,7 +2786,7 @@ static int img_map(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
blk = img_open(image_opts, filename, fmt, 0, false, false);
|
||||
blk = img_open(image_opts, filename, fmt, 0, false, false, force_share);
|
||||
if (!blk) {
|
||||
return 1;
|
||||
}
|
||||
@ -2806,6 +2849,7 @@ static int img_snapshot(int argc, char **argv)
|
||||
bool quiet = false;
|
||||
Error *err = NULL;
|
||||
bool image_opts = false;
|
||||
bool force_share = false;
|
||||
|
||||
bdrv_oflags = BDRV_O_RDWR;
|
||||
/* Parse commandline parameters */
|
||||
@ -2814,9 +2858,10 @@ static int img_snapshot(int argc, char **argv)
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"object", required_argument, 0, OPTION_OBJECT},
|
||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||
{"force-share", no_argument, 0, 'U'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":la:c:d:hq",
|
||||
c = getopt_long(argc, argv, ":la:c:d:hqU",
|
||||
long_options, NULL);
|
||||
if (c == -1) {
|
||||
break;
|
||||
@ -2866,6 +2911,9 @@ static int img_snapshot(int argc, char **argv)
|
||||
case 'q':
|
||||
quiet = true;
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
case OPTION_OBJECT: {
|
||||
QemuOpts *opts;
|
||||
opts = qemu_opts_parse_noisily(&qemu_object_opts,
|
||||
@ -2892,7 +2940,8 @@ static int img_snapshot(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Open the image */
|
||||
blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet);
|
||||
blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet,
|
||||
force_share);
|
||||
if (!blk) {
|
||||
return 1;
|
||||
}
|
||||
@ -2956,6 +3005,7 @@ static int img_rebase(int argc, char **argv)
|
||||
int c, flags, src_flags, ret;
|
||||
bool writethrough, src_writethrough;
|
||||
int unsafe = 0;
|
||||
bool force_share = false;
|
||||
int progress = 0;
|
||||
bool quiet = false;
|
||||
Error *local_err = NULL;
|
||||
@ -2972,9 +3022,10 @@ static int img_rebase(int argc, char **argv)
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"object", required_argument, 0, OPTION_OBJECT},
|
||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||
{"force-share", no_argument, 0, 'U'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":hf:F:b:upt:T:q",
|
||||
c = getopt_long(argc, argv, ":hf:F:b:upt:T:qU",
|
||||
long_options, NULL);
|
||||
if (c == -1) {
|
||||
break;
|
||||
@ -3024,6 +3075,9 @@ static int img_rebase(int argc, char **argv)
|
||||
case OPTION_IMAGE_OPTS:
|
||||
image_opts = true;
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3072,7 +3126,8 @@ static int img_rebase(int argc, char **argv)
|
||||
* Ignore the old backing file for unsafe rebase in case we want to correct
|
||||
* the reference to a renamed or moved backing file.
|
||||
*/
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
|
||||
false);
|
||||
if (!blk) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
@ -3097,6 +3152,13 @@ static int img_rebase(int argc, char **argv)
|
||||
qdict_put_str(options, "driver", bs->backing_format);
|
||||
}
|
||||
|
||||
if (force_share) {
|
||||
if (!options) {
|
||||
options = qdict_new();
|
||||
}
|
||||
qdict_put(options, BDRV_OPT_FORCE_SHARE,
|
||||
qbool_from_bool(true));
|
||||
}
|
||||
bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
|
||||
blk_old_backing = blk_new_open(backing_name, NULL,
|
||||
options, src_flags, &local_err);
|
||||
@ -3109,11 +3171,12 @@ static int img_rebase(int argc, char **argv)
|
||||
}
|
||||
|
||||
if (out_baseimg[0]) {
|
||||
options = qdict_new();
|
||||
if (out_basefmt) {
|
||||
options = qdict_new();
|
||||
qdict_put_str(options, "driver", out_basefmt);
|
||||
} else {
|
||||
options = NULL;
|
||||
}
|
||||
if (force_share) {
|
||||
qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
|
||||
}
|
||||
|
||||
blk_new_backing = blk_new_open(out_baseimg, NULL,
|
||||
@ -3419,7 +3482,8 @@ static int img_resize(int argc, char **argv)
|
||||
qemu_opts_del(param);
|
||||
|
||||
blk = img_open(image_opts, filename, fmt,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE, false, quiet);
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE, false, quiet,
|
||||
false);
|
||||
if (!blk) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
@ -3573,7 +3637,8 @@ static int img_amend(int argc, char **argv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
|
||||
false);
|
||||
if (!blk) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
@ -3741,6 +3806,7 @@ static int img_bench(int argc, char **argv)
|
||||
bool writethrough = false;
|
||||
struct timeval t1, t2;
|
||||
int i;
|
||||
bool force_share = false;
|
||||
|
||||
for (;;) {
|
||||
static const struct option long_options[] = {
|
||||
@ -3749,9 +3815,10 @@ static int img_bench(int argc, char **argv)
|
||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||
{"pattern", required_argument, 0, OPTION_PATTERN},
|
||||
{"no-drain", no_argument, 0, OPTION_NO_DRAIN},
|
||||
{"force-share", no_argument, 0, 'U'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":hc:d:f:no:qs:S:t:w", long_options, NULL);
|
||||
c = getopt_long(argc, argv, ":hc:d:f:no:qs:S:t:wU", long_options, NULL);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
@ -3845,6 +3912,9 @@ static int img_bench(int argc, char **argv)
|
||||
flags |= BDRV_O_RDWR;
|
||||
is_write = true;
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
case OPTION_PATTERN:
|
||||
{
|
||||
unsigned long res;
|
||||
@ -3892,7 +3962,8 @@ static int img_bench(int argc, char **argv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
|
||||
blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
|
||||
force_share);
|
||||
if (!blk) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
@ -4059,6 +4130,7 @@ static int img_dd(int argc, char **argv)
|
||||
const char *fmt = NULL;
|
||||
int64_t size = 0;
|
||||
int64_t block_count = 0, out_pos, in_pos;
|
||||
bool force_share = false;
|
||||
struct DdInfo dd = {
|
||||
.flags = 0,
|
||||
.count = 0,
|
||||
@ -4087,10 +4159,11 @@ static int img_dd(int argc, char **argv)
|
||||
const struct option long_options[] = {
|
||||
{ "help", no_argument, 0, 'h'},
|
||||
{ "image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||
{ "force-share", no_argument, 0, 'U'},
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
while ((c = getopt_long(argc, argv, ":hf:O:", long_options, NULL))) {
|
||||
while ((c = getopt_long(argc, argv, ":hf:O:U", long_options, NULL))) {
|
||||
if (c == EOF) {
|
||||
break;
|
||||
}
|
||||
@ -4110,6 +4183,9 @@ static int img_dd(int argc, char **argv)
|
||||
case 'h':
|
||||
help();
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
case OPTION_IMAGE_OPTS:
|
||||
image_opts = true;
|
||||
break;
|
||||
@ -4154,7 +4230,8 @@ static int img_dd(int argc, char **argv)
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
blk1 = img_open(image_opts, in.filename, fmt, 0, false, false);
|
||||
blk1 = img_open(image_opts, in.filename, fmt, 0, false, false,
|
||||
force_share);
|
||||
|
||||
if (!blk1) {
|
||||
ret = -1;
|
||||
@ -4222,7 +4299,7 @@ static int img_dd(int argc, char **argv)
|
||||
}
|
||||
|
||||
blk2 = img_open(image_opts, out.filename, out_fmt, BDRV_O_RDWR,
|
||||
false, false);
|
||||
false, false, false);
|
||||
|
||||
if (!blk2) {
|
||||
ret = -1;
|
||||
|
@ -740,13 +740,13 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
|
||||
}
|
||||
|
||||
if (bflag) {
|
||||
if (offset & 0x1ff) {
|
||||
printf("offset %" PRId64 " is not sector aligned\n",
|
||||
if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
|
||||
printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
|
||||
offset);
|
||||
return 0;
|
||||
}
|
||||
if (count & 0x1ff) {
|
||||
printf("count %"PRId64" is not sector aligned\n",
|
||||
if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
|
||||
printf("%"PRId64" is not a sector-aligned value for 'count'\n",
|
||||
count);
|
||||
return 0;
|
||||
}
|
||||
@ -1050,14 +1050,14 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
|
||||
}
|
||||
|
||||
if (bflag || cflag) {
|
||||
if (offset & 0x1ff) {
|
||||
printf("offset %" PRId64 " is not sector aligned\n",
|
||||
if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
|
||||
printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
|
||||
offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count & 0x1ff) {
|
||||
printf("count %"PRId64" is not sector aligned\n",
|
||||
if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
|
||||
printf("%"PRId64" is not a sector-aligned value for 'count'\n",
|
||||
count);
|
||||
return 0;
|
||||
}
|
||||
@ -1760,7 +1760,7 @@ out:
|
||||
static int alloc_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
int64_t offset, sector_num, nb_sectors, remaining;
|
||||
int64_t offset, sector_num, nb_sectors, remaining, count;
|
||||
char s1[64];
|
||||
int num, ret;
|
||||
int64_t sum_alloc;
|
||||
@ -1769,25 +1769,31 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
|
||||
if (offset < 0) {
|
||||
print_cvtnum_err(offset, argv[1]);
|
||||
return 0;
|
||||
} else if (offset & 0x1ff) {
|
||||
printf("offset %" PRId64 " is not sector aligned\n",
|
||||
} else if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
|
||||
printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
|
||||
offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc == 3) {
|
||||
nb_sectors = cvtnum(argv[2]);
|
||||
if (nb_sectors < 0) {
|
||||
print_cvtnum_err(nb_sectors, argv[2]);
|
||||
count = cvtnum(argv[2]);
|
||||
if (count < 0) {
|
||||
print_cvtnum_err(count, argv[2]);
|
||||
return 0;
|
||||
} else if (nb_sectors > INT_MAX) {
|
||||
printf("length argument cannot exceed %d, given %s\n",
|
||||
INT_MAX, argv[2]);
|
||||
} else if (count > INT_MAX * BDRV_SECTOR_SIZE) {
|
||||
printf("length argument cannot exceed %llu, given %s\n",
|
||||
INT_MAX * BDRV_SECTOR_SIZE, argv[2]);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
nb_sectors = 1;
|
||||
count = BDRV_SECTOR_SIZE;
|
||||
}
|
||||
if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
|
||||
printf("%" PRId64 " is not a sector-aligned value for 'count'\n",
|
||||
count);
|
||||
return 0;
|
||||
}
|
||||
nb_sectors = count >> BDRV_SECTOR_BITS;
|
||||
|
||||
remaining = nb_sectors;
|
||||
sum_alloc = 0;
|
||||
@ -1811,8 +1817,8 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
|
||||
|
||||
cvtstr(offset, s1, sizeof(s1));
|
||||
|
||||
printf("%"PRId64"/%"PRId64" sectors allocated at offset %s\n",
|
||||
sum_alloc, nb_sectors, s1);
|
||||
printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n",
|
||||
sum_alloc << BDRV_SECTOR_BITS, nb_sectors << BDRV_SECTOR_BITS, s1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1822,8 +1828,8 @@ static const cmdinfo_t alloc_cmd = {
|
||||
.argmin = 1,
|
||||
.argmax = 2,
|
||||
.cfunc = alloc_f,
|
||||
.args = "off [sectors]",
|
||||
.oneline = "checks if a sector is present in the file",
|
||||
.args = "offset [count]",
|
||||
.oneline = "checks if offset is allocated in the file",
|
||||
};
|
||||
|
||||
|
||||
@ -1862,7 +1868,7 @@ static int map_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int64_t offset;
|
||||
int64_t nb_sectors, total_sectors;
|
||||
char s1[64];
|
||||
char s1[64], s2[64];
|
||||
int64_t num;
|
||||
int ret;
|
||||
const char *retstr;
|
||||
@ -1888,10 +1894,11 @@ static int map_f(BlockBackend *blk, int argc, char **argv)
|
||||
}
|
||||
|
||||
retstr = ret ? " allocated" : "not allocated";
|
||||
cvtstr(offset << 9ULL, s1, sizeof(s1));
|
||||
printf("[% 24" PRId64 "] % 8" PRId64 "/% 8" PRId64 " sectors %s "
|
||||
"at offset %s (%d)\n",
|
||||
offset << 9ULL, num, nb_sectors, retstr, s1, ret);
|
||||
cvtstr(num << BDRV_SECTOR_BITS, s1, sizeof(s1));
|
||||
cvtstr(offset << BDRV_SECTOR_BITS, s2, sizeof(s2));
|
||||
printf("%s (0x%" PRIx64 ") bytes %s at offset %s (0x%" PRIx64 ")\n",
|
||||
s1, num << BDRV_SECTOR_BITS, retstr,
|
||||
s2, offset << BDRV_SECTOR_BITS);
|
||||
|
||||
offset += num;
|
||||
nb_sectors -= num;
|
||||
|
42
qemu-io.c
42
qemu-io.c
@ -20,6 +20,7 @@
|
||||
#include "qemu/readline.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "block/block_int.h"
|
||||
@ -53,7 +54,8 @@ static const cmdinfo_t close_cmd = {
|
||||
.oneline = "close the current open file",
|
||||
};
|
||||
|
||||
static int openfile(char *name, int flags, bool writethrough, QDict *opts)
|
||||
static int openfile(char *name, int flags, bool writethrough, bool force_share,
|
||||
QDict *opts)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
BlockDriverState *bs;
|
||||
@ -64,6 +66,18 @@ static int openfile(char *name, int flags, bool writethrough, QDict *opts)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (force_share) {
|
||||
if (!opts) {
|
||||
opts = qdict_new();
|
||||
}
|
||||
if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE)
|
||||
&& !qdict_get_bool(opts, BDRV_OPT_FORCE_SHARE)) {
|
||||
error_report("-U conflicts with image options");
|
||||
QDECREF(opts);
|
||||
return 1;
|
||||
}
|
||||
qdict_put(opts, BDRV_OPT_FORCE_SHARE, qbool_from_bool(true));
|
||||
}
|
||||
qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err);
|
||||
if (!qemuio_blk) {
|
||||
error_reportf_err(local_err, "can't open%s%s: ",
|
||||
@ -108,6 +122,7 @@ static void open_help(void)
|
||||
" -r, -- open file read-only\n"
|
||||
" -s, -- use snapshot file\n"
|
||||
" -n, -- disable host cache, short for -t none\n"
|
||||
" -U, -- force shared permissions\n"
|
||||
" -k, -- use kernel AIO implementation (on Linux only)\n"
|
||||
" -t, -- use the given cache mode for the image\n"
|
||||
" -d, -- use the given discard mode for the image\n"
|
||||
@ -124,7 +139,7 @@ static const cmdinfo_t open_cmd = {
|
||||
.argmin = 1,
|
||||
.argmax = -1,
|
||||
.flags = CMD_NOFILE_OK,
|
||||
.args = "[-rsnk] [-t cache] [-d discard] [-o options] [path]",
|
||||
.args = "[-rsnkU] [-t cache] [-d discard] [-o options] [path]",
|
||||
.oneline = "open the file specified by path",
|
||||
.help = open_help,
|
||||
};
|
||||
@ -147,8 +162,9 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
|
||||
int c;
|
||||
QemuOpts *qopts;
|
||||
QDict *opts;
|
||||
bool force_share = false;
|
||||
|
||||
while ((c = getopt(argc, argv, "snro:kt:d:")) != -1) {
|
||||
while ((c = getopt(argc, argv, "snro:kt:d:U")) != -1) {
|
||||
switch (c) {
|
||||
case 's':
|
||||
flags |= BDRV_O_SNAPSHOT;
|
||||
@ -188,6 +204,9 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
default:
|
||||
qemu_opts_reset(&empty_opts);
|
||||
return qemuio_command_usage(&open_cmd);
|
||||
@ -211,9 +230,9 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
|
||||
qemu_opts_reset(&empty_opts);
|
||||
|
||||
if (optind == argc - 1) {
|
||||
return openfile(argv[optind], flags, writethrough, opts);
|
||||
return openfile(argv[optind], flags, writethrough, force_share, opts);
|
||||
} else if (optind == argc) {
|
||||
return openfile(NULL, flags, writethrough, opts);
|
||||
return openfile(NULL, flags, writethrough, force_share, opts);
|
||||
} else {
|
||||
QDECREF(opts);
|
||||
return qemuio_command_usage(&open_cmd);
|
||||
@ -257,6 +276,7 @@ static void usage(const char *name)
|
||||
" -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
|
||||
" specify tracing options\n"
|
||||
" see qemu-img(1) man page for full description\n"
|
||||
" -U, --force-share force shared permissions\n"
|
||||
" -h, --help display this help and exit\n"
|
||||
" -V, --version output version information and exit\n"
|
||||
"\n"
|
||||
@ -436,7 +456,7 @@ static QemuOptsList file_opts = {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int readonly = 0;
|
||||
const char *sopt = "hVc:d:f:rsnmkt:T:";
|
||||
const char *sopt = "hVc:d:f:rsnmkt:T:U";
|
||||
const struct option lopt[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
@ -452,6 +472,7 @@ int main(int argc, char **argv)
|
||||
{ "trace", required_argument, NULL, 'T' },
|
||||
{ "object", required_argument, NULL, OPTION_OBJECT },
|
||||
{ "image-opts", no_argument, NULL, OPTION_IMAGE_OPTS },
|
||||
{ "force-share", no_argument, 0, 'U'},
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
int c;
|
||||
@ -462,6 +483,7 @@ int main(int argc, char **argv)
|
||||
QDict *opts = NULL;
|
||||
const char *format = NULL;
|
||||
char *trace_file = NULL;
|
||||
bool force_share = false;
|
||||
|
||||
#ifdef CONFIG_POSIX
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
@ -524,6 +546,9 @@ int main(int argc, char **argv)
|
||||
case 'h':
|
||||
usage(progname);
|
||||
exit(0);
|
||||
case 'U':
|
||||
force_share = true;
|
||||
break;
|
||||
case OPTION_OBJECT: {
|
||||
QemuOpts *qopts;
|
||||
qopts = qemu_opts_parse_noisily(&qemu_object_opts,
|
||||
@ -595,7 +620,7 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
opts = qemu_opts_to_qdict(qopts, NULL);
|
||||
if (openfile(NULL, flags, writethrough, opts)) {
|
||||
if (openfile(NULL, flags, writethrough, force_share, opts)) {
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
@ -603,7 +628,8 @@ int main(int argc, char **argv)
|
||||
opts = qdict_new();
|
||||
qdict_put_str(opts, "driver", format);
|
||||
}
|
||||
if (openfile(argv[optind], flags, writethrough, opts)) {
|
||||
if (openfile(argv[optind], flags, writethrough,
|
||||
force_share, opts)) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
18
qmp.c
18
qmp.c
@ -196,18 +196,12 @@ void qmp_cont(Error **errp)
|
||||
}
|
||||
|
||||
/* Continuing after completed migration. Images have been inactivated to
|
||||
* allow the destination to take control. Need to get control back now. */
|
||||
if (runstate_check(RUN_STATE_FINISH_MIGRATE) ||
|
||||
runstate_check(RUN_STATE_POSTMIGRATE))
|
||||
{
|
||||
bdrv_invalidate_cache_all(&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
blk_resume_after_migration(&local_err);
|
||||
* allow the destination to take control. Need to get control back now.
|
||||
*
|
||||
* If there are no inactive block nodes (e.g. because the VM was just
|
||||
* paused rather than completing a migration), bdrv_inactivate_all() simply
|
||||
* doesn't do anything. */
|
||||
bdrv_invalidate_cache_all(&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
|
@ -92,7 +92,7 @@ static void test_after_failed_device_add(void)
|
||||
static void test_drive_del_device_del(void)
|
||||
{
|
||||
/* Start with a drive used by a device that unplugs instantaneously */
|
||||
qtest_start("-drive if=none,id=drive0,file=/dev/null,format=raw"
|
||||
qtest_start("-drive if=none,id=drive0,file=null-co://,format=raw"
|
||||
" -device virtio-scsi-pci"
|
||||
" -device scsi-hd,drive=drive0,id=dev0");
|
||||
|
||||
|
@ -22,7 +22,7 @@ int main(int argc, char **argv)
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/nvme/nop", nop);
|
||||
|
||||
qtest_start("-drive id=drv0,if=none,file=/dev/null,format=raw "
|
||||
qtest_start("-drive id=drv0,if=none,file=null-co://,format=raw "
|
||||
"-device nvme,drive=drv0,serial=foo");
|
||||
ret = g_test_run();
|
||||
|
||||
|
@ -542,8 +542,8 @@ Testing conversion with -B TEST_DIR/t.IMGFMT.base
|
||||
|
||||
Checking if backing clusters are allocated when they shouldn't
|
||||
|
||||
0/128 sectors allocated at offset 1 MiB
|
||||
0/128 sectors allocated at offset 4.001 GiB
|
||||
0/65536 bytes allocated at offset 1 MiB
|
||||
0/65536 bytes allocated at offset 4.001 GiB
|
||||
Reading
|
||||
|
||||
=== IO: pattern 42
|
||||
@ -1086,8 +1086,8 @@ Testing conversion with -o backing_file=TEST_DIR/t.IMGFMT.base
|
||||
|
||||
Checking if backing clusters are allocated when they shouldn't
|
||||
|
||||
0/128 sectors allocated at offset 1 MiB
|
||||
0/128 sectors allocated at offset 4.001 GiB
|
||||
0/65536 bytes allocated at offset 1 MiB
|
||||
0/65536 bytes allocated at offset 4.001 GiB
|
||||
Reading
|
||||
|
||||
=== IO: pattern 42
|
||||
|
@ -63,8 +63,8 @@ class TestSingleDrive(iotests.QMPTestCase):
|
||||
def test_stream_intermediate(self):
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
self.assertNotEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
|
||||
qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img),
|
||||
self.assertNotEqual(qemu_io('-f', 'raw', '-rU', '-c', 'map', backing_img),
|
||||
qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', mid_img),
|
||||
'image file map matches backing file before streaming')
|
||||
|
||||
result = self.vm.qmp('block-stream', device='mid', job_id='stream-mid')
|
||||
@ -114,7 +114,7 @@ class TestSingleDrive(iotests.QMPTestCase):
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
# The image map is empty before the operation
|
||||
empty_map = qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img)
|
||||
empty_map = qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', test_img)
|
||||
|
||||
# This is a no-op: no data should ever be copied from the base image
|
||||
result = self.vm.qmp('block-stream', device='drive0', base=mid_img)
|
||||
@ -197,8 +197,8 @@ class TestParallelOps(iotests.QMPTestCase):
|
||||
|
||||
# Check that the maps don't match before the streaming operations
|
||||
for i in range(2, self.num_imgs, 2):
|
||||
self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]),
|
||||
qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]),
|
||||
self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i]),
|
||||
qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i-1]),
|
||||
'image file map matches backing file before streaming')
|
||||
|
||||
# Create all streaming jobs
|
||||
@ -351,8 +351,8 @@ class TestParallelOps(iotests.QMPTestCase):
|
||||
def test_stream_base_node_name(self):
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[4]),
|
||||
qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[3]),
|
||||
self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[4]),
|
||||
qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[3]),
|
||||
'image file map matches backing file before streaming')
|
||||
|
||||
# Error: the base node does not exist
|
||||
@ -422,8 +422,8 @@ class TestQuorum(iotests.QMPTestCase):
|
||||
if not iotests.supports_quorum():
|
||||
return
|
||||
|
||||
self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.children[0]),
|
||||
qemu_io('-f', iotests.imgfmt, '-c', 'map', self.backing[0]),
|
||||
self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.children[0]),
|
||||
qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.backing[0]),
|
||||
'image file map matches backing file before streaming')
|
||||
|
||||
self.assert_no_active_block_jobs()
|
||||
|
@ -192,7 +192,7 @@ echo "== Verify image content =="
|
||||
|
||||
function verify_io()
|
||||
{
|
||||
if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "compat: 0.10" > /dev/null); then
|
||||
if ($QEMU_IMG info -U -f "$IMGFMT" "$TEST_IMG" | grep "compat: 0.10" > /dev/null); then
|
||||
# For v2 images, discarded clusters are read from the backing file
|
||||
# Keep the variable empty so that the backing file value can be used as
|
||||
# the default below
|
||||
|
@ -458,17 +458,18 @@ class TestDriveCompression(iotests.QMPTestCase):
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def do_prepare_drives(self, fmt, args):
|
||||
def do_prepare_drives(self, fmt, args, attach_target):
|
||||
self.vm = iotests.VM().add_drive(test_img)
|
||||
|
||||
qemu_img('create', '-f', fmt, blockdev_target_img,
|
||||
str(TestDriveCompression.image_len), *args)
|
||||
self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
|
||||
if attach_target:
|
||||
self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
|
||||
|
||||
self.vm.launch()
|
||||
|
||||
def do_test_compress_complete(self, cmd, format, **args):
|
||||
self.do_prepare_drives(format['type'], format['args'])
|
||||
def do_test_compress_complete(self, cmd, format, attach_target, **args):
|
||||
self.do_prepare_drives(format['type'], format['args'], attach_target)
|
||||
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
@ -484,15 +485,16 @@ class TestDriveCompression(iotests.QMPTestCase):
|
||||
|
||||
def test_complete_compress_drive_backup(self):
|
||||
for format in TestDriveCompression.fmt_supports_compression:
|
||||
self.do_test_compress_complete('drive-backup', format,
|
||||
self.do_test_compress_complete('drive-backup', format, False,
|
||||
target=blockdev_target_img, mode='existing')
|
||||
|
||||
def test_complete_compress_blockdev_backup(self):
|
||||
for format in TestDriveCompression.fmt_supports_compression:
|
||||
self.do_test_compress_complete('blockdev-backup', format, target='drive1')
|
||||
self.do_test_compress_complete('blockdev-backup', format, True,
|
||||
target='drive1')
|
||||
|
||||
def do_test_compress_cancel(self, cmd, format, **args):
|
||||
self.do_prepare_drives(format['type'], format['args'])
|
||||
def do_test_compress_cancel(self, cmd, format, attach_target, **args):
|
||||
self.do_prepare_drives(format['type'], format['args'], attach_target)
|
||||
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
@ -506,15 +508,16 @@ class TestDriveCompression(iotests.QMPTestCase):
|
||||
|
||||
def test_compress_cancel_drive_backup(self):
|
||||
for format in TestDriveCompression.fmt_supports_compression:
|
||||
self.do_test_compress_cancel('drive-backup', format,
|
||||
self.do_test_compress_cancel('drive-backup', format, False,
|
||||
target=blockdev_target_img, mode='existing')
|
||||
|
||||
def test_compress_cancel_blockdev_backup(self):
|
||||
for format in TestDriveCompression.fmt_supports_compression:
|
||||
self.do_test_compress_cancel('blockdev-backup', format, target='drive1')
|
||||
self.do_test_compress_cancel('blockdev-backup', format, True,
|
||||
target='drive1')
|
||||
|
||||
def do_test_compress_pause(self, cmd, format, **args):
|
||||
self.do_prepare_drives(format['type'], format['args'])
|
||||
def do_test_compress_pause(self, cmd, format, attach_target, **args):
|
||||
self.do_prepare_drives(format['type'], format['args'], attach_target)
|
||||
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
@ -546,12 +549,13 @@ class TestDriveCompression(iotests.QMPTestCase):
|
||||
|
||||
def test_compress_pause_drive_backup(self):
|
||||
for format in TestDriveCompression.fmt_supports_compression:
|
||||
self.do_test_compress_pause('drive-backup', format,
|
||||
self.do_test_compress_pause('drive-backup', format, False,
|
||||
target=blockdev_target_img, mode='existing')
|
||||
|
||||
def test_compress_pause_blockdev_backup(self):
|
||||
for format in TestDriveCompression.fmt_supports_compression:
|
||||
self.do_test_compress_pause('blockdev-backup', format, target='drive1')
|
||||
self.do_test_compress_pause('blockdev-backup', format, True,
|
||||
target='drive1')
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['raw', 'qcow2'])
|
||||
|
@ -135,7 +135,7 @@ qemu-img: Error while amending options: Input/output error
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Marking image as corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed
|
||||
qcow2: Marking image as corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed
|
||||
read failed: Input/output error
|
||||
|
||||
=== Testing unaligned pre-allocated zero cluster ===
|
||||
@ -166,7 +166,7 @@ discard 65536/65536 bytes at offset 0
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Image is corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further non-fatal corruption events will be suppressed
|
||||
qcow2: Image is corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further non-fatal corruption events will be suppressed
|
||||
read failed: Input/output error
|
||||
read failed: Input/output error
|
||||
|
||||
@ -176,7 +176,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 131072/131072 bytes at offset 0
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Image is corrupt: Cannot free unaligned cluster 0x52a00; further non-fatal corruption events will be suppressed
|
||||
qcow2: Marking image as corrupt: Data cluster offset 0x62a00 unaligned (L2 offset: 0x40000, L2 index: 0x1); further corruption events will be suppressed
|
||||
qcow2: Marking image as corrupt: Cluster allocation offset 0x62a00 unaligned (L2 offset: 0x40000, L2 index: 0x1); further corruption events will be suppressed
|
||||
discard 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read failed: Input/output error
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test case for discarding preallocated zero clusters in qcow2
|
||||
# Test case for preallocated zero clusters in qcow2
|
||||
#
|
||||
# Copyright (C) 2013 Red Hat, Inc.
|
||||
#
|
||||
@ -55,8 +55,134 @@ _make_test_img $IMG_SIZE
|
||||
$QEMU_IO -c "write 0 256k" -c "write -z 0 256k" -c "write 64M 512" \
|
||||
-c "discard 0 $IMG_SIZE" -c "read -P 0 0 $IMG_SIZE" "$TEST_IMG" \
|
||||
| _filter_qemu_io
|
||||
|
||||
# Check the image (there shouldn't be any leaks)
|
||||
_check_test_img
|
||||
# Map the image (we want all clusters to be gone)
|
||||
$QEMU_IMG map "$TEST_IMG"
|
||||
|
||||
_cleanup_test_img
|
||||
|
||||
|
||||
echo
|
||||
echo '=== Writing to preallocated zero clusters ==='
|
||||
echo
|
||||
|
||||
_make_test_img $IMG_SIZE
|
||||
|
||||
# Create data clusters (not aligned to an L2 table)
|
||||
$QEMU_IO -c 'write -P 42 1M 256k' "$TEST_IMG" | _filter_qemu_io
|
||||
orig_map=$($QEMU_IMG map --output=json "$TEST_IMG")
|
||||
|
||||
# Convert the data clusters to preallocated zero clusters
|
||||
$QEMU_IO -c 'write -z 1M 256k' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# Now write to them (with a COW needed for the head and tail)
|
||||
$QEMU_IO -c "write -P 23 $(((1024 + 32) * 1024)) 192k" "$TEST_IMG" \
|
||||
| _filter_qemu_io
|
||||
|
||||
# Check metadata correctness
|
||||
_check_test_img
|
||||
|
||||
# Check data correctness
|
||||
$QEMU_IO -c "read -P 0 $(( 1024 * 1024)) 32k" \
|
||||
-c "read -P 23 $(((1024 + 32) * 1024)) 192k" \
|
||||
-c "read -P 0 $(((1024 + 32 + 192) * 1024)) 32k" \
|
||||
"$TEST_IMG" \
|
||||
| _filter_qemu_io
|
||||
|
||||
# Check that we have actually reused the original area
|
||||
new_map=$($QEMU_IMG map --output=json "$TEST_IMG")
|
||||
if [ "$new_map" = "$orig_map" ]; then
|
||||
echo 'Successfully reused original clusters.'
|
||||
else
|
||||
echo 'Failed to reuse original clusters.'
|
||||
echo 'Original map:'
|
||||
echo "$orig_map"
|
||||
echo 'New map:'
|
||||
echo "$new_map"
|
||||
fi
|
||||
|
||||
_cleanup_test_img
|
||||
|
||||
|
||||
echo
|
||||
echo '=== Writing to a snapshotted preallocated zero cluster ==='
|
||||
echo
|
||||
|
||||
_make_test_img 64k
|
||||
|
||||
# Create a preallocated zero cluster
|
||||
$QEMU_IO -c 'write -P 42 0 64k' -c 'write -z 0 64k' "$TEST_IMG" \
|
||||
| _filter_qemu_io
|
||||
|
||||
# Snapshot it
|
||||
$QEMU_IMG snapshot -c foo "$TEST_IMG"
|
||||
|
||||
# Write to the cluster
|
||||
$QEMU_IO -c 'write -P 23 0 64k' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# Check metadata correctness
|
||||
_check_test_img
|
||||
|
||||
# Check data correctness
|
||||
$QEMU_IO -c 'read -P 23 0 64k' "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG snapshot -a foo "$TEST_IMG"
|
||||
$QEMU_IO -c 'read -P 0 0 64k' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
_cleanup_test_img
|
||||
|
||||
|
||||
echo
|
||||
echo '=== Consecutive write to a preallocated zero cluster ==='
|
||||
echo
|
||||
|
||||
_make_test_img 192k
|
||||
|
||||
# Create three normal clusters
|
||||
$QEMU_IO -c 'write -P 42 0 192k' "$TEST_IMG" | _filter_qemu_io
|
||||
orig_map=$($QEMU_IMG map --output=json "$TEST_IMG")
|
||||
|
||||
# Make the middle cluster a preallocated zero cluster
|
||||
$QEMU_IO -c 'write -z 64k 64k' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# Try to overwrite everything: This should reuse the whole range. To test that
|
||||
# this only issues a single continuous write request, use blkdebug.
|
||||
$QEMU_IO -c 'write -P 42 0 192k' \
|
||||
"json:{
|
||||
'driver': '$IMGFMT',
|
||||
'file': {
|
||||
'driver': 'blkdebug',
|
||||
'image.filename': '$TEST_IMG',
|
||||
'set-state': [{
|
||||
'event': 'write_aio',
|
||||
'new_state': 2
|
||||
}],
|
||||
'inject-error': [{
|
||||
'event': 'write_aio',
|
||||
'state': 2
|
||||
}]
|
||||
}
|
||||
}" \
|
||||
| _filter_qemu_io
|
||||
|
||||
# Check metadata correctness
|
||||
_check_test_img
|
||||
|
||||
# Check that we have actually reused the original area
|
||||
new_map=$($QEMU_IMG map --output=json "$TEST_IMG")
|
||||
if [ "$new_map" = "$orig_map" ]; then
|
||||
echo 'Successfully reused original clusters.'
|
||||
else
|
||||
echo 'Failed to reuse original clusters.'
|
||||
echo 'Original map:'
|
||||
echo "$orig_map"
|
||||
echo 'New map:'
|
||||
echo "$new_map"
|
||||
fi
|
||||
|
||||
_cleanup_test_img
|
||||
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
|
@ -14,4 +14,50 @@ discard 67109376/67109376 bytes at offset 0
|
||||
read 67109376/67109376 bytes at offset 0
|
||||
64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
Offset Length Mapped to File
|
||||
|
||||
=== Writing to preallocated zero clusters ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67109376
|
||||
wrote 262144/262144 bytes at offset 1048576
|
||||
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 262144/262144 bytes at offset 1048576
|
||||
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 196608/196608 bytes at offset 1081344
|
||||
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
read 32768/32768 bytes at offset 1048576
|
||||
32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 196608/196608 bytes at offset 1081344
|
||||
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 32768/32768 bytes at offset 1277952
|
||||
32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Successfully reused original clusters.
|
||||
|
||||
=== Writing to a snapshotted preallocated zero cluster ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
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)
|
||||
|
||||
=== Consecutive write to a preallocated zero cluster ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=196608
|
||||
wrote 196608/196608 bytes at offset 0
|
||||
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 65536/65536 bytes at offset 65536
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 196608/196608 bytes at offset 0
|
||||
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
Successfully reused original clusters.
|
||||
*** done
|
||||
|
@ -45,7 +45,7 @@ _cleanup()
|
||||
rm -f "${TEST_DIR}/${i}-${snapshot_virt0}"
|
||||
rm -f "${TEST_DIR}/${i}-${snapshot_virt1}"
|
||||
done
|
||||
rm -f "${TEST_IMG}.1" "${TEST_IMG}.2"
|
||||
rm -f "${TEST_IMG}" "${TEST_IMG}.1" "${TEST_IMG}.2" "${TEST_IMG}.base"
|
||||
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
@ -87,24 +87,26 @@ function create_group_snapshot()
|
||||
}
|
||||
|
||||
# ${1}: unique identifier for the snapshot filename
|
||||
# ${2}: true: open backing images; false: don't open them (default)
|
||||
# ${2}: extra_params to the blockdev-add command
|
||||
# ${3}: filename
|
||||
function do_blockdev_add()
|
||||
{
|
||||
cmd="{ 'execute': 'blockdev-add', 'arguments':
|
||||
{ 'driver': 'qcow2', 'node-name': 'snap_${1}', ${2}
|
||||
'file':
|
||||
{ 'driver': 'file', 'filename': '${3}',
|
||||
'node-name': 'file_${1}' } } }"
|
||||
_send_qemu_cmd $h "${cmd}" "return"
|
||||
}
|
||||
|
||||
# ${1}: unique identifier for the snapshot filename
|
||||
function add_snapshot_image()
|
||||
{
|
||||
if [ "${2}" = "true" ]; then
|
||||
extra_params=""
|
||||
else
|
||||
extra_params="'backing': '', "
|
||||
fi
|
||||
base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}"
|
||||
snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}"
|
||||
_make_test_img -b "${base_image}" "$size"
|
||||
mv "${TEST_IMG}" "${snapshot_file}"
|
||||
cmd="{ 'execute': 'blockdev-add', 'arguments':
|
||||
{ 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params}
|
||||
'file':
|
||||
{ 'driver': 'file', 'filename': '${snapshot_file}',
|
||||
'node-name': 'file_${1}' } } }"
|
||||
_send_qemu_cmd $h "${cmd}" "return"
|
||||
do_blockdev_add "$1" "'backing': '', " "${snapshot_file}"
|
||||
}
|
||||
|
||||
# ${1}: unique identifier for the snapshot filename
|
||||
@ -222,7 +224,10 @@ echo === Invalid command - snapshot node has a backing image ===
|
||||
echo
|
||||
|
||||
SNAPSHOTS=$((${SNAPSHOTS}+1))
|
||||
add_snapshot_image ${SNAPSHOTS} true
|
||||
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img "$size"
|
||||
_make_test_img -b "${TEST_IMG}.base" "$size"
|
||||
do_blockdev_add ${SNAPSHOTS} "" "${TEST_IMG}"
|
||||
blockdev_snapshot ${SNAPSHOTS} error
|
||||
|
||||
echo
|
||||
|
@ -78,7 +78,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
|
||||
|
||||
=== Invalid command - snapshot node has a backing image ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/12-snapshot-v0.IMGFMT
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
{"return": {}}
|
||||
{"error": {"class": "GenericError", "desc": "The snapshot already has a backing image"}}
|
||||
|
||||
|
@ -82,8 +82,7 @@ run_qemu -drive driver=$IMGFMT,id=disk,node-name=test-node,file="$TEST_IMG" <<EO
|
||||
"driver": "$IMGFMT",
|
||||
"node-name": "disk",
|
||||
"file": {
|
||||
"driver": "file",
|
||||
"filename": "$TEST_IMG"
|
||||
"driver": "null-co"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,8 +91,7 @@ run_qemu -drive driver=$IMGFMT,id=disk,node-name=test-node,file="$TEST_IMG" <<EO
|
||||
"driver": "$IMGFMT",
|
||||
"node-name": "test-node",
|
||||
"file": {
|
||||
"driver": "file",
|
||||
"filename": "$TEST_IMG"
|
||||
"driver": "null-co"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,9 @@ echo "vm2: qemu process running successfully"
|
||||
echo "vm2: flush io, and quit"
|
||||
_send_qemu_cmd $h2 'qemu-io disk flush' "(qemu)"
|
||||
_send_qemu_cmd $h2 'quit' ""
|
||||
_send_qemu_cmd $h1 'quit' ""
|
||||
|
||||
wait
|
||||
echo "Check image pattern"
|
||||
${QEMU_IO} -c "read -P 0x22 0 4M" "${TEST_IMG}" | _filter_testdir | _filter_qemu_io
|
||||
|
||||
|
@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Image resized.
|
||||
[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
|
||||
64 KiB (0x10000) bytes allocated at offset 0 bytes (0x0)
|
||||
Offset Length Mapped to File
|
||||
|
||||
=== Testing map on an image file truncated outside of qemu ===
|
||||
@ -17,5 +17,5 @@ wrote 65536/65536 bytes at offset 0
|
||||
Image resized.
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) qemu-io drv0 map
|
||||
[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
|
||||
64 KiB (0x10000) bytes allocated at offset 0 bytes (0x0)
|
||||
*** done
|
||||
|
@ -112,7 +112,7 @@ read 3145728/3145728 bytes at offset 0
|
||||
3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 63963136/63963136 bytes at offset 3145728
|
||||
61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
|
||||
convert -c -S 0:
|
||||
read 3145728/3145728 bytes at offset 0
|
||||
@ -134,7 +134,7 @@ read 30408704/30408704 bytes at offset 3145728
|
||||
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 33554432/33554432 bytes at offset 33554432
|
||||
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
|
||||
convert -c -S 0 with source backing file:
|
||||
read 3145728/3145728 bytes at offset 0
|
||||
@ -152,7 +152,7 @@ read 30408704/30408704 bytes at offset 3145728
|
||||
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 33554432/33554432 bytes at offset 33554432
|
||||
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
|
||||
convert -c -S 0 -B ...
|
||||
read 3145728/3145728 bytes at offset 0
|
||||
@ -176,11 +176,11 @@ wrote 1024/1024 bytes at offset 17408
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
convert -S 4k
|
||||
[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 8192},
|
||||
[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 1024, "length": 7168, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 9216},
|
||||
{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 10240},
|
||||
{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
|
||||
|
||||
convert -c -S 4k
|
||||
@ -192,9 +192,9 @@ convert -c -S 4k
|
||||
{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
|
||||
|
||||
convert -S 8k
|
||||
[{ "start": 0, "length": 9216, "depth": 0, "zero": false, "data": true, "offset": 8192},
|
||||
[{ "start": 0, "length": 9216, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 17408},
|
||||
{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
|
||||
|
||||
convert -c -S 8k
|
||||
|
@ -2,39 +2,39 @@ QA output created by 146
|
||||
|
||||
=== Testing VPC Autodetect ===
|
||||
|
||||
[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0)
|
||||
126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing VPC with current_size force ===
|
||||
|
||||
[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0)
|
||||
127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing VPC with chs force ===
|
||||
|
||||
[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0)
|
||||
126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Hyper-V Autodetect ===
|
||||
|
||||
[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0)
|
||||
127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Hyper-V with current_size force ===
|
||||
|
||||
[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0)
|
||||
127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Hyper-V with chs force ===
|
||||
|
||||
[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0)
|
||||
126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing d2v Autodetect ===
|
||||
|
||||
[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1)
|
||||
251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing d2v with current_size force ===
|
||||
|
||||
[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1)
|
||||
251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing d2v with chs force ===
|
||||
|
||||
[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1)
|
||||
251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Image create, default ===
|
||||
|
||||
@ -42,15 +42,15 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296
|
||||
|
||||
=== Read created image, default opts ====
|
||||
|
||||
[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Read created image, force_size_calc=chs ====
|
||||
|
||||
[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Read created image, force_size_calc=current_size ====
|
||||
|
||||
[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Image create, force_size ===
|
||||
|
||||
@ -58,13 +58,13 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 forc
|
||||
|
||||
=== Read created image, default opts ====
|
||||
|
||||
[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Read created image, force_size_calc=chs ====
|
||||
|
||||
[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Read created image, force_size_calc=current_size ====
|
||||
|
||||
[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
*** done
|
||||
|
233
tests/qemu-iotests/153
Executable file
233
tests/qemu-iotests/153
Executable file
@ -0,0 +1,233 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test image locking
|
||||
#
|
||||
# Copyright 2016, 2017 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=famz@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
|
||||
rm -f "${TEST_IMG}.base"
|
||||
rm -f "${TEST_IMG}.convert"
|
||||
rm -f "${TEST_IMG}.a"
|
||||
rm -f "${TEST_IMG}.b"
|
||||
rm -f "${TEST_IMG}.lnk"
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
. ./common.qemu
|
||||
|
||||
size=32M
|
||||
|
||||
_check_ofd()
|
||||
{
|
||||
_make_test_img $size >/dev/null
|
||||
if $QEMU_IMG_PROG info --image-opts "driver=file,locking=on,filename=$TEST_IMG" 2>&1 |
|
||||
grep -q 'falling back to POSIX file'; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
_check_ofd || _notrun "OFD lock not available"
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
_run_cmd()
|
||||
{
|
||||
echo
|
||||
(echo "$@"; "$@" 2>&1 1>/dev/null) | _filter_testdir
|
||||
}
|
||||
|
||||
function _do_run_qemu()
|
||||
{
|
||||
(
|
||||
if ! test -t 0; then
|
||||
while read cmd; do
|
||||
echo $cmd
|
||||
done
|
||||
fi
|
||||
echo quit
|
||||
) | $QEMU -nographic -monitor stdio -serial none "$@" 1>/dev/null
|
||||
}
|
||||
|
||||
function _run_qemu_with_images()
|
||||
{
|
||||
_do_run_qemu \
|
||||
$(for i in $@; do echo "-drive if=none,file=$i"; done) 2>&1 \
|
||||
| _filter_testdir | _filter_qemu
|
||||
}
|
||||
|
||||
echo "== readonly=off,force-share=on should be rejected =="
|
||||
_run_qemu_with_images null-co://,readonly=off,force-share=on
|
||||
|
||||
for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do
|
||||
echo
|
||||
echo "== Creating base image =="
|
||||
TEST_IMG="${TEST_IMG}.base" _make_test_img $size
|
||||
|
||||
echo
|
||||
echo "== Creating test image =="
|
||||
$QEMU_IMG create -f $IMGFMT "${TEST_IMG}" -b ${TEST_IMG}.base | _filter_img_create
|
||||
|
||||
echo
|
||||
echo "== Launching QEMU, opts: '$opts1' =="
|
||||
_launch_qemu -drive file="${TEST_IMG}",if=none,$opts1
|
||||
h=$QEMU_HANDLE
|
||||
|
||||
for opts2 in "" "read-only=on" "read-only=on,force-share=on"; do
|
||||
echo
|
||||
echo "== Launching another QEMU, opts: '$opts2' =="
|
||||
echo "quit" | \
|
||||
$QEMU -nographic -monitor stdio \
|
||||
-drive file="${TEST_IMG}",if=none,$opts2 2>&1 1>/dev/null | \
|
||||
_filter_testdir | _filter_qemu
|
||||
done
|
||||
|
||||
for L in "" "-U"; do
|
||||
|
||||
echo
|
||||
echo "== Running utility commands $L =="
|
||||
_run_cmd $QEMU_IO $L -c "read 0 512" "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IO $L -r -c "read 0 512" "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IO -c "open $L ${TEST_IMG}" -c "read 0 512"
|
||||
_run_cmd $QEMU_IO -c "open -r $L ${TEST_IMG}" -c "read 0 512"
|
||||
_run_cmd $QEMU_IMG info $L "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IMG check $L "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IMG compare $L "${TEST_IMG}" "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IMG map $L "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IMG amend -o "" $L "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IMG commit $L "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IMG resize $L "${TEST_IMG}" $size
|
||||
_run_cmd $QEMU_IMG rebase $L "${TEST_IMG}" -b "${TEST_IMG}.base"
|
||||
_run_cmd $QEMU_IMG snapshot -l $L "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IMG convert $L "${TEST_IMG}" "${TEST_IMG}.convert"
|
||||
_run_cmd $QEMU_IMG dd $L if="${TEST_IMG}" of="${TEST_IMG}.convert" bs=512 count=1
|
||||
_run_cmd $QEMU_IMG bench $L -c 1 "${TEST_IMG}"
|
||||
_run_cmd $QEMU_IMG bench $L -w -c 1 "${TEST_IMG}"
|
||||
done
|
||||
_send_qemu_cmd $h "{ 'execute': 'quit', }" ""
|
||||
echo
|
||||
echo "Round done"
|
||||
_cleanup_qemu
|
||||
done
|
||||
|
||||
for opt1 in $test_opts; do
|
||||
for opt2 in $test_opts; do
|
||||
echo
|
||||
echo "== Two devices with the same image ($opt1 - $opt2) =="
|
||||
_run_qemu_with_images "${TEST_IMG},$opt1" "${TEST_IMG},$opt2"
|
||||
done
|
||||
done
|
||||
|
||||
echo "== Creating ${TEST_IMG}.[abc] ==" | _filter_testdir
|
||||
(
|
||||
$QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}"
|
||||
$QEMU_IMG create -f qcow2 "${TEST_IMG}.b" -b "${TEST_IMG}"
|
||||
$QEMU_IMG create -f qcow2 "${TEST_IMG}.c" -b "${TEST_IMG}.b"
|
||||
) | _filter_img_create
|
||||
|
||||
echo
|
||||
echo "== Two devices sharing the same file in backing chain =="
|
||||
_run_qemu_with_images "${TEST_IMG}.a" "${TEST_IMG}.b"
|
||||
_run_qemu_with_images "${TEST_IMG}.a" "${TEST_IMG}.c"
|
||||
|
||||
echo
|
||||
echo "== Backing image also as an active device =="
|
||||
_run_qemu_with_images "${TEST_IMG}.a" "${TEST_IMG}"
|
||||
|
||||
echo
|
||||
echo "== Backing image also as an active device (ro) =="
|
||||
_run_qemu_with_images "${TEST_IMG}.a" "${TEST_IMG},readonly=on"
|
||||
|
||||
echo
|
||||
echo "== Symbolic link =="
|
||||
rm -f "${TEST_IMG}.lnk" &>/dev/null
|
||||
ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link"
|
||||
_run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}"
|
||||
|
||||
echo
|
||||
echo "== Closing an image should unlock it =="
|
||||
_launch_qemu
|
||||
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{ 'execute': 'qmp_capabilities' }" \
|
||||
'return'
|
||||
|
||||
echo "Adding drive"
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{ 'execute': 'human-monitor-command',
|
||||
'arguments': { 'command-line': 'drive_add 0 if=none,id=d0,file=${TEST_IMG}' } }" \
|
||||
""
|
||||
|
||||
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
|
||||
|
||||
echo "Closing drive"
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{ 'execute': 'human-monitor-command',
|
||||
'arguments': { 'command-line': 'drive_del d0' } }" \
|
||||
""
|
||||
|
||||
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
|
||||
|
||||
echo "Adding two and closing one"
|
||||
for d in d0 d1; do
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{ 'execute': 'human-monitor-command',
|
||||
'arguments': { 'command-line': 'drive_add 0 if=none,id=$d,file=${TEST_IMG},readonly=on' } }" \
|
||||
""
|
||||
done
|
||||
|
||||
_run_cmd $QEMU_IMG info "${TEST_IMG}"
|
||||
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{ 'execute': 'human-monitor-command',
|
||||
'arguments': { 'command-line': 'drive_del d0' } }" \
|
||||
""
|
||||
|
||||
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
|
||||
|
||||
echo "Closing the other"
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{ 'execute': 'human-monitor-command',
|
||||
'arguments': { 'command-line': 'drive_del d1' } }" \
|
||||
""
|
||||
|
||||
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
|
||||
|
||||
_cleanup_qemu
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
390
tests/qemu-iotests/153.out
Normal file
390
tests/qemu-iotests/153.out
Normal file
@ -0,0 +1,390 @@
|
||||
QA output created by 153
|
||||
== readonly=off,force-share=on should be rejected ==
|
||||
QEMU_PROG: -drive if=none,file=null-co://,readonly=off,force-share=on: force-share=on can only be used with read-only images
|
||||
|
||||
== Creating base image ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
|
||||
|
||||
== Creating test image ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
|
||||
== Launching QEMU, opts: '' ==
|
||||
|
||||
== Launching another QEMU, opts: '' ==
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
== Launching another QEMU, opts: 'read-only=on' ==
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,read-only=on: Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
== Launching another QEMU, opts: 'read-only=on,force-share=on' ==
|
||||
|
||||
== Running utility commands ==
|
||||
|
||||
_qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper info TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper check TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper map TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper commit TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper resize TEST_DIR/t.qcow2 32M
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper convert TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper dd if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
== Running utility commands -U ==
|
||||
|
||||
_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
|
||||
can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
|
||||
|
||||
_qemu_io_wrapper -U -r -c read 0 512 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_io_wrapper -c open -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
|
||||
|
||||
_qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
_qemu_img_wrapper info -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper check -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper map -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
|
||||
qemu-img: unrecognized option '-U'
|
||||
Try 'qemu-img --help' for more information
|
||||
|
||||
_qemu_img_wrapper commit -U TEST_DIR/t.qcow2
|
||||
qemu-img: unrecognized option '-U'
|
||||
Try 'qemu-img --help' for more information
|
||||
|
||||
_qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M
|
||||
qemu-img: unrecognized option '-U'
|
||||
Try 'qemu-img --help' for more information
|
||||
|
||||
_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper convert -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
|
||||
|
||||
_qemu_img_wrapper dd -U if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
|
||||
|
||||
_qemu_img_wrapper bench -U -c 1 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper bench -U -w -c 1 TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': force-share=on can only be used with read-only images
|
||||
|
||||
Round done
|
||||
|
||||
== Creating base image ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
|
||||
|
||||
== Creating test image ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
|
||||
== Launching QEMU, opts: 'read-only=on' ==
|
||||
|
||||
== Launching another QEMU, opts: '' ==
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
== Launching another QEMU, opts: 'read-only=on' ==
|
||||
|
||||
== Launching another QEMU, opts: 'read-only=on,force-share=on' ==
|
||||
|
||||
== Running utility commands ==
|
||||
|
||||
_qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
_qemu_img_wrapper info TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper check TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper map TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper commit TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper resize TEST_DIR/t.qcow2 32M
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper convert TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
|
||||
|
||||
_qemu_img_wrapper dd if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
|
||||
|
||||
_qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
== Running utility commands -U ==
|
||||
|
||||
_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
|
||||
can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
|
||||
|
||||
_qemu_io_wrapper -U -r -c read 0 512 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_io_wrapper -c open -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
|
||||
|
||||
_qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
_qemu_img_wrapper info -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper check -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper map -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
|
||||
qemu-img: unrecognized option '-U'
|
||||
Try 'qemu-img --help' for more information
|
||||
|
||||
_qemu_img_wrapper commit -U TEST_DIR/t.qcow2
|
||||
qemu-img: unrecognized option '-U'
|
||||
Try 'qemu-img --help' for more information
|
||||
|
||||
_qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M
|
||||
qemu-img: unrecognized option '-U'
|
||||
Try 'qemu-img --help' for more information
|
||||
|
||||
_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
_qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper convert -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
|
||||
|
||||
_qemu_img_wrapper dd -U if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
|
||||
|
||||
_qemu_img_wrapper bench -U -c 1 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper bench -U -w -c 1 TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': force-share=on can only be used with read-only images
|
||||
|
||||
Round done
|
||||
|
||||
== Creating base image ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
|
||||
|
||||
== Creating test image ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
|
||||
== Launching QEMU, opts: 'read-only=on,force-share=on' ==
|
||||
|
||||
== Launching another QEMU, opts: '' ==
|
||||
|
||||
== Launching another QEMU, opts: 'read-only=on' ==
|
||||
|
||||
== Launching another QEMU, opts: 'read-only=on,force-share=on' ==
|
||||
|
||||
== Running utility commands ==
|
||||
|
||||
_qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
_qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
_qemu_img_wrapper info TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper check TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper map TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper commit TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper resize TEST_DIR/t.qcow2 32M
|
||||
|
||||
_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
|
||||
|
||||
_qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper convert TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
|
||||
|
||||
_qemu_img_wrapper dd if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
|
||||
|
||||
_qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
|
||||
|
||||
== Running utility commands -U ==
|
||||
|
||||
_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
|
||||
can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
|
||||
|
||||
_qemu_io_wrapper -U -r -c read 0 512 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_io_wrapper -c open -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
|
||||
|
||||
_qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
_qemu_img_wrapper info -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper check -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper map -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
|
||||
qemu-img: unrecognized option '-U'
|
||||
Try 'qemu-img --help' for more information
|
||||
|
||||
_qemu_img_wrapper commit -U TEST_DIR/t.qcow2
|
||||
qemu-img: unrecognized option '-U'
|
||||
Try 'qemu-img --help' for more information
|
||||
|
||||
_qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M
|
||||
qemu-img: unrecognized option '-U'
|
||||
Try 'qemu-img --help' for more information
|
||||
|
||||
_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
|
||||
|
||||
_qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper convert -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
|
||||
|
||||
_qemu_img_wrapper dd -U if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
|
||||
|
||||
_qemu_img_wrapper bench -U -c 1 TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_img_wrapper bench -U -w -c 1 TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': force-share=on can only be used with read-only images
|
||||
|
||||
Round done
|
||||
== Creating TEST_DIR/t.qcow2.[abc] ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT.a', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT
|
||||
Formatting 'TEST_DIR/t.IMGFMT.b', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT
|
||||
Formatting 'TEST_DIR/t.IMGFMT.c', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.b
|
||||
|
||||
== Two devices sharing the same file in backing chain ==
|
||||
|
||||
== Backing image also as an active device ==
|
||||
QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
== Backing image also as an active device (ro) ==
|
||||
|
||||
== Symbolic link ==
|
||||
QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
|
||||
== Closing an image should unlock it ==
|
||||
{"return": {}}
|
||||
Adding drive
|
||||
|
||||
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
Closing drive
|
||||
|
||||
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
|
||||
Adding two and closing one
|
||||
|
||||
_qemu_img_wrapper info TEST_DIR/t.qcow2
|
||||
|
||||
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
Closing the other
|
||||
|
||||
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
|
||||
*** done
|
@ -2,7 +2,7 @@
|
||||
#
|
||||
# qcow2 specific bdrv_pwrite_zeroes tests with backing files (complements 034)
|
||||
#
|
||||
# Copyright (C) 2016 Red Hat, Inc.
|
||||
# Copyright (C) 2016-2017 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
|
||||
@ -42,7 +42,10 @@ _supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
CLUSTER_SIZE=4k
|
||||
size=128M
|
||||
size=$((128 * 1024 * 1024))
|
||||
|
||||
# This test requires zero clusters, added in v3 images
|
||||
_unsupported_imgopts compat=0.10
|
||||
|
||||
echo
|
||||
echo == backing file contains zeros ==
|
||||
@ -299,6 +302,159 @@ $QEMU_IO -c "read -P 0 75k 1k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
echo
|
||||
echo == unaligned image tail cluster, no allocation needed ==
|
||||
|
||||
# With no backing file, write to all or part of unallocated partial cluster
|
||||
# will mark the cluster as zero, but does not allocate.
|
||||
# Re-create the image each time to get back to unallocated clusters.
|
||||
|
||||
# Write at the front: sector-wise, the request is: 128m... | 00 -- -- --
|
||||
_make_test_img $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at the back: sector-wise, the request is: 128m... | -- -- -- 00
|
||||
_make_test_img $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at middle: sector-wise, the request is: 128m... | -- 00 00 --
|
||||
_make_test_img $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write entire cluster: sector-wise, the request is: 128m... | 00 00 00 00
|
||||
_make_test_img $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Repeat with backing file holding unallocated cluster.
|
||||
# TODO: Note that this forces an allocation, because we aren't yet able to
|
||||
# quickly detect that reads beyond EOF of the backing file are always zero
|
||||
CLUSTER_SIZE=2048 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
|
||||
|
||||
# Write at the front: sector-wise, the request is:
|
||||
# backing: 128m... | -- --
|
||||
# active: 128m... | 00 -- -- --
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at the back: sector-wise, the request is:
|
||||
# backing: 128m... | -- --
|
||||
# active: 128m... | -- -- -- 00
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at middle: sector-wise, the request is:
|
||||
# backing: 128m... | -- --
|
||||
# active: 128m... | -- 00 00 --
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write entire cluster: sector-wise, the request is:
|
||||
# backing: 128m... | -- --
|
||||
# active: 128m... | 00 00 00 00
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Repeat with backing file holding zero'd cluster
|
||||
# TODO: Note that this forces an allocation, because we aren't yet able to
|
||||
# quickly detect that reads beyond EOF of the backing file are always zero
|
||||
$QEMU_IO -c "write -z $size 512" "$TEST_IMG.base" | _filter_qemu_io
|
||||
|
||||
# Write at the front: sector-wise, the request is:
|
||||
# backing: 128m... | 00 00
|
||||
# active: 128m... | 00 -- -- --
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at the back: sector-wise, the request is:
|
||||
# backing: 128m... | 00 00
|
||||
# active: 128m... | -- -- -- 00
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at middle: sector-wise, the request is:
|
||||
# backing: 128m... | 00 00
|
||||
# active: 128m... | -- 00 00 --
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write entire cluster: sector-wise, the request is:
|
||||
# backing: 128m... | 00 00
|
||||
# active: 128m... | 00 00 00 00
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# A preallocated cluster maintains its allocation, whether it stays as
|
||||
# data due to a partial write:
|
||||
# Convert 128m... | XX XX => ... | XX 00
|
||||
_make_test_img $((size + 1024))
|
||||
$QEMU_IO -c "write -P 1 $((size)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 1 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# or because it is the entire cluster and can use the zero flag:
|
||||
# Convert 128m... | XX XX => ... | 00 00
|
||||
$QEMU_IO -c "write -z $((size)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $size 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
echo
|
||||
echo == unaligned image tail cluster, allocation required ==
|
||||
|
||||
# Write beyond backing file must COW
|
||||
# Backing file: 128m... | XX --
|
||||
# Active layer: 128m... | -- -- 00 --
|
||||
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -P 1 $((size)) 512" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z $((size + 1024)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 1 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size + 512)) 1536" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Writes at boundaries of (partial) cluster must not lose mid-cluster data
|
||||
# Backing file: 128m: ... | -- XX
|
||||
# Active layer: 128m: ... | 00 -- -- 00
|
||||
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -P 1 $((size + 512)) 512" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 1 $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size + 1024)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 1 $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size + 1024)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
|
@ -42,9 +42,9 @@ read 1024/1024 bytes at offset 65536
|
||||
read 2048/2048 bytes at offset 67584
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 36864, "length": 28672, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 69632, "length": 134148096, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== backing file contains non-zero data after write_zeroes ==
|
||||
@ -69,9 +69,9 @@ read 1024/1024 bytes at offset 44032
|
||||
read 3072/3072 bytes at offset 40960
|
||||
3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 36864, "length": 4096, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 40960, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 40960, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 45056, "length": 134172672, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== write_zeroes covers non-zero data ==
|
||||
@ -143,13 +143,13 @@ read 1024/1024 bytes at offset 67584
|
||||
read 5120/5120 bytes at offset 68608
|
||||
5 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 36864, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 40960, "length": 8192, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 49152, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 49152, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 53248, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 57344, "length": 8192, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 28672},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 69632, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 73728, "length": 134144000, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
@ -186,13 +186,13 @@ read 1024/1024 bytes at offset 72704
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 36864, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 36864, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 40960, "length": 8192, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 49152, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 53248, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 53248, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 57344, "length": 8192, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 69632, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 28672},
|
||||
{ "start": 69632, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 73728, "length": 134144000, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== spanning two clusters, partially overwriting backing file ==
|
||||
@ -212,7 +212,7 @@ read 1024/1024 bytes at offset 5120
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 2048/2048 bytes at offset 6144
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
[{ "start": 0, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 8192, "length": 134209536, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== spanning multiple clusters, non-zero in first cluster ==
|
||||
@ -227,7 +227,7 @@ read 2048/2048 bytes at offset 65536
|
||||
read 10240/10240 bytes at offset 67584
|
||||
10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 69632, "length": 8192, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
@ -257,7 +257,7 @@ read 2048/2048 bytes at offset 75776
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 8192, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== spanning multiple clusters, partially overwriting backing file ==
|
||||
@ -278,8 +278,136 @@ read 2048/2048 bytes at offset 74752
|
||||
read 1024/1024 bytes at offset 76800
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 69632, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== unaligned image tail cluster, no allocation needed ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
|
||||
wrote 512/512 bytes at offset 134219264
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
|
||||
wrote 1024/1024 bytes at offset 134218240
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
|
||||
wrote 2048/2048 bytes at offset 134217728
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134219264
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 1024/1024 bytes at offset 134218240
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 2048/2048 bytes at offset 134217728
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134219264
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 1024/1024 bytes at offset 134218240
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 2048/2048 bytes at offset 134217728
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134218752
|
||||
wrote 1024/1024 bytes at offset 134217728
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
1024/1024 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
wrote 1024/1024 bytes at offset 134217728
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
1024/1024 bytes allocated at offset 128 MiB
|
||||
read 1024/1024 bytes at offset 134217728
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 134217728, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 1024, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
|
||||
== unaligned image tail cluster, allocation required ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 134218752
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 1536/1536 bytes at offset 134218240
|
||||
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 1024/1024 bytes at offset 134218752
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 134219264
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 1024/1024 bytes at offset 134218752
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
*** done
|
||||
|
@ -30,6 +30,8 @@ status=1 # failure is the default!
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
rm -f "$TEST_IMG.2"
|
||||
rm -f "$TEST_IMG.3"
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
@ -86,6 +88,9 @@ size=720k
|
||||
|
||||
_make_test_img $size
|
||||
|
||||
TEST_IMG="$TEST_IMG.2" _make_test_img $size
|
||||
TEST_IMG="$TEST_IMG.3" _make_test_img $size
|
||||
|
||||
# Default drive semantics:
|
||||
#
|
||||
# By default you get a single empty floppy drive. You can override it with
|
||||
@ -105,7 +110,7 @@ echo === Using -fda/-fdb options ===
|
||||
|
||||
check_floppy_qtree -fda "$TEST_IMG"
|
||||
check_floppy_qtree -fdb "$TEST_IMG"
|
||||
check_floppy_qtree -fda "$TEST_IMG" -fdb "$TEST_IMG"
|
||||
check_floppy_qtree -fda "$TEST_IMG" -fdb "$TEST_IMG.2"
|
||||
|
||||
|
||||
echo
|
||||
@ -114,7 +119,7 @@ echo === Using -drive options ===
|
||||
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG"
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG",index=1
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=floppy,file="$TEST_IMG",index=1
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=floppy,file="$TEST_IMG.2",index=1
|
||||
|
||||
echo
|
||||
echo
|
||||
@ -122,7 +127,7 @@ echo === Using -drive if=none and -global ===
|
||||
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
|
||||
-global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1
|
||||
|
||||
echo
|
||||
@ -131,7 +136,7 @@ echo === Using -drive if=none and -device ===
|
||||
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
|
||||
-device floppy,drive=none0 -device floppy,drive=none1,unit=1
|
||||
|
||||
echo
|
||||
@ -139,58 +144,58 @@ echo
|
||||
echo === Mixing -fdX and -global ===
|
||||
|
||||
# Working
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -global isa-fdc.driveB=none0
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -global isa-fdc.driveA=none0
|
||||
|
||||
# Conflicting (-fdX wins)
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -global isa-fdc.driveA=none0
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -global isa-fdc.driveB=none0
|
||||
|
||||
echo
|
||||
echo
|
||||
echo === Mixing -fdX and -device ===
|
||||
|
||||
# Working
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=1
|
||||
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=0
|
||||
|
||||
# Conflicting
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1
|
||||
check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=0
|
||||
check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=1
|
||||
|
||||
echo
|
||||
echo
|
||||
echo === Mixing -drive and -device ===
|
||||
|
||||
# Working
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=1
|
||||
|
||||
# Conflicting
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=0
|
||||
|
||||
echo
|
||||
echo
|
||||
echo === Mixing -global and -device ===
|
||||
|
||||
# Working
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
|
||||
-global isa-fdc.driveA=none0 -device floppy,drive=none1
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
|
||||
-global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1
|
||||
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
|
||||
-global isa-fdc.driveB=none0 -device floppy,drive=none1
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
|
||||
-global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0
|
||||
|
||||
# Conflicting
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
|
||||
-global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
|
||||
check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
|
||||
-global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1
|
||||
|
||||
echo
|
||||
@ -199,8 +204,8 @@ echo === Too many floppy drives ===
|
||||
|
||||
# Working
|
||||
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" \
|
||||
-drive if=none,file="$TEST_IMG" \
|
||||
-drive if=none,file="$TEST_IMG" \
|
||||
-drive if=none,file="$TEST_IMG.2" \
|
||||
-drive if=none,file="$TEST_IMG.3" \
|
||||
-global isa-fdc.driveB=none0 \
|
||||
-device floppy,drive=none1
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
QA output created by 172
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=737280
|
||||
Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=737280
|
||||
Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=737280
|
||||
|
||||
|
||||
=== Default ===
|
||||
@ -99,7 +101,7 @@ Testing: -fdb TEST_DIR/t.qcow2
|
||||
share-rw = false
|
||||
drive-type = "288"
|
||||
|
||||
Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2
|
||||
Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -205,7 +207,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
|
||||
share-rw = false
|
||||
drive-type = "288"
|
||||
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t.qcow2.2,index=1
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -300,7 +302,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -395,7 +397,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 -device floppy,drive=none1,unit=1
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0 -device floppy,drive=none1,unit=1
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -436,7 +438,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
|
||||
|
||||
=== Mixing -fdX and -global ===
|
||||
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -474,7 +476,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -512,7 +514,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -539,7 +541,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -569,7 +571,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
|
||||
|
||||
=== Mixing -fdX and -device ===
|
||||
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -607,7 +609,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=1
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -645,7 +647,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -683,7 +685,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=0
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -721,18 +723,18 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
|
||||
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=0
|
||||
QEMU_PROG: -device floppy,drive=none0,unit=0: Floppy unit 0 is in use
|
||||
QEMU_PROG: -device floppy,drive=none0,unit=0: Device initialization failed.
|
||||
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
|
||||
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=1
|
||||
QEMU_PROG: -device floppy,drive=none0,unit=1: Floppy unit 1 is in use
|
||||
QEMU_PROG: -device floppy,drive=none0,unit=1: Device initialization failed.
|
||||
|
||||
|
||||
=== Mixing -drive and -device ===
|
||||
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -770,7 +772,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=1
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -808,14 +810,14 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=0
|
||||
QEMU_PROG: -device floppy,drive=none0,unit=0: Floppy unit 0 is in use
|
||||
QEMU_PROG: -device floppy,drive=none0,unit=0: Device initialization failed.
|
||||
|
||||
|
||||
=== Mixing -global and -device ===
|
||||
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0 -device floppy,drive=none1
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -853,7 +855,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -891,7 +893,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0 -device floppy,drive=none1
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -929,7 +931,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0
|
||||
|
||||
dev: isa-fdc, id ""
|
||||
iobase = 1008 (0x3f0)
|
||||
@ -967,18 +969,18 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
|
||||
share-rw = false
|
||||
drive-type = "144"
|
||||
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0
|
||||
QEMU_PROG: -device floppy,drive=none1,unit=0: Floppy unit 0 is in use
|
||||
QEMU_PROG: -device floppy,drive=none1,unit=0: Device initialization failed.
|
||||
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1
|
||||
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1
|
||||
QEMU_PROG: -device floppy,drive=none1,unit=1: Floppy unit 1 is in use
|
||||
QEMU_PROG: -device floppy,drive=none1,unit=1: Device initialization failed.
|
||||
|
||||
|
||||
=== Too many floppy drives ===
|
||||
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1
|
||||
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -drive if=none,file=TEST_DIR/t.qcow2.3 -global isa-fdc.driveB=none0 -device floppy,drive=none1
|
||||
QEMU_PROG: -device floppy,drive=none1: Can't create floppy unit 2, bus supports only 2 units
|
||||
QEMU_PROG: -device floppy,drive=none1: Device initialization failed.
|
||||
|
||||
|
114
tests/qemu-iotests/177
Executable file
114
tests/qemu-iotests/177
Executable file
@ -0,0 +1,114 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test corner cases with unusual block geometries
|
||||
#
|
||||
# Copyright (C) 2016-2017 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=eblake@redhat.com
|
||||
|
||||
seq=`basename $0`
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here=`pwd`
|
||||
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 qcow2
|
||||
_supported_proto file
|
||||
|
||||
CLUSTER_SIZE=1M
|
||||
size=128M
|
||||
options=driver=blkdebug,image.driver=qcow2
|
||||
|
||||
echo
|
||||
echo "== setting up files =="
|
||||
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img $size
|
||||
$QEMU_IO -c "write -P 11 0 $size" "$TEST_IMG.base" | _filter_qemu_io
|
||||
_make_test_img -b "$TEST_IMG.base"
|
||||
$QEMU_IO -c "write -P 22 0 $size" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# Limited to 64k max-transfer
|
||||
echo
|
||||
echo "== constrained alignment and max-transfer =="
|
||||
limits=align=4k,max-transfer=64k
|
||||
$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
|
||||
-c "write -P 33 1000 128k" -c "read -P 33 1000 128k" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== write zero with constrained max-transfer =="
|
||||
limits=align=512,max-transfer=64k,opt-write-zero=$CLUSTER_SIZE
|
||||
$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
|
||||
-c "write -z 8003584 2093056" | _filter_qemu_io
|
||||
|
||||
# non-power-of-2 write-zero/discard alignments
|
||||
echo
|
||||
echo "== non-power-of-2 write zeroes limits =="
|
||||
|
||||
limits=align=512,opt-write-zero=15M,max-write-zero=15M,opt-discard=15M,max-discard=15M
|
||||
$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
|
||||
-c "write -z 32M 32M" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== non-power-of-2 discard limits =="
|
||||
|
||||
limits=align=512,opt-write-zero=15M,max-write-zero=15M,opt-discard=15M,max-discard=15M
|
||||
$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
|
||||
-c "discard 80000001 30M" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== verify image content =="
|
||||
|
||||
function verify_io()
|
||||
{
|
||||
if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" |
|
||||
grep "compat: 0.10" > /dev/null); then
|
||||
# For v2 images, discarded clusters are read from the backing file
|
||||
discarded=11
|
||||
else
|
||||
# Discarded clusters are zeroed for v3 or later
|
||||
discarded=0
|
||||
fi
|
||||
|
||||
echo read -P 22 0 1000
|
||||
echo read -P 33 1000 128k
|
||||
echo read -P 22 132072 7871512
|
||||
echo read -P 0 8003584 2093056
|
||||
echo read -P 22 10096640 23457792
|
||||
echo read -P 0 32M 32M
|
||||
echo read -P 22 64M 13M
|
||||
echo read -P $discarded 77M 29M
|
||||
echo read -P 22 106M 22M
|
||||
}
|
||||
|
||||
verify_io | $QEMU_IO -r "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
_check_test_img
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
status=0
|
49
tests/qemu-iotests/177.out
Normal file
49
tests/qemu-iotests/177.out
Normal file
@ -0,0 +1,49 @@
|
||||
QA output created by 177
|
||||
|
||||
== setting up files ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
|
||||
wrote 134217728/134217728 bytes at offset 0
|
||||
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 134217728/134217728 bytes at offset 0
|
||||
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== constrained alignment and max-transfer ==
|
||||
wrote 131072/131072 bytes at offset 1000
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 131072/131072 bytes at offset 1000
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== write zero with constrained max-transfer ==
|
||||
wrote 2093056/2093056 bytes at offset 8003584
|
||||
1.996 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== non-power-of-2 write zeroes limits ==
|
||||
wrote 33554432/33554432 bytes at offset 33554432
|
||||
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== non-power-of-2 discard limits ==
|
||||
discard 31457280/31457280 bytes at offset 80000001
|
||||
30 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== verify image content ==
|
||||
read 1000/1000 bytes at offset 0
|
||||
1000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 131072/131072 bytes at offset 1000
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 7871512/7871512 bytes at offset 132072
|
||||
7.507 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 2093056/2093056 bytes at offset 8003584
|
||||
1.996 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 23457792/23457792 bytes at offset 10096640
|
||||
22.371 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 33554432/33554432 bytes at offset 33554432
|
||||
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 13631488/13631488 bytes at offset 67108864
|
||||
13 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 30408704/30408704 bytes at offset 80740352
|
||||
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 23068672/23068672 bytes at offset 111149056
|
||||
22 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
*** done
|
130
tests/qemu-iotests/179
Executable file
130
tests/qemu-iotests/179
Executable file
@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test case for write zeroes with unmap
|
||||
#
|
||||
# Copyright (C) 2017 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=eblake@redhat.com
|
||||
|
||||
seq="$(basename $0)"
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here="$PWD"
|
||||
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 qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
# v2 images can't mark clusters as zero
|
||||
_unsupported_imgopts compat=0.10
|
||||
|
||||
echo
|
||||
echo '=== Testing write zeroes with unmap ==='
|
||||
echo
|
||||
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
|
||||
_make_test_img -b "$TEST_IMG.base"
|
||||
|
||||
# Offsets chosen at or near 2M boundaries so test works at all cluster sizes
|
||||
# 8k and larger (smaller clusters fail due to non-contiguous allocations)
|
||||
|
||||
# Aligned writes to unallocated cluster should not allocate mapping, but must
|
||||
# mark cluster as zero, whether or not unmap was requested
|
||||
$QEMU_IO -c "write -z -u 2M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 6M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Unaligned writes need not allocate mapping if the cluster already reads
|
||||
# as zero, but must mark cluster as zero, whether or not unmap was requested
|
||||
$QEMU_IO -c "write -z -u 10485761 2097150" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 14680065 2097150" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Requesting unmap of normal data must deallocate; omitting unmap should
|
||||
# preserve the mapping
|
||||
$QEMU_IO -c "write 18M 14M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 20M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 24M 6M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Likewise when writing on already-mapped zero data
|
||||
$QEMU_IO -c "write -z -u 26M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 28M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Writing on unmapped zeroes does not allocate
|
||||
$QEMU_IO -c "write -z 32M 8M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 34M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 36M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Writing zero overrides a backing file, regardless of backing cluster type
|
||||
$QEMU_IO -c "write -z 40M 8M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write 48M 8M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 42M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 44M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 50M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 52M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 58M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 60M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Final check that mappings are correct and images are still sane
|
||||
TEST_IMG="$TEST_IMG.base" _check_test_img
|
||||
_check_test_img
|
||||
|
||||
echo
|
||||
echo '=== Testing cache optimization ==='
|
||||
echo
|
||||
|
||||
BLKDBG_TEST_IMG="blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG.base"
|
||||
|
||||
cat > "$TEST_DIR/blkdebug.conf" <<EOF
|
||||
[inject-error]
|
||||
event = "l2_update"
|
||||
errno = "5"
|
||||
immediately = "on"
|
||||
once = "off"
|
||||
EOF
|
||||
|
||||
# None of the following writes should trigger an L2 update, because the
|
||||
# cluster already reads as zero, and we don't have to change allocation
|
||||
$QEMU_IO -c "w -z -u 20M 2M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "w -z 20M 2M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "w -z 28M 2M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo '*** done'
|
||||
status=0
|
156
tests/qemu-iotests/179.out
Normal file
156
tests/qemu-iotests/179.out
Normal file
@ -0,0 +1,156 @@
|
||||
QA output created by 179
|
||||
|
||||
=== Testing write zeroes with unmap ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 2097152/2097152 bytes at offset 2097152
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 6291456
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
56 MiB (0x3800000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 2097150/2097150 bytes at offset 10485761
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097150/2097150 bytes at offset 14680065
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
|
||||
2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
|
||||
48 MiB (0x3000000) bytes not allocated at offset 16 MiB (0x1000000)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 14680064/14680064 bytes at offset 18874368
|
||||
14 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 20971520
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 6291456/6291456 bytes at offset 25165824
|
||||
6 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
|
||||
2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000)
|
||||
14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000)
|
||||
32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000)
|
||||
[{ "start": 0, "length": 18874368, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 20971520, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 25165824, "length": 6291456, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 33554432, "length": 33554432, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 2097152/2097152 bytes at offset 27262976
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 29360128
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
|
||||
2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000)
|
||||
14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000)
|
||||
32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000)
|
||||
[{ "start": 0, "length": 18874368, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 20971520, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 25165824, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 27262976, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 29360128, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 33554432, "length": 33554432, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 8388608/8388608 bytes at offset 33554432
|
||||
8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 35651584
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 37748736
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
|
||||
2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000)
|
||||
22 MiB (0x1600000) bytes allocated at offset 18 MiB (0x1200000)
|
||||
24 MiB (0x1800000) bytes not allocated at offset 40 MiB (0x2800000)
|
||||
[{ "start": 0, "length": 18874368, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 20971520, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 25165824, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 27262976, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 29360128, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 33554432, "length": 33554432, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 8388608/8388608 bytes at offset 41943040
|
||||
8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 8388608/8388608 bytes at offset 50331648
|
||||
8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 44040192
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 46137344
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 52428800
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 54525952
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 60817408
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 62914560
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
42 MiB (0x2a00000) bytes not allocated at offset 0 bytes (0x0)
|
||||
4 MiB (0x400000) bytes allocated at offset 42 MiB (0x2a00000)
|
||||
4 MiB (0x400000) bytes not allocated at offset 46 MiB (0x2e00000)
|
||||
4 MiB (0x400000) bytes allocated at offset 50 MiB (0x3200000)
|
||||
4 MiB (0x400000) bytes not allocated at offset 54 MiB (0x3600000)
|
||||
4 MiB (0x400000) bytes allocated at offset 58 MiB (0x3a00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 62 MiB (0x3e00000)
|
||||
[{ "start": 0, "length": 18874368, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 18874368, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 20971520, "length": 2097152, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 23068672, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 25165824, "length": 2097152, "depth": 1, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 27262976, "length": 2097152, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 29360128, "length": 2097152, "depth": 1, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 31457280, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 33554432, "length": 10485760, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 44040192, "length": 4194304, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 48234496, "length": 2097152, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 50331648, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 52428800, "length": 4194304, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 56623104, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 58720256, "length": 2097152, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 60817408, "length": 4194304, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 65011712, "length": 2097152, "depth": 1, "zero": true, "data": false}]
|
||||
No errors were found on the image.
|
||||
No errors were found on the image.
|
||||
|
||||
=== Testing cache optimization ===
|
||||
|
||||
wrote 2097152/2097152 bytes at offset 20971520
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 20971520
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 29360128
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
68
tests/qemu-iotests/182
Executable file
68
tests/qemu-iotests/182
Executable file
@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test image locking for POSIX locks
|
||||
#
|
||||
# Copyright 2017 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=famz@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
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
size=32M
|
||||
|
||||
_make_test_img $size
|
||||
|
||||
echo "Starting QEMU"
|
||||
_launch_qemu -drive file=$TEST_IMG,if=none,id=drive0,file.locking=on \
|
||||
-device virtio-blk-pci,drive=drive0
|
||||
|
||||
echo
|
||||
echo "Starting a second QEMU using the same image should fail"
|
||||
echo 'quit' | $QEMU -monitor stdio \
|
||||
-drive file=$TEST_IMG,if=none,id=drive0,file.locking=on \
|
||||
-device virtio-blk-pci,drive=drive0 2>&1 | _filter_testdir 2>&1 |
|
||||
_filter_qemu |
|
||||
sed -e '/falling back to POSIX file/d' \
|
||||
-e '/locks can be lost unexpectedly/d'
|
||||
|
||||
_cleanup_qemu
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
8
tests/qemu-iotests/182.out
Normal file
8
tests/qemu-iotests/182.out
Normal file
@ -0,0 +1,8 @@
|
||||
QA output created by 182
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432
|
||||
Starting QEMU
|
||||
|
||||
Starting a second QEMU using the same image should fail
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0,file.locking=on: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
*** done
|
@ -152,10 +152,12 @@ _filter_img_info()
|
||||
-e "/log_size: [0-9]\\+/d"
|
||||
}
|
||||
|
||||
# filter out offsets and file names from qemu-img map
|
||||
# filter out offsets and file names from qemu-img map; good for both
|
||||
# human and json output
|
||||
_filter_qemu_img_map()
|
||||
{
|
||||
sed -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \
|
||||
-e 's/"offset": [0-9]\+/"offset": OFFSET/g' \
|
||||
-e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
function do_is_allocated() {
|
||||
local start=$1
|
||||
local size=$(( $2 / 512))
|
||||
local size=$2
|
||||
local step=$3
|
||||
local count=$4
|
||||
|
||||
|
@ -154,6 +154,7 @@
|
||||
149 rw auto sudo
|
||||
150 rw auto quick
|
||||
152 rw auto quick
|
||||
153 rw auto quick
|
||||
154 rw auto backing quick
|
||||
155 rw auto
|
||||
156 rw auto quick
|
||||
@ -169,4 +170,7 @@
|
||||
174 auto
|
||||
175 auto quick
|
||||
176 rw auto backing
|
||||
177 rw auto quick
|
||||
179 rw auto quick
|
||||
181 rw auto migration
|
||||
182 rw auto quick
|
||||
|
@ -179,7 +179,8 @@ static BlockBackend *start_primary(void)
|
||||
char *cmdline;
|
||||
|
||||
cmdline = g_strdup_printf("driver=replication,mode=primary,node-name=xxx,"
|
||||
"file.driver=qcow2,file.file.filename=%s"
|
||||
"file.driver=qcow2,file.file.filename=%s,"
|
||||
"file.file.locking=off"
|
||||
, p_local_disk);
|
||||
opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false);
|
||||
g_free(cmdline);
|
||||
@ -310,7 +311,9 @@ static BlockBackend *start_secondary(void)
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* add s_local_disk and forge S_LOCAL_DISK_ID */
|
||||
cmdline = g_strdup_printf("file.filename=%s,driver=qcow2", s_local_disk);
|
||||
cmdline = g_strdup_printf("file.filename=%s,driver=qcow2,"
|
||||
"file.locking=off",
|
||||
s_local_disk);
|
||||
opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false);
|
||||
g_free(cmdline);
|
||||
|
||||
@ -331,8 +334,10 @@ static BlockBackend *start_secondary(void)
|
||||
/* add S_(ACTIVE/HIDDEN)_DISK and forge S_ID */
|
||||
cmdline = g_strdup_printf("driver=replication,mode=secondary,top-id=%s,"
|
||||
"file.driver=qcow2,file.file.filename=%s,"
|
||||
"file.file.locking=off,"
|
||||
"file.backing.driver=qcow2,"
|
||||
"file.backing.file.filename=%s,"
|
||||
"file.backing.file.locking=off,"
|
||||
"file.backing.backing=%s"
|
||||
, S_ID, s_active_disk, s_hidden_disk
|
||||
, S_LOCAL_DISK_ID);
|
||||
|
@ -79,7 +79,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
const char *cmd = "-device piix3-usb-uhci,id=uhci,addr=1d.0"
|
||||
" -drive id=drive0,if=none,file=/dev/null,format=raw"
|
||||
" -drive id=drive0,if=none,file=null-co://,format=raw"
|
||||
" -device usb-tablet,bus=uhci.0,port=1";
|
||||
int ret;
|
||||
|
||||
|
@ -89,7 +89,7 @@ int main(int argc, char **argv)
|
||||
qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug);
|
||||
|
||||
qtest_start("-device nec-usb-xhci,id=xhci"
|
||||
" -drive id=drive0,if=none,file=/dev/null,format=raw");
|
||||
" -drive id=drive0,if=none,file=null-co://,format=raw");
|
||||
ret = g_test_run();
|
||||
qtest_end();
|
||||
|
||||
|
@ -63,7 +63,7 @@ static QOSState *pci_test_start(void)
|
||||
const char *arch = qtest_get_arch();
|
||||
char *tmp_path;
|
||||
const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw "
|
||||
"-drive if=none,id=drive1,file=/dev/null,format=raw "
|
||||
"-drive if=none,id=drive1,file=null-co://,format=raw "
|
||||
"-device virtio-blk-pci,id=drv0,drive=drive0,"
|
||||
"addr=%x.%x";
|
||||
|
||||
|
@ -35,7 +35,7 @@ typedef struct {
|
||||
static QOSState *qvirtio_scsi_start(const char *extra_opts)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
const char *cmd = "-drive id=drv0,if=none,file=/dev/null,format=raw "
|
||||
const char *cmd = "-drive id=drv0,if=none,file=null-co://,format=raw "
|
||||
"-device virtio-scsi-pci,id=vs0 "
|
||||
"-device scsi-hd,bus=vs0.0,drive=drv0 %s";
|
||||
|
||||
@ -195,7 +195,8 @@ static void hotplug(void)
|
||||
QDict *response;
|
||||
QOSState *qs;
|
||||
|
||||
qs = qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw");
|
||||
qs = qvirtio_scsi_start(
|
||||
"-drive id=drv1,if=none,file=null-co://,format=raw");
|
||||
response = qmp("{\"execute\": \"device_add\","
|
||||
" \"arguments\": {"
|
||||
" \"driver\": \"scsi-hd\","
|
||||
|
48
util/osdep.c
48
util/osdep.c
@ -38,6 +38,14 @@ extern int madvise(caddr_t, size_t, int);
|
||||
#include "qemu/error-report.h"
|
||||
#include "monitor/monitor.h"
|
||||
|
||||
#ifdef F_OFD_SETLK
|
||||
#define QEMU_SETLK F_OFD_SETLK
|
||||
#define QEMU_GETLK F_OFD_GETLK
|
||||
#else
|
||||
#define QEMU_SETLK F_SETLK
|
||||
#define QEMU_GETLK F_GETLK
|
||||
#endif
|
||||
|
||||
static bool fips_enabled = false;
|
||||
|
||||
static const char *hw_version = QEMU_HW_VERSION;
|
||||
@ -140,6 +148,46 @@ static int qemu_parse_fdset(const char *param)
|
||||
{
|
||||
return qemu_parse_fd(param);
|
||||
}
|
||||
|
||||
static int qemu_lock_fcntl(int fd, int64_t start, int64_t len, int fl_type)
|
||||
{
|
||||
int ret;
|
||||
struct flock fl = {
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = start,
|
||||
.l_len = len,
|
||||
.l_type = fl_type,
|
||||
};
|
||||
ret = fcntl(fd, QEMU_SETLK, &fl);
|
||||
return ret == -1 ? -errno : 0;
|
||||
}
|
||||
|
||||
int qemu_lock_fd(int fd, int64_t start, int64_t len, bool exclusive)
|
||||
{
|
||||
return qemu_lock_fcntl(fd, start, len, exclusive ? F_WRLCK : F_RDLCK);
|
||||
}
|
||||
|
||||
int qemu_unlock_fd(int fd, int64_t start, int64_t len)
|
||||
{
|
||||
return qemu_lock_fcntl(fd, start, len, F_UNLCK);
|
||||
}
|
||||
|
||||
int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive)
|
||||
{
|
||||
int ret;
|
||||
struct flock fl = {
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = start,
|
||||
.l_len = len,
|
||||
.l_type = exclusive ? F_WRLCK : F_RDLCK,
|
||||
};
|
||||
ret = fcntl(fd, QEMU_GETLK, &fl);
|
||||
if (ret == -1) {
|
||||
return -errno;
|
||||
} else {
|
||||
return fl.l_type == F_UNLCK ? 0 : -EAGAIN;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user