diff options
45 files changed, 2333 insertions, 1311 deletions
@@ -981,6 +981,33 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options, *child_flags = flags; } +static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, + const char *filename, Error **errp) +{ + BlockDriverState *parent = c->opaque; + int orig_flags = bdrv_get_flags(parent); + int ret; + + if (!(orig_flags & BDRV_O_RDWR)) { + ret = bdrv_reopen(parent, orig_flags | BDRV_O_RDWR, errp); + if (ret < 0) { + return ret; + } + } + + ret = bdrv_change_backing_file(parent, filename, + base->drv ? base->drv->format_name : ""); + if (ret < 0) { + error_setg_errno(errp, ret, "Could not update backing file link"); + } + + if (!(orig_flags & BDRV_O_RDWR)) { + bdrv_reopen(parent, orig_flags, NULL); + } + + return ret; +} + const BdrvChildRole child_backing = { .get_parent_desc = bdrv_child_get_parent_desc, .attach = bdrv_backing_attach, @@ -989,6 +1016,7 @@ const BdrvChildRole child_backing = { .drained_begin = bdrv_child_cb_drained_begin, .drained_end = bdrv_child_cb_drained_end, .inactivate = bdrv_child_cb_inactivate, + .update_filename = bdrv_backing_update_filename, }; static int bdrv_open_flags(BlockDriverState *bs, int flags) @@ -3463,28 +3491,16 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs) * if active == top, that is considered an error * */ -int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, - BlockDriverState *base, const char *backing_file_str) +int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, + const char *backing_file_str) { - BlockDriverState *new_top_bs = NULL; + BdrvChild *c, *next; Error *local_err = NULL; int ret = -EIO; - if (!top->drv || !base->drv) { - goto exit; - } + bdrv_ref(top); - new_top_bs = bdrv_find_overlay(active, top); - - if (new_top_bs == NULL) { - /* we could not find the image above 'top', this is an error */ - goto exit; - } - - /* special case of new_top_bs->backing->bs already pointing to base - nothing - * to do, no intermediate images */ - if (backing_bs(new_top_bs) == base) { - ret = 0; + if (!top->drv || !base->drv) { goto exit; } @@ -3494,22 +3510,43 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, } /* success - we can delete the intermediate states, and link top->base */ + /* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once + * we've figured out how they should work. */ backing_file_str = backing_file_str ? backing_file_str : base->filename; - ret = bdrv_change_backing_file(new_top_bs, backing_file_str, - base->drv ? base->drv->format_name : ""); - if (ret) { - goto exit; - } - bdrv_set_backing_hd(new_top_bs, base, &local_err); - if (local_err) { - ret = -EPERM; - error_report_err(local_err); - goto exit; + QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) { + /* Check whether we are allowed to switch c from top to base */ + GSList *ignore_children = g_slist_prepend(NULL, c); + bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, + ignore_children, &local_err); + if (local_err) { + ret = -EPERM; + error_report_err(local_err); + goto exit; + } + g_slist_free(ignore_children); + + /* If so, update the backing file path in the image file */ + if (c->role->update_filename) { + ret = c->role->update_filename(c, base, backing_file_str, + &local_err); + if (ret < 0) { + bdrv_abort_perm_update(base); + error_report_err(local_err); + goto exit; + } + } + + /* Do the actual switch in the in-memory graph. + * Completes bdrv_check_update_perm() transaction internally. */ + bdrv_ref(base); + bdrv_replace_child(c, base); + bdrv_unref(top); } ret = 0; exit: + bdrv_unref(top); return ret; } @@ -3545,12 +3582,18 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, assert(!(bs->open_flags & BDRV_O_INACTIVE)); ret = drv->bdrv_truncate(bs, offset, prealloc, errp); - if (ret == 0) { - ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); - bdrv_dirty_bitmap_truncate(bs); - bdrv_parent_cb_resize(bs); - atomic_inc(&bs->write_gen); + if (ret < 0) { + return ret; + } + ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count"); + } else { + offset = bs->total_sectors * BDRV_SECTOR_SIZE; } + bdrv_dirty_bitmap_truncate(bs, offset); + bdrv_parent_cb_resize(bs); + atomic_inc(&bs->write_gen); return ret; } @@ -4488,7 +4531,7 @@ void bdrv_img_create(const char *filename, const char *fmt, /* The size for the image must always be specified, unless we have a backing * file and we have not been forbidden from opening it. */ - size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); + size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, img_size); if (backing_file && !(flags & BDRV_O_NO_BACKING)) { BlockDriverState *bs; char *full_backing = g_new0(char, PATH_MAX); diff --git a/block/backup.c b/block/backup.c index 517c300a49..06ddbfd03d 100644 --- a/block/backup.c +++ b/block/backup.c @@ -372,10 +372,10 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap); clusters_per_iter = MAX((granularity / job->cluster_size), 1); - dbi = bdrv_dirty_iter_new(job->sync_bitmap, 0); + dbi = bdrv_dirty_iter_new(job->sync_bitmap); /* Find the next dirty sector(s) */ - while ((offset = bdrv_dirty_iter_next(dbi) * BDRV_SECTOR_SIZE) >= 0) { + while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) { cluster = offset / job->cluster_size; /* Fake progress updates for any clusters we skipped */ @@ -403,8 +403,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) /* If the bitmap granularity is smaller than the backup granularity, * we need to advance the iterator pointer to the next cluster. */ if (granularity < job->cluster_size) { - bdrv_set_dirty_iter(dbi, - cluster * job->cluster_size / BDRV_SECTOR_SIZE); + bdrv_set_dirty_iter(dbi, cluster * job->cluster_size); } last_cluster = cluster - 1; diff --git a/block/commit.c b/block/commit.c index 8f0e83578a..5036eec434 100644 --- a/block/commit.c +++ b/block/commit.c @@ -36,13 +36,11 @@ enum { typedef struct CommitBlockJob { BlockJob common; RateLimit limit; - BlockDriverState *active; BlockDriverState *commit_top_bs; BlockBackend *top; BlockBackend *base; BlockdevOnError on_error; int base_flags; - int orig_overlay_flags; char *backing_file_str; } CommitBlockJob; @@ -81,18 +79,15 @@ static void commit_complete(BlockJob *job, void *opaque) { CommitBlockJob *s = container_of(job, CommitBlockJob, common); CommitCompleteData *data = opaque; - BlockDriverState *active = s->active; BlockDriverState *top = blk_bs(s->top); BlockDriverState *base = blk_bs(s->base); - BlockDriverState *overlay_bs = bdrv_find_overlay(active, s->commit_top_bs); + BlockDriverState *commit_top_bs = s->commit_top_bs; int ret = data->ret; bool remove_commit_top_bs = false; - /* Make sure overlay_bs and top stay around until bdrv_set_backing_hd() */ + /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ bdrv_ref(top); - if (overlay_bs) { - bdrv_ref(overlay_bs); - } + bdrv_ref(commit_top_bs); /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before * the normal backing chain can be restored. */ @@ -100,9 +95,9 @@ static void commit_complete(BlockJob *job, void *opaque) if (!block_job_is_cancelled(&s->common) && ret == 0) { /* success */ - ret = bdrv_drop_intermediate(active, s->commit_top_bs, base, + ret = bdrv_drop_intermediate(s->commit_top_bs, base, s->backing_file_str); - } else if (overlay_bs) { + } else { /* XXX Can (or should) we somehow keep 'consistent read' blocked even * after the failed/cancelled commit job is gone? If we already wrote * something to base, the intermediate images aren't valid any more. */ @@ -115,9 +110,6 @@ static void commit_complete(BlockJob *job, void *opaque) if (s->base_flags != bdrv_get_flags(base)) { bdrv_reopen(base, s->base_flags, NULL); } - if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) { - bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL); - } g_free(s->backing_file_str); blk_unref(s->top); @@ -134,10 +126,13 @@ static void commit_complete(BlockJob *job, void *opaque) * filter driver from the backing chain. Do this as the final step so that * the 'consistent read' permission can be granted. */ if (remove_commit_top_bs) { - bdrv_set_backing_hd(overlay_bs, top, &error_abort); + bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL, + &error_abort); + bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs), + &error_abort); } - bdrv_unref(overlay_bs); + bdrv_unref(commit_top_bs); bdrv_unref(top); } @@ -283,10 +278,8 @@ void commit_start(const char *job_id, BlockDriverState *bs, { CommitBlockJob *s; BlockReopenQueue *reopen_queue = NULL; - int orig_overlay_flags; int orig_base_flags; BlockDriverState *iter; - BlockDriverState *overlay_bs; BlockDriverState *commit_top_bs = NULL; Error *local_err = NULL; int ret; @@ -297,31 +290,19 @@ void commit_start(const char *job_id, BlockDriverState *bs, return; } - overlay_bs = bdrv_find_overlay(bs, top); - - if (overlay_bs == NULL) { - error_setg(errp, "Could not find overlay image for %s:", top->filename); - return; - } - s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL, speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); if (!s) { return; } - orig_base_flags = bdrv_get_flags(base); - orig_overlay_flags = bdrv_get_flags(overlay_bs); - - /* convert base & overlay_bs to r/w, if necessary */ + /* convert base to r/w, if necessary */ + orig_base_flags = bdrv_get_flags(base); if (!(orig_base_flags & BDRV_O_RDWR)) { reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL, orig_base_flags | BDRV_O_RDWR); } - if (!(orig_overlay_flags & BDRV_O_RDWR)) { - reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, NULL, - orig_overlay_flags | BDRV_O_RDWR); - } + if (reopen_queue) { bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err); if (local_err != NULL) { @@ -350,7 +331,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, error_propagate(errp, local_err); goto fail; } - bdrv_set_backing_hd(overlay_bs, commit_top_bs, &local_err); + bdrv_replace_node(top, commit_top_bs, &local_err); if (local_err) { bdrv_unref(commit_top_bs); commit_top_bs = NULL; @@ -382,14 +363,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, goto fail; } - /* overlay_bs must be blocked because it needs to be modified to - * update the backing image string. */ - ret = block_job_add_bdrv(&s->common, "overlay of top", overlay_bs, - BLK_PERM_GRAPH_MOD, BLK_PERM_ALL, errp); - if (ret < 0) { - goto fail; - } - s->base = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE, @@ -408,13 +381,8 @@ void commit_start(const char *job_id, BlockDriverState *bs, goto fail; } - s->active = bs; - - s->base_flags = orig_base_flags; - s->orig_overlay_flags = orig_overlay_flags; - + s->base_flags = orig_base_flags; s->backing_file_str = g_strdup(backing_file_str); - s->on_error = on_error; trace_commit_start(bs, base, top, s); @@ -429,7 +397,7 @@ fail: blk_unref(s->top); } if (commit_top_bs) { - bdrv_set_backing_hd(overlay_bs, top, &error_abort); + bdrv_replace_node(commit_top_bs, top, &error_abort); } block_job_early_fail(&s->common); } diff --git a/block/crypto.c b/block/crypto.c index 58ef6f2f52..60ddf8623e 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -279,6 +279,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, return -EINVAL; } + bs->supported_write_flags = BDRV_REQ_FUA & + bs->file->bs->supported_write_flags; + opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { @@ -364,8 +367,9 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, PreallocMode prealloc, Error **errp) { BlockCrypto *crypto = bs->opaque; - size_t payload_offset = + uint64_t payload_offset = qcrypto_block_get_payload_offset(crypto->block); + assert(payload_offset < (INT64_MAX - offset)); offset += payload_offset; @@ -379,66 +383,65 @@ static void block_crypto_close(BlockDriverState *bs) } -#define BLOCK_CRYPTO_MAX_SECTORS 32 +/* + * 1 MB bounce buffer gives good performance / memory tradeoff + * when using cache=none|directsync. + */ +#define BLOCK_CRYPTO_MAX_IO_SIZE (1024 * 1024) static coroutine_fn int -block_crypto_co_readv(BlockDriverState *bs, int64_t sector_num, - int remaining_sectors, QEMUIOVector *qiov) +block_crypto_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { BlockCrypto *crypto = bs->opaque; - int cur_nr_sectors; /* number of sectors in current iteration */ + uint64_t cur_bytes; /* number of bytes in current iteration */ uint64_t bytes_done = 0; uint8_t *cipher_data = NULL; QEMUIOVector hd_qiov; int ret = 0; - size_t payload_offset = - qcrypto_block_get_payload_offset(crypto->block) / 512; + uint64_t sector_size = qcrypto_block_get_sector_size(crypto->block); + uint64_t payload_offset = qcrypto_block_get_payload_offset(crypto->block); + + assert(!flags); + assert(payload_offset < INT64_MAX); + assert(QEMU_IS_ALIGNED(offset, sector_size)); + assert(QEMU_IS_ALIGNED(bytes, sector_size)); qemu_iovec_init(&hd_qiov, qiov->niov); - /* Bounce buffer so we have a linear mem region for - * entire sector. XXX optimize so we avoid bounce - * buffer in case that qiov->niov == 1 + /* Bounce buffer because we don't wish to expose cipher text + * in qiov which points to guest memory. */ cipher_data = - qemu_try_blockalign(bs->file->bs, MIN(BLOCK_CRYPTO_MAX_SECTORS * 512, + qemu_try_blockalign(bs->file->bs, MIN(BLOCK_CRYPTO_MAX_IO_SIZE, qiov->size)); if (cipher_data == NULL) { ret = -ENOMEM; goto cleanup; } - while (remaining_sectors) { - cur_nr_sectors = remaining_sectors; - - if (cur_nr_sectors > BLOCK_CRYPTO_MAX_SECTORS) { - cur_nr_sectors = BLOCK_CRYPTO_MAX_SECTORS; - } + while (bytes) { + cur_bytes = MIN(bytes, BLOCK_CRYPTO_MAX_IO_SIZE); qemu_iovec_reset(&hd_qiov); - qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512); + qemu_iovec_add(&hd_qiov, cipher_data, cur_bytes); - ret = bdrv_co_readv(bs->file, - payload_offset + sector_num, - cur_nr_sectors, &hd_qiov); + ret = bdrv_co_preadv(bs->file, payload_offset + offset + bytes_done, + cur_bytes, &hd_qiov, 0); if (ret < 0) { goto cleanup; } - if (qcrypto_block_decrypt(crypto->block, - sector_num, - cipher_data, cur_nr_sectors * 512, - NULL) < 0) { + if (qcrypto_block_decrypt(crypto->block, offset + bytes_done, + cipher_data, cur_bytes, NULL) < 0) { ret = -EIO; goto cleanup; } - qemu_iovec_from_buf(qiov, bytes_done, - cipher_data, cur_nr_sectors * 512); + qemu_iovec_from_buf(qiov, bytes_done, cipher_data, cur_bytes); - remaining_sectors -= cur_nr_sectors; - sector_num += cur_nr_sectors; - bytes_done += cur_nr_sectors * 512; + bytes -= cur_bytes; + bytes_done += cur_bytes; } cleanup: @@ -450,63 +453,58 @@ block_crypto_co_readv(BlockDriverState *bs, int64_t sector_num, static coroutine_fn int -block_crypto_co_writev(BlockDriverState *bs, int64_t sector_num, - int remaining_sectors, QEMUIOVector *qiov) +block_crypto_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { BlockCrypto *crypto = bs->opaque; - int cur_nr_sectors; /* number of sectors in current iteration */ + uint64_t cur_bytes; /* number of bytes in current iteration */ uint64_t bytes_done = 0; uint8_t *cipher_data = NULL; QEMUIOVector hd_qiov; int ret = 0; - size_t payload_offset = - qcrypto_block_get_payload_offset(crypto->block) / 512; + uint64_t sector_size = qcrypto_block_get_sector_size(crypto->block); + uint64_t payload_offset = qcrypto_block_get_payload_offset(crypto->block); + + assert(!(flags & ~BDRV_REQ_FUA)); + assert(payload_offset < INT64_MAX); + assert(QEMU_IS_ALIGNED(offset, sector_size)); + assert(QEMU_IS_ALIGNED(bytes, sector_size)); qemu_iovec_init(&hd_qiov, qiov->niov); - /* Bounce buffer so we have a linear mem region for - * entire sector. XXX optimize so we avoid bounce - * buffer in case that qiov->niov == 1 + /* Bounce buffer because we're not permitted to touch + * contents of qiov - it points to guest memory. */ cipher_data = - qemu_try_blockalign(bs->file->bs, MIN(BLOCK_CRYPTO_MAX_SECTORS * 512, + qemu_try_blockalign(bs->file->bs, MIN(BLOCK_CRYPTO_MAX_IO_SIZE, qiov->size)); if (cipher_data == NULL) { ret = -ENOMEM; goto cleanup; } - while (remaining_sectors) { - cur_nr_sectors = remaining_sectors; + while (bytes) { + cur_bytes = MIN(bytes, BLOCK_CRYPTO_MAX_IO_SIZE); - if (cur_nr_sectors > BLOCK_CRYPTO_MAX_SECTORS) { - cur_nr_sectors = BLOCK_CRYPTO_MAX_SECTORS; - } - - qemu_iovec_to_buf(qiov, bytes_done, - cipher_data, cur_nr_sectors * 512); + qemu_iovec_to_buf(qiov, bytes_done, cipher_data, cur_bytes); - if (qcrypto_block_encrypt(crypto->block, - sector_num, - cipher_data, cur_nr_sectors * 512, - NULL) < 0) { + if (qcrypto_block_encrypt(crypto->block, offset + bytes_done, + cipher_data, cur_bytes, NULL) < 0) { ret = -EIO; goto cleanup; } qemu_iovec_reset(&hd_qiov); - qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512); + qemu_iovec_add(&hd_qiov, cipher_data, cur_bytes); - ret = bdrv_co_writev(bs->file, - payload_offset + sector_num, - cur_nr_sectors, &hd_qiov); + ret = bdrv_co_pwritev(bs->file, payload_offset + offset + bytes_done, + cur_bytes, &hd_qiov, flags); if (ret < 0) { goto cleanup; } - remaining_sectors -= cur_nr_sectors; - sector_num += cur_nr_sectors; - bytes_done += cur_nr_sectors * 512; + bytes -= cur_bytes; + bytes_done += cur_bytes; } cleanup: @@ -516,13 +514,22 @@ block_crypto_co_writev(BlockDriverState *bs, int64_t sector_num, return ret; } +static void block_crypto_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BlockCrypto *crypto = bs->opaque; + uint64_t sector_size = qcrypto_block_get_sector_size(crypto->block); + bs->bl.request_alignment = sector_size; /* No sub-sector I/O */ +} + static int64_t block_crypto_getlength(BlockDriverState *bs) { BlockCrypto *crypto = bs->opaque; int64_t len = bdrv_getlength(bs->file->bs); - ssize_t offset = qcrypto_block_get_payload_offset(crypto->block); + uint64_t offset = qcrypto_block_get_payload_offset(crypto->block); + assert(offset < INT64_MAX); + assert(offset < len); len -= offset; @@ -613,8 +620,9 @@ BlockDriver bdrv_crypto_luks = { .bdrv_truncate = block_crypto_truncate, .create_opts = &block_crypto_create_opts_luks, - .bdrv_co_readv = block_crypto_co_readv, - .bdrv_co_writev = block_crypto_co_writev, + .bdrv_refresh_limits = block_crypto_refresh_limits, + .bdrv_co_preadv = block_crypto_co_preadv, + .bdrv_co_pwritev = block_crypto_co_pwritev, .bdrv_getlength = block_crypto_getlength, .bdrv_get_info = block_crypto_get_info_luks, .bdrv_get_specific_info = block_crypto_get_specific_info_luks, diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 30462d4f9a..bd04e991b1 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -1,7 +1,7 @@ /* * Block Dirty Bitmap * - * Copyright (c) 2016 Red Hat. Inc + * Copyright (c) 2016-2017 Red Hat. Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,11 +38,11 @@ */ struct BdrvDirtyBitmap { QemuMutex *mutex; - HBitmap *bitmap; /* Dirty sector bitmap implementation */ + HBitmap *bitmap; /* Dirty bitmap implementation */ HBitmap *meta; /* Meta dirty bitmap */ BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ char *name; /* Optional non-empty unique ID */ - int64_t size; /* Size of the bitmap (Number of sectors) */ + int64_t size; /* Size of the bitmap, in bytes */ bool disabled; /* Bitmap is disabled. It ignores all writes to the device */ int active_iterators; /* How many iterators are active */ @@ -115,17 +115,14 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, { int64_t bitmap_size; BdrvDirtyBitmap *bitmap; - uint32_t sector_granularity; - assert((granularity & (granularity - 1)) == 0); + assert(is_power_of_2(granularity) && granularity >= BDRV_SECTOR_SIZE); if (name && bdrv_find_dirty_bitmap(bs, name)) { error_setg(errp, "Bitmap already exists: %s", name); return NULL; } - sector_granularity = granularity >> BDRV_SECTOR_BITS; - assert(sector_granularity); - bitmap_size = bdrv_nb_sectors(bs); + bitmap_size = bdrv_getlength(bs); if (bitmap_size < 0) { error_setg_errno(errp, -bitmap_size, "could not get length of device"); errno = -bitmap_size; @@ -133,7 +130,7 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, } bitmap = g_new0(BdrvDirtyBitmap, 1); bitmap->mutex = &bs->dirty_bitmap_mutex; - bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity)); + bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(granularity)); bitmap->size = bitmap_size; bitmap->name = g_strdup(name); bitmap->disabled = false; @@ -173,45 +170,6 @@ void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap) qemu_mutex_unlock(bitmap->mutex); } -int bdrv_dirty_bitmap_get_meta_locked(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, int64_t sector, - int nb_sectors) -{ - uint64_t i; - int sectors_per_bit = 1 << hbitmap_granularity(bitmap->meta); - - /* To optimize: we can make hbitmap to internally check the range in a - * coarse level, or at least do it word by word. */ - for (i = sector; i < sector + nb_sectors; i += sectors_per_bit) { - if (hbitmap_get(bitmap->meta, i)) { - return true; - } - } - return false; -} - -int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, int64_t sector, - int nb_sectors) -{ - bool dirty; - - qemu_mutex_lock(bitmap->mutex); - dirty = bdrv_dirty_bitmap_get_meta_locked(bs, bitmap, sector, nb_sectors); - qemu_mutex_unlock(bitmap->mutex); - - return dirty; -} - -void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, int64_t sector, - int nb_sectors) -{ - qemu_mutex_lock(bitmap->mutex); - hbitmap_reset(bitmap->meta, sector, nb_sectors); - qemu_mutex_unlock(bitmap->mutex); -} - int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap) { return bitmap->size; @@ -341,17 +299,16 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, * Truncates _all_ bitmaps attached to a BDS. * Called with BQL taken. */ -void bdrv_dirty_bitmap_truncate(BlockDriverState *bs) +void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) { BdrvDirtyBitmap *bitmap; - uint64_t size = bdrv_nb_sectors(bs); bdrv_dirty_bitmaps_lock(bs); QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { assert(!bdrv_dirty_bitmap_frozen(bitmap)); assert(!bitmap->active_iterators); - hbitmap_truncate(bitmap->bitmap, size); - bitmap->size = size; + hbitmap_truncate(bitmap->bitmap, bytes); + bitmap->size = bytes; } bdrv_dirty_bitmaps_unlock(bs); } @@ -461,7 +418,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1); BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1); - info->count = bdrv_get_dirty_count(bm) << BDRV_SECTOR_BITS; + info->count = bdrv_get_dirty_count(bm); info->granularity = bdrv_dirty_bitmap_granularity(bm); info->has_name = !!bm->name; info->name = g_strdup(bm->name); @@ -476,13 +433,13 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) } /* Called within bdrv_dirty_bitmap_lock..unlock */ -int bdrv_get_dirty_locked(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, - int64_t sector) +bool bdrv_get_dirty_locked(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + int64_t offset) { if (bitmap) { - return hbitmap_get(bitmap->bitmap, sector); + return hbitmap_get(bitmap->bitmap, offset); } else { - return 0; + return false; } } @@ -508,19 +465,13 @@ uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs) uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap) { - return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap); -} - -uint32_t bdrv_dirty_bitmap_meta_granularity(BdrvDirtyBitmap *bitmap) -{ - return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->meta); + return 1U << hbitmap_granularity(bitmap->bitmap); } -BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap, - uint64_t first_sector) +BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap) { BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1); - hbitmap_iter_init(&iter->hbi, bitmap->bitmap, first_sector); + hbitmap_iter_init(&iter->hbi, bitmap->bitmap, 0); iter->bitmap = bitmap; bitmap->active_iterators++; return iter; @@ -552,35 +503,35 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) /* Called within bdrv_dirty_bitmap_lock..unlock */ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int64_t nr_sectors) + int64_t offset, int64_t bytes) { assert(bdrv_dirty_bitmap_enabled(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap)); - hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); + hbitmap_set(bitmap->bitmap, offset, bytes); } void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int64_t nr_sectors) + int64_t offset, int64_t bytes) { bdrv_dirty_bitmap_lock(bitmap); - bdrv_set_dirty_bitmap_locked(bitmap, cur_sector, nr_sectors); + bdrv_set_dirty_bitmap_locked(bitmap, offset, bytes); bdrv_dirty_bitmap_unlock(bitmap); } /* Called within bdrv_dirty_bitmap_lock..unlock */ void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int64_t nr_sectors) + int64_t offset, int64_t bytes) { assert(bdrv_dirty_bitmap_enabled(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap)); - hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors); + hbitmap_reset(bitmap->bitmap, offset, bytes); } void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int64_t nr_sectors) + int64_t offset, int64_t bytes) { bdrv_dirty_bitmap_lock(bitmap); - bdrv_reset_dirty_bitmap_locked(bitmap, cur_sector, nr_sectors); + bdrv_reset_dirty_bitmap_locked(bitmap, offset, bytes); bdrv_dirty_bitmap_unlock(bitmap); } @@ -610,42 +561,42 @@ void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) } uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, - uint64_t start, uint64_t count) + uint64_t offset, uint64_t bytes) { - return hbitmap_serialization_size(bitmap->bitmap, start, count); + return hbitmap_serialization_size(bitmap->bitmap, offset, bytes); } uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap) { - return hbitmap_serialization_granularity(bitmap->bitmap); + return hbitmap_serialization_align(bitmap->bitmap); } void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, - uint8_t *buf, uint64_t start, - uint64_t count) + uint8_t *buf, uint64_t offset, + uint64_t bytes) { - hbitmap_serialize_part(bitmap->bitmap, buf, start, count); + hbitmap_serialize_part(bitmap->bitmap, buf, offset, bytes); } void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, - uint8_t *buf, uint64_t start, - uint64_t count, bool finish) + uint8_t *buf, uint64_t offset, + uint64_t bytes, bool finish) { - hbitmap_deserialize_part(bitmap->bitmap, buf, start, count, finish); + hbitmap_deserialize_part(bitmap->bitmap, buf, offset, bytes, finish); } void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, - uint64_t start, uint64_t count, + uint64_t offset, uint64_t bytes, bool finish) { - hbitmap_deserialize_zeroes(bitmap->bitmap, start, count, finish); + hbitmap_deserialize_zeroes(bitmap->bitmap, offset, bytes, finish); } void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, - uint64_t start, uint64_t count, + uint64_t offset, uint64_t bytes, bool finish) { - hbitmap_deserialize_ones(bitmap->bitmap, start, count, finish); + hbitmap_deserialize_ones(bitmap->bitmap, offset, bytes, finish); } void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap) @@ -653,8 +604,7 @@ void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap) hbitmap_deserialize_finish(bitmap->bitmap); } -void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, - int64_t nr_sectors) +void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes) { BdrvDirtyBitmap *bitmap; @@ -668,7 +618,7 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, continue; } assert(!bdrv_dirty_bitmap_readonly(bitmap)); - hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); + hbitmap_set(bitmap->bitmap, offset, bytes); } bdrv_dirty_bitmaps_unlock(bs); } @@ -676,9 +626,9 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, /** * Advance a BdrvDirtyBitmapIter to an arbitrary offset. */ -void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *iter, int64_t sector_num) +void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *iter, int64_t offset) { - hbitmap_iter_init(&iter->hbi, iter->hbi.hb, sector_num); + hbitmap_iter_init(&iter->hbi, iter->hbi.hb, offset); } int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap) diff --git a/block/io.c b/block/io.c index 4378ae4c7d..8e419070b5 100644 --- a/block/io.c +++ b/block/io.c @@ -34,6 +34,9 @@ #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ +/* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ +#define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) + static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags); @@ -945,68 +948,114 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, BlockDriver *drv = bs->drv; struct iovec iov; - QEMUIOVector bounce_qiov; + QEMUIOVector local_qiov; int64_t cluster_offset; unsigned int cluster_bytes; size_t skip_bytes; int ret; + int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, + BDRV_REQUEST_MAX_BYTES); + unsigned int progress = 0; /* FIXME We cannot require callers to have write permissions when all they * are doing is a read request. If we did things right, write permissions * would be obtained anyway, but internally by the copy-on-read code. As - * long as it is implemented here rather than in a separat filter driver, + * long as it is implemented here rather than in a separate filter driver, * the copy-on-read code doesn't have its own BdrvChild, however, for which * it could request permissions. Therefore we have to bypass the permission * system for the moment. */ // assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); /* Cover entire cluster so no additional backing file I/O is required when - * allocating cluster in the image file. + * allocating cluster in the image file. Note that this value may exceed + * BDRV_REQUEST_MAX_BYTES (even when the original read did not), which + * is one reason we loop rather than doing it all at once. */ bdrv_round_to_clusters(bs, offset, bytes, &cluster_offset, &cluster_bytes); + skip_bytes = offset - cluster_offset; trace_bdrv_co_do_copy_on_readv(bs, offset, bytes, cluster_offset, cluster_bytes); - iov.iov_len = cluster_bytes; - iov.iov_base = bounce_buffer = qemu_try_blockalign(bs, iov.iov_len); + bounce_buffer = qemu_try_blockalign(bs, + MIN(MIN(max_transfer, cluster_bytes), + MAX_BOUNCE_BUFFER)); if (bounce_buffer == NULL) { ret = -ENOMEM; goto err; } - qemu_iovec_init_external(&bounce_qiov, &iov, 1); + while (cluster_bytes) { + int64_t pnum; - ret = bdrv_driver_preadv(bs, cluster_offset, cluster_bytes, - &bounce_qiov, 0); - if (ret < 0) { - goto err; - } + ret = bdrv_is_allocated(bs, cluster_offset, + MIN(cluster_bytes, max_transfer), &pnum); + if (ret < 0) { + /* Safe to treat errors in querying allocation as if + * unallocated; we'll probably fail again soon on the + * read, but at least that will set a decent errno. + */ + pnum = MIN(cluster_bytes, max_transfer); + } - if (drv->bdrv_co_pwrite_zeroes && - buffer_is_zero(bounce_buffer, iov.iov_len)) { - /* FIXME: Should we (perhaps conditionally) be setting - * BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy - * that still correctly reads as zero? */ - ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, cluster_bytes, 0); - } else { - /* This does not change the data on the disk, it is not necessary - * to flush even in cache=writethrough mode. - */ - ret = bdrv_driver_pwritev(bs, cluster_offset, cluster_bytes, - &bounce_qiov, 0); - } + assert(skip_bytes < pnum); - if (ret < 0) { - /* It might be okay to ignore write errors for guest requests. If this - * is a deliberate copy-on-read then we don't want to ignore the error. - * Simply report it in all cases. - */ - goto err; - } + if (ret <= 0) { + /* Must copy-on-read; use the bounce buffer */ + iov.iov_base = bounce_buffer; + iov.iov_len = pnum = MIN(pnum, MAX_BOUNCE_BUFFER); + qemu_iovec_init_external(&local_qiov, &iov, 1); - skip_bytes = offset - cluster_offset; - qemu_iovec_from_buf(qiov, 0, bounce_buffer + skip_bytes, bytes); + ret = bdrv_driver_preadv(bs, cluster_offset, pnum, + &local_qiov, 0); + if (ret < 0) { + goto err; + } + + bdrv_debug_event(bs, BLKDBG_COR_WRITE); + if (drv->bdrv_co_pwrite_zeroes && + buffer_is_zero(bounce_buffer, pnum)) { + /* FIXME: Should we (perhaps conditionally) be setting + * BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy + * that still correctly reads as zero? */ + ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum, 0); + } else { + /* This does not change the data on the disk, it is not + * necessary to flush even in cache=writethrough mode. + */ + ret = bdrv_driver_pwritev(bs, cluster_offset, pnum, + &local_qiov, 0); + } + + if (ret < 0) { + /* It might be okay to ignore write errors for guest + * requests. If this is a deliberate copy-on-read + * then we don't want to ignore the error. Simply + * report it in all cases. + */ + goto err; + } + + qemu_iovec_from_buf(qiov, progress, bounce_buffer + skip_bytes, + pnum - skip_bytes); + } else { + /* Read directly into the destination */ + qemu_iovec_init(&local_qiov, qiov->niov); + qemu_iovec_concat(&local_qiov, qiov, progress, pnum - skip_bytes); + ret = bdrv_driver_preadv(bs, offset + progress, local_qiov.size, + &local_qiov, 0); + qemu_iovec_destroy(&local_qiov); + if (ret < 0) { + goto err; + } + } + + cluster_offset += pnum; + cluster_bytes -= pnum; + progress += pnum - skip_bytes; + skip_bytes = 0; + } + ret = 0; err: qemu_vfree(bounce_buffer); @@ -1212,9 +1261,6 @@ int coroutine_fn bdrv_co_readv(BdrvChild *child, int64_t sector_num, return bdrv_co_do_readv(child, sector_num, nb_sectors, qiov, 0); } -/* Maximum buffer for write zeroes fallback, in bytes */ -#define MAX_WRITE_ZEROES_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) - static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { @@ -1229,8 +1275,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int max_write_zeroes = MIN_NON_ZERO(bs->bl.max_pwrite_zeroes, INT_MAX); int alignment = MAX(bs->bl.pwrite_zeroes_alignment, bs->bl.request_alignment); - int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, - MAX_WRITE_ZEROES_BOUNCE_BUFFER); + int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); assert(alignment % bs->bl.request_alignment == 0); head = offset % alignment; @@ -1334,7 +1379,6 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, bool waited; int ret; - int64_t start_sector = offset >> BDRV_SECTOR_BITS; int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); uint64_t bytes_remaining = bytes; int max_transfer; @@ -1409,7 +1453,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE); atomic_inc(&bs->write_gen); - bdrv_set_dirty(bs, start_sector, end_sector - start_sector); + bdrv_set_dirty(bs, offset, bytes); stat64_max(&bs->wr_highest_offset, offset + bytes); @@ -1778,6 +1822,10 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, *pnum = 0; return BDRV_BLOCK_EOF; } + if (!nb_sectors) { + *pnum = 0; + return 0; + } n = total_sectors - sector_num; if (n < nb_sectors) { @@ -2438,8 +2486,7 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, ret = 0; out: atomic_inc(&bs->write_gen); - bdrv_set_dirty(bs, req.offset >> BDRV_SECTOR_BITS, - req.bytes >> BDRV_SECTOR_BITS); + bdrv_set_dirty(bs, req.offset, req.bytes); tracked_request_end(&req); bdrv_dec_in_flight(bs); return ret; diff --git a/block/mirror.c b/block/mirror.c index 6f5cb9f26c..153758ca9f 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -141,8 +141,7 @@ static void mirror_write_complete(void *opaque, int ret) if (ret < 0) { BlockErrorAction action; - bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset >> BDRV_SECTOR_BITS, - op->bytes >> BDRV_SECTOR_BITS); + bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset, op->bytes); action = mirror_error_action(s, false, -ret); if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { s->ret = ret; @@ -161,8 +160,7 @@ static void mirror_read_complete(void *opaque, int ret) if (ret < 0) { BlockErrorAction action; - bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset >> BDRV_SECTOR_BITS, - op->bytes >> BDRV_SECTOR_BITS); + bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset, op->bytes); action = mirror_error_action(s, true, -ret); if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { s->ret = ret; @@ -336,12 +334,11 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES); bdrv_dirty_bitmap_lock(s->dirty_bitmap); - offset = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE; + offset = bdrv_dirty_iter_next(s->dbi); if (offset < 0) { bdrv_set_dirty_iter(s->dbi, 0); - offset = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE; - trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap) * - BDRV_SECTOR_SIZE); + offset = bdrv_dirty_iter_next(s->dbi); + trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); assert(offset >= 0); } bdrv_dirty_bitmap_unlock(s->dirty_bitmap); @@ -362,19 +359,18 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) int64_t next_offset = offset + nb_chunks * s->granularity; int64_t next_chunk = next_offset / s->granularity; if (next_offset >= s->bdev_length || - !bdrv_get_dirty_locked(source, s->dirty_bitmap, - next_offset >> BDRV_SECTOR_BITS)) { + !bdrv_get_dirty_locked(source, s->dirty_bitmap, next_offset)) { break; } if (test_bit(next_chunk, s->in_flight_bitmap)) { break; } - next_dirty = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE; + next_dirty = bdrv_dirty_iter_next(s->dbi); if (next_dirty > next_offset || next_dirty < 0) { /* The bitmap iterator's cache is stale, refresh it */ - bdrv_set_dirty_iter(s->dbi, next_offset >> BDRV_SECTOR_BITS); - next_dirty = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE; + bdrv_set_dirty_iter(s->dbi, next_offset); + next_dirty = bdrv_dirty_iter_next(s->dbi); } assert(next_dirty == next_offset); nb_chunks++; @@ -384,8 +380,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) * calling bdrv_get_block_status_above could yield - if some blocks are * marked dirty in this window, we need to know. */ - bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, offset >> BDRV_SECTOR_BITS, - nb_chunks * sectors_per_chunk); + bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, offset, + nb_chunks * s->granularity); bdrv_dirty_bitmap_unlock(s->dirty_bitmap); bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks); @@ -616,25 +612,23 @@ static void mirror_throttle(MirrorBlockJob *s) static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) { - int64_t sector_num, end; + int64_t offset; BlockDriverState *base = s->base; BlockDriverState *bs = s->source; BlockDriverState *target_bs = blk_bs(s->target); - int ret, n; + int ret; int64_t count; - end = s->bdev_length / BDRV_SECTOR_SIZE; - if (base == NULL && !bdrv_has_zero_init(target_bs)) { if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { - bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, end); + bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); return 0; } s->initial_zeroing_ongoing = true; - for (sector_num = 0; sector_num < end; ) { - int nb_sectors = MIN(end - sector_num, - QEMU_ALIGN_DOWN(INT_MAX, s->granularity) >> BDRV_SECTOR_BITS); + for (offset = 0; offset < s->bdev_length; ) { + int bytes = MIN(s->bdev_length - offset, + QEMU_ALIGN_DOWN(INT_MAX, s->granularity)); mirror_throttle(s); @@ -650,9 +644,8 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) continue; } - mirror_do_zero_or_discard(s, sector_num * BDRV_SECTOR_SIZE, - nb_sectors * BDRV_SECTOR_SIZE, false); - sector_num += nb_sectors; + mirror_do_zero_or_discard(s, offset, bytes, false); + offset += bytes; } mirror_wait_for_all_io(s); @@ -660,10 +653,10 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) } /* First part, loop on the sectors and initialize the dirty bitmap. */ - for (sector_num = 0; sector_num < end; ) { + for (offset = 0; offset < s->bdev_length; ) { /* Just to make sure we are not exceeding int limit. */ - int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS, - end - sector_num); + int bytes = MIN(s->bdev_length - offset, + QEMU_ALIGN_DOWN(INT_MAX, s->granularity)); mirror_throttle(s); @@ -671,21 +664,16 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) return 0; } - ret = bdrv_is_allocated_above(bs, base, sector_num * BDRV_SECTOR_SIZE, - nb_sectors * BDRV_SECTOR_SIZE, &count); + ret = bdrv_is_allocated_above(bs, base, offset, bytes, &count); if (ret < 0) { return ret; } - /* TODO: Relax this once bdrv_is_allocated_above and dirty - * bitmaps no longer require sector alignment. */ - assert(QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)); - n = count >> BDRV_SECTOR_BITS; - assert(n > 0); + assert(count); if (ret == 1) { - bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n); + bdrv_set_dirty_bitmap(s->dirty_bitmap, offset, count); } - sector_num += n; + offset += count; } return 0; } @@ -796,7 +784,7 @@ static void coroutine_fn mirror_run(void *opaque) } assert(!s->dbi); - s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap, 0); + s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap); for (;;) { uint64_t delay_ns = 0; int64_t cnt, delta; @@ -811,11 +799,10 @@ static void coroutine_fn mirror_run(void *opaque) cnt = bdrv_get_dirty_count(s->dirty_bitmap); /* s->common.offset contains the number of bytes already processed so - * far, cnt is the number of dirty sectors remaining and + * far, cnt is the number of dirty bytes remaining and * s->bytes_in_flight is the number of bytes currently being * processed; together those are the current total operation length */ - s->common.len = s->common.offset + s->bytes_in_flight + - cnt * BDRV_SECTOR_SIZE; + s->common.len = s->common.offset + s->bytes_in_flight + cnt; /* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that bdrv_drain_all() returns. @@ -827,8 +814,7 @@ static void coroutine_fn mirror_run(void *opaque) s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || (cnt == 0 && s->in_flight > 0)) { - trace_mirror_yield(s, cnt * BDRV_SECTOR_SIZE, - s->buf_free_count, s->in_flight); + trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight); mirror_wait_for_io(s); continue; } else if (cnt != 0) { @@ -869,7 +855,7 @@ static void coroutine_fn mirror_run(void *opaque) * whether to switch to target check one last time if I/O has * come in the meanwhile, and if not flush the data to disk. */ - trace_mirror_before_drain(s, cnt * BDRV_SECTOR_SIZE); + trace_mirror_before_drain(s, cnt); bdrv_drained_begin(bs); cnt = bdrv_get_dirty_count(s->dirty_bitmap); @@ -888,8 +874,7 @@ static void coroutine_fn mirror_run(void *opaque) } ret = 0; - trace_mirror_before_sleep(s, cnt * BDRV_SECTOR_SIZE, - s->synced, delay_ns); + trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); if (!s->synced) { block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns); if (block_job_is_cancelled(&s->common)) { @@ -1056,6 +1041,10 @@ static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) { + if (bs->backing == NULL) { + /* we can be here after failed bdrv_append in mirror_start_job */ + return 0; + } return bdrv_co_flush(bs->backing->bs); } @@ -1073,6 +1062,11 @@ static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs, static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs, QDict *opts) { + if (bs->backing == NULL) { + /* we can be here after failed bdrv_attach_child in + * bdrv_set_backing_hd */ + return; + } bdrv_refresh_filename(bs->backing->bs); pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->backing->bs->filename); diff --git a/block/qcow.c b/block/qcow.c index f450b00cfc..9569deeaf0 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -478,7 +478,9 @@ static int get_cluster_offset(BlockDriverState *bs, for(i = 0; i < s->cluster_sectors; i++) { if (i < n_start || i >= n_end) { memset(s->cluster_data, 0x00, 512); - if (qcrypto_block_encrypt(s->crypto, start_sect + i, + if (qcrypto_block_encrypt(s->crypto, + (start_sect + i) * + BDRV_SECTOR_SIZE, s->cluster_data, BDRV_SECTOR_SIZE, NULL) < 0) { @@ -668,7 +670,8 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, } if (bs->encrypted) { assert(s->crypto); - if (qcrypto_block_decrypt(s->crypto, sector_num, buf, + if (qcrypto_block_decrypt(s->crypto, + sector_num * BDRV_SECTOR_SIZE, buf, n * BDRV_SECTOR_SIZE, NULL) < 0) { ret = -EIO; break; @@ -740,8 +743,8 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, } if (bs->encrypted) { assert(s->crypto); - if (qcrypto_block_encrypt(s->crypto, sector_num, buf, - n * BDRV_SECTOR_SIZE, NULL) < 0) { + if (qcrypto_block_encrypt(s->crypto, sector_num * BDRV_SECTOR_SIZE, + buf, n * BDRV_SECTOR_SIZE, NULL) < 0) { ret = -EIO; break; } diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 14f41d0427..f45e46cfbd 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -269,15 +269,16 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) return 0; } -/* This function returns the number of disk sectors covered by a single qcow2 - * cluster of bitmap data. */ -static uint64_t sectors_covered_by_bitmap_cluster(const BDRVQcow2State *s, - const BdrvDirtyBitmap *bitmap) +/* Return the disk size covered by a single qcow2 cluster of bitmap data. */ +static uint64_t bytes_covered_by_bitmap_cluster(const BDRVQcow2State *s, + const BdrvDirtyBitmap *bitmap) { - uint32_t sector_granularity = - bdrv_dirty_bitmap_granularity(bitmap) >> BDRV_SECTOR_BITS; + uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap); + uint64_t limit = granularity * (s->cluster_size << 3); - return (uint64_t)sector_granularity * (s->cluster_size << 3); + assert(QEMU_IS_ALIGNED(limit, + bdrv_dirty_bitmap_serialization_align(bitmap))); + return limit; } /* load_bitmap_data @@ -290,7 +291,7 @@ static int load_bitmap_data(BlockDriverState *bs, { int ret = 0; BDRVQcow2State *s = bs->opaque; - uint64_t sector, sbc; + uint64_t offset, limit; uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); uint8_t *buf = NULL; uint64_t i, tab_size = @@ -302,28 +303,28 @@ static int load_bitmap_data(BlockDriverState *bs, } buf = g_malloc(s->cluster_size); - sbc = sectors_covered_by_bitmap_cluster(s, bitmap); - for (i = 0, sector = 0; i < tab_size; ++i, sector += sbc) { - uint64_t count = MIN(bm_size - sector, sbc); + limit = bytes_covered_by_bitmap_cluster(s, bitmap); + for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) { + uint64_t count = MIN(bm_size - offset, limit); uint64_t entry = bitmap_table[i]; - uint64_t offset = entry & BME_TABLE_ENTRY_OFFSET_MASK; + uint64_t data_offset = entry & BME_TABLE_ENTRY_OFFSET_MASK; assert(check_table_entry(entry, s->cluster_size) == 0); - if (offset == 0) { + if (data_offset == 0) { if (entry & BME_TABLE_ENTRY_FLAG_ALL_ONES) { - bdrv_dirty_bitmap_deserialize_ones(bitmap, sector, count, + bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false); } else { /* No need to deserialize zeros because the dirty bitmap is * already cleared */ } } else { - ret = bdrv_pread(bs->file, offset, buf, s->cluster_size); + ret = bdrv_pread(bs->file, data_offset, buf, s->cluster_size); if (ret < 0) { goto finish; } - bdrv_dirty_bitmap_deserialize_part(bitmap, buf, sector, count, + bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count, false); } } @@ -1071,8 +1072,8 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, { int ret; BDRVQcow2State *s = bs->opaque; - int64_t sector; - uint64_t sbc; + int64_t offset; + uint64_t limit; uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); const char *bm_name = bdrv_dirty_bitmap_name(bitmap); uint8_t *buf = NULL; @@ -1095,20 +1096,25 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, return NULL; } - dbi = bdrv_dirty_iter_new(bitmap, 0); + dbi = bdrv_dirty_iter_new(bitmap); buf = g_malloc(s->cluster_size); - sbc = sectors_covered_by_bitmap_cluster(s, bitmap); - assert(DIV_ROUND_UP(bm_size, sbc) == tb_size); + limit = bytes_covered_by_bitmap_cluster(s, bitmap); + assert(DIV_ROUND_UP(bm_size, limit) == tb_size); - while ((sector = bdrv_dirty_iter_next(dbi)) != -1) { - uint64_t cluster = sector / sbc; + while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) { + uint64_t cluster = offset / limit; uint64_t end, write_size; int64_t off; - sector = cluster * sbc; - end = MIN(bm_size, sector + sbc); - write_size = - bdrv_dirty_bitmap_serialization_size(bitmap, sector, end - sector); + /* + * We found the first dirty offset, but want to write out the + * entire cluster of the bitmap that includes that offset, + * including any leading zero bits. + */ + offset = QEMU_ALIGN_DOWN(offset, limit); + end = MIN(bm_size, offset + limit); + write_size = bdrv_dirty_bitmap_serialization_size(bitmap, offset, + end - offset); assert(write_size <= s->cluster_size); off = qcow2_alloc_clusters(bs, s->cluster_size); @@ -1120,7 +1126,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, } tb[cluster] = off; - bdrv_dirty_bitmap_serialize_part(bitmap, buf, sector, end - sector); + bdrv_dirty_bitmap_serialize_part(bitmap, buf, offset, end - offset); if (write_size < s->cluster_size) { memset(buf + write_size, 0, s->cluster_size - write_size); } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index d2518d1893..0e5aec81cb 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -446,15 +446,13 @@ static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs, { if (bytes && bs->encrypted) { BDRVQcow2State *s = bs->opaque; - int64_t sector = (s->crypt_physical_offset ? + int64_t offset = (s->crypt_physical_offset ? (cluster_offset + offset_in_cluster) : - (src_cluster_offset + offset_in_cluster)) - >> BDRV_SECTOR_BITS; + (src_cluster_offset + offset_in_cluster)); assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0); assert((bytes & ~BDRV_SECTOR_MASK) == 0); assert(s->crypto); - if (qcrypto_block_encrypt(s->crypto, sector, buffer, - bytes, NULL) < 0) { + if (qcrypto_block_encrypt(s->crypto, offset, buffer, bytes, NULL) < 0) { return false; } } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 88d5a3f1ad..aa3fd6cf17 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -3181,3 +3181,25 @@ out: g_free(reftable_tmp); return ret; } + +int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size) +{ + BDRVQcow2State *s = bs->opaque; + int64_t i; + + for (i = size_to_clusters(s, size) - 1; i >= 0; i--) { + uint64_t refcount; + int ret = qcow2_get_refcount(bs, i, &refcount); + if (ret < 0) { + fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", + i, strerror(-ret)); + return ret; + } + if (refcount > 0) { + return i; + } + } + qcow2_signal_corruption(bs, true, -1, -1, + "There are no references in the refcount table."); + return -EIO; +} diff --git a/block/qcow2.c b/block/qcow2.c index 970006fc1d..f63d1831f8 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1811,7 +1811,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, if (qcrypto_block_decrypt(s->crypto, (s->crypt_physical_offset ? cluster_offset + offset_in_cluster : - offset) >> BDRV_SECTOR_BITS, + offset), cluster_data, cur_bytes, NULL) < 0) { @@ -1946,7 +1946,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, if (qcrypto_block_encrypt(s->crypto, (s->crypt_physical_offset ? cluster_offset + offset_in_cluster : - offset) >> BDRV_SECTOR_BITS, + offset), cluster_data, cur_bytes, NULL) < 0) { ret = -EIO; @@ -3107,6 +3107,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, new_l1_size = size_to_l1(s, offset); if (offset < old_length) { + int64_t last_cluster, old_file_size; if (prealloc != PREALLOC_MODE_OFF) { error_setg(errp, "Preallocation can't be used for shrinking an image"); @@ -3135,6 +3136,28 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, "Failed to discard unused refblocks"); return ret; } + + old_file_size = bdrv_getlength(bs->file->bs); + if (old_file_size < 0) { + error_setg_errno(errp, -old_file_size, + "Failed to inquire current file length"); + return old_file_size; + } + last_cluster = qcow2_get_last_cluster(bs, old_file_size); + if (last_cluster < 0) { + error_setg_errno(errp, -last_cluster, + "Failed to find the last cluster"); + return last_cluster; + } + if ((last_cluster + 1) * s->cluster_size < old_file_size) { + ret = bdrv_truncate(bs->file, (last_cluster + 1) * s->cluster_size, + PREALLOC_MODE_OFF, NULL); + if (ret < 0) { + warn_report("Failed to truncate the tail of the image: %s", + strerror(-ret)); + ret = 0; + } + } } else { ret = qcow2_grow_l1_table(bs, new_l1_size, true); if (ret < 0) { @@ -3167,7 +3190,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (old_file_size < 0) { error_setg_errno(errp, -old_file_size, "Failed to inquire current file length"); - return ret; + return old_file_size; } nb_new_data_clusters = DIV_ROUND_UP(offset - old_length, @@ -3196,7 +3219,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (allocation_start < 0) { error_setg_errno(errp, -allocation_start, "Failed to resize refcount structures"); - return -allocation_start; + return allocation_start; } clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start, @@ -3673,20 +3696,19 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, */ required = virtual_size; } else { - int cluster_sectors = cluster_size / BDRV_SECTOR_SIZE; - int64_t sector_num; + int64_t offset; int pnum = 0; - for (sector_num = 0; - sector_num < ssize / BDRV_SECTOR_SIZE; - sector_num += pnum) { - int nb_sectors = MIN(ssize / BDRV_SECTOR_SIZE - sector_num, - BDRV_REQUEST_MAX_SECTORS); + for (offset = 0; offset < ssize; + offset += pnum * BDRV_SECTOR_SIZE) { + int nb_sectors = MIN(ssize - offset, + BDRV_REQUEST_MAX_BYTES) / BDRV_SECTOR_SIZE; BlockDriverState *file; int64_t ret; ret = bdrv_get_block_status_above(in_bs, NULL, - sector_num, nb_sectors, + offset >> BDRV_SECTOR_BITS, + nb_sectors, &pnum, &file); if (ret < 0) { error_setg_errno(&local_err, -ret, @@ -3699,12 +3721,11 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, } else if ((ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED)) == (BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED)) { /* Extend pnum to end of cluster for next iteration */ - pnum = ROUND_UP(sector_num + pnum, cluster_sectors) - - sector_num; + pnum = (ROUND_UP(offset + pnum * BDRV_SECTOR_SIZE, + cluster_size) - offset) >> BDRV_SECTOR_BITS; /* Count clusters we've seen */ - required += (sector_num % cluster_sectors + pnum) * - BDRV_SECTOR_SIZE; + required += offset % cluster_size + pnum * BDRV_SECTOR_SIZE; } } } diff --git a/block/qcow2.h b/block/qcow2.h index 5a289a81e2..782a206ecb 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -597,6 +597,7 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, BlockDriverAmendStatusCB *status_cb, void *cb_opaque, Error **errp); int qcow2_shrink_reftable(BlockDriverState *bs); +int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size); /* qcow2-cluster.c functions */ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, diff --git a/crypto/block-luks.c b/crypto/block-luks.c index 36bc856084..d418ac30b8 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -846,8 +846,9 @@ qcrypto_block_luks_open(QCryptoBlock *block, } } + block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; block->payload_offset = luks->header.payload_offset * - QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; + block->sector_size; luks->cipher_alg = cipheralg; luks->cipher_mode = ciphermode; @@ -1240,8 +1241,9 @@ qcrypto_block_luks_create(QCryptoBlock *block, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS); + block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; block->payload_offset = luks->header.payload_offset * - QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; + block->sector_size; /* Reserve header space to match payload offset */ initfunc(block, block->payload_offset, opaque, &local_err); @@ -1397,29 +1399,33 @@ static void qcrypto_block_luks_cleanup(QCryptoBlock *block) static int qcrypto_block_luks_decrypt(QCryptoBlock *block, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp) { + assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); + assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); return qcrypto_block_decrypt_helper(block->cipher, block->niv, block->ivgen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, - startsector, buf, len, errp); + offset, buf, len, errp); } static int qcrypto_block_luks_encrypt(QCryptoBlock *block, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp) { + assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); + assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); return qcrypto_block_encrypt_helper(block->cipher, block->niv, block->ivgen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, - startsector, buf, len, errp); + offset, buf, len, errp); } diff --git a/crypto/block-qcow.c b/crypto/block-qcow.c index a456fe338b..8817d6aaa7 100644 --- a/crypto/block-qcow.c +++ b/crypto/block-qcow.c @@ -80,6 +80,7 @@ qcrypto_block_qcow_init(QCryptoBlock *block, goto fail; } + block->sector_size = QCRYPTO_BLOCK_QCOW_SECTOR_SIZE; block->payload_offset = 0; return 0; @@ -142,29 +143,33 @@ qcrypto_block_qcow_cleanup(QCryptoBlock *block) static int qcrypto_block_qcow_decrypt(QCryptoBlock *block, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp) { + assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE)); + assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE)); return qcrypto_block_decrypt_helper(block->cipher, block->niv, block->ivgen, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE, - startsector, buf, len, errp); + offset, buf, len, errp); } static int qcrypto_block_qcow_encrypt(QCryptoBlock *block, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp) { + assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE)); + assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE)); return qcrypto_block_encrypt_helper(block->cipher, block->niv, block->ivgen, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE, - startsector, buf, len, errp); + offset, buf, len, errp); } diff --git a/crypto/block.c b/crypto/block.c index c382393d9a..f206d5eea8 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -127,22 +127,22 @@ QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block, int qcrypto_block_decrypt(QCryptoBlock *block, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp) { - return block->driver->decrypt(block, startsector, buf, len, errp); + return block->driver->decrypt(block, offset, buf, len, errp); } int qcrypto_block_encrypt(QCryptoBlock *block, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp) { - return block->driver->encrypt(block, startsector, buf, len, errp); + return block->driver->encrypt(block, offset, buf, len, errp); } @@ -170,6 +170,12 @@ uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block) } +uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block) +{ + return block->sector_size; +} + + void qcrypto_block_free(QCryptoBlock *block) { if (!block) { @@ -188,13 +194,17 @@ int qcrypto_block_decrypt_helper(QCryptoCipher *cipher, size_t niv, QCryptoIVGen *ivgen, int sectorsize, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp) { uint8_t *iv; int ret = -1; + uint64_t startsector = offset / sectorsize; + + assert(QEMU_IS_ALIGNED(offset, sectorsize)); + assert(QEMU_IS_ALIGNED(len, sectorsize)); iv = niv ? g_new0(uint8_t, niv) : NULL; @@ -237,13 +247,17 @@ int qcrypto_block_encrypt_helper(QCryptoCipher *cipher, size_t niv, QCryptoIVGen *ivgen, int sectorsize, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp) { uint8_t *iv; int ret = -1; + uint64_t startsector = offset / sectorsize; + + assert(QEMU_IS_ALIGNED(offset, sectorsize)); + assert(QEMU_IS_ALIGNED(len, sectorsize)); iv = niv ? g_new0(uint8_t, niv) : NULL; diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h index 0edb810e22..41840abcec 100644 --- a/crypto/blockpriv.h +++ b/crypto/blockpriv.h @@ -36,6 +36,7 @@ struct QCryptoBlock { QCryptoHashAlgorithm kdfhash; size_t niv; uint64_t payload_offset; /* In bytes */ + uint64_t sector_size; /* In bytes */ }; struct QCryptoBlockDriver { @@ -81,7 +82,7 @@ int qcrypto_block_decrypt_helper(QCryptoCipher *cipher, size_t niv, QCryptoIVGen *ivgen, int sectorsize, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp); @@ -90,7 +91,7 @@ int qcrypto_block_encrypt_helper(QCryptoCipher *cipher, size_t niv, QCryptoIVGen *ivgen, int sectorsize, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp); diff --git a/hw/block/onenand.c b/hw/block/onenand.c index 30e40f3914..de65c9ebb9 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -520,10 +520,6 @@ static void onenand_command(OneNANDState *s) s->intstatus |= ONEN_INT; for (b = 0; b < s->blocks; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) break; diff --git a/include/block/block.h b/include/block/block.h index 3c3af462e4..d5c2731a03 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -315,8 +315,7 @@ int bdrv_commit(BlockDriverState *bs); int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt); void bdrv_register(BlockDriver *bdrv); -int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, - BlockDriverState *base, +int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, const char *backing_file_str); BlockDriverState *bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index 99abe2ce74..7e8a206239 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -544,6 +544,12 @@ struct BdrvChildRole { void (*attach)(BdrvChild *child); void (*detach)(BdrvChild *child); + + /* Notifies the parent that the filename of its child has changed (e.g. + * because the direct child was removed from the backing chain), so that it + * can update its reference. */ + int (*update_filename)(BdrvChild *child, BlockDriverState *new_base, + const char *filename, Error **errp); }; extern const BdrvChildRole child_file; @@ -1028,7 +1034,7 @@ void blk_dev_eject_request(BlockBackend *blk, bool force); bool blk_dev_is_tray_open(BlockBackend *blk); bool blk_dev_is_medium_locked(BlockBackend *blk); -void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int64_t nr_sect); +void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); bool bdrv_requests_pending(BlockDriverState *bs); void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index a79a58d2c3..3579a7597c 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -34,44 +34,33 @@ void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap); -uint32_t bdrv_dirty_bitmap_meta_granularity(BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap); const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap); int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap); DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap); void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int64_t nr_sectors); + int64_t offset, int64_t bytes); void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int64_t nr_sectors); -int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, int64_t sector, - int nb_sectors); -int bdrv_dirty_bitmap_get_meta_locked(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, int64_t sector, - int nb_sectors); -void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, int64_t sector, - int nb_sectors); + int64_t offset, int64_t bytes); BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap); -BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap, - uint64_t first_sector); +BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap); void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter); uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, - uint64_t start, uint64_t count); + uint64_t offset, uint64_t bytes); uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, - uint8_t *buf, uint64_t start, - uint64_t count); + uint8_t *buf, uint64_t offset, + uint64_t bytes); void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, - uint8_t *buf, uint64_t start, - uint64_t count, bool finish); + uint8_t *buf, uint64_t offset, + uint64_t bytes, bool finish); void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, - uint64_t start, uint64_t count, + uint64_t offset, uint64_t bytes, bool finish); void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, - uint64_t start, uint64_t count, + uint64_t offset, uint64_t bytes, bool finish); void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); @@ -83,17 +72,17 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, /* Functions that require manual locking. */ void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap); -int bdrv_get_dirty_locked(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, - int64_t sector); +bool bdrv_get_dirty_locked(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + int64_t offset); void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int64_t nr_sectors); + int64_t offset, int64_t bytes); void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, - int64_t cur_sector, int64_t nr_sectors); + int64_t offset, int64_t bytes); int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); -void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t sector_num); +void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset); int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); -void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); +void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes); bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap); bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); diff --git a/include/crypto/block.h b/include/crypto/block.h index f0e543bee1..cd18f46d56 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -161,18 +161,19 @@ QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block, /** * @qcrypto_block_decrypt: * @block: the block encryption object - * @startsector: the sector from which @buf was read + * @offset: the position at which @iov was read * @buf: the buffer to decrypt * @len: the length of @buf in bytes * @errp: pointer to a NULL-initialized error object * * Decrypt @len bytes of cipher text in @buf, writing - * plain text back into @buf + * plain text back into @buf. @len and @offset must be + * a multiple of the encryption format sector size. * * Returns 0 on success, -1 on failure */ int qcrypto_block_decrypt(QCryptoBlock *block, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp); @@ -180,18 +181,19 @@ int qcrypto_block_decrypt(QCryptoBlock *block, /** * @qcrypto_block_encrypt: * @block: the block encryption object - * @startsector: the sector to which @buf will be written + * @offset: the position at which @iov will be written * @buf: the buffer to decrypt * @len: the length of @buf in bytes * @errp: pointer to a NULL-initialized error object * * Encrypt @len bytes of plain text in @buf, writing - * cipher text back into @buf + * cipher text back into @buf. @len and @offset must be + * a multiple of the encryption format sector size. * * Returns 0 on success, -1 on failure */ int qcrypto_block_encrypt(QCryptoBlock *block, - uint64_t startsector, + uint64_t offset, uint8_t *buf, size_t len, Error **errp); @@ -241,6 +243,21 @@ QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block); uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block); /** + * qcrypto_block_get_sector_size: + * @block: the block encryption object + * + * Get the size of sectors used for payload encryption. A new + * IV is used at the start of each sector. The encryption + * sector size is not required to match the sector size of the + * underlying storage. For example LUKS will always use a 512 + * byte sector size, even if the volume is on a disk with 4k + * sectors. + * + * Returns: the sector in bytes + */ +uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block); + +/** * qcrypto_block_free: * @block: the block encryption object * diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index d3a74a21fc..81e78043d1 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -159,16 +159,16 @@ bool hbitmap_get(const HBitmap *hb, uint64_t item); bool hbitmap_is_serializable(const HBitmap *hb); /** - * hbitmap_serialization_granularity: + * hbitmap_serialization_align: * @hb: HBitmap to operate on. * - * Granularity of serialization chunks, used by other serialization functions. - * For every chunk: + * Required alignment of serialization chunks, used by other serialization + * functions. For every chunk: * 1. Chunk start should be aligned to this granularity. * 2. Chunk size should be aligned too, except for last chunk (for which * start + count == hb->size) */ -uint64_t hbitmap_serialization_granularity(const HBitmap *hb); +uint64_t hbitmap_serialization_align(const HBitmap *hb); /** * hbitmap_serialization_size: diff --git a/migration/block.c b/migration/block.c index 606ad4db92..3282809583 100644 --- a/migration/block.c +++ b/migration/block.c @@ -334,7 +334,8 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov, 0, blk_mig_read_cb, blk); - bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector, nr_sectors); + bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector * BDRV_SECTOR_SIZE, + nr_sectors * BDRV_SECTOR_SIZE); aio_context_release(blk_get_aio_context(bmds->blk)); qemu_mutex_unlock_iothread(); @@ -535,13 +536,16 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, blk_mig_unlock(); } bdrv_dirty_bitmap_lock(bmds->dirty_bitmap); - if (bdrv_get_dirty_locked(bs, bmds->dirty_bitmap, sector)) { + if (bdrv_get_dirty_locked(bs, bmds->dirty_bitmap, + sector * BDRV_SECTOR_SIZE)) { if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) { nr_sectors = total_sectors - sector; } else { nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; } - bdrv_reset_dirty_bitmap_locked(bmds->dirty_bitmap, sector, nr_sectors); + bdrv_reset_dirty_bitmap_locked(bmds->dirty_bitmap, + sector * BDRV_SECTOR_SIZE, + nr_sectors * BDRV_SECTOR_SIZE); bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap); blk = g_new(BlkMigBlock, 1); @@ -672,7 +676,7 @@ static int64_t get_remaining_dirty(void) aio_context_release(blk_get_aio_context(bmds->blk)); } - return dirty << BDRV_SECTOR_BITS; + return dirty; } diff --git a/qapi/block-core.json b/qapi/block-core.json index 750bb0c77c..ab96e348e6 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2538,6 +2538,8 @@ # # @l1_shrink_free_l2_clusters: discard the l2 tables. (since 2.11) # +# @cor_write: a write due to copy-on-read (since 2.11) +# # Since: 2.9 ## { 'enum': 'BlkdebugEvent', 'prefix': 'BLKDBG', @@ -2555,7 +2557,8 @@ 'flush_to_disk', 'pwritev_rmw_head', 'pwritev_rmw_after_head', 'pwritev_rmw_tail', 'pwritev_rmw_after_tail', 'pwritev', 'pwritev_zero', 'pwritev_done', 'empty_image_prepare', - 'l1_shrink_write_table', 'l1_shrink_free_l2_clusters' ] } + 'l1_shrink_write_table', 'l1_shrink_free_l2_clusters', + 'cor_write'] } ## # @BlkdebugInjectErrorOptions: @@ -102,6 +102,7 @@ static void open_help(void) " Opens a file for subsequent use by all of the other qemu-io commands.\n" " -r, -- open file read-only\n" " -s, -- use snapshot file\n" +" -C, -- use copy-on-read\n" " -n, -- disable host cache, short for -t none\n" " -U, -- force shared permissions\n" " -k, -- use kernel AIO implementation (on Linux only)\n" @@ -120,7 +121,7 @@ static const cmdinfo_t open_cmd = { .argmin = 1, .argmax = -1, .flags = CMD_NOFILE_OK, - .args = "[-rsnkU] [-t cache] [-d discard] [-o options] [path]", + .args = "[-rsCnkU] [-t cache] [-d discard] [-o options] [path]", .oneline = "open the file specified by path", .help = open_help, }; @@ -145,7 +146,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv) QDict *opts; bool force_share = false; - while ((c = getopt(argc, argv, "snro:kt:d:U")) != -1) { + while ((c = getopt(argc, argv, "snCro:kt:d:U")) != -1) { switch (c) { case 's': flags |= BDRV_O_SNAPSHOT; @@ -154,6 +155,9 @@ static int open_f(BlockBackend *blk, int argc, char **argv) flags |= BDRV_O_NOCACHE; writethrough = false; break; + case 'C': + flags |= BDRV_O_COPY_ON_READ; + break; case 'r': readonly = 1; break; @@ -251,6 +255,7 @@ static void usage(const char *name) " -r, --read-only export read-only\n" " -s, --snapshot use snapshot file\n" " -n, --nocache disable host cache, short for -t none\n" +" -C, --copy-on-read enable copy-on-read\n" " -m, --misalign misalign allocations for O_DIRECT\n" " -k, --native-aio use kernel AIO implementation (on Linux only)\n" " -t, --cache=MODE use the given cache mode for the image\n" @@ -439,7 +444,7 @@ static QemuOptsList file_opts = { int main(int argc, char **argv) { int readonly = 0; - const char *sopt = "hVc:d:f:rsnmkt:T:U"; + const char *sopt = "hVc:d:f:rsnCmkt:T:U"; const struct option lopt[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -448,6 +453,7 @@ int main(int argc, char **argv) { "read-only", no_argument, NULL, 'r' }, { "snapshot", no_argument, NULL, 's' }, { "nocache", no_argument, NULL, 'n' }, + { "copy-on-read", no_argument, NULL, 'C' }, { "misalign", no_argument, NULL, 'm' }, { "native-aio", no_argument, NULL, 'k' }, { "discard", required_argument, NULL, 'd' }, @@ -492,6 +498,9 @@ int main(int argc, char **argv) flags |= BDRV_O_NOCACHE; writethrough = false; break; + case 'C': + flags |= BDRV_O_COPY_ON_READ; + break; case 'd': if (bdrv_parse_discard_flags(optarg, &flags) < 0) { error_report("Invalid discard option: %s", optarg); diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index d745cb4cde..18838948fa 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -287,10 +287,6 @@ class TestParallelOps(iotests.QMPTestCase): result = self.vm.qmp('block-stream', device='node6', base=self.imgs[4], job_id='stream-node6-v2') self.assert_qmp(result, 'error/class', 'GenericError') - # This fails because block-commit needs to block node6, the overlay of the 'top' image - result = self.vm.qmp('block-stream', device='node7', base=self.imgs[5], job_id='stream-node6-v3') - self.assert_qmp(result, 'error/class', 'GenericError') - # This fails because block-commit currently blocks the active layer even if it's not used result = self.vm.qmp('block-stream', device='drive0', base=self.imgs[5], job_id='stream-drive0') self.assert_qmp(result, 'error/class', 'GenericError') diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out index c6e0ac2da3..724d7b2508 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -11,7 +11,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then +./common.rc: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; else exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; @@ -50,7 +50,7 @@ read 512/512 bytes at offset 0 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then +./common.rc: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; else exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; @@ -68,7 +68,7 @@ incompatible_features 0x0 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then +./common.rc: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; else exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; @@ -91,7 +91,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then +./common.rc: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; else exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; @@ -105,7 +105,7 @@ Data may be corrupted, or further writes to the image may corrupt it. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then +./common.rc: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; else exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index a431b7f305..942485de99 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -57,7 +57,7 @@ No errors were found on the image. 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) -./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then +./common.rc: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; else exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; @@ -219,7 +219,7 @@ No errors were found on the image. 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) -./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then +./common.rc: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; else exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out index c0e753483b..05efd74d17 100644 --- a/tests/qemu-iotests/137.out +++ b/tests/qemu-iotests/137.out @@ -31,7 +31,7 @@ Cache clean interval too big Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then +./common.rc: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; else exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@"; diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index 74d7b79a0b..a3932db3de 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -27,7 +27,7 @@ disk = os.path.join(iotests.test_dir, 'disk') disk_size = 0x40000000 # 1G # regions for qemu_io: (start, count) in bytes -regions1 = ((0, 0x100000), +regions1 = ((0x0fff00, 0x10000), (0x200000, 0x100000)) regions2 = ((0x10000000, 0x20000), diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 new file mode 100755 index 0000000000..ac2b88fd78 --- /dev/null +++ b/tests/qemu-iotests/191 @@ -0,0 +1,153 @@ +#!/bin/bash +# +# Test commit block job where top has two parents +# +# 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=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +MIG_SOCKET="${TEST_DIR}/migrate" + +_cleanup() +{ + rm -f "${TEST_IMG}.mid" + rm -f "${TEST_IMG}.ovl2" + rm -f "${TEST_IMG}.ovl3" + _cleanup_test_img + _cleanup_qemu +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_unsupported_imgopts compat=0.10 +_supported_proto file +_supported_os Linux + +size=64M + +echo +echo === Preparing and starting VM === +echo + +TEST_IMG="${TEST_IMG}.base" _make_test_img $size +TEST_IMG="${TEST_IMG}.mid" _make_test_img -b "${TEST_IMG}.base" +_make_test_img -b "${TEST_IMG}.mid" +TEST_IMG="${TEST_IMG}.ovl2" _make_test_img -b "${TEST_IMG}.mid" + +$QEMU_IO -c 'write -P 0x55 1M 64k' "${TEST_IMG}.mid" | _filter_qemu_io + +qemu_comm_method="qmp" +qmp_pretty="y" + +_launch_qemu \ + -blockdev "driver=${IMGFMT},file.driver=file,file.filename=${TEST_IMG}.base,node-name=base" \ + -blockdev "driver=${IMGFMT},file.driver=file,file.filename=${TEST_IMG}.mid,node-name=mid,backing=base" \ + -blockdev "driver=${IMGFMT},file.driver=file,file.filename=${TEST_IMG},node-name=top,backing=mid" \ + -blockdev "driver=${IMGFMT},file.driver=file,file.filename=${TEST_IMG}.ovl2,node-name=top2,backing=mid" +h=$QEMU_HANDLE +_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" '^}' + +echo +echo === Perform commit job === +echo + +_send_qemu_cmd $h \ + "{ 'execute': 'block-commit', + 'arguments': { 'job-id': 'commit0', + 'device': 'top', + 'base':'$TEST_IMG.base', + 'top': '$TEST_IMG.mid' } }" \ + "BLOCK_JOB_COMPLETED" +_send_qemu_cmd $h "" "^}" + +echo +echo === Check that both top and top2 point to base now === +echo + +_send_qemu_cmd $h "{ 'execute': 'query-named-block-nodes' }" "^}" | + _filter_generated_node_ids + +_send_qemu_cmd $h "{ 'execute': 'quit' }" "^}" +wait=1 _cleanup_qemu + +_img_info +TEST_IMG="$TEST_IMG.ovl2" _img_info + + +echo +echo === Preparing and starting VM with -drive === +echo + +TEST_IMG="${TEST_IMG}.base" _make_test_img $size +TEST_IMG="${TEST_IMG}.mid" _make_test_img -b "${TEST_IMG}.base" +_make_test_img -b "${TEST_IMG}.mid" +TEST_IMG="${TEST_IMG}.ovl2" _make_test_img -b "${TEST_IMG}.mid" +TEST_IMG="${TEST_IMG}.ovl3" _make_test_img -b "${TEST_IMG}.ovl2" + +$QEMU_IO -c 'write -P 0x55 1M 64k' "${TEST_IMG}.mid" | _filter_qemu_io + +qemu_comm_method="qmp" +qmp_pretty="y" + +_launch_qemu \ + -drive "driver=${IMGFMT},file=${TEST_IMG},node-name=top,backing.node-name=mid" \ + -drive "driver=${IMGFMT},file=${TEST_IMG}.ovl3,node-name=top2,backing.backing=mid" +h=$QEMU_HANDLE +_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" '^}' + +echo +echo === Perform commit job === +echo + +_send_qemu_cmd $h \ + "{ 'execute': 'block-commit', + 'arguments': { 'job-id': 'commit0', + 'device': 'top', + 'base':'$TEST_IMG.base', + 'top': '$TEST_IMG.mid' } }" \ + "BLOCK_JOB_COMPLETED" +_send_qemu_cmd $h "" "^}" + +echo +echo === Check that both top and top2 point to base now === +echo + +_send_qemu_cmd $h "{ 'execute': 'query-named-block-nodes' }" "^}" | + _filter_generated_node_ids + +_send_qemu_cmd $h "{ 'execute': 'quit' }" "^}" +wait=1 _cleanup_qemu + +_img_info +TEST_IMG="$TEST_IMG.ovl2" _img_info + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out new file mode 100644 index 0000000000..7bfcd2d5d8 --- /dev/null +++ b/tests/qemu-iotests/191.out @@ -0,0 +1,827 @@ +QA output created by 191 + +=== Preparing and starting VM === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid +Formatting 'TEST_DIR/t.IMGFMT.ovl2', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid +wrote 65536/65536 bytes at offset 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{ + "return": { + } +} + +=== Perform commit job === + +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "BLOCK_JOB_COMPLETED", + "data": { + "device": "commit0", + "len": 67108864, + "offset": 67108864, + "speed": 0, + "type": "commit" + } +} + +=== Check that both top and top2 point to base now === + +{ + "return": [ + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.base", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 397312, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.ovl2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "TEST_DIR/t.qcow2.base", + "backing-filename": "TEST_DIR/t.qcow2.base", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "top2", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "TEST_DIR/t.qcow2.base", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.ovl2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "TEST_DIR/t.qcow2.ovl2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "NODE_NAME", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.ovl2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.base", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 397312, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "TEST_DIR/t.qcow2.base", + "backing-filename": "TEST_DIR/t.qcow2.base", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "top", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "TEST_DIR/t.qcow2.base", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "TEST_DIR/t.qcow2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "NODE_NAME", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.base", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 397312, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.mid", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 397312, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "TEST_DIR/t.qcow2.base", + "backing-filename": "TEST_DIR/t.qcow2.base", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "mid", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "TEST_DIR/t.qcow2.base", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.mid", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 393216, + "filename": "TEST_DIR/t.qcow2.mid", + "format": "file", + "actual-size": 397312, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "NODE_NAME", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.mid", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.base", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 397312, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "base", + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.base", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 393216, + "filename": "TEST_DIR/t.qcow2.base", + "format": "file", + "actual-size": 397312, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "NODE_NAME", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.base", + "encryption_key_missing": false + } + ] +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "SHUTDOWN", + "data": { + "guest": false + } +} +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +backing file: TEST_DIR/t.IMGFMT.base +backing file format: IMGFMT +image: TEST_DIR/t.IMGFMT.ovl2 +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +backing file: TEST_DIR/t.IMGFMT.base +backing file format: IMGFMT + +=== Preparing and starting VM with -drive === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid +Formatting 'TEST_DIR/t.IMGFMT.ovl2', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid +Formatting 'TEST_DIR/t.IMGFMT.ovl3', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.ovl2 +wrote 65536/65536 bytes at offset 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{ + "return": { + } +} + +=== Perform commit job === + +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "BLOCK_JOB_COMPLETED", + "data": { + "device": "commit0", + "len": 67108864, + "offset": 67108864, + "speed": 0, + "type": "commit" + } +} + +=== Check that both top and top2 point to base now === + +{ + "return": [ + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.base", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 397312, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.ovl2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "TEST_DIR/t.qcow2.base", + "backing-filename": "TEST_DIR/t.qcow2.base", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "NODE_NAME", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "TEST_DIR/t.qcow2.base", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.ovl2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "TEST_DIR/t.qcow2.ovl2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "NODE_NAME", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.ovl2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.base", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 397312, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.ovl2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "TEST_DIR/t.qcow2.base", + "backing-filename": "TEST_DIR/t.qcow2.base", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.ovl3", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "TEST_DIR/t.qcow2.ovl2", + "backing-filename": "TEST_DIR/t.qcow2.ovl2", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "top2", + "backing_file_depth": 2, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "TEST_DIR/t.qcow2.ovl2", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.ovl3", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "TEST_DIR/t.qcow2.ovl3", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "NODE_NAME", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.ovl3", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.base", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 397312, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "NODE_NAME", + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.base", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 393216, + "filename": "TEST_DIR/t.qcow2.base", + "format": "file", + "actual-size": 397312, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "NODE_NAME", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2.base", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2.base", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 397312, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 67108864, + "filename": "TEST_DIR/t.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "TEST_DIR/t.qcow2.base", + "backing-filename": "TEST_DIR/t.qcow2.base", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "top", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "TEST_DIR/t.qcow2.base", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "TEST_DIR/t.qcow2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "NODE_NAME", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2", + "encryption_key_missing": false + } + ] +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "SHUTDOWN", + "data": { + "guest": false + } +} +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +backing file: TEST_DIR/t.IMGFMT.base +backing file format: IMGFMT +image: TEST_DIR/t.IMGFMT.ovl2 +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +backing file: TEST_DIR/t.IMGFMT.base +backing file format: IMGFMT +*** done diff --git a/tests/qemu-iotests/195 b/tests/qemu-iotests/195 index 05a239cbf5..e7a403ded2 100755 --- a/tests/qemu-iotests/195 +++ b/tests/qemu-iotests/195 @@ -44,15 +44,16 @@ _supported_os Linux function do_run_qemu() { - echo Testing: "$@" | _filter_imgfmt + echo Testing: "$@" $QEMU -nographic -qmp-pretty stdio -serial none "$@" echo } function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp \ - | _filter_qemu_io | _filter_generated_node_ids + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_imgfmt | _filter_qemu \ + | _filter_qmp | _filter_qemu_io \ + | _filter_generated_node_ids } size=64M diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 new file mode 100755 index 0000000000..887eb4f496 --- /dev/null +++ b/tests/qemu-iotests/197 @@ -0,0 +1,109 @@ +#!/bin/bash +# +# Test case for copy-on-read into qcow2 +# +# 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! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +TEST_WRAP="$TEST_DIR/t.wrap.qcow2" +BLKDBG_CONF="$TEST_DIR/blkdebug.conf" + +# Sanity check: our use of blkdebug fails if $TEST_DIR contains spaces +# or other problems +case "$TEST_DIR" in + *[^-_a-zA-Z0-9/]*) + _notrun "Suspicious TEST_DIR='$TEST_DIR', cowardly refusing to run" ;; +esac + +_cleanup() +{ + _cleanup_test_img + rm -f "$BLKDBG_CONF" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# Test is supported for any backing file; but we force qcow2 for our wrapper. +_supported_fmt generic +_supported_proto generic +_supported_os Linux +# LUKS support may be possible, but it complicates things. +_unsupported_fmt luks + +echo +echo '=== Copy-on-read ===' +echo + +# Prep the images +_make_test_img 4G +$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io +IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \ + _make_test_img -F "$IMGFMT" -b "$TEST_IMG" | _filter_img_create +$QEMU_IO -f qcow2 -c "write -z -u 1M 64k" "$TEST_WRAP" | _filter_qemu_io + +# Ensure that a read of two clusters, but where one is already allocated, +# does not re-write the allocated cluster +cat > "$BLKDBG_CONF" <<EOF +[inject-error] +event = "cor_write" +sector = "2048" +EOF +$QEMU_IO -c "open -C \ + -o driver=blkdebug,config=$BLKDBG_CONF,image.driver=qcow2 $TEST_WRAP" \ + -c "read -P 0 1M 128k" | _filter_qemu_io + +# Read the areas we want copied. A zero-length read should still be a +# no-op. The next read is under 2G, but aligned so that rounding to +# clusters copies more than 2G of zeroes. The final read will pick up +# the non-zero data in the same cluster. Since a 2G read may exhaust +# memory on some machines (particularly 32-bit), we skip the test if +# that fails due to memory pressure. +$QEMU_IO -f qcow2 -C -c "read 0 0" "$TEST_WRAP" | _filter_qemu_io +output=$($QEMU_IO -f qcow2 -C -c "read -P 0 1k $((2*1024*1024*1024 - 512))" \ + "$TEST_WRAP" 2>&1 | _filter_qemu_io) +case $output in + *allocate*) + _notrun "Insufficent memory to run test" ;; + *) printf '%s\n' "$output" ;; +esac +$QEMU_IO -f qcow2 -C -c "read -P 0 $((3*1024*1024*1024 + 1024)) 1k" \ + "$TEST_WRAP" | _filter_qemu_io + +# Copy-on-read is incompatible with read-only +$QEMU_IO -f qcow2 -C -r "$TEST_WRAP" 2>&1 | _filter_testdir + +# Break the backing chain, and show that images are identical, and that +# we properly copied over explicit zeros. +$QEMU_IMG rebase -u -b "" -f qcow2 "$TEST_WRAP" +$QEMU_IO -f qcow2 -c map "$TEST_WRAP" +_check_test_img +$QEMU_IMG compare -f $IMGFMT -F qcow2 "$TEST_IMG" "$TEST_WRAP" + +# success, all done +echo '*** done' +status=0 diff --git a/tests/qemu-iotests/197.out b/tests/qemu-iotests/197.out new file mode 100644 index 0000000000..52b4137d7b --- /dev/null +++ b/tests/qemu-iotests/197.out @@ -0,0 +1,26 @@ +QA output created by 197 + +=== Copy-on-read === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 +wrote 1024/1024 bytes at offset 3221225472 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.wrap.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +wrote 65536/65536 bytes at offset 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1048576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 0/0 bytes at offset 0 +0 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2147483136/2147483136 bytes at offset 1024 +2 GiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 3221226496 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +can't open device TEST_DIR/t.wrap.qcow2: Can't use copy-on-read on read-only device +2 GiB (0x80010000) bytes allocated at offset 0 bytes (0x0) +1023.938 MiB (0x3fff0000) bytes not allocated at offset 2 GiB (0x80010000) +64 KiB (0x10000) bytes allocated at offset 3 GiB (0xc0000000) +1023.938 MiB (0x3fff0000) bytes not allocated at offset 3 GiB (0xc0010000) +No errors were found on the image. +Images are identical. +*** done diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 4583a0c269..e6b6ff7a04 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -30,12 +30,9 @@ interrupt=true # by default don't output timestamps timestamp=${TIMESTAMP:=false} -# generic initialization -iam=check - _init_error() { - echo "$iam: $1" >&2 + echo "check: $1" >&2 exit 1 } @@ -60,18 +57,489 @@ fi build_root="$build_iotests/../.." -if [ -x "$build_iotests/socket_scm_helper" ] +# we need common.env +if ! . "$build_iotests/common.env" then - export SOCKET_SCM_HELPER="$build_iotests/socket_scm_helper" + _init_error "failed to source common.env (make sure the qemu-iotests are run from tests/qemu-iotests in the build tree)" fi -# if ./qemu exists, it should be prioritized and will be chosen by common.config -if [[ -z "$QEMU_PROG" && ! -x './qemu' ]] +# we need common.config +if ! . "$source_iotests/common.config" then - arch=$(uname -m 2> /dev/null) + _init_error "failed to source common.config" +fi + +_full_imgfmt_details() +{ + if [ -n "$IMGOPTS" ]; then + echo "$IMGFMT ($IMGOPTS)" + else + echo "$IMGFMT" + fi +} + +_full_platform_details() +{ + os=`uname -s` + host=`hostname -s` + kernel=`uname -r` + platform=`uname -m` + echo "$os/$platform $host $kernel" +} + +# $1 = prog to look for +set_prog_path() +{ + p=`command -v $1 2> /dev/null` + if [ -n "$p" -a -x "$p" ]; then + realpath -- "$(type -p "$p")" + else + return 1 + fi +} + +if [ -z "$TEST_DIR" ]; then + TEST_DIR=`pwd`/scratch +fi + +if [ ! -e "$TEST_DIR" ]; then + mkdir "$TEST_DIR" +fi + +diff="diff -u" +verbose=false +debug=false +group=false +xgroup=false +imgopts=false +showme=false +sortme=false +expunge=true +have_test_arg=false +cachemode=false + +tmp="${TEST_DIR}"/$$ +rm -f $tmp.list $tmp.tmp $tmp.sed + +export IMGFMT=raw +export IMGFMT_GENERIC=true +export IMGPROTO=file +export IMGOPTS="" +export CACHEMODE="writeback" +export QEMU_IO_OPTIONS="" +export QEMU_IO_OPTIONS_NO_FMT="" +export CACHEMODE_IS_DEFAULT=true +export QEMU_OPTIONS="-nodefaults -machine accel=qtest" +export VALGRIND_QEMU= +export IMGKEYSECRET= +export IMGOPTSSYNTAX=false + +# Save current tty settings, since an aborting qemu call may leave things +# screwed up +STTY_RESTORE= +if test -t 0; then + STTY_RESTORE=$(stty -g) +fi + +for r +do - if [[ -n $arch && -x "$build_root/$arch-softmmu/qemu-system-$arch" ]] + if $group then + # arg after -g + group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ +s/ .*//p +}'` + if [ -z "$group_list" ] + then + echo "Group \"$r\" is empty or not defined?" + exit 1 + fi + [ ! -s $tmp.list ] && touch $tmp.list + for t in $group_list + do + if grep -s "^$t\$" $tmp.list >/dev/null + then + : + else + echo "$t" >>$tmp.list + fi + done + group=false + continue + + elif $xgroup + then + # arg after -x + # Populate $tmp.list with all tests + awk '/^[0-9]{3,}/ {print $1}' "${source_iotests}/group" > $tmp.list 2>/dev/null + group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ +s/ .*//p +}'` + if [ -z "$group_list" ] + then + echo "Group \"$r\" is empty or not defined?" + exit 1 + fi + numsed=0 + rm -f $tmp.sed + for t in $group_list + do + if [ $numsed -gt 100 ] + then + sed -f $tmp.sed <$tmp.list >$tmp.tmp + mv $tmp.tmp $tmp.list + numsed=0 + rm -f $tmp.sed + fi + echo "/^$t\$/d" >>$tmp.sed + numsed=`expr $numsed + 1` + done + sed -f $tmp.sed <$tmp.list >$tmp.tmp + mv $tmp.tmp $tmp.list + xgroup=false + continue + + elif $imgopts + then + IMGOPTS="$r" + imgopts=false + continue + elif $cachemode + then + CACHEMODE="$r" + CACHEMODE_IS_DEFAULT=false + cachemode=false + continue + fi + + xpand=true + case "$r" + in + + -\? | -h | --help) # usage + echo "Usage: $0 [options] [testlist]"' + +common options + -v verbose + -d debug + +image format options + -raw test raw (default) + -bochs test bochs + -cloop test cloop + -parallels test parallels + -qcow test qcow + -qcow2 test qcow2 + -qed test qed + -vdi test vdi + -vpc test vpc + -vhdx test vhdx + -vmdk test vmdk + -luks test luks + +image protocol options + -file test file (default) + -rbd test rbd + -sheepdog test sheepdog + -nbd test nbd + -ssh test ssh + -nfs test nfs + -vxhs test vxhs + +other options + -xdiff graphical mode diff + -nocache use O_DIRECT on backing file + -misalign misalign memory allocations + -n show me, do not run tests + -o options -o options to pass to qemu-img create/convert + -T output timestamps + -c mode cache mode + +testlist options + -g group[,group...] include tests from these groups + -x group[,group...] exclude tests from these groups + NNN include test NNN + NNN-NNN include test range (eg. 012-021) +' + exit 0 + ;; + + -raw) + IMGFMT=raw + xpand=false + ;; + + -bochs) + IMGFMT=bochs + IMGFMT_GENERIC=false + xpand=false + ;; + + -cloop) + IMGFMT=cloop + IMGFMT_GENERIC=false + xpand=false + ;; + + -parallels) + IMGFMT=parallels + IMGFMT_GENERIC=false + xpand=false + ;; + + -qcow) + IMGFMT=qcow + xpand=false + ;; + + -qcow2) + IMGFMT=qcow2 + xpand=false + ;; + + -luks) + IMGOPTSSYNTAX=true + IMGFMT=luks + IMGKEYSECRET=123456 + xpand=false + ;; + + -qed) + IMGFMT=qed + xpand=false + ;; + + -vdi) + IMGFMT=vdi + xpand=false + ;; + + -vmdk) + IMGFMT=vmdk + xpand=false + ;; + + -vpc) + IMGFMT=vpc + xpand=false + ;; + + -vhdx) + IMGFMT=vhdx + xpand=false + ;; + + -file) + IMGPROTO=file + xpand=false + ;; + + -rbd) + IMGPROTO=rbd + xpand=false + ;; + + -sheepdog) + IMGPROTO=sheepdog + xpand=false + ;; + + -nbd) + IMGPROTO=nbd + xpand=false + ;; + + -vxhs) + IMGPROTO=vxhs + xpand=false + ;; + + -ssh) + IMGPROTO=ssh + xpand=false + ;; + + -nfs) + IMGPROTO=nfs + xpand=false + ;; + + -nocache) + CACHEMODE="none" + CACHEMODE_IS_DEFAULT=false + xpand=false + ;; + + -misalign) + QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --misalign" + xpand=false + ;; + + -valgrind) + VALGRIND_QEMU='y' + xpand=false + ;; + + -g) # -g group ... pick from group file + group=true + xpand=false + ;; + + -xdiff) # graphical diff mode + xpand=false + + if [ ! -z "$DISPLAY" ] + then + command -v xdiff >/dev/null 2>&1 && diff=xdiff + command -v gdiff >/dev/null 2>&1 && diff=gdiff + command -v tkdiff >/dev/null 2>&1 && diff=tkdiff + command -v xxdiff >/dev/null 2>&1 && diff=xxdiff + fi + ;; + + -n) # show me, don't do it + showme=true + xpand=false + ;; + -o) + imgopts=true + xpand=false + ;; + -c) + cachemode=true + xpand=false + ;; + -T) # turn on timestamp output + timestamp=true + xpand=false + ;; + + -v) + verbose=true + xpand=false + ;; + -d) + debug=true + xpand=false + ;; + -x) # -x group ... exclude from group file + xgroup=true + xpand=false + ;; + '[0-9][0-9][0-9] [0-9][0-9][0-9][0-9]') + echo "No tests?" + status=1 + exit $status + ;; + + [0-9]*-[0-9]*) + eval `echo $r | sed -e 's/^/start=/' -e 's/-/ end=/'` + ;; + + [0-9]*-) + eval `echo $r | sed -e 's/^/start=/' -e 's/-//'` + end=`echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //'` + if [ -z "$end" ] + then + echo "No tests in range \"$r\"?" + status=1 + exit $status + fi + ;; + + *) + start=$r + end=$r + ;; + + esac + + # get rid of leading 0s as can be interpreted as octal + start=`echo $start | sed 's/^0*//'` + end=`echo $end | sed 's/^0*//'` + + if $xpand + then + have_test_arg=true + awk </dev/null ' +BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \ + | while read id + do + if grep -s "^$id " "$source_iotests/group" >/dev/null + then + # in group file ... OK + echo $id >>$tmp.list + else + if [ -f expunged ] && $expunge && egrep "^$id([ ]|\$)" expunged >/dev/null + then + # expunged ... will be reported, but not run, later + echo $id >>$tmp.list + else + # oops + if [ "$start" == "$end" -a "$id" == "$end" ] + then + echo "$id - unknown test" + exit 1 + else + echo "$id - unknown test, ignored" + fi + fi + fi + done || exit 1 + fi + +done + +# Set qemu-io cache mode with $CACHEMODE we have +QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --cache $CACHEMODE" + +QEMU_IO_OPTIONS_NO_FMT="$QEMU_IO_OPTIONS" +if [ "$IMGOPTSSYNTAX" != "true" ]; then + QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS -f $IMGFMT" +fi + +# Set default options for qemu-img create -o if they were not specified +if [ "$IMGFMT" == "qcow2" ] && ! (echo "$IMGOPTS" | grep "compat=" > /dev/null); then + IMGOPTS=$(_optstr_add "$IMGOPTS" "compat=1.1") +fi +if [ "$IMGFMT" == "luks" ] && ! (echo "$IMGOPTS" | grep "iter-time=" > /dev/null); then + IMGOPTS=$(_optstr_add "$IMGOPTS" "iter-time=10") +fi + +if [ -z "$SAMPLE_IMG_DIR" ]; then + SAMPLE_IMG_DIR="$source_iotests/sample_images" +fi + +export TEST_DIR +export SAMPLE_IMG_DIR + +if [ -s $tmp.list ] +then + # found some valid test numbers ... this is good + : +else + if $have_test_arg + then + # had test numbers, but none in group file ... do nothing + touch $tmp.list + else + # no test numbers, do everything from group file + sed -n -e '/^[0-9][0-9][0-9]*/s/[ ].*//p' <"$source_iotests/group" >$tmp.list + fi +fi + +# should be sort -n, but this did not work for Linux when this +# was ported from IRIX +# +list=`sort $tmp.list` +rm -f $tmp.list $tmp.tmp $tmp.sed + +if [ -z "$QEMU_PROG" ] +then + if [ -x "$build_iotests/qemu" ]; then + export QEMU_PROG="$build_iotests/qemu" + elif [ -x "$build_root/$arch-softmmu/qemu-system-$arch" ]; then export QEMU_PROG="$build_root/$arch-softmmu/qemu-system-$arch" else pushd "$build_root" > /dev/null @@ -84,52 +552,67 @@ then fi done popd > /dev/null + [ "$QEMU_PROG" = "" ] && _init_error "qemu not found" fi fi +export QEMU_PROG=$(realpath -- "$(type -p "$QEMU_PROG")") -if [[ -z $QEMU_IMG_PROG && -x "$build_root/qemu-img" && ! -x './qemu-img' ]] -then - export QEMU_IMG_PROG="$build_root/qemu-img" +if [ -z "$QEMU_IMG_PROG" ]; then + if [ -x "$build_iotests/qemu-img" ]; then + export QEMU_IMG_PROG="$build_iotests/qemu-img" + elif [ -x "$build_root/qemu-img" ]; then + export QEMU_IMG_PROG="$build_root/qemu-img" + else + _init_error "qemu-img not found" + fi fi +export QEMU_IMG_PROG=$(realpath -- "$(type -p "$QEMU_IMG_PROG")") -if [[ -z $QEMU_IO_PROG && -x "$build_root/qemu-io" && ! -x './qemu-io' ]] -then - export QEMU_IO_PROG="$build_root/qemu-io" +if [ -z "$QEMU_IO_PROG" ]; then + if [ -x "$build_iotests/qemu-io" ]; then + export QEMU_IO_PROG="$build_iotests/qemu-io" + elif [ -x "$build_root/qemu-io" ]; then + export QEMU_IO_PROG="$build_root/qemu-io" + else + _init_error "qemu-io not found" + fi fi +export QEMU_IO_PROG=$(realpath -- "$(type -p "$QEMU_IO_PROG")") -if [[ -z $QEMU_NBD_PROG && -x "$build_root/qemu-nbd" && ! -x './qemu-nbd' ]] -then - export QEMU_NBD_PROG="$build_root/qemu-nbd" +if [ -z $QEMU_NBD_PROG ]; then + if [ -x "$build_iotests/qemu-nbd" ]; then + export QEMU_NBD_PROG="$build_iotests/qemu-nbd" + elif [ -x "$build_root/qemu-nbd" ]; then + export QEMU_NBD_PROG="$build_root/qemu-nbd" + else + _init_error "qemu-nbd not found" + fi fi +export QEMU_NBD_PROG=$(realpath -- "$(type -p "$QEMU_NBD_PROG")") -# we need common.env -if ! . "$build_iotests/common.env" -then - _init_error "failed to source common.env (make sure the qemu-iotests are run from tests/qemu-iotests in the build tree)" +if [ -z "$QEMU_VXHS_PROG" ]; then + export QEMU_VXHS_PROG="`set_prog_path qnio_server`" fi -# we need common.config -if ! . "$source_iotests/common.config" +if [ -x "$build_iotests/socket_scm_helper" ] then - _init_error "failed to source common.config" + export SOCKET_SCM_HELPER="$build_iotests/socket_scm_helper" fi -# we need common.rc -if ! . "$source_iotests/common.rc" -then - _init_error "failed to source common.rc" +default_machine=$($QEMU_PROG -machine help | sed -n '/(default)/ s/ .*//p') +default_alias_machine=$($QEMU_PROG -machine help | \ + sed -n "/(alias of $default_machine)/ { s/ .*//p; q; }") +if [[ "$default_alias_machine" ]]; then + default_machine="$default_alias_machine" fi -# we need common -. "$source_iotests/common" +export QEMU_DEFAULT_MACHINE="$default_machine" TIMESTAMP_FILE=check.time-$IMGPROTO-$IMGFMT -tmp="${TEST_DIR}"/$$ - _wallclock() { - date "+%H %M %S" | $AWK_PROG '{ print $1*3600 + $2*60 + $3 }' + date "+%H %M %S" | awk '{ print $1*3600 + $2*60 + $3 }' } _timestamp() @@ -140,12 +623,6 @@ _timestamp() _wrapup() { - # for hangcheck ... - # remove files that were used by hangcheck - # - [ -f "${TEST_DIR}"/check.pid ] && rm -rf "${TEST_DIR}"/check.pid - [ -f "${TEST_DIR}"/check.sts ] && rm -rf "${TEST_DIR}"/check.sts - if $showme then : @@ -154,7 +631,7 @@ _wrapup() if [ -f $TIMESTAMP_FILE -a -f $tmp.time ] then cat $TIMESTAMP_FILE $tmp.time \ - | $AWK_PROG ' + | awk ' { t[$1] = $2 } END { if (NR > 0) { for (i in t) print i " " t[i] @@ -194,6 +671,9 @@ END { if (NR > 0) { needwrap=false fi + if test -n "$STTY_RESTORE"; then + stty $STTY_RESTORE + fi rm -f "${TEST_DIR}"/*.out "${TEST_DIR}"/*.err "${TEST_DIR}"/*.time rm -f "${TEST_DIR}"/check.pid "${TEST_DIR}"/check.sts rm -f $tmp.* @@ -201,24 +681,6 @@ END { if (NR > 0) { trap "_wrapup; exit \$status" 0 1 2 3 15 -# for hangcheck ... -# Save pid of check in a well known place, so that hangcheck can be sure it -# has the right pid (getting the pid from ps output is not reliable enough). -# -rm -rf "${TEST_DIR}"/check.pid -echo $$ > "${TEST_DIR}"/check.pid - -# for hangcheck ... -# Save the status of check in a well known place, so that hangcheck can be -# sure to know where check is up to (getting test number from ps output is -# not reliable enough since the trace stuff has been introduced). -# -rm -rf "${TEST_DIR}"/check.sts -echo "preamble" > "${TEST_DIR}"/check.sts - -# don't leave old full output behind on a clean run -rm -f check.full - [ -f $TIMESTAMP_FILE ] || touch $TIMESTAMP_FILE FULL_IMGFMT_DETAILS=`_full_imgfmt_details` @@ -276,9 +738,6 @@ do fi rm -f core $seq.notrun - # for hangcheck ... - echo "$seq" > "${TEST_DIR}"/check.sts - start=`_wallclock` $timestamp && printf %s " [$(date "+%T")]" diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common deleted file mode 100644 index d34c11c056..0000000000 --- a/tests/qemu-iotests/common +++ /dev/null @@ -1,459 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2009 Red Hat, Inc. -# Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. -# -# 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. -# -# This program is distributed in the hope that it would 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/>. -# -# -# common procedures for QA scripts -# - -_setenvironment() -{ - MSGVERB="text:action" - export MSGVERB -} - -rm -f "$OUTPUT_DIR/$iam.out" -_setenvironment - -check=${check-true} - -diff="diff -u" -verbose=false -debug=false -group=false -xgroup=false -imgopts=false -showme=false -sortme=false -expunge=true -have_test_arg=false -randomize=false -cachemode=false -rm -f $tmp.list $tmp.tmp $tmp.sed - -export IMGFMT=raw -export IMGFMT_GENERIC=true -export IMGPROTO=file -export IMGOPTS="" -export CACHEMODE="writeback" -export QEMU_IO_OPTIONS="" -export QEMU_IO_OPTIONS_NO_FMT="" -export CACHEMODE_IS_DEFAULT=true -export QEMU_OPTIONS="-nodefaults -machine accel=qtest" -export VALGRIND_QEMU= -export IMGKEYSECRET= -export IMGOPTSSYNTAX=false - -for r -do - - if $group - then - # arg after -g - group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ -s/ .*//p -}'` - if [ -z "$group_list" ] - then - echo "Group \"$r\" is empty or not defined?" - exit 1 - fi - [ ! -s $tmp.list ] && touch $tmp.list - for t in $group_list - do - if grep -s "^$t\$" $tmp.list >/dev/null - then - : - else - echo "$t" >>$tmp.list - fi - done - group=false - continue - - elif $xgroup - then - # arg after -x - # Populate $tmp.list with all tests - awk '/^[0-9]{3,}/ {print $1}' "${source_iotests}/group" > $tmp.list 2>/dev/null - group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ -s/ .*//p -}'` - if [ -z "$group_list" ] - then - echo "Group \"$r\" is empty or not defined?" - exit 1 - fi - numsed=0 - rm -f $tmp.sed - for t in $group_list - do - if [ $numsed -gt 100 ] - then - sed -f $tmp.sed <$tmp.list >$tmp.tmp - mv $tmp.tmp $tmp.list - numsed=0 - rm -f $tmp.sed - fi - echo "/^$t\$/d" >>$tmp.sed - numsed=`expr $numsed + 1` - done - sed -f $tmp.sed <$tmp.list >$tmp.tmp - mv $tmp.tmp $tmp.list - xgroup=false - continue - - elif $imgopts - then - IMGOPTS="$r" - imgopts=false - continue - elif $cachemode - then - CACHEMODE="$r" - CACHEMODE_IS_DEFAULT=false - cachemode=false - continue - fi - - xpand=true - case "$r" - in - - -\? | -h | --help) # usage - echo "Usage: $0 [options] [testlist]"' - -common options - -v verbose - -d debug - -image format options - -raw test raw (default) - -bochs test bochs - -cloop test cloop - -parallels test parallels - -qcow test qcow - -qcow2 test qcow2 - -qed test qed - -vdi test vdi - -vpc test vpc - -vhdx test vhdx - -vmdk test vmdk - -luks test luks - -image protocol options - -file test file (default) - -rbd test rbd - -sheepdog test sheepdog - -nbd test nbd - -ssh test ssh - -nfs test nfs - -vxhs test vxhs - -other options - -xdiff graphical mode diff - -nocache use O_DIRECT on backing file - -misalign misalign memory allocations - -n show me, do not run tests - -o options -o options to pass to qemu-img create/convert - -T output timestamps - -r randomize test order - -c mode cache mode - -testlist options - -g group[,group...] include tests from these groups - -x group[,group...] exclude tests from these groups - NNN include test NNN - NNN-NNN include test range (eg. 012-021) -' - exit 0 - ;; - - -raw) - IMGFMT=raw - xpand=false - ;; - - -bochs) - IMGFMT=bochs - IMGFMT_GENERIC=false - xpand=false - ;; - - -cloop) - IMGFMT=cloop - IMGFMT_GENERIC=false - xpand=false - ;; - - -parallels) - IMGFMT=parallels - IMGFMT_GENERIC=false - xpand=false - ;; - - -qcow) - IMGFMT=qcow - xpand=false - ;; - - -qcow2) - IMGFMT=qcow2 - xpand=false - ;; - - -luks) - IMGOPTSSYNTAX=true - IMGFMT=luks - IMGKEYSECRET=123456 - xpand=false - ;; - - -qed) - IMGFMT=qed - xpand=false - ;; - - -vdi) - IMGFMT=vdi - xpand=false - ;; - - -vmdk) - IMGFMT=vmdk - xpand=false - ;; - - -vpc) - IMGFMT=vpc - xpand=false - ;; - - -vhdx) - IMGFMT=vhdx - xpand=false - ;; - - -file) - IMGPROTO=file - xpand=false - ;; - - -rbd) - IMGPROTO=rbd - xpand=false - ;; - - -sheepdog) - IMGPROTO=sheepdog - xpand=false - ;; - - -nbd) - IMGPROTO=nbd - xpand=false - ;; - - -vxhs) - IMGPROTO=vxhs - xpand=false - ;; - - -ssh) - IMGPROTO=ssh - xpand=false - ;; - - -nfs) - IMGPROTO=nfs - xpand=false - ;; - - -nocache) - CACHEMODE="none" - CACHEMODE_IS_DEFAULT=false - xpand=false - ;; - - -misalign) - QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --misalign" - xpand=false - ;; - - -valgrind) - VALGRIND_QEMU='y' - xpand=false - ;; - - -g) # -g group ... pick from group file - group=true - xpand=false - ;; - - -xdiff) # graphical diff mode - xpand=false - - if [ ! -z "$DISPLAY" ] - then - command -v xdiff >/dev/null 2>&1 && diff=xdiff - command -v gdiff >/dev/null 2>&1 && diff=gdiff - command -v tkdiff >/dev/null 2>&1 && diff=tkdiff - command -v xxdiff >/dev/null 2>&1 && diff=xxdiff - fi - ;; - - -n) # show me, don't do it - showme=true - xpand=false - ;; - -o) - imgopts=true - xpand=false - ;; - -c) - cachemode=true - xpand=false - ;; - -r) # randomize test order - randomize=true - xpand=false - ;; - - -T) # turn on timestamp output - timestamp=true - xpand=false - ;; - - -v) - verbose=true - xpand=false - ;; - -d) - debug=true - xpand=false - ;; - -x) # -x group ... exclude from group file - xgroup=true - xpand=false - ;; - '[0-9][0-9][0-9] [0-9][0-9][0-9][0-9]') - echo "No tests?" - status=1 - exit $status - ;; - - [0-9]*-[0-9]*) - eval `echo $r | sed -e 's/^/start=/' -e 's/-/ end=/'` - ;; - - [0-9]*-) - eval `echo $r | sed -e 's/^/start=/' -e 's/-//'` - end=`echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //'` - if [ -z "$end" ] - then - echo "No tests in range \"$r\"?" - status=1 - exit $status - fi - ;; - - *) - start=$r - end=$r - ;; - - esac - - # get rid of leading 0s as can be interpreted as octal - start=`echo $start | sed 's/^0*//'` - end=`echo $end | sed 's/^0*//'` - - if $xpand - then - have_test_arg=true - $AWK_PROG </dev/null ' -BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \ - | while read id - do - if grep -s "^$id " "$source_iotests/group" >/dev/null - then - # in group file ... OK - echo $id >>$tmp.list - else - if [ -f expunged ] && $expunge && egrep "^$id([ ]|\$)" expunged >/dev/null - then - # expunged ... will be reported, but not run, later - echo $id >>$tmp.list - else - # oops - if [ "$start" == "$end" -a "$id" == "$end" ] - then - echo "$id - unknown test" - exit 1 - else - echo "$id - unknown test, ignored" - fi - fi - fi - done || exit 1 - fi - -done - -# Set qemu-io cache mode with $CACHEMODE we have -QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --cache $CACHEMODE" - -QEMU_IO_OPTIONS_NO_FMT="$QEMU_IO_OPTIONS" -if [ "$IMGOPTSSYNTAX" != "true" ]; then - QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS -f $IMGFMT" -fi - -# Set default options for qemu-img create -o if they were not specified -_set_default_imgopts - -if [ -s $tmp.list ] -then - # found some valid test numbers ... this is good - : -else - if $have_test_arg - then - # had test numbers, but none in group file ... do nothing - touch $tmp.list - else - # no test numbers, do everything from group file - sed -n -e '/^[0-9][0-9][0-9]*/s/[ ].*//p' <"$source_iotests/group" >$tmp.list - fi -fi - -# should be sort -n, but this did not work for Linux when this -# was ported from IRIX -# -list=`sort $tmp.list` -rm -f $tmp.list $tmp.tmp $tmp.sed - -if $randomize -then - list=`echo $list | awk -f randomize.awk` -fi - -[ "$QEMU" = "" ] && _fatal "qemu not found" -[ "$QEMU_IMG" = "" ] && _fatal "qemu-img not found" -[ "$QEMU_IO" = "" ] && _fatal "qemu-io not found" - -if [ "$IMGPROTO" = "nbd" ] ; then - [ "$QEMU_NBD" = "" ] && _fatal "qemu-nbd not found" -fi diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config index e0883a0c65..cdcda54546 100644 --- a/tests/qemu-iotests/common.config +++ b/tests/qemu-iotests/common.config @@ -15,218 +15,28 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # -# -# setup and check for config parameters, and in particular -# -# EMAIL - email of the script runner. -# TEST_DIR - scratch test directory -# -# - These can be added to $HOST_CONFIG_DIR (witch default to ./config) -# below or a separate local configuration file can be used (using -# the HOST_OPTIONS variable). -# - This script is shared by the stress test system and the auto-qa -# system (includes both regression test and benchmark components). -# - this script shouldn't make any assertions about filesystem -# validity or mountedness. -# - # all tests should use a common language setting to prevent golden # output mismatches. export LANG=C PATH=".:$PATH" -HOST=`hostname -s 2> /dev/null` HOSTOS=`uname -s` +arch=`uname -m` -EMAIL=root@localhost # where auto-qa will send its status messages -export HOST_OPTIONS=${HOST_OPTIONS:=local.config} -export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"} export PWD=`pwd` -export _QEMU_HANDLE=0 - -# $1 = prog to look for, $2* = default pathnames if not found in $PATH -set_prog_path() -{ - p=`command -v $1 2> /dev/null` - if [ -n "$p" -a -x "$p" ]; then - echo $p - return 0 - fi - p=$1 - - shift - for f; do - if [ -x $f ]; then - echo $f - return 0 - fi - done - - echo "" - return 1 -} - -_fatal() -{ - echo "$*" - status=1 - exit 1 -} - -export AWK_PROG="`set_prog_path awk`" -[ "$AWK_PROG" = "" ] && _fatal "awk not found" - -export SED_PROG="`set_prog_path sed`" -[ "$SED_PROG" = "" ] && _fatal "sed not found" - -export PS_ALL_FLAGS="-ef" - -if [ -z "$QEMU_PROG" ]; then - export QEMU_PROG="`set_prog_path qemu`" -fi - -if [ -z "$QEMU_IMG_PROG" ]; then - export QEMU_IMG_PROG="`set_prog_path qemu-img`" -fi - -if [ -z "$QEMU_IO_PROG" ]; then - export QEMU_IO_PROG="`set_prog_path qemu-io`" -fi - -if [ -z "$QEMU_NBD_PROG" ]; then - export QEMU_NBD_PROG="`set_prog_path qemu-nbd`" -fi - -if [ -z "$QEMU_VXHS_PROG" ]; then - export QEMU_VXHS_PROG="`set_prog_path qnio_server`" -fi - -export QEMU_PROG=$(realpath -- "$(type -p "$QEMU_PROG")") -export QEMU_IMG_PROG=$(realpath -- "$(type -p "$QEMU_IMG_PROG")") -export QEMU_IO_PROG=$(realpath -- "$(type -p "$QEMU_IO_PROG")") -export QEMU_NBD_PROG=$(realpath -- "$(type -p "$QEMU_NBD_PROG")") - -# This program is not built as part of qemu but (possibly) provided by the -# system, so it may not be present at all -if [ -n "$QEMU_VXHS_PROG" ]; then - export QEMU_VXHS_PROG=$(realpath -- "$(type -p "$QEMU_VXHS_PROG")") -fi - -_qemu_wrapper() -{ - ( - if [ -n "${QEMU_NEED_PID}" ]; then - echo $BASHPID > "${QEMU_TEST_DIR}/qemu-${_QEMU_HANDLE}.pid" - fi - exec "$QEMU_PROG" $QEMU_OPTIONS "$@" - ) -} - -_qemu_img_wrapper() -{ - (exec "$QEMU_IMG_PROG" $QEMU_IMG_OPTIONS "$@") -} +# make sure we have a standard umask +umask 022 -_qemu_io_wrapper() +_optstr_add() { - local VALGRIND_LOGFILE="${TEST_DIR}"/$$.valgrind - local QEMU_IO_ARGS="$QEMU_IO_OPTIONS" - if [ "$IMGOPTSSYNTAX" = "true" ]; then - QEMU_IO_ARGS="--image-opts $QEMU_IO_ARGS" - if [ -n "$IMGKEYSECRET" ]; then - QEMU_IO_ARGS="--object secret,id=keysec0,data=$IMGKEYSECRET $QEMU_IO_ARGS" - fi + if [ -n "$1" ]; then + echo "$1,$2" + else + echo "$2" fi - local RETVAL - ( - if [ "${VALGRIND_QEMU}" == "y" ]; then - exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" - else - exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" - fi - ) - RETVAL=$? - if [ "${VALGRIND_QEMU}" == "y" ]; then - if [ $RETVAL == 99 ]; then - cat "${VALGRIND_LOGFILE}" - fi - rm -f "${VALGRIND_LOGFILE}" - fi - (exit $RETVAL) } -_qemu_nbd_wrapper() -{ - ( - echo $BASHPID > "${QEMU_TEST_DIR}/qemu-nbd.pid" - exec "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS "$@" - ) -} - -_qemu_vxhs_wrapper() -{ - ( - echo $BASHPID > "${TEST_DIR}/qemu-vxhs.pid" - exec "$QEMU_VXHS_PROG" $QEMU_VXHS_OPTIONS "$@" - ) -} - -export QEMU=_qemu_wrapper -export QEMU_IMG=_qemu_img_wrapper -export QEMU_IO=_qemu_io_wrapper -export QEMU_NBD=_qemu_nbd_wrapper -export QEMU_VXHS=_qemu_vxhs_wrapper - -QEMU_IMG_EXTRA_ARGS= -if [ "$IMGOPTSSYNTAX" = "true" ]; then - QEMU_IMG_EXTRA_ARGS="--image-opts $QEMU_IMG_EXTRA_ARGS" - if [ -n "$IMGKEYSECRET" ]; then - QEMU_IMG_EXTRA_ARGS="--object secret,id=keysec0,data=$IMGKEYSECRET $QEMU_IMG_EXTRA_ARGS" - fi -fi -export QEMU_IMG_EXTRA_ARGS - - -default_machine=$($QEMU -machine help | sed -n '/(default)/ s/ .*//p') -default_alias_machine=$($QEMU -machine help | \ - sed -n "/(alias of $default_machine)/ { s/ .*//p; q; }") -if [[ "$default_alias_machine" ]]; then - default_machine="$default_alias_machine" -fi - -export QEMU_DEFAULT_MACHINE="$default_machine" - -[ -f /etc/qemu-iotest.config ] && . /etc/qemu-iotest.config - -if [ -z "$TEST_DIR" ]; then - TEST_DIR=`pwd`/scratch -fi - -QEMU_TEST_DIR="${TEST_DIR}" - -if [ ! -e "$TEST_DIR" ]; then - mkdir "$TEST_DIR" -fi - -if [ ! -d "$TEST_DIR" ]; then - echo "common.config: Error: \$TEST_DIR ($TEST_DIR) is not a directory" - exit 1 -fi - -export TEST_DIR - -if [ -z "$SAMPLE_IMG_DIR" ]; then - SAMPLE_IMG_DIR="$source_iotests/sample_images" -fi - -if [ ! -d "$SAMPLE_IMG_DIR" ]; then - echo "common.config: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory" - exit 1 -fi - -export SAMPLE_IMG_DIR - # make sure this script returns success true diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 9d5442ecd9..227b37e941 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -111,6 +111,7 @@ _filter_img_create() sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ + -e 's#nbd:127.0.0.1:10810#TEST_DIR/t.IMGFMT#g' \ -e "s# encryption=off##g" \ -e "s# cluster_size=[0-9]\\+##g" \ -e "s# table_size=[0-9]\\+##g" \ diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu index 7645f1dc72..7b3052dc79 100644 --- a/tests/qemu-iotests/common.qemu +++ b/tests/qemu-iotests/common.qemu @@ -31,6 +31,7 @@ QEMU_FIFO_IN="${QEMU_TEST_DIR}/qmp-in-$$" QEMU_FIFO_OUT="${QEMU_TEST_DIR}/qmp-out-$$" QEMU_HANDLE=0 +export _QEMU_HANDLE=0 # If bash version is >= 4.1, these will be overwritten and dynamic # file descriptor values assigned. @@ -55,13 +56,13 @@ function _timed_wait_for() shift QEMU_STATUS[$h]=0 - while read -t ${QEMU_COMM_TIMEOUT} resp <&${QEMU_OUT[$h]} + while IFS= read -t ${QEMU_COMM_TIMEOUT} resp <&${QEMU_OUT[$h]} do if [ -z "${silent}" ]; then echo "${resp}" | _filter_testdir | _filter_qemu \ | _filter_qemu_io | _filter_qmp | _filter_hmp fi - grep -q "${*}" < <(echo ${resp}) + grep -q "${*}" < <(echo "${resp}") if [ $? -eq 0 ]; then return fi @@ -129,6 +130,7 @@ function _send_qemu_cmd() # $qemu_comm_method: set this variable to 'monitor' (case insensitive) # to use the QEMU HMP monitor for communication. # Otherwise, the default of QMP is used. +# $qmp_pretty: Set this variable to 'y' to enable QMP pretty printing. # $keep_stderr: Set this variable to 'y' to keep QEMU's stderr output on stderr. # If this variable is empty, stderr will be redirected to stdout. # Returns: @@ -145,7 +147,11 @@ function _launch_qemu() comm="-monitor stdio" else local qemu_comm_method="qmp" - comm="-monitor none -qmp stdio" + if [ "$qmp_pretty" = "y" ]; then + comm="-monitor none -qmp-pretty stdio" + else + comm="-monitor none -qmp stdio" + fi fi fifo_out=${QEMU_FIFO_OUT}_${_QEMU_HANDLE} @@ -192,6 +198,9 @@ function _launch_qemu() then # Don't print response, since it has version information in it silent=yes _timed_wait_for ${_QEMU_HANDLE} "capabilities" + if [ "$qmp_pretty" = "y" ]; then + silent=yes _timed_wait_for ${_QEMU_HANDLE} "^}" + fi fi QEMU_HANDLE=${_QEMU_HANDLE} let _QEMU_HANDLE++ diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 8d486dbeb4..0e8a33c696 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -40,21 +40,84 @@ poke_file() printf "$3" | dd "of=$1" bs=1 "seek=$2" conv=notrunc &>/dev/null } -# we need common.config -if [ "$iam" != "check" ] -then - if ! . ./common.config - then - echo "$iam: failed to source common.config" - exit 1 - fi + +if ! . ./common.config + then + echo "$0: failed to source common.config" + exit 1 fi -# make sure we have a standard umask -umask 022 +_qemu_wrapper() +{ + ( + if [ -n "${QEMU_NEED_PID}" ]; then + echo $BASHPID > "${QEMU_TEST_DIR}/qemu-${_QEMU_HANDLE}.pid" + fi + exec "$QEMU_PROG" $QEMU_OPTIONS "$@" + ) +} + +_qemu_img_wrapper() +{ + (exec "$QEMU_IMG_PROG" $QEMU_IMG_OPTIONS "$@") +} + +_qemu_io_wrapper() +{ + local VALGRIND_LOGFILE="${TEST_DIR}"/$$.valgrind + local QEMU_IO_ARGS="$QEMU_IO_OPTIONS" + if [ "$IMGOPTSSYNTAX" = "true" ]; then + QEMU_IO_ARGS="--image-opts $QEMU_IO_ARGS" + if [ -n "$IMGKEYSECRET" ]; then + QEMU_IO_ARGS="--object secret,id=keysec0,data=$IMGKEYSECRET $QEMU_IO_ARGS" + fi + fi + local RETVAL + ( + if [ "${VALGRIND_QEMU}" == "y" ]; then + exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" + else + exec "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" + fi + ) + RETVAL=$? + if [ "${VALGRIND_QEMU}" == "y" ]; then + if [ $RETVAL == 99 ]; then + cat "${VALGRIND_LOGFILE}" + fi + rm -f "${VALGRIND_LOGFILE}" + fi + (exit $RETVAL) +} + +_qemu_nbd_wrapper() +{ + ( + echo $BASHPID > "${QEMU_TEST_DIR}/qemu-nbd.pid" + exec "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS "$@" + ) +} + +_qemu_vxhs_wrapper() +{ + ( + echo $BASHPID > "${TEST_DIR}/qemu-vxhs.pid" + exec "$QEMU_VXHS_PROG" $QEMU_VXHS_OPTIONS "$@" + ) +} + +export QEMU=_qemu_wrapper +export QEMU_IMG=_qemu_img_wrapper +export QEMU_IO=_qemu_io_wrapper +export QEMU_NBD=_qemu_nbd_wrapper +export QEMU_VXHS=_qemu_vxhs_wrapper if [ "$IMGOPTSSYNTAX" = "true" ]; then DRIVER="driver=$IMGFMT" + QEMU_IMG_EXTRA_ARGS="--image-opts $QEMU_IMG_EXTRA_ARGS" + if [ -n "$IMGKEYSECRET" ]; then + QEMU_IMG_EXTRA_ARGS="--object secret,id=keysec0,data=$IMGKEYSECRET $QEMU_IMG_EXTRA_ARGS" + fi if [ "$IMGFMT" = "luks" ]; then DRIVER="$DRIVER,key-secret=keysec0" fi @@ -74,6 +137,7 @@ if [ "$IMGOPTSSYNTAX" = "true" ]; then TEST_IMG="$DRIVER,file.driver=$IMGPROTO,file.filename=$TEST_DIR/t.$IMGFMT" fi else + QEMU_IMG_EXTRA_ARGS= if [ "$IMGPROTO" = "file" ]; then TEST_IMG=$TEST_DIR/t.$IMGFMT elif [ "$IMGPROTO" = "nbd" ]; then @@ -94,24 +158,25 @@ else fi ORIG_TEST_IMG="$TEST_IMG" -_optstr_add() -{ - if [ -n "$1" ]; then - echo "$1,$2" - else - echo "$2" - fi -} +if [ -z "$TEST_DIR" ]; then + TEST_DIR=`pwd`/scratch +fi -_set_default_imgopts() -{ - if [ "$IMGFMT" == "qcow2" ] && ! (echo "$IMGOPTS" | grep "compat=" > /dev/null); then - IMGOPTS=$(_optstr_add "$IMGOPTS" "compat=1.1") - fi - if [ "$IMGFMT" == "luks" ] && ! (echo "$IMGOPTS" | grep "iter-time=" > /dev/null); then - IMGOPTS=$(_optstr_add "$IMGOPTS" "iter-time=10") - fi -} +QEMU_TEST_DIR="${TEST_DIR}" + +if [ ! -e "$TEST_DIR" ]; then + mkdir "$TEST_DIR" +fi + +if [ ! -d "$TEST_DIR" ]; then + echo "common.config: Error: \$TEST_DIR ($TEST_DIR) is not a directory" + exit 1 +fi + +if [ ! -d "$SAMPLE_IMG_DIR" ]; then + echo "common.config: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory" + exit 1 +fi _use_sample_img() { @@ -293,51 +358,6 @@ _img_info() done } -_get_pids_by_name() -{ - if [ $# -ne 1 ] - then - echo "Usage: _get_pids_by_name process-name" 1>&2 - exit 1 - fi - - # Algorithm ... all ps(1) variants have a time of the form MM:SS or - # HH:MM:SS before the psargs field, use this as the search anchor. - # - # Matches with $1 (process-name) occur if the first psarg is $1 - # or ends in /$1 ... the matching uses sed's regular expressions, - # so passing a regex into $1 will work. - - ps $PS_ALL_FLAGS \ - | sed -n \ - -e 's/$/ /' \ - -e 's/[ ][ ]*/ /g' \ - -e 's/^ //' \ - -e 's/^[^ ]* //' \ - -e "/[0-9]:[0-9][0-9] *[^ ]*\/$1 /s/ .*//p" \ - -e "/[0-9]:[0-9][0-9] *$1 /s/ .*//p" -} - -# fqdn for localhost -# -_get_fqdn() -{ - host=`hostname` - $NSLOOKUP_PROG $host | $AWK_PROG '{ if ($1 == "Name:") print $2 }' -} - -# check if run as root -# -_need_to_be_root() -{ - id=`id | $SED_PROG -e 's/(.*//' -e 's/.*=//'` - if [ "$id" -ne 0 ] - then - echo "Arrgh ... you need to be root (not uid=$id) to run this test" - exit 1 - fi -} - # bail out, setting up .notrun file # _notrun() @@ -473,46 +493,5 @@ _require_command() [ -x "$c" ] || _notrun "$1 utility required, skipped this test" } -_full_imgfmt_details() -{ - if [ -n "$IMGOPTS" ]; then - echo "$IMGFMT ($IMGOPTS)" - else - echo "$IMGFMT" - fi -} - -_full_platform_details() -{ - os=`uname -s` - host=`hostname -s` - kernel=`uname -r` - platform=`uname -m` - echo "$os/$platform $host $kernel" -} - -_link_out_file() -{ - if [ -z "$1" ]; then - echo Error must pass \$seq. - exit - fi - rm -f $1 - if [ "`uname`" == "IRIX64" ] || [ "`uname`" == "IRIX" ]; then - ln -s $1.irix $1 - elif [ "`uname`" == "Linux" ]; then - ln -s $1.linux $1 - else - echo Error test $seq does not run on the operating system: `uname` - exit - fi -} - -_die() -{ - echo $@ - exit 1 -} - # make sure this script returns success true diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index cdccee319e..83da427c0a 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -188,6 +188,8 @@ 188 rw auto quick 189 rw auto 190 rw auto quick +191 rw auto 192 rw auto quick 194 rw auto migration quick 195 rw auto quick +197 rw auto quick diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index 1acb353889..af41642346 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -738,15 +738,15 @@ static void test_hbitmap_meta_one(TestHBitmapData *data, const void *unused) } } -static void test_hbitmap_serialize_granularity(TestHBitmapData *data, - const void *unused) +static void test_hbitmap_serialize_align(TestHBitmapData *data, + const void *unused) { int r; hbitmap_test_init(data, L3 * 2, 3); g_assert(hbitmap_is_serializable(data->hb)); - r = hbitmap_serialization_granularity(data->hb); + r = hbitmap_serialization_align(data->hb); g_assert_cmpint(r, ==, 64 << 3); } @@ -974,8 +974,8 @@ int main(int argc, char **argv) hbitmap_test_add("/hbitmap/meta/word", test_hbitmap_meta_word); hbitmap_test_add("/hbitmap/meta/sector", test_hbitmap_meta_sector); - hbitmap_test_add("/hbitmap/serialize/granularity", - test_hbitmap_serialize_granularity); + hbitmap_test_add("/hbitmap/serialize/align", + test_hbitmap_serialize_align); hbitmap_test_add("/hbitmap/serialize/basic", test_hbitmap_serialize_basic); hbitmap_test_add("/hbitmap/serialize/part", diff --git a/util/hbitmap.c b/util/hbitmap.c index 21535cc90b..2f9d0fdbd0 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -413,14 +413,14 @@ bool hbitmap_is_serializable(const HBitmap *hb) { /* Every serialized chunk must be aligned to 64 bits so that endianness * requirements can be fulfilled on both 64 bit and 32 bit hosts. - * We have hbitmap_serialization_granularity() which converts this + * We have hbitmap_serialization_align() which converts this * alignment requirement from bitmap bits to items covered (e.g. sectors). * That value is: * 64 << hb->granularity * Since this value must not exceed UINT64_MAX, hb->granularity must be * less than 58 (== 64 - 6, where 6 is ld(64), i.e. 1 << 6 == 64). * - * In order for hbitmap_serialization_granularity() to always return a + * In order for hbitmap_serialization_align() to always return a * meaningful value, bitmaps that are to be serialized must have a * granularity of less than 58. */ @@ -437,7 +437,7 @@ bool hbitmap_get(const HBitmap *hb, uint64_t item) return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0; } -uint64_t hbitmap_serialization_granularity(const HBitmap *hb) +uint64_t hbitmap_serialization_align(const HBitmap *hb) { assert(hbitmap_is_serializable(hb)); @@ -454,7 +454,7 @@ static void serialization_chunk(const HBitmap *hb, unsigned long **first_el, uint64_t *el_count) { uint64_t last = start + count - 1; - uint64_t gran = hbitmap_serialization_granularity(hb); + uint64_t gran = hbitmap_serialization_align(hb); assert((start & (gran - 1)) == 0); assert((last >> hb->granularity) < hb->size); |