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:
Stefan Hajnoczi 2017-05-12 09:29:49 -04:00
commit 3753e255da
62 changed files with 3092 additions and 616 deletions

View File

@ -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
View File

@ -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;
}

View File

@ -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,

View File

@ -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) {

View File

@ -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,

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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
{

View File

@ -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;
}

View File

@ -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,

View File

@ -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) {

View File

@ -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);

View File

@ -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,
};

View File

@ -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);

View File

@ -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;

View File

@ -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"

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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': {

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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
View File

@ -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;

View File

@ -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");

View File

@ -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();

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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'])

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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"}}

View File

@ -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"
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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

View 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
View 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
View 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
View 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

View 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

View File

@ -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
}

View File

@ -18,7 +18,7 @@
function do_is_allocated() {
local start=$1
local size=$(( $2 / 512))
local size=$2
local step=$3
local count=$4

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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";

View File

@ -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\","

View File

@ -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
/*