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); 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 */ /* This function is to find a node in the bs graph */
BlockDriverState *bdrv_find_node(const char *node_name) 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); BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_GROW);
/* Calculate the number of refcount blocks needed so far */ /* Calculate the number of refcount blocks needed so far; this will be the
uint64_t blocks_used = DIV_ROUND_UP(cluster_index, s->refcount_block_size); * 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)) { if (blocks_used > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) {
return -EFBIG; return -EFBIG;

View File

@ -46,6 +46,7 @@ enum vhd_type {
#define VHD_TIMESTAMP_BASE 946684800 #define VHD_TIMESTAMP_BASE 946684800
#define VHD_MAX_SECTORS (65535LL * 255 * 255) #define VHD_MAX_SECTORS (65535LL * 255 * 255)
#define VHD_MAX_GEOMETRY (65535LL * 16 * 255)
// always big-endian // always big-endian
typedef struct vhd_footer { typedef struct vhd_footer {
@ -65,7 +66,7 @@ typedef struct vhd_footer {
char creator_os[4]; // "Wi2k" char creator_os[4]; // "Wi2k"
uint64_t orig_size; uint64_t orig_size;
uint64_t size; uint64_t current_size;
uint16_t cyls; uint16_t cyls;
uint8_t heads; uint8_t heads;
@ -215,13 +216,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
bs->total_sectors = (int64_t) bs->total_sectors = (int64_t)
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
/* images created with disk2vhd report a far higher virtual size /* Images that have exactly the maximum geometry are probably bigger and
* than expected with the cyls * heads * sectors_per_cyl formula. * would be truncated if we adhered to the geometry for them. Rely on
* use the footer->size instead if the image was created with * footer->current_size for them. */
* disk2vhd. if (bs->total_sectors == VHD_MAX_GEOMETRY) {
*/ bs->total_sectors = be64_to_cpu(footer->current_size) /
if (!strncmp(footer->creator_app, "d2v", 4)) { BDRV_SECTOR_SIZE;
bs->total_sectors = be64_to_cpu(footer->size) / BDRV_SECTOR_SIZE;
} }
/* Allow a maximum disk size of approximately 2 TB */ /* 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); 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; return block_offset;
} }
@ -602,7 +570,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
{ {
BDRVVPCState *s = bs->opaque; BDRVVPCState *s = bs->opaque;
VHDFooter *footer = (VHDFooter*) s->footer_buf; VHDFooter *footer = (VHDFooter*) s->footer_buf;
int64_t start, offset, next; int64_t start, offset;
bool allocated; bool allocated;
int n; int n;
@ -626,20 +594,18 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
*pnum += n; *pnum += n;
sector_num += n; sector_num += n;
nb_sectors -= 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) { if (nb_sectors == 0) {
break; break;
} }
offset = get_sector_offset(bs, sector_num, 0); offset = get_sector_offset(bs, sector_num, 0);
} while ((allocated && offset == next) || (!allocated && offset == -1)); } while (offset == -1);
if (allocated) { return 0;
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
} else {
return 0;
}
} }
/* /*
@ -659,26 +625,20 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
{ {
uint32_t cyls_times_heads; uint32_t cyls_times_heads;
/* Allow a maximum disk size of approximately 2 TB */ total_sectors = MIN(total_sectors, VHD_MAX_GEOMETRY);
if (total_sectors > 65535LL * 255 * 255) {
return -EFBIG;
}
if (total_sectors > 65535 * 16 * 63) { if (total_sectors >= 65535LL * 16 * 63) {
*secs_per_cyl = 255; *secs_per_cyl = 255;
if (total_sectors > 65535 * 16 * 255) { *heads = 16;
*heads = 255;
} else {
*heads = 16;
}
cyls_times_heads = total_sectors / *secs_per_cyl; cyls_times_heads = total_sectors / *secs_per_cyl;
} else { } else {
*secs_per_cyl = 17; *secs_per_cyl = 17;
cyls_times_heads = total_sectors / *secs_per_cyl; cyls_times_heads = total_sectors / *secs_per_cyl;
*heads = (cyls_times_heads + 1023) / 1024; *heads = (cyls_times_heads + 1023) / 1024;
if (*heads < 4) if (*heads < 4) {
*heads = 4; *heads = 4;
}
if (cyls_times_heads >= (*heads * 1024) || *heads > 16) { if (cyls_times_heads >= (*heads * 1024) || *heads > 16) {
*secs_per_cyl = 31; *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 * Calculate matching total_size and geometry. Increase the number of
* sectors requested until we get enough (or fail). This ensures that * sectors requested until we get enough (or fail). This ensures that
* qemu-img convert doesn't truncate images, but rather rounds up. * 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++) { for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
if (calculate_geometry(total_sectors + i, &cyls, &heads, calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
&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; ret = -EFBIG;
goto out; 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 */ /* Prepare the Hard Disk Footer */
memset(buf, 0, 1024); 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->major = cpu_to_be16(0x0005);
footer->minor = cpu_to_be16(0x0003); footer->minor = cpu_to_be16(0x0003);
footer->orig_size = cpu_to_be64(total_size); 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->cyls = cpu_to_be16(cyls);
footer->heads = heads; footer->heads = heads;
footer->secs_per_cyl = secs_per_cyl; footer->secs_per_cyl = secs_per_cyl;

View File

@ -1016,18 +1016,18 @@ fail:
void hmp_commit(Monitor *mon, const QDict *qdict) void hmp_commit(Monitor *mon, const QDict *qdict)
{ {
const char *device = qdict_get_str(qdict, "device"); const char *device = qdict_get_str(qdict, "device");
BlockDriverState *bs; BlockBackend *blk;
int ret; int ret;
if (!strcmp(device, "all")) { if (!strcmp(device, "all")) {
ret = bdrv_commit_all(); ret = bdrv_commit_all();
} else { } else {
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
monitor_printf(mon, "Device '%s' not found\n", device); monitor_printf(mon, "Device '%s' not found\n", device);
return; return;
} }
ret = bdrv_commit(bs); ret = bdrv_commit(blk_bs(blk));
} }
if (ret < 0) { if (ret < 0) {
monitor_printf(mon, "'commit' error for '%s': %s\n", device, 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, const char *name,
Error **errp) Error **errp)
{ {
BlockDriverState *bs = bdrv_find(device); BlockDriverState *bs;
BlockBackend *blk;
AioContext *aio_context; AioContext *aio_context;
QEMUSnapshotInfo sn; QEMUSnapshotInfo sn;
Error *local_err = NULL; Error *local_err = NULL;
SnapshotInfo *info = NULL; SnapshotInfo *info = NULL;
int ret; int ret;
if (!bs) { blk = blk_by_name(device);
if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return NULL; return NULL;
} }
bs = blk_bs(blk);
if (!has_id) { if (!has_id) {
id = NULL; id = NULL;
@ -1205,6 +1208,7 @@ static void internal_snapshot_prepare(BlkTransactionState *common,
Error *local_err = NULL; Error *local_err = NULL;
const char *device; const char *device;
const char *name; const char *name;
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
QEMUSnapshotInfo old_sn, *sn; QEMUSnapshotInfo old_sn, *sn;
bool ret; bool ret;
@ -1223,11 +1227,12 @@ static void internal_snapshot_prepare(BlkTransactionState *common,
name = internal->name; name = internal->name;
/* 2. check for validation */ /* 2. check for validation */
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return; return;
} }
bs = blk_bs(blk);
/* AioContext is released in .clean() */ /* AioContext is released in .clean() */
state->aio_context = bdrv_get_aio_context(bs); 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); DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
BlockDriverState *bs; BlockDriverState *bs;
BlockBackend *blk;
DriveBackup *backup; DriveBackup *backup;
Error *local_err = NULL; Error *local_err = NULL;
assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
backup = common->action->drive_backup; backup = common->action->drive_backup;
bs = bdrv_find(backup->device); blk = blk_by_name(backup->device);
if (!bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device);
return; return;
} }
bs = blk_bs(blk);
/* AioContext is released in .clean() */ /* AioContext is released in .clean() */
state->aio_context = bdrv_get_aio_context(bs); 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); BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
BlockdevBackup *backup; BlockdevBackup *backup;
BlockDriverState *bs, *target; BlockDriverState *bs, *target;
BlockBackend *blk;
Error *local_err = NULL; Error *local_err = NULL;
assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
backup = common->action->blockdev_backup; backup = common->action->blockdev_backup;
bs = bdrv_find(backup->device); blk = blk_by_name(backup->device);
if (!bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device);
return; return;
} }
bs = blk_bs(blk);
target = bdrv_find(backup->target); blk = blk_by_name(backup->target);
if (!target) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target); error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target);
return; return;
} }
target = blk_bs(blk);
/* AioContext is released in .clean() */ /* AioContext is released in .clean() */
state->aio_context = bdrv_get_aio_context(bs); 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; ThrottleConfig cfg;
BlockDriverState *bs; BlockDriverState *bs;
BlockBackend *blk;
AioContext *aio_context; AioContext *aio_context;
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return; return;
} }
bs = blk_bs(blk);
memset(&cfg, 0, sizeof(cfg)); memset(&cfg, 0, sizeof(cfg));
cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps; 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, bool has_on_error, BlockdevOnError on_error,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *base_bs = NULL; BlockDriverState *base_bs = NULL;
AioContext *aio_context; AioContext *aio_context;
@ -2101,11 +2114,12 @@ void qmp_block_stream(const char *device,
on_error = BLOCKDEV_ON_ERROR_REPORT; on_error = BLOCKDEV_ON_ERROR_REPORT;
} }
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return; return;
} }
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
@ -2155,6 +2169,7 @@ void qmp_block_commit(const char *device,
bool has_speed, int64_t speed, bool has_speed, int64_t speed,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *base_bs, *top_bs; BlockDriverState *base_bs, *top_bs;
AioContext *aio_context; 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 * 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 * perform the device lookup before any generic errors that may occur in a
* scenario in which all optional arguments are omitted. */ * scenario in which all optional arguments are omitted. */
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return; return;
} }
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); 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, bool has_on_target_error, BlockdevOnError on_target_error,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *target_bs; BlockDriverState *target_bs;
BlockDriverState *source = NULL; BlockDriverState *source = NULL;
@ -2281,11 +2298,12 @@ void qmp_drive_backup(const char *device, const char *target,
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
} }
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return; return;
} }
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
@ -2385,6 +2403,7 @@ void qmp_blockdev_backup(const char *device, const char *target,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *target_bs; BlockDriverState *target_bs;
Error *local_err = NULL; 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; on_target_error = BLOCKDEV_ON_ERROR_REPORT;
} }
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return; return;
} }
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
target_bs = bdrv_find(target); blk = blk_by_name(target);
if (!target_bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, target); error_set(errp, QERR_DEVICE_NOT_FOUND, target);
goto out; goto out;
} }
target_bs = blk_bs(blk);
bdrv_ref(target_bs); bdrv_ref(target_bs);
bdrv_set_aio_context(target_bs, aio_context); 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, bool has_on_target_error, BlockdevOnError on_target_error,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *source, *target_bs; BlockDriverState *source, *target_bs;
AioContext *aio_context; AioContext *aio_context;
@ -2481,11 +2503,12 @@ void qmp_drive_mirror(const char *device, const char *target,
return; return;
} }
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return; return;
} }
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
@ -2623,12 +2646,14 @@ out:
static BlockJob *find_block_job(const char *device, AioContext **aio_context, static BlockJob *find_block_job(const char *device, AioContext **aio_context,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
goto notfound; goto notfound;
} }
bs = blk_bs(blk);
*aio_context = bdrv_get_aio_context(bs); *aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(*aio_context); aio_context_acquire(*aio_context);
@ -2733,6 +2758,7 @@ void qmp_change_backing_file(const char *device,
const char *backing_file, const char *backing_file,
Error **errp) Error **errp)
{ {
BlockBackend *blk;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
AioContext *aio_context; AioContext *aio_context;
BlockDriverState *image_bs = NULL; BlockDriverState *image_bs = NULL;
@ -2741,12 +2767,12 @@ void qmp_change_backing_file(const char *device,
int open_flags; int open_flags;
int ret; int ret;
/* find the top layer BDS of the chain */ blk = blk_by_name(device);
bs = bdrv_find(device); if (!blk) {
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device); error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return; return;
} }
bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); 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_lock_medium(BlockDriverState *bs, bool locked);
void bdrv_eject(BlockDriverState *bs, bool eject_flag); void bdrv_eject(BlockDriverState *bs, bool eject_flag);
const char *bdrv_get_format_name(BlockDriverState *bs); const char *bdrv_get_format_name(BlockDriverState *bs);
BlockDriverState *bdrv_find(const char *name);
BlockDriverState *bdrv_find_node(const char *node_name); BlockDriverState *bdrv_find_node(const char *node_name);
BlockDeviceInfoList *bdrv_named_nodes_list(void); BlockDeviceInfoList *bdrv_named_nodes_list(void);
BlockDriverState *bdrv_lookup_bs(const char *device, BlockDriverState *bdrv_lookup_bs(const char *device,

View File

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

View File

@ -73,6 +73,7 @@
#include "block/qapi.h" #include "block/qapi.h"
#include "qapi/qmp-event.h" #include "qapi/qmp-event.h"
#include "qapi-event.h" #include "qapi-event.h"
#include "sysemu/block-backend.h"
/* for hmp_info_irq/pic */ /* for hmp_info_irq/pic */
#if defined(TARGET_SPARC) #if defined(TARGET_SPARC)
@ -5414,15 +5415,15 @@ int monitor_read_block_device_key(Monitor *mon, const char *device,
BlockCompletionFunc *completion_cb, BlockCompletionFunc *completion_cb,
void *opaque) void *opaque)
{ {
BlockDriverState *bs; BlockBackend *blk;
bs = bdrv_find(device); blk = blk_by_name(device);
if (!bs) { if (!blk) {
monitor_printf(mon, "Device not found %s\n", device); monitor_printf(mon, "Device not found %s\n", device);
return -1; 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 = { 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 112 rw auto
113 rw auto quick 113 rw auto quick
114 rw auto quick 114 rw auto quick
115 rw auto
116 rw auto quick 116 rw auto quick
121 rw auto
123 rw auto quick 123 rw auto quick
128 rw auto quick 128 rw auto quick