Merge remote-tracking branch 'mreitz/block' into queue-block

* mreitz/block:
  block/vpc: remove disabled code from get_sector_offset
  block/vpc: rename footer->size -> footer->current_size
  block/vpc: make calculate_geometry spec conform
  vpc: Ignore geometry for large images
  block/vpc: optimize vpc_co_get_block_status
  block: Drop bdrv_find
  blockdev: Convert bdrv_find to blk_by_name
  migration: Convert bdrv_find to blk_by_name
  monitor: Convert bdrv_find to blk_by_name
  iotests: Test non-self-referential qcow2 refblocks
  iotests: Add tests for refcount table growth
  qcow2: Respect new_block in alloc_refcount_block()
This commit is contained in:
Kevin Wolf 2015-03-16 17:11:12 +01:00
commit 47aced5078
12 changed files with 350 additions and 120 deletions

View File

@ -3821,15 +3821,6 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
g_free(formats);
}
/* This function is to find block backend bs */
/* TODO convert callers to blk_by_name(), then remove */
BlockDriverState *bdrv_find(const char *name)
{
BlockBackend *blk = blk_by_name(name);
return blk ? blk_bs(blk) : NULL;
}
/* This function is to find a node in the bs graph */
BlockDriverState *bdrv_find_node(const char *node_name)
{

View File

@ -466,8 +466,20 @@ static int alloc_refcount_block(BlockDriverState *bs,
*/
BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_GROW);
/* Calculate the number of refcount blocks needed so far */
uint64_t blocks_used = DIV_ROUND_UP(cluster_index, s->refcount_block_size);
/* Calculate the number of refcount blocks needed so far; this will be the
* basis for calculating the index of the first cluster used for the
* self-describing refcount structures which we are about to create.
*
* Because we reached this point, there cannot be any refcount entries for
* cluster_index or higher indices yet. However, because new_block has been
* allocated to describe that cluster (and it will assume this role later
* on), we cannot use that index; also, new_block may actually have a higher
* cluster index than cluster_index, so it needs to be taken into account
* here (and 1 needs to be added to its value because that cluster is used).
*/
uint64_t blocks_used = DIV_ROUND_UP(MAX(cluster_index + 1,
(new_block >> s->cluster_bits) + 1),
s->refcount_block_size);
if (blocks_used > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) {
return -EFBIG;

View File

@ -46,6 +46,7 @@ enum vhd_type {
#define VHD_TIMESTAMP_BASE 946684800
#define VHD_MAX_SECTORS (65535LL * 255 * 255)
#define VHD_MAX_GEOMETRY (65535LL * 16 * 255)
// always big-endian
typedef struct vhd_footer {
@ -65,7 +66,7 @@ typedef struct vhd_footer {
char creator_os[4]; // "Wi2k"
uint64_t orig_size;
uint64_t size;
uint64_t current_size;
uint16_t cyls;
uint8_t heads;
@ -215,13 +216,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
bs->total_sectors = (int64_t)
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
/* images created with disk2vhd report a far higher virtual size
* than expected with the cyls * heads * sectors_per_cyl formula.
* use the footer->size instead if the image was created with
* disk2vhd.
*/
if (!strncmp(footer->creator_app, "d2v", 4)) {
bs->total_sectors = be64_to_cpu(footer->size) / BDRV_SECTOR_SIZE;
/* Images that have exactly the maximum geometry are probably bigger and
* would be truncated if we adhered to the geometry for them. Rely on
* footer->current_size for them. */
if (bs->total_sectors == VHD_MAX_GEOMETRY) {
bs->total_sectors = be64_to_cpu(footer->current_size) /
BDRV_SECTOR_SIZE;
}
/* Allow a maximum disk size of approximately 2 TB */
@ -376,38 +376,6 @@ static inline int64_t get_sector_offset(BlockDriverState *bs,
bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
}
// printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
// sector_num, pagetable_index, pageentry_index,
// bitmap_offset, block_offset);
// disabled by reason
#if 0
#ifdef CACHE
if (bitmap_offset != s->last_bitmap)
{
lseek(s->fd, bitmap_offset, SEEK_SET);
s->last_bitmap = bitmap_offset;
// Scary! Bitmap is stored as big endian 32bit entries,
// while we used to look it up byte by byte
read(s->fd, s->pageentry_u8, 512);
for (i = 0; i < 128; i++)
be32_to_cpus(&s->pageentry_u32[i]);
}
if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
return -1;
#else
lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
read(s->fd, &bitmap_entry, 1);
if ((bitmap_entry >> (pageentry_index % 8)) & 1)
return -1; // not allocated
#endif
#endif
return block_offset;
}
@ -602,7 +570,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
{
BDRVVPCState *s = bs->opaque;
VHDFooter *footer = (VHDFooter*) s->footer_buf;
int64_t start, offset, next;
int64_t start, offset;
bool allocated;
int n;
@ -626,20 +594,18 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
*pnum += n;
sector_num += n;
nb_sectors -= n;
next = start + (*pnum * BDRV_SECTOR_SIZE);
/* *pnum can't be greater than one block for allocated
* sectors since there is always a bitmap in between. */
if (allocated) {
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
}
if (nb_sectors == 0) {
break;
}
offset = get_sector_offset(bs, sector_num, 0);
} while ((allocated && offset == next) || (!allocated && offset == -1));
} while (offset == -1);
if (allocated) {
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
} else {
return 0;
}
return 0;
}
/*
@ -659,26 +625,20 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
{
uint32_t cyls_times_heads;
/* Allow a maximum disk size of approximately 2 TB */
if (total_sectors > 65535LL * 255 * 255) {
return -EFBIG;
}
total_sectors = MIN(total_sectors, VHD_MAX_GEOMETRY);
if (total_sectors > 65535 * 16 * 63) {
if (total_sectors >= 65535LL * 16 * 63) {
*secs_per_cyl = 255;
if (total_sectors > 65535 * 16 * 255) {
*heads = 255;
} else {
*heads = 16;
}
*heads = 16;
cyls_times_heads = total_sectors / *secs_per_cyl;
} else {
*secs_per_cyl = 17;
cyls_times_heads = total_sectors / *secs_per_cyl;
*heads = (cyls_times_heads + 1023) / 1024;
if (*heads < 4)
if (*heads < 4) {
*heads = 4;
}
if (cyls_times_heads >= (*heads * 1024) || *heads > 16) {
*secs_per_cyl = 31;
@ -834,20 +794,28 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
* Calculate matching total_size and geometry. Increase the number of
* sectors requested until we get enough (or fail). This ensures that
* qemu-img convert doesn't truncate images, but rather rounds up.
*
* If the image size can't be represented by a spec conform CHS geometry,
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
* the image size from the VHD footer to calculate total_sectors.
*/
total_sectors = total_size / BDRV_SECTOR_SIZE;
total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
if (calculate_geometry(total_sectors + i, &cyls, &heads,
&secs_per_cyl))
{
calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
}
if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
total_sectors = total_size / BDRV_SECTOR_SIZE;
/* Allow a maximum disk size of approximately 2 TB */
if (total_sectors > VHD_MAX_SECTORS) {
ret = -EFBIG;
goto out;
}
} else {
total_sectors = (int64_t)cyls * heads * secs_per_cyl;
total_size = total_sectors * BDRV_SECTOR_SIZE;
}
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
total_size = total_sectors * BDRV_SECTOR_SIZE;
/* Prepare the Hard Disk Footer */
memset(buf, 0, 1024);
@ -869,7 +837,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
footer->major = cpu_to_be16(0x0005);
footer->minor = cpu_to_be16(0x0003);
footer->orig_size = cpu_to_be64(total_size);
footer->size = cpu_to_be64(total_size);
footer->current_size = cpu_to_be64(total_size);
footer->cyls = cpu_to_be16(cyls);
footer->heads = heads;
footer->secs_per_cyl = secs_per_cyl;

View File

@ -1016,18 +1016,18 @@ fail:
void hmp_commit(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
BlockDriverState *bs;
BlockBackend *blk;
int ret;
if (!strcmp(device, "all")) {
ret = bdrv_commit_all();
} else {
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
monitor_printf(mon, "Device '%s' not found\n", device);
return;
}
ret = bdrv_commit(bs);
ret = bdrv_commit(blk_bs(blk));
}
if (ret < 0) {
monitor_printf(mon, "'commit' error for '%s': %s\n", device,
@ -1092,17 +1092,20 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
const char *name,
Error **errp)
{
BlockDriverState *bs = bdrv_find(device);
BlockDriverState *bs;
BlockBackend *blk;
AioContext *aio_context;
QEMUSnapshotInfo sn;
Error *local_err = NULL;
SnapshotInfo *info = NULL;
int ret;
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return NULL;
}
bs = blk_bs(blk);
if (!has_id) {
id = NULL;
@ -1205,6 +1208,7 @@ static void internal_snapshot_prepare(BlkTransactionState *common,
Error *local_err = NULL;
const char *device;
const char *name;
BlockBackend *blk;
BlockDriverState *bs;
QEMUSnapshotInfo old_sn, *sn;
bool ret;
@ -1223,11 +1227,12 @@ static void internal_snapshot_prepare(BlkTransactionState *common,
name = internal->name;
/* 2. check for validation */
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
bs = blk_bs(blk);
/* AioContext is released in .clean() */
state->aio_context = bdrv_get_aio_context(bs);
@ -1494,17 +1499,19 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
BlockDriverState *bs;
BlockBackend *blk;
DriveBackup *backup;
Error *local_err = NULL;
assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
backup = common->action->drive_backup;
bs = bdrv_find(backup->device);
if (!bs) {
blk = blk_by_name(backup->device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device);
return;
}
bs = blk_bs(blk);
/* AioContext is released in .clean() */
state->aio_context = bdrv_get_aio_context(bs);
@ -1559,22 +1566,25 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp)
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
BlockdevBackup *backup;
BlockDriverState *bs, *target;
BlockBackend *blk;
Error *local_err = NULL;
assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
backup = common->action->blockdev_backup;
bs = bdrv_find(backup->device);
if (!bs) {
blk = blk_by_name(backup->device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device);
return;
}
bs = blk_bs(blk);
target = bdrv_find(backup->target);
if (!target) {
blk = blk_by_name(backup->target);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target);
return;
}
target = blk_bs(blk);
/* AioContext is released in .clean() */
state->aio_context = bdrv_get_aio_context(bs);
@ -1881,13 +1891,15 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
{
ThrottleConfig cfg;
BlockDriverState *bs;
BlockBackend *blk;
AioContext *aio_context;
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
bs = blk_bs(blk);
memset(&cfg, 0, sizeof(cfg));
cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps;
@ -2091,6 +2103,7 @@ void qmp_block_stream(const char *device,
bool has_on_error, BlockdevOnError on_error,
Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
BlockDriverState *base_bs = NULL;
AioContext *aio_context;
@ -2101,11 +2114,12 @@ void qmp_block_stream(const char *device,
on_error = BLOCKDEV_ON_ERROR_REPORT;
}
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
@ -2155,6 +2169,7 @@ void qmp_block_commit(const char *device,
bool has_speed, int64_t speed,
Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
BlockDriverState *base_bs, *top_bs;
AioContext *aio_context;
@ -2173,11 +2188,12 @@ void qmp_block_commit(const char *device,
* live commit feature versions; for this to work, we must make sure to
* perform the device lookup before any generic errors that may occur in a
* scenario in which all optional arguments are omitted. */
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
@ -2258,6 +2274,7 @@ void qmp_drive_backup(const char *device, const char *target,
bool has_on_target_error, BlockdevOnError on_target_error,
Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
BlockDriverState *target_bs;
BlockDriverState *source = NULL;
@ -2281,11 +2298,12 @@ void qmp_drive_backup(const char *device, const char *target,
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
@ -2385,6 +2403,7 @@ void qmp_blockdev_backup(const char *device, const char *target,
BlockdevOnError on_target_error,
Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
BlockDriverState *target_bs;
Error *local_err = NULL;
@ -2400,20 +2419,22 @@ void qmp_blockdev_backup(const char *device, const char *target,
on_target_error = BLOCKDEV_ON_ERROR_REPORT;
}
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
target_bs = bdrv_find(target);
if (!target_bs) {
blk = blk_by_name(target);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, target);
goto out;
}
target_bs = blk_bs(blk);
bdrv_ref(target_bs);
bdrv_set_aio_context(target_bs, aio_context);
@ -2442,6 +2463,7 @@ void qmp_drive_mirror(const char *device, const char *target,
bool has_on_target_error, BlockdevOnError on_target_error,
Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
BlockDriverState *source, *target_bs;
AioContext *aio_context;
@ -2481,11 +2503,12 @@ void qmp_drive_mirror(const char *device, const char *target,
return;
}
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
@ -2623,12 +2646,14 @@ out:
static BlockJob *find_block_job(const char *device, AioContext **aio_context,
Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
goto notfound;
}
bs = blk_bs(blk);
*aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(*aio_context);
@ -2733,6 +2758,7 @@ void qmp_change_backing_file(const char *device,
const char *backing_file,
Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs = NULL;
AioContext *aio_context;
BlockDriverState *image_bs = NULL;
@ -2741,12 +2767,12 @@ void qmp_change_backing_file(const char *device,
int open_flags;
int ret;
/* find the top layer BDS of the chain */
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);

View File

@ -381,7 +381,6 @@ int bdrv_media_changed(BlockDriverState *bs);
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
const char *bdrv_get_format_name(BlockDriverState *bs);
BlockDriverState *bdrv_find(const char *name);
BlockDriverState *bdrv_find_node(const char *node_name);
BlockDeviceInfoList *bdrv_named_nodes_list(void);
BlockDriverState *bdrv_lookup_bs(const char *device,

View File

@ -23,6 +23,7 @@
#include "migration/block.h"
#include "migration/migration.h"
#include "sysemu/blockdev.h"
#include "sysemu/block-backend.h"
#include <assert.h>
#define BLOCK_SIZE (1 << 20)
@ -783,6 +784,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
char device_name[256];
int64_t addr;
BlockDriverState *bs, *bs_prev = NULL;
BlockBackend *blk;
uint8_t *buf;
int64_t total_sectors = 0;
int nr_sectors;
@ -800,12 +802,13 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
qemu_get_buffer(f, (uint8_t *)device_name, len);
device_name[len] = '\0';
bs = bdrv_find(device_name);
if (!bs) {
blk = blk_by_name(device_name);
if (!blk) {
fprintf(stderr, "Error unknown block device %s\n",
device_name);
return -EINVAL;
}
bs = blk_bs(blk);
if (bs != bs_prev) {
bs_prev = bs;

View File

@ -73,6 +73,7 @@
#include "block/qapi.h"
#include "qapi/qmp-event.h"
#include "qapi-event.h"
#include "sysemu/block-backend.h"
/* for hmp_info_irq/pic */
#if defined(TARGET_SPARC)
@ -5414,15 +5415,15 @@ int monitor_read_block_device_key(Monitor *mon, const char *device,
BlockCompletionFunc *completion_cb,
void *opaque)
{
BlockDriverState *bs;
BlockBackend *blk;
bs = bdrv_find(device);
if (!bs) {
blk = blk_by_name(device);
if (!blk) {
monitor_printf(mon, "Device not found %s\n", device);
return -1;
}
return monitor_read_bdrv_key_start(mon, bs, completion_cb, opaque);
return monitor_read_bdrv_key_start(mon, blk_bs(blk), completion_cb, opaque);
}
QemuOptsList qemu_mon_opts = {

95
tests/qemu-iotests/115 Executable file
View File

@ -0,0 +1,95 @@
#!/bin/bash
#
# Test case for non-self-referential qcow2 refcount blocks
#
# Copyright (C) 2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=mreitz@redhat.com
seq="$(basename $0)"
echo "QA output created by $seq"
here="$PWD"
tmp=/tmp/$$
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
# This test relies on refcounts being 64 bits wide (which does not work with
# compat=0.10)
_unsupported_imgopts 'refcount_bits=\([^6]\|.\([^4]\|$\)\)' 'compat=0.10'
echo
echo '=== Testing large refcount and L1 table ==='
echo
# Create an image with an L1 table and a refcount table that each span twice the
# number of clusters which can be described by a single refblock; therefore, at
# least two refblocks cannot count their own refcounts because all the clusters
# they describe are part of the L1 table or refcount table.
# One refblock can describe (with cluster_size=512 and refcount_bits=64)
# 512/8 = 64 clusters, therefore the L1 table should cover 128 clusters, which
# equals 128 * (512/8) = 8192 entries (actually, 8192 - 512/8 = 8129 would
# suffice, but it does not really matter). 8192 L2 tables can in turn describe
# 8192 * 512/8 = 524,288 clusters which cover a space of 256 MB.
# Since with refcount_bits=64 every refcount block entry is 64 bits wide (just
# like the L2 table entries), the same calculation applies to the refcount table
# as well; the difference is that while for the L1 table the guest disk size is
# concerned, for the refcount table it is the image length that has to be at
# least 256 MB. We can achieve that by using preallocation=metadata for an image
# which has a guest disk size of 256 MB.
IMGOPTS="$IMGOPTS,refcount_bits=64,cluster_size=512,preallocation=metadata" \
_make_test_img 256M
# We know for sure that the L1 and refcount tables do not overlap with any other
# structure because the metadata overlap checks would have caught that case.
# Because qemu refuses to open qcow2 files whose L1 table does not cover the
# whole guest disk size, it is definitely large enough. On the other hand, to
# test whether the refcount table is large enough, we simply have to verify that
# indeed all the clusters are allocated, which is done by qemu-img check.
# The final thing we need to test is whether the tables are actually covered by
# refcount blocks; since all clusters of the tables are referenced, we can use
# qemu-img check for that purpose, too.
$QEMU_IMG check "$TEST_IMG" | \
sed -e 's/^.* = \([0-9]\+\.[0-9]\+% allocated\).*\(clusters\)$/\1 \2/' \
-e '/^Image end offset/d'
# (Note that we cannot use _check_test_img because that function filters out the
# allocation status)
# success, all done
echo '*** done'
rm -f $seq.full
status=0

View File

@ -0,0 +1,8 @@
QA output created by 115
=== Testing large refcount and L1 table ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=268435456 preallocation='metadata'
No errors were found on the image.
100.00% allocated clusters
*** done

102
tests/qemu-iotests/121 Executable file
View File

@ -0,0 +1,102 @@
#!/bin/bash
#
# Test cases for qcow2 refcount table growth
#
# Copyright (C) 2015 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=mreitz@redhat.com
seq="$(basename $0)"
echo "QA output created by $seq"
here="$PWD"
tmp=/tmp/$$
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
echo
echo '=== New refcount structures may not conflict with existing structures ==='
echo
echo '--- Test 1 ---'
echo
# Preallocation speeds up the write operation, but preallocating everything will
# destroy the purpose of the write; so preallocate one KB less than what would
# cause a reftable growth...
IMGOPTS='preallocation=metadata,cluster_size=1k' _make_test_img 64512K
# ...and make the image the desired size afterwards.
$QEMU_IMG resize "$TEST_IMG" 65M
# The first write results in a growth of the refcount table during an allocation
# which has precisely the required size so that the new refcount block allocated
# in alloc_refcount_block() is right after cluster_index; this did lead to a
# different refcount block being written to disk (a zeroed cluster) than what is
# cached (a refblock with one entry having a refcount of 1), and the second
# write would then result in that cached cluster being marked dirty and then
# in it being written to disk.
# This should not happen, the new refcount structures may not conflict with
# new_block.
# (Note that for some reason, 'write 63M 1K' does not trigger the problem)
$QEMU_IO -c 'write 62M 1025K' -c 'write 64M 1M' "$TEST_IMG" | _filter_qemu_io
_check_test_img
echo
echo '--- Test 2 ---'
echo
IMGOPTS='preallocation=metadata,cluster_size=1k' _make_test_img 64513K
# This results in an L1 table growth which in turn results in some clusters at
# the start of the image becoming free
$QEMU_IMG resize "$TEST_IMG" 65M
# This write results in a refcount table growth; but the refblock allocated
# immediately before that (new_block) takes cluster index 4 (which is now free)
# and is thus not self-describing (in contrast to test 1, where new_block was
# self-describing). The refcount table growth algorithm then used to place the
# new refcount structures at cluster index 65536 (which is the same as the
# cluster_index parameter in this case), allocating a new refcount block for
# that cluster while new_block already existed, leaking new_block.
# Therefore, the new refcount structures may not be put at cluster_index
# (because new_block already describes that cluster, and the new structures try
# to be self-describing).
$QEMU_IO -c 'write 63M 130K' "$TEST_IMG" | _filter_qemu_io
_check_test_img
# success, all done
echo
echo '*** done'
rm -f $seq.full
status=0

View File

@ -0,0 +1,23 @@
QA output created by 121
=== New refcount structures may not conflict with existing structures ===
--- Test 1 ---
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=66060288 preallocation='metadata'
Image resized.
wrote 1049600/1049600 bytes at offset 65011712
1.001 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 1048576/1048576 bytes at offset 67108864
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
--- Test 2 ---
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=66061312 preallocation='metadata'
Image resized.
wrote 133120/133120 bytes at offset 66060288
130 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
*** done

View File

@ -119,6 +119,8 @@
112 rw auto
113 rw auto quick
114 rw auto quick
115 rw auto
116 rw auto quick
121 rw auto
123 rw auto quick
128 rw auto quick