Block patches for the block queue.
-----BEGIN PGP SIGNATURE----- iQFGBAABCAAwFiEEkb62CjDbPohX0Rgp9AfbAGHVz0AFAlkUWPkSHG1yZWl0ekBy ZWRoYXQuY29tAAoJEPQH2wBh1c9AQuYIAKu7UHbUc3rL4FOErA2kor/Lp55ApCfi WbBaeh06jNPXQ5/S7jie30WAlc0DT+wwfWlFTl7gnYsNmXI3yrBkbEbnZMA2uSEz qz+MEEx81v3BS7MDM5sKjoJVEt76zCS3f7MbrLYLrkdHg3AGo2tfMtIlABFM+I6T lkfxYqosR0pvN8bSPAyoAQvVKYefLdi+O0poG6WruCMcF58dmZn8GzwVfnncGbqz vsd2wcm983S+a7PgGT2VBNXfyBqZ0tHqn/gl5Bc5leZTEhV9DQSh8Z0DKBh+o5Cv 8iyrmklpGdr7sI+YHHkB1zecQxI854HaB/4+Hv8KudHXI9hVmqXyXaQ= =JcaJ -----END PGP SIGNATURE----- Merge remote-tracking branch 'mreitz/tags/pull-block-2017-05-11' into queue-block Block patches for the block queue. # gpg: Signature made Thu May 11 14:28:41 2017 CEST # gpg: using RSA key 0xF407DB0061D5CF40 # gpg: Good signature from "Max Reitz <mreitz@redhat.com>" # Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40 * mreitz/tags/pull-block-2017-05-11: (22 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: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
commit
d541e201bd
@ -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
|
||||
|
266
block/blkdebug.c
266
block/blkdebug.c
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Block protocol for I/O error injection
|
||||
*
|
||||
* Copyright (C) 2016-2017 Red Hat, Inc.
|
||||
* Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@ -37,7 +38,12 @@
|
||||
typedef struct BDRVBlkdebugState {
|
||||
int state;
|
||||
int new_state;
|
||||
int align;
|
||||
uint64_t align;
|
||||
uint64_t max_transfer;
|
||||
uint64_t opt_write_zero;
|
||||
uint64_t max_write_zero;
|
||||
uint64_t opt_discard;
|
||||
uint64_t max_discard;
|
||||
|
||||
/* For blkdebug_refresh_filename() */
|
||||
char *config_file;
|
||||
@ -342,6 +348,31 @@ static QemuOptsList runtime_opts = {
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Required alignment in bytes",
|
||||
},
|
||||
{
|
||||
.name = "max-transfer",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Maximum transfer size in bytes",
|
||||
},
|
||||
{
|
||||
.name = "opt-write-zero",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Optimum write zero alignment in bytes",
|
||||
},
|
||||
{
|
||||
.name = "max-write-zero",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Maximum write zero size in bytes",
|
||||
},
|
||||
{
|
||||
.name = "opt-discard",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Optimum discard alignment in bytes",
|
||||
},
|
||||
{
|
||||
.name = "max-discard",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Maximum discard size in bytes",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@ -352,8 +383,8 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
uint64_t align;
|
||||
int ret;
|
||||
uint64_t align;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
@ -382,19 +413,69 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set request alignment */
|
||||
align = qemu_opt_get_size(opts, "align", 0);
|
||||
if (align < INT_MAX && is_power_of_2(align)) {
|
||||
s->align = align;
|
||||
} else if (align) {
|
||||
error_setg(errp, "Invalid alignment");
|
||||
ret = -EINVAL;
|
||||
bs->supported_write_flags = BDRV_REQ_FUA &
|
||||
bs->file->bs->supported_write_flags;
|
||||
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
||||
bs->file->bs->supported_zero_flags;
|
||||
ret = -EINVAL;
|
||||
|
||||
/* Set alignment overrides */
|
||||
s->align = qemu_opt_get_size(opts, "align", 0);
|
||||
if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) {
|
||||
error_setg(errp, "Cannot meet constraints with align %" PRIu64,
|
||||
s->align);
|
||||
goto out;
|
||||
}
|
||||
align = MAX(s->align, bs->file->bs->bl.request_alignment);
|
||||
|
||||
s->max_transfer = qemu_opt_get_size(opts, "max-transfer", 0);
|
||||
if (s->max_transfer &&
|
||||
(s->max_transfer >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->max_transfer, align))) {
|
||||
error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64,
|
||||
s->max_transfer);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero", 0);
|
||||
if (s->opt_write_zero &&
|
||||
(s->opt_write_zero >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->opt_write_zero, align))) {
|
||||
error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64,
|
||||
s->opt_write_zero);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero", 0);
|
||||
if (s->max_write_zero &&
|
||||
(s->max_write_zero >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->max_write_zero,
|
||||
MAX(s->opt_write_zero, align)))) {
|
||||
error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64,
|
||||
s->max_write_zero);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->opt_discard = qemu_opt_get_size(opts, "opt-discard", 0);
|
||||
if (s->opt_discard &&
|
||||
(s->opt_discard >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->opt_discard, align))) {
|
||||
error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64,
|
||||
s->opt_discard);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->max_discard = qemu_opt_get_size(opts, "max-discard", 0);
|
||||
if (s->max_discard &&
|
||||
(s->max_discard >= INT_MAX ||
|
||||
!QEMU_IS_ALIGNED(s->max_discard,
|
||||
MAX(s->opt_discard, align)))) {
|
||||
error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64,
|
||||
s->max_discard);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (ret < 0) {
|
||||
g_free(s->config_file);
|
||||
@ -403,11 +484,30 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int inject_error(BlockDriverState *bs, BlkdebugRule *rule)
|
||||
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
int error = rule->options.inject.error;
|
||||
bool immediately = rule->options.inject.immediately;
|
||||
BlkdebugRule *rule = NULL;
|
||||
int error;
|
||||
bool immediately;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
if (inject_offset == -1 ||
|
||||
(bytes && inject_offset >= offset &&
|
||||
inject_offset < offset + bytes))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rule || !rule->options.inject.error) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
immediately = rule->options.inject.immediately;
|
||||
error = rule->options.inject.error;
|
||||
|
||||
if (rule->options.inject.once) {
|
||||
QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
|
||||
@ -426,21 +526,18 @@ static int coroutine_fn
|
||||
blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
int err;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
if (inject_offset == -1 ||
|
||||
(inject_offset >= offset && inject_offset < offset + bytes))
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* Sanity check block layer guarantees */
|
||||
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
|
||||
if (bs->bl.max_transfer) {
|
||||
assert(bytes <= bs->bl.max_transfer);
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, rule);
|
||||
err = rule_check(bs, offset, bytes);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||
@ -450,21 +547,18 @@ static int coroutine_fn
|
||||
blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
int err;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
if (inject_offset == -1 ||
|
||||
(inject_offset >= offset && inject_offset < offset + bytes))
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* Sanity check block layer guarantees */
|
||||
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
|
||||
if (bs->bl.max_transfer) {
|
||||
assert(bytes <= bs->bl.max_transfer);
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, rule);
|
||||
err = rule_check(bs, offset, bytes);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||
@ -472,22 +566,81 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
|
||||
static int blkdebug_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
int err = rule_check(bs, 0, 0);
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
if (rule->options.inject.offset == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, rule);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_flush(bs->file->bs);
|
||||
}
|
||||
|
||||
static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int count,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
uint32_t align = MAX(bs->bl.request_alignment,
|
||||
bs->bl.pwrite_zeroes_alignment);
|
||||
int err;
|
||||
|
||||
/* Only pass through requests that are larger than requested
|
||||
* preferred alignment (so that we test the fallback to writes on
|
||||
* unaligned portions), and check that the block layer never hands
|
||||
* us anything unaligned that crosses an alignment boundary. */
|
||||
if (count < align) {
|
||||
assert(QEMU_IS_ALIGNED(offset, align) ||
|
||||
QEMU_IS_ALIGNED(offset + count, align) ||
|
||||
DIV_ROUND_UP(offset, align) ==
|
||||
DIV_ROUND_UP(offset + count, align));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
assert(QEMU_IS_ALIGNED(offset, align));
|
||||
assert(QEMU_IS_ALIGNED(count, align));
|
||||
if (bs->bl.max_pwrite_zeroes) {
|
||||
assert(count <= bs->bl.max_pwrite_zeroes);
|
||||
}
|
||||
|
||||
err = rule_check(bs, offset, count);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_pwrite_zeroes(bs->file, offset, count, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int count)
|
||||
{
|
||||
uint32_t align = bs->bl.pdiscard_alignment;
|
||||
int err;
|
||||
|
||||
/* Only pass through requests that are larger than requested
|
||||
* minimum alignment, and ensure that unaligned requests do not
|
||||
* cross optimum discard boundaries. */
|
||||
if (count < bs->bl.request_alignment) {
|
||||
assert(QEMU_IS_ALIGNED(offset, align) ||
|
||||
QEMU_IS_ALIGNED(offset + count, align) ||
|
||||
DIV_ROUND_UP(offset, align) ==
|
||||
DIV_ROUND_UP(offset + count, align));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||
assert(QEMU_IS_ALIGNED(count, bs->bl.request_alignment));
|
||||
if (align && count >= align) {
|
||||
assert(QEMU_IS_ALIGNED(offset, align));
|
||||
assert(QEMU_IS_ALIGNED(count, align));
|
||||
}
|
||||
if (bs->bl.max_pdiscard) {
|
||||
assert(count <= bs->bl.max_pdiscard);
|
||||
}
|
||||
|
||||
err = rule_check(bs, offset, count);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_pdiscard(bs->file->bs, offset, count);
|
||||
}
|
||||
|
||||
static void blkdebug_close(BlockDriverState *bs)
|
||||
{
|
||||
@ -715,6 +868,21 @@ static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
if (s->align) {
|
||||
bs->bl.request_alignment = s->align;
|
||||
}
|
||||
if (s->max_transfer) {
|
||||
bs->bl.max_transfer = s->max_transfer;
|
||||
}
|
||||
if (s->opt_write_zero) {
|
||||
bs->bl.pwrite_zeroes_alignment = s->opt_write_zero;
|
||||
}
|
||||
if (s->max_write_zero) {
|
||||
bs->bl.max_pwrite_zeroes = s->max_write_zero;
|
||||
}
|
||||
if (s->opt_discard) {
|
||||
bs->bl.pdiscard_alignment = s->opt_discard;
|
||||
}
|
||||
if (s->max_discard) {
|
||||
bs->bl.max_pdiscard = s->max_discard;
|
||||
}
|
||||
}
|
||||
|
||||
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
@ -742,6 +910,8 @@ static BlockDriver bdrv_blkdebug = {
|
||||
.bdrv_co_preadv = blkdebug_co_preadv,
|
||||
.bdrv_co_pwritev = blkdebug_co_pwritev,
|
||||
.bdrv_co_flush_to_disk = blkdebug_co_flush,
|
||||
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = blkdebug_co_pdiscard,
|
||||
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
|
||||
|
@ -309,7 +309,7 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
||||
uint64_t *l2_table, uint64_t stop_flags)
|
||||
{
|
||||
int i;
|
||||
int first_cluster_type;
|
||||
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;
|
||||
@ -321,8 +321,7 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
||||
/* 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 &&
|
||||
(first_entry & L2E_OFFSET_MASK) != 0));
|
||||
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;
|
||||
@ -334,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;
|
||||
@ -493,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);
|
||||
@ -515,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;
|
||||
}
|
||||
|
||||
@ -550,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);
|
||||
@ -608,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);
|
||||
@ -866,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:
|
||||
@ -876,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();
|
||||
@ -1177,8 +1184,8 @@ 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 &&
|
||||
(entry & L2E_OFFSET_MASK) != 0 && (entry & QCOW_OFLAG_COPIED) &&
|
||||
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)))
|
||||
{
|
||||
@ -1504,25 +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:
|
||||
/* Preallocated zero clusters should be discarded in any case */
|
||||
if (!full_discard && (old_l2_entry & L2E_OFFSET_MASK) == 0) {
|
||||
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 */
|
||||
@ -1542,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;
|
||||
@ -1594,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) {
|
||||
@ -1606,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 {
|
||||
@ -1624,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;
|
||||
@ -1732,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 */
|
||||
@ -1774,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);
|
||||
}
|
||||
@ -1784,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);
|
||||
}
|
||||
@ -1793,7 +1820,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
|
||||
ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0);
|
||||
if (ret < 0) {
|
||||
if (!preallocated) {
|
||||
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
QCOW2_DISCARD_ALWAYS);
|
||||
}
|
||||
|
@ -1028,18 +1028,17 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (l2_entry & L2E_OFFSET_MASK) {
|
||||
if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, false, -1, -1,
|
||||
"Cannot free unaligned cluster %#llx",
|
||||
l2_entry & L2E_OFFSET_MASK);
|
||||
} else {
|
||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||
nb_clusters << s->cluster_bits, type);
|
||||
}
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, false, -1, -1,
|
||||
"Cannot free unaligned cluster %#llx",
|
||||
l2_entry & L2E_OFFSET_MASK);
|
||||
} else {
|
||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||
nb_clusters << s->cluster_bits, type);
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
break;
|
||||
default:
|
||||
@ -1059,9 +1058,9 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
int64_t l1_table_offset, int l1_size, int addend)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;
|
||||
uint64_t *l1_table, *l2_table, l2_offset, entry, l1_size2, refcount;
|
||||
bool l1_allocated = false;
|
||||
int64_t old_offset, old_l2_offset;
|
||||
int64_t old_entry, old_l2_offset;
|
||||
int i, j, l1_modified = 0, nb_csectors;
|
||||
int ret;
|
||||
|
||||
@ -1089,15 +1088,16 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for(i = 0;i < l1_size; i++)
|
||||
for (i = 0; i < l1_size; i++) {
|
||||
be64_to_cpus(&l1_table[i]);
|
||||
}
|
||||
} else {
|
||||
assert(l1_size == s->l1_size);
|
||||
l1_table = s->l1_table;
|
||||
l1_allocated = false;
|
||||
}
|
||||
|
||||
for(i = 0; i < l1_size; i++) {
|
||||
for (i = 0; i < l1_size; i++) {
|
||||
l2_offset = l1_table[i];
|
||||
if (l2_offset) {
|
||||
old_l2_offset = l2_offset;
|
||||
@ -1117,81 +1117,79 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for(j = 0; j < s->l2_size; j++) {
|
||||
for (j = 0; j < s->l2_size; j++) {
|
||||
uint64_t cluster_index;
|
||||
uint64_t offset;
|
||||
|
||||
offset = be64_to_cpu(l2_table[j]);
|
||||
old_offset = offset;
|
||||
offset &= ~QCOW_OFLAG_COPIED;
|
||||
entry = be64_to_cpu(l2_table[j]);
|
||||
old_entry = entry;
|
||||
entry &= ~QCOW_OFLAG_COPIED;
|
||||
offset = entry & L2E_OFFSET_MASK;
|
||||
|
||||
switch (qcow2_get_cluster_type(offset)) {
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
nb_csectors = ((offset >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
if (addend != 0) {
|
||||
ret = update_refcount(bs,
|
||||
(offset & s->cluster_offset_mask) & ~511,
|
||||
switch (qcow2_get_cluster_type(entry)) {
|
||||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
nb_csectors = ((entry >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
if (addend != 0) {
|
||||
ret = update_refcount(bs,
|
||||
(entry & s->cluster_offset_mask) & ~511,
|
||||
nb_csectors * 512, abs(addend), addend < 0,
|
||||
QCOW2_DISCARD_SNAPSHOT);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
/* compressed clusters are never modified */
|
||||
refcount = 2;
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Data "
|
||||
"cluster offset %#llx "
|
||||
"unaligned (L2 offset: %#"
|
||||
PRIx64 ", L2 index: %#x)",
|
||||
offset & L2E_OFFSET_MASK,
|
||||
l2_offset, j);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
|
||||
if (!cluster_index) {
|
||||
/* unallocated */
|
||||
refcount = 0;
|
||||
break;
|
||||
}
|
||||
if (addend != 0) {
|
||||
ret = qcow2_update_cluster_refcount(bs,
|
||||
cluster_index, abs(addend), addend < 0,
|
||||
QCOW2_DISCARD_SNAPSHOT);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_get_refcount(bs, cluster_index, &refcount);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* compressed clusters are never modified */
|
||||
refcount = 2;
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
refcount = 0;
|
||||
break;
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Cluster "
|
||||
"allocation offset %#" PRIx64
|
||||
" unaligned (L2 offset: %#"
|
||||
PRIx64 ", L2 index: %#x)",
|
||||
offset, l2_offset, j);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
default:
|
||||
abort();
|
||||
cluster_index = offset >> s->cluster_bits;
|
||||
assert(cluster_index);
|
||||
if (addend != 0) {
|
||||
ret = qcow2_update_cluster_refcount(bs,
|
||||
cluster_index, abs(addend), addend < 0,
|
||||
QCOW2_DISCARD_SNAPSHOT);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_get_refcount(bs, cluster_index, &refcount);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
refcount = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
if (refcount == 1) {
|
||||
offset |= QCOW_OFLAG_COPIED;
|
||||
entry |= QCOW_OFLAG_COPIED;
|
||||
}
|
||||
if (offset != old_offset) {
|
||||
if (entry != old_entry) {
|
||||
if (addend > 0) {
|
||||
qcow2_cache_set_dependency(bs, s->l2_table_cache,
|
||||
s->refcount_block_cache);
|
||||
}
|
||||
l2_table[j] = cpu_to_be64(offset);
|
||||
l2_table[j] = cpu_to_be64(entry);
|
||||
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache,
|
||||
l2_table);
|
||||
}
|
||||
@ -1441,12 +1439,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if ((l2_entry & L2E_OFFSET_MASK) == 0) {
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
{
|
||||
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||
@ -1476,6 +1469,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
break;
|
||||
}
|
||||
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
break;
|
||||
|
||||
@ -1638,10 +1632,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
for (j = 0; j < s->l2_size; j++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
||||
uint64_t data_offset = l2_entry & L2E_OFFSET_MASK;
|
||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
|
||||
if ((cluster_type == QCOW2_CLUSTER_NORMAL) ||
|
||||
((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) {
|
||||
if (cluster_type == QCOW2_CLUSTER_NORMAL ||
|
||||
cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||
ret = qcow2_get_refcount(bs,
|
||||
data_offset >> s->cluster_bits,
|
||||
&refcount);
|
||||
|
@ -440,10 +440,9 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
|
||||
/* The VM state isn't needed any more in the active L1 table; in fact, it
|
||||
* hurts by causing expensive COW for the next snapshot. */
|
||||
qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
|
||||
align_offset(sn->vm_state_size, s->cluster_size)
|
||||
>> BDRV_SECTOR_BITS,
|
||||
QCOW2_DISCARD_NEVER, false);
|
||||
qcow2_cluster_discard(bs, qcow2_vm_state_offset(s),
|
||||
align_offset(sn->vm_state_size, s->cluster_size),
|
||||
QCOW2_DISCARD_NEVER, false);
|
||||
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
|
@ -1385,7 +1385,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
||||
*file = bs->file->bs;
|
||||
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
|
||||
}
|
||||
if (ret == QCOW2_CLUSTER_ZERO) {
|
||||
if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) {
|
||||
status |= BDRV_BLOCK_ZERO;
|
||||
} else if (ret != QCOW2_CLUSTER_UNALLOCATED) {
|
||||
status |= BDRV_BLOCK_DATA;
|
||||
@ -1482,7 +1482,8 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||
qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
|
||||
break;
|
||||
|
||||
@ -2450,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;
|
||||
}
|
||||
@ -2468,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;
|
||||
@ -2491,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;
|
||||
}
|
||||
@ -2502,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;
|
||||
@ -2525,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;
|
||||
}
|
||||
@ -2833,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));
|
||||
@ -2852,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;
|
||||
}
|
||||
|
@ -349,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,
|
||||
@ -443,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 {
|
||||
@ -547,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,
|
||||
|
@ -121,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
|
||||
|
@ -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);
|
||||
|
@ -2434,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
|
||||
#
|
||||
@ -2446,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'] } }
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Image resized.
|
||||
[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
|
||||
64 KiB (0x10000) bytes allocated at offset 0 bytes (0x0)
|
||||
Offset Length Mapped to File
|
||||
|
||||
=== Testing map on an image file truncated outside of qemu ===
|
||||
@ -17,5 +17,5 @@ wrote 65536/65536 bytes at offset 0
|
||||
Image resized.
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) qemu-io drv0 map
|
||||
[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
|
||||
64 KiB (0x10000) bytes allocated at offset 0 bytes (0x0)
|
||||
*** done
|
||||
|
@ -112,7 +112,7 @@ read 3145728/3145728 bytes at offset 0
|
||||
3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 63963136/63963136 bytes at offset 3145728
|
||||
61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
|
||||
convert -c -S 0:
|
||||
read 3145728/3145728 bytes at offset 0
|
||||
@ -134,7 +134,7 @@ read 30408704/30408704 bytes at offset 3145728
|
||||
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 33554432/33554432 bytes at offset 33554432
|
||||
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
|
||||
convert -c -S 0 with source backing file:
|
||||
read 3145728/3145728 bytes at offset 0
|
||||
@ -152,7 +152,7 @@ read 30408704/30408704 bytes at offset 3145728
|
||||
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 33554432/33554432 bytes at offset 33554432
|
||||
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
|
||||
convert -c -S 0 -B ...
|
||||
read 3145728/3145728 bytes at offset 0
|
||||
@ -176,11 +176,11 @@ wrote 1024/1024 bytes at offset 17408
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
convert -S 4k
|
||||
[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 8192},
|
||||
[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 1024, "length": 7168, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 9216},
|
||||
{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 10240},
|
||||
{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
|
||||
|
||||
convert -c -S 4k
|
||||
@ -192,9 +192,9 @@ convert -c -S 4k
|
||||
{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
|
||||
|
||||
convert -S 8k
|
||||
[{ "start": 0, "length": 9216, "depth": 0, "zero": false, "data": true, "offset": 8192},
|
||||
[{ "start": 0, "length": 9216, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 17408},
|
||||
{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
|
||||
|
||||
convert -c -S 8k
|
||||
|
@ -2,39 +2,39 @@ QA output created by 146
|
||||
|
||||
=== Testing VPC Autodetect ===
|
||||
|
||||
[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0)
|
||||
126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing VPC with current_size force ===
|
||||
|
||||
[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0)
|
||||
127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing VPC with chs force ===
|
||||
|
||||
[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0)
|
||||
126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Hyper-V Autodetect ===
|
||||
|
||||
[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0)
|
||||
127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Hyper-V with current_size force ===
|
||||
|
||||
[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0)
|
||||
127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Hyper-V with chs force ===
|
||||
|
||||
[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0)
|
||||
126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing d2v Autodetect ===
|
||||
|
||||
[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1)
|
||||
251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing d2v with current_size force ===
|
||||
|
||||
[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1)
|
||||
251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing d2v with chs force ===
|
||||
|
||||
[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1)
|
||||
251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Image create, default ===
|
||||
|
||||
@ -42,15 +42,15 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296
|
||||
|
||||
=== Read created image, default opts ====
|
||||
|
||||
[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Read created image, force_size_calc=chs ====
|
||||
|
||||
[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Read created image, force_size_calc=current_size ====
|
||||
|
||||
[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Testing Image create, force_size ===
|
||||
|
||||
@ -58,13 +58,13 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 forc
|
||||
|
||||
=== Read created image, default opts ====
|
||||
|
||||
[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Read created image, force_size_calc=chs ====
|
||||
|
||||
[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Read created image, force_size_calc=current_size ====
|
||||
|
||||
[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0)
|
||||
4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0)
|
||||
*** done
|
||||
|
@ -2,7 +2,7 @@
|
||||
#
|
||||
# qcow2 specific bdrv_pwrite_zeroes tests with backing files (complements 034)
|
||||
#
|
||||
# Copyright (C) 2016 Red Hat, Inc.
|
||||
# Copyright (C) 2016-2017 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -42,7 +42,10 @@ _supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
CLUSTER_SIZE=4k
|
||||
size=128M
|
||||
size=$((128 * 1024 * 1024))
|
||||
|
||||
# This test requires zero clusters, added in v3 images
|
||||
_unsupported_imgopts compat=0.10
|
||||
|
||||
echo
|
||||
echo == backing file contains zeros ==
|
||||
@ -299,6 +302,159 @@ $QEMU_IO -c "read -P 0 75k 1k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
echo
|
||||
echo == unaligned image tail cluster, no allocation needed ==
|
||||
|
||||
# With no backing file, write to all or part of unallocated partial cluster
|
||||
# will mark the cluster as zero, but does not allocate.
|
||||
# Re-create the image each time to get back to unallocated clusters.
|
||||
|
||||
# Write at the front: sector-wise, the request is: 128m... | 00 -- -- --
|
||||
_make_test_img $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at the back: sector-wise, the request is: 128m... | -- -- -- 00
|
||||
_make_test_img $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at middle: sector-wise, the request is: 128m... | -- 00 00 --
|
||||
_make_test_img $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write entire cluster: sector-wise, the request is: 128m... | 00 00 00 00
|
||||
_make_test_img $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Repeat with backing file holding unallocated cluster.
|
||||
# TODO: Note that this forces an allocation, because we aren't yet able to
|
||||
# quickly detect that reads beyond EOF of the backing file are always zero
|
||||
CLUSTER_SIZE=2048 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
|
||||
|
||||
# Write at the front: sector-wise, the request is:
|
||||
# backing: 128m... | -- --
|
||||
# active: 128m... | 00 -- -- --
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at the back: sector-wise, the request is:
|
||||
# backing: 128m... | -- --
|
||||
# active: 128m... | -- -- -- 00
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at middle: sector-wise, the request is:
|
||||
# backing: 128m... | -- --
|
||||
# active: 128m... | -- 00 00 --
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write entire cluster: sector-wise, the request is:
|
||||
# backing: 128m... | -- --
|
||||
# active: 128m... | 00 00 00 00
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Repeat with backing file holding zero'd cluster
|
||||
# TODO: Note that this forces an allocation, because we aren't yet able to
|
||||
# quickly detect that reads beyond EOF of the backing file are always zero
|
||||
$QEMU_IO -c "write -z $size 512" "$TEST_IMG.base" | _filter_qemu_io
|
||||
|
||||
# Write at the front: sector-wise, the request is:
|
||||
# backing: 128m... | 00 00
|
||||
# active: 128m... | 00 -- -- --
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at the back: sector-wise, the request is:
|
||||
# backing: 128m... | 00 00
|
||||
# active: 128m... | -- -- -- 00
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write at middle: sector-wise, the request is:
|
||||
# backing: 128m... | 00 00
|
||||
# active: 128m... | -- 00 00 --
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Write entire cluster: sector-wise, the request is:
|
||||
# backing: 128m... | 00 00
|
||||
# active: 128m... | 00 00 00 00
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# A preallocated cluster maintains its allocation, whether it stays as
|
||||
# data due to a partial write:
|
||||
# Convert 128m... | XX XX => ... | XX 00
|
||||
_make_test_img $((size + 1024))
|
||||
$QEMU_IO -c "write -P 1 $((size)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 1 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# or because it is the entire cluster and can use the zero flag:
|
||||
# Convert 128m... | XX XX => ... | 00 00
|
||||
$QEMU_IO -c "write -z $((size)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "alloc $size 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $size 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
echo
|
||||
echo == unaligned image tail cluster, allocation required ==
|
||||
|
||||
# Write beyond backing file must COW
|
||||
# Backing file: 128m... | XX --
|
||||
# Active layer: 128m... | -- -- 00 --
|
||||
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -P 1 $((size)) 512" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z $((size + 1024)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 1 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size + 512)) 1536" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Writes at boundaries of (partial) cluster must not lose mid-cluster data
|
||||
# Backing file: 128m: ... | -- XX
|
||||
# Active layer: 128m: ... | 00 -- -- 00
|
||||
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
|
||||
_make_test_img -b "$TEST_IMG.base" $((size + 2048))
|
||||
$QEMU_IO -c "write -P 1 $((size + 512)) 512" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 1 $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size + 1024)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 1 $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "read -P 0 $((size + 1024)) 1024" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
|
@ -42,9 +42,9 @@ read 1024/1024 bytes at offset 65536
|
||||
read 2048/2048 bytes at offset 67584
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 36864, "length": 28672, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 69632, "length": 134148096, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== backing file contains non-zero data after write_zeroes ==
|
||||
@ -69,9 +69,9 @@ read 1024/1024 bytes at offset 44032
|
||||
read 3072/3072 bytes at offset 40960
|
||||
3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 36864, "length": 4096, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 40960, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 40960, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 45056, "length": 134172672, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== write_zeroes covers non-zero data ==
|
||||
@ -143,13 +143,13 @@ read 1024/1024 bytes at offset 67584
|
||||
read 5120/5120 bytes at offset 68608
|
||||
5 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 36864, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 40960, "length": 8192, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 49152, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 49152, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 53248, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 57344, "length": 8192, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 28672},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 69632, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 73728, "length": 134144000, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
@ -186,13 +186,13 @@ read 1024/1024 bytes at offset 72704
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 32768, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 36864, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 36864, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 40960, "length": 8192, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 49152, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 53248, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 53248, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 57344, "length": 8192, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 69632, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 28672},
|
||||
{ "start": 69632, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 73728, "length": 134144000, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== spanning two clusters, partially overwriting backing file ==
|
||||
@ -212,7 +212,7 @@ read 1024/1024 bytes at offset 5120
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 2048/2048 bytes at offset 6144
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
[{ "start": 0, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 8192, "length": 134209536, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== spanning multiple clusters, non-zero in first cluster ==
|
||||
@ -227,7 +227,7 @@ read 2048/2048 bytes at offset 65536
|
||||
read 10240/10240 bytes at offset 67584
|
||||
10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 69632, "length": 8192, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
@ -257,7 +257,7 @@ read 2048/2048 bytes at offset 75776
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 8192, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== spanning multiple clusters, partially overwriting backing file ==
|
||||
@ -278,8 +278,136 @@ read 2048/2048 bytes at offset 74752
|
||||
read 1024/1024 bytes at offset 76800
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
|
||||
{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 69632, "length": 4096, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
|
||||
{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}]
|
||||
|
||||
== unaligned image tail cluster, no allocation needed ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
|
||||
wrote 512/512 bytes at offset 134219264
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
|
||||
wrote 1024/1024 bytes at offset 134218240
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
|
||||
wrote 2048/2048 bytes at offset 134217728
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134219264
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 1024/1024 bytes at offset 134218240
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 2048/2048 bytes at offset 134217728
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134219264
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 1024/1024 bytes at offset 134218240
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 2048/2048 bytes at offset 134217728
|
||||
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2048/2048 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134218752
|
||||
wrote 1024/1024 bytes at offset 134217728
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
1024/1024 bytes allocated at offset 128 MiB
|
||||
[{ "start": 0, "length": 134217728, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
wrote 1024/1024 bytes at offset 134217728
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
1024/1024 bytes allocated at offset 128 MiB
|
||||
read 1024/1024 bytes at offset 134217728
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 134217728, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 1024, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
|
||||
== unaligned image tail cluster, allocation required ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 134218752
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 1536/1536 bytes at offset 134218240
|
||||
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 1024/1024 bytes at offset 134218752
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 134219264
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134217728
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 134218240
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 1024/1024 bytes at offset 134218752
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
|
||||
*** done
|
||||
|
114
tests/qemu-iotests/177
Executable file
114
tests/qemu-iotests/177
Executable file
@ -0,0 +1,114 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test corner cases with unusual block geometries
|
||||
#
|
||||
# Copyright (C) 2016-2017 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# creator
|
||||
owner=eblake@redhat.com
|
||||
|
||||
seq=`basename $0`
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here=`pwd`
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
|
||||
CLUSTER_SIZE=1M
|
||||
size=128M
|
||||
options=driver=blkdebug,image.driver=qcow2
|
||||
|
||||
echo
|
||||
echo "== setting up files =="
|
||||
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img $size
|
||||
$QEMU_IO -c "write -P 11 0 $size" "$TEST_IMG.base" | _filter_qemu_io
|
||||
_make_test_img -b "$TEST_IMG.base"
|
||||
$QEMU_IO -c "write -P 22 0 $size" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# Limited to 64k max-transfer
|
||||
echo
|
||||
echo "== constrained alignment and max-transfer =="
|
||||
limits=align=4k,max-transfer=64k
|
||||
$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
|
||||
-c "write -P 33 1000 128k" -c "read -P 33 1000 128k" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== write zero with constrained max-transfer =="
|
||||
limits=align=512,max-transfer=64k,opt-write-zero=$CLUSTER_SIZE
|
||||
$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
|
||||
-c "write -z 8003584 2093056" | _filter_qemu_io
|
||||
|
||||
# non-power-of-2 write-zero/discard alignments
|
||||
echo
|
||||
echo "== non-power-of-2 write zeroes limits =="
|
||||
|
||||
limits=align=512,opt-write-zero=15M,max-write-zero=15M,opt-discard=15M,max-discard=15M
|
||||
$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
|
||||
-c "write -z 32M 32M" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== non-power-of-2 discard limits =="
|
||||
|
||||
limits=align=512,opt-write-zero=15M,max-write-zero=15M,opt-discard=15M,max-discard=15M
|
||||
$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
|
||||
-c "discard 80000001 30M" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== verify image content =="
|
||||
|
||||
function verify_io()
|
||||
{
|
||||
if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" |
|
||||
grep "compat: 0.10" > /dev/null); then
|
||||
# For v2 images, discarded clusters are read from the backing file
|
||||
discarded=11
|
||||
else
|
||||
# Discarded clusters are zeroed for v3 or later
|
||||
discarded=0
|
||||
fi
|
||||
|
||||
echo read -P 22 0 1000
|
||||
echo read -P 33 1000 128k
|
||||
echo read -P 22 132072 7871512
|
||||
echo read -P 0 8003584 2093056
|
||||
echo read -P 22 10096640 23457792
|
||||
echo read -P 0 32M 32M
|
||||
echo read -P 22 64M 13M
|
||||
echo read -P $discarded 77M 29M
|
||||
echo read -P 22 106M 22M
|
||||
}
|
||||
|
||||
verify_io | $QEMU_IO -r "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
_check_test_img
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
status=0
|
49
tests/qemu-iotests/177.out
Normal file
49
tests/qemu-iotests/177.out
Normal file
@ -0,0 +1,49 @@
|
||||
QA output created by 177
|
||||
|
||||
== setting up files ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
|
||||
wrote 134217728/134217728 bytes at offset 0
|
||||
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 134217728/134217728 bytes at offset 0
|
||||
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== constrained alignment and max-transfer ==
|
||||
wrote 131072/131072 bytes at offset 1000
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 131072/131072 bytes at offset 1000
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== write zero with constrained max-transfer ==
|
||||
wrote 2093056/2093056 bytes at offset 8003584
|
||||
1.996 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== non-power-of-2 write zeroes limits ==
|
||||
wrote 33554432/33554432 bytes at offset 33554432
|
||||
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== non-power-of-2 discard limits ==
|
||||
discard 31457280/31457280 bytes at offset 80000001
|
||||
30 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== verify image content ==
|
||||
read 1000/1000 bytes at offset 0
|
||||
1000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 131072/131072 bytes at offset 1000
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 7871512/7871512 bytes at offset 132072
|
||||
7.507 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 2093056/2093056 bytes at offset 8003584
|
||||
1.996 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 23457792/23457792 bytes at offset 10096640
|
||||
22.371 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 33554432/33554432 bytes at offset 33554432
|
||||
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 13631488/13631488 bytes at offset 67108864
|
||||
13 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 30408704/30408704 bytes at offset 80740352
|
||||
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 23068672/23068672 bytes at offset 111149056
|
||||
22 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
*** done
|
130
tests/qemu-iotests/179
Executable file
130
tests/qemu-iotests/179
Executable file
@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test case for write zeroes with unmap
|
||||
#
|
||||
# Copyright (C) 2017 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# creator
|
||||
owner=eblake@redhat.com
|
||||
|
||||
seq="$(basename $0)"
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here="$PWD"
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
# v2 images can't mark clusters as zero
|
||||
_unsupported_imgopts compat=0.10
|
||||
|
||||
echo
|
||||
echo '=== Testing write zeroes with unmap ==='
|
||||
echo
|
||||
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
|
||||
_make_test_img -b "$TEST_IMG.base"
|
||||
|
||||
# Offsets chosen at or near 2M boundaries so test works at all cluster sizes
|
||||
# 8k and larger (smaller clusters fail due to non-contiguous allocations)
|
||||
|
||||
# Aligned writes to unallocated cluster should not allocate mapping, but must
|
||||
# mark cluster as zero, whether or not unmap was requested
|
||||
$QEMU_IO -c "write -z -u 2M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 6M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Unaligned writes need not allocate mapping if the cluster already reads
|
||||
# as zero, but must mark cluster as zero, whether or not unmap was requested
|
||||
$QEMU_IO -c "write -z -u 10485761 2097150" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 14680065 2097150" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Requesting unmap of normal data must deallocate; omitting unmap should
|
||||
# preserve the mapping
|
||||
$QEMU_IO -c "write 18M 14M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 20M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 24M 6M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Likewise when writing on already-mapped zero data
|
||||
$QEMU_IO -c "write -z -u 26M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 28M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Writing on unmapped zeroes does not allocate
|
||||
$QEMU_IO -c "write -z 32M 8M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 34M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 36M 2M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
|
||||
|
||||
# Writing zero overrides a backing file, regardless of backing cluster type
|
||||
$QEMU_IO -c "write -z 40M 8M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write 48M 8M" "$TEST_IMG.base" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 42M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 44M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 50M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 52M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z -u 58M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "write -z 60M 2M" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "map" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# Final check that mappings are correct and images are still sane
|
||||
TEST_IMG="$TEST_IMG.base" _check_test_img
|
||||
_check_test_img
|
||||
|
||||
echo
|
||||
echo '=== Testing cache optimization ==='
|
||||
echo
|
||||
|
||||
BLKDBG_TEST_IMG="blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG.base"
|
||||
|
||||
cat > "$TEST_DIR/blkdebug.conf" <<EOF
|
||||
[inject-error]
|
||||
event = "l2_update"
|
||||
errno = "5"
|
||||
immediately = "on"
|
||||
once = "off"
|
||||
EOF
|
||||
|
||||
# None of the following writes should trigger an L2 update, because the
|
||||
# cluster already reads as zero, and we don't have to change allocation
|
||||
$QEMU_IO -c "w -z -u 20M 2M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "w -z 20M 2M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "w -z 28M 2M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo '*** done'
|
||||
status=0
|
156
tests/qemu-iotests/179.out
Normal file
156
tests/qemu-iotests/179.out
Normal file
@ -0,0 +1,156 @@
|
||||
QA output created by 179
|
||||
|
||||
=== Testing write zeroes with unmap ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
wrote 2097152/2097152 bytes at offset 2097152
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 6291456
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
56 MiB (0x3800000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 2097150/2097150 bytes at offset 10485761
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097150/2097150 bytes at offset 14680065
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
|
||||
2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
|
||||
48 MiB (0x3000000) bytes not allocated at offset 16 MiB (0x1000000)
|
||||
[{ "start": 0, "length": 67108864, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 14680064/14680064 bytes at offset 18874368
|
||||
14 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 20971520
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 6291456/6291456 bytes at offset 25165824
|
||||
6 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
|
||||
2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000)
|
||||
14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000)
|
||||
32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000)
|
||||
[{ "start": 0, "length": 18874368, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 20971520, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 25165824, "length": 6291456, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 33554432, "length": 33554432, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 2097152/2097152 bytes at offset 27262976
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 29360128
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
|
||||
2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000)
|
||||
14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000)
|
||||
32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000)
|
||||
[{ "start": 0, "length": 18874368, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 20971520, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 25165824, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 27262976, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 29360128, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 33554432, "length": 33554432, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 8388608/8388608 bytes at offset 33554432
|
||||
8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 35651584
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 37748736
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
|
||||
2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
|
||||
2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
|
||||
2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
|
||||
2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000)
|
||||
22 MiB (0x1600000) bytes allocated at offset 18 MiB (0x1200000)
|
||||
24 MiB (0x1800000) bytes not allocated at offset 40 MiB (0x2800000)
|
||||
[{ "start": 0, "length": 18874368, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 20971520, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 25165824, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 27262976, "length": 2097152, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 29360128, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 33554432, "length": 33554432, "depth": 0, "zero": true, "data": false}]
|
||||
wrote 8388608/8388608 bytes at offset 41943040
|
||||
8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 8388608/8388608 bytes at offset 50331648
|
||||
8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 44040192
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 46137344
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 52428800
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 54525952
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 60817408
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 62914560
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
42 MiB (0x2a00000) bytes not allocated at offset 0 bytes (0x0)
|
||||
4 MiB (0x400000) bytes allocated at offset 42 MiB (0x2a00000)
|
||||
4 MiB (0x400000) bytes not allocated at offset 46 MiB (0x2e00000)
|
||||
4 MiB (0x400000) bytes allocated at offset 50 MiB (0x3200000)
|
||||
4 MiB (0x400000) bytes not allocated at offset 54 MiB (0x3600000)
|
||||
4 MiB (0x400000) bytes allocated at offset 58 MiB (0x3a00000)
|
||||
2 MiB (0x200000) bytes not allocated at offset 62 MiB (0x3e00000)
|
||||
[{ "start": 0, "length": 18874368, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 18874368, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 20971520, "length": 2097152, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 23068672, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 25165824, "length": 2097152, "depth": 1, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 27262976, "length": 2097152, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 29360128, "length": 2097152, "depth": 1, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 31457280, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 33554432, "length": 10485760, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 44040192, "length": 4194304, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 48234496, "length": 2097152, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 50331648, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 52428800, "length": 4194304, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 56623104, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 58720256, "length": 2097152, "depth": 1, "zero": true, "data": false},
|
||||
{ "start": 60817408, "length": 4194304, "depth": 0, "zero": true, "data": false},
|
||||
{ "start": 65011712, "length": 2097152, "depth": 1, "zero": true, "data": false}]
|
||||
No errors were found on the image.
|
||||
No errors were found on the image.
|
||||
|
||||
=== Testing cache optimization ===
|
||||
|
||||
wrote 2097152/2097152 bytes at offset 20971520
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 20971520
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 2097152/2097152 bytes at offset 29360128
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
@ -152,10 +152,12 @@ _filter_img_info()
|
||||
-e "/log_size: [0-9]\\+/d"
|
||||
}
|
||||
|
||||
# filter out offsets and file names from qemu-img map
|
||||
# filter out offsets and file names from qemu-img map; good for both
|
||||
# human and json output
|
||||
_filter_qemu_img_map()
|
||||
{
|
||||
sed -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \
|
||||
-e 's/"offset": [0-9]\+/"offset": OFFSET/g' \
|
||||
-e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
function do_is_allocated() {
|
||||
local start=$1
|
||||
local size=$(( $2 / 512))
|
||||
local size=$2
|
||||
local step=$3
|
||||
local count=$4
|
||||
|
||||
|
@ -170,5 +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
|
||||
|
Loading…
Reference in New Issue
Block a user