diff options
163 files changed, 3007 insertions, 1194 deletions
@@ -109,4 +109,3 @@ cscope.* tags TAGS *~ -/tests/qemu-iotests/common.env diff --git a/MAINTAINERS b/MAINTAINERS index 7ef4879..e857cf8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -608,11 +608,25 @@ S: Maintained F: hw/misc/edu.c IDE -M: Kevin Wolf <kwolf@redhat.com> -M: Stefan Hajnoczi <stefanha@redhat.com> -S: Odd Fixes +M: John Snow <jsnow@redhat.com> +L: qemu-block@nongnu.org +S: Supported F: include/hw/ide.h F: hw/ide/ +F: hw/block/block.c +F: hw/block/cdrom.c +F: hw/block/hd-geometry.c +F: tests/ide-test.c +F: tests/ahci-test.c +T: git git://github.com/jnsnow/qemu.git ide + +Floppy +M: John Snow <jsnow@redhat.com> +L: qemu-block@nongnu.org +S: Supported +F: hw/block/fdc.c +F: include/hw/block/fdc.h +T: git git://github.com/jnsnow/qemu.git ide OMAP M: Peter Maydell <peter.maydell@linaro.org> @@ -773,6 +787,18 @@ F: tests/qemu-iotests/ T: git git://repo.or.cz/qemu/kevin.git block T: git git://github.com/stefanha/qemu.git block +Block Jobs +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org +S: Supported +F: blockjob.c +F: include/block/blockjob.h +F: block/backup.c +F: block/commit.c +F: block/stream.h +F: block/mirror.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + Character Devices M: Anthony Liguori <aliguori@amazon.com> M: Paolo Bonzini <pbonzini@redhat.com> @@ -1082,20 +1108,28 @@ F: block/vmdk.c RBD M: Josh Durgin <josh.durgin@inktank.com> +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org S: Supported F: block/rbd.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block Sheepdog M: Hitoshi Mitake <mitake.hitoshi@lab.ntt.co.jp> M: Liu Yuan <namei.unix@gmail.com> +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org L: sheepdog@lists.wpkg.org S: Supported F: block/sheepdog.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block VHDX M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org S: Supported F: block/vhdx* +T: git git://github.com/codyprime/qemu-kvm-jtc.git block VDI M: Stefan Weil <sw@weilnetz.de> @@ -1110,19 +1144,42 @@ S: Supported F: block/iscsi.c NFS +M: Jeff Cody <jcody@redhat.com> M: Peter Lieven <pl@kamp.de> +L: qemu-block@nongnu.org S: Maintained F: block/nfs.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block SSH M: Richard W.M. Jones <rjones@redhat.com> +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org S: Supported F: block/ssh.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block ARCHIPELAGO M: Chrysostomos Nanakos <chris@include.gr> +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org S: Maintained F: block/archipelago.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +CURL +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org +S: Supported +F: block/curl.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +GLUSTER +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org +S: Supported +F: block/gluster.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block Bootdevice M: Gonglei <arei.gonglei@huawei.com> @@ -84,6 +84,9 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) ifdef BUILD_DOCS DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qmp-commands.txt +ifdef CONFIG_LINUX +DOCS+=kvm_stat.1 +endif ifdef CONFIG_VIRTFS DOCS+=fsdev/virtfs-proxy-helper.1 endif @@ -490,6 +493,12 @@ qemu-nbd.8: qemu-nbd.texi $(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ " GEN $@") +kvm_stat.1: scripts/kvm/kvm_stat.texi + $(call quiet-command, \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< kvm_stat.pod && \ + $(POD2MAN) --section=1 --center=" " --release=" " kvm_stat.pod > $@, \ + " GEN $@") + dvi: qemu-doc.dvi qemu-tech.dvi html: qemu-doc.html qemu-tech.html info: qemu-doc.info qemu-tech.info @@ -568,6 +568,40 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) } } +/** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz struct and return 0. + * On failure return -errno. + * @bs must not be empty. + */ +int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_probe_blocksizes) { + return drv->bdrv_probe_blocksizes(bs, bsz); + } + + return -ENOTSUP; +} + +/** + * Try to get @bs's geometry (cyls, heads, sectors). + * On success, store them in @geo struct and return 0. + * On failure return -errno. + * @bs must not be empty. + */ +int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_probe_geometry) { + return drv->bdrv_probe_geometry(bs, geo); + } + + return -ENOTSUP; +} + /* * Create a uniquely-named empty temporary file. * Return 0 upon success, otherwise a negative errno value. diff --git a/block/archipelago.c b/block/archipelago.c index a8114b5..855655c 100644 --- a/block/archipelago.c +++ b/block/archipelago.c @@ -291,7 +291,7 @@ static int qemu_archipelago_init(BDRVArchipelagoState *s) ret = qemu_archipelago_xseg_init(s); if (ret < 0) { - error_report("Cannot initialize XSEG. Aborting...\n"); + error_report("Cannot initialize XSEG. Aborting..."); goto err_exit; } @@ -645,7 +645,7 @@ static int qemu_archipelago_create_volume(Error **errp, const char *volname, target = xseg_get_target(xseg, req); if (!target) { - error_setg(errp, "Cannot get XSEG target.\n"); + error_setg(errp, "Cannot get XSEG target."); goto err_exit; } memcpy(target, volname, targetlen); @@ -889,7 +889,7 @@ static BlockAIOCB *qemu_archipelago_aio_rw(BlockDriverState *bs, return &aio_cb->common; err_exit: - error_report("qemu_archipelago_aio_rw(): I/O Error\n"); + error_report("qemu_archipelago_aio_rw(): I/O Error"); qemu_aio_unref(aio_cb); return NULL; } diff --git a/block/blkdebug.c b/block/blkdebug.c index 9ce35cd..63611e0 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -472,12 +472,14 @@ static BlockAIOCB *inject_error(BlockDriverState *bs, int error = rule->options.inject.error; struct BlkdebugAIOCB *acb; QEMUBH *bh; + bool immediately = rule->options.inject.immediately; if (rule->options.inject.once) { - QSIMPLEQ_INIT(&s->active_rules); + QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next); + remove_rule(rule); } - if (rule->options.inject.immediately) { + if (immediately) { return NULL; } diff --git a/block/block-backend.c b/block/block-backend.c index bfb0418..48b6e4c 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -892,3 +892,13 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) { return bdrv_load_vmstate(blk->bs, buf, pos, size); } + +int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) +{ + return bdrv_probe_blocksizes(blk->bs, bsz); +} + +int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) +{ + return bdrv_probe_geometry(blk->bs, geo); +} diff --git a/block/iscsi.c b/block/iscsi.c index 1fa855a..3e34b1f 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1326,7 +1326,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, goto out; } - if (iscsi_url->user != NULL) { + if (iscsi_url->user[0] != '\0') { ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd); if (ret != 0) { diff --git a/block/nbd.c b/block/nbd.c index 697c021..6634a69 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -274,6 +274,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, */ sock = nbd_establish_connection(bs, errp); if (sock < 0) { + g_free(export); return sock; } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 183177d..ed2b44d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1640,7 +1640,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, for (i = 0; i < l1_size; i++) { uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; bool l2_dirty = false; - int l2_refcount; + uint64_t l2_refcount; if (!l2_offset) { /* unallocated */ @@ -1672,9 +1672,9 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - l2_refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); - if (l2_refcount < 0) { - ret = l2_refcount; + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &l2_refcount); + if (ret < 0) { goto fail; } @@ -1707,7 +1707,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, /* For shared L2 tables, set the refcount accordingly (it is * already 1 and needs to be l2_refcount) */ ret = qcow2_update_cluster_refcount(bs, - offset >> s->cluster_bits, l2_refcount - 1, + offset >> s->cluster_bits, + refcount_diff(1, l2_refcount), false, QCOW2_DISCARD_OTHER); if (ret < 0) { qcow2_free_clusters(bs, offset, s->cluster_size, diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 9b80ca7..dc8d186 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -29,8 +29,52 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, - int64_t offset, int64_t length, - int addend, enum qcow2_discard_type type); + int64_t offset, int64_t length, uint64_t addend, + bool decrease, enum qcow2_discard_type type); + +static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index); + +static void set_refcount_ro0(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro1(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro2(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro3(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro4(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro5(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro6(void *refcount_array, uint64_t index, + uint64_t value); + + +static Qcow2GetRefcountFunc *const get_refcount_funcs[] = { + &get_refcount_ro0, + &get_refcount_ro1, + &get_refcount_ro2, + &get_refcount_ro3, + &get_refcount_ro4, + &get_refcount_ro5, + &get_refcount_ro6 +}; + +static Qcow2SetRefcountFunc *const set_refcount_funcs[] = { + &set_refcount_ro0, + &set_refcount_ro1, + &set_refcount_ro2, + &set_refcount_ro3, + &set_refcount_ro4, + &set_refcount_ro5, + &set_refcount_ro6 +}; /*********************************************************/ @@ -42,6 +86,11 @@ int qcow2_refcount_init(BlockDriverState *bs) unsigned int refcount_table_size2, i; int ret; + assert(s->refcount_order >= 0 && s->refcount_order <= 6); + + s->get_refcount = get_refcount_funcs[s->refcount_order]; + s->set_refcount = set_refcount_funcs[s->refcount_order]; + assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t)); refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); s->refcount_table = g_try_malloc(refcount_table_size2); @@ -72,6 +121,95 @@ void qcow2_refcount_close(BlockDriverState *bs) } +static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 8] >> (index % 8)) & 0x1; +} + +static void set_refcount_ro0(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 1)); + ((uint8_t *)refcount_array)[index / 8] &= ~(0x1 << (index % 8)); + ((uint8_t *)refcount_array)[index / 8] |= value << (index % 8); +} + +static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 4] >> (2 * (index % 4))) + & 0x3; +} + +static void set_refcount_ro1(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 2)); + ((uint8_t *)refcount_array)[index / 4] &= ~(0x3 << (2 * (index % 4))); + ((uint8_t *)refcount_array)[index / 4] |= value << (2 * (index % 4)); +} + +static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 2] >> (4 * (index % 2))) + & 0xf; +} + +static void set_refcount_ro2(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 4)); + ((uint8_t *)refcount_array)[index / 2] &= ~(0xf << (4 * (index % 2))); + ((uint8_t *)refcount_array)[index / 2] |= value << (4 * (index % 2)); +} + +static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index) +{ + return ((const uint8_t *)refcount_array)[index]; +} + +static void set_refcount_ro3(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 8)); + ((uint8_t *)refcount_array)[index] = value; +} + +static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index) +{ + return be16_to_cpu(((const uint16_t *)refcount_array)[index]); +} + +static void set_refcount_ro4(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 16)); + ((uint16_t *)refcount_array)[index] = cpu_to_be16(value); +} + +static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index) +{ + return be32_to_cpu(((const uint32_t *)refcount_array)[index]); +} + +static void set_refcount_ro5(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 32)); + ((uint32_t *)refcount_array)[index] = cpu_to_be32(value); +} + +static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index) +{ + return be64_to_cpu(((const uint64_t *)refcount_array)[index]); +} + +static void set_refcount_ro6(void *refcount_array, uint64_t index, + uint64_t value) +{ + ((uint64_t *)refcount_array)[index] = cpu_to_be64(value); +} + + static int load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset, void **refcount_block) @@ -87,26 +225,29 @@ static int load_refcount_block(BlockDriverState *bs, } /* - * Returns the refcount of the cluster given by its index. Any non-negative - * return value is the refcount of the cluster, negative values are -errno - * and indicate an error. + * Retrieves the refcount of the cluster given by its index and stores it in + * *refcount. Returns 0 on success and -errno on failure. */ -int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index) +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t *refcount) { BDRVQcowState *s = bs->opaque; uint64_t refcount_table_index, block_index; int64_t refcount_block_offset; int ret; - uint16_t *refcount_block; - uint16_t refcount; + void *refcount_block; refcount_table_index = cluster_index >> s->refcount_block_bits; - if (refcount_table_index >= s->refcount_table_size) + if (refcount_table_index >= s->refcount_table_size) { + *refcount = 0; return 0; + } refcount_block_offset = s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; - if (!refcount_block_offset) + if (!refcount_block_offset) { + *refcount = 0; return 0; + } if (offset_into_cluster(s, refcount_block_offset)) { qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64 @@ -116,21 +257,20 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index) } ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, - (void**) &refcount_block); + &refcount_block); if (ret < 0) { return ret; } block_index = cluster_index & (s->refcount_block_size - 1); - refcount = be16_to_cpu(refcount_block[block_index]); + *refcount = s->get_refcount(refcount_block, block_index); - ret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + ret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); if (ret < 0) { return ret; } - return refcount; + return 0; } /* @@ -169,7 +309,7 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, * Returns 0 on success or -errno in error case */ static int alloc_refcount_block(BlockDriverState *bs, - int64_t cluster_index, uint16_t **refcount_block) + int64_t cluster_index, void **refcount_block) { BDRVQcowState *s = bs->opaque; unsigned int refcount_table_index; @@ -196,7 +336,7 @@ static int alloc_refcount_block(BlockDriverState *bs, } return load_refcount_block(bs, refcount_block_offset, - (void**) refcount_block); + refcount_block); } } @@ -246,7 +386,7 @@ static int alloc_refcount_block(BlockDriverState *bs, if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) { /* Zero the new refcount block before updating it */ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, - (void**) refcount_block); + refcount_block); if (ret < 0) { goto fail_block; } @@ -256,11 +396,11 @@ static int alloc_refcount_block(BlockDriverState *bs, /* The block describes itself, need to update the cache */ int block_index = (new_block >> s->cluster_bits) & (s->refcount_block_size - 1); - (*refcount_block)[block_index] = cpu_to_be16(1); + s->set_refcount(*refcount_block, block_index, 1); } else { /* Described somewhere else. This can recurse at most twice before we * arrive at a block that describes itself. */ - ret = update_refcount(bs, new_block, s->cluster_size, 1, + ret = update_refcount(bs, new_block, s->cluster_size, 1, false, QCOW2_DISCARD_NEVER); if (ret < 0) { goto fail_block; @@ -274,7 +414,7 @@ static int alloc_refcount_block(BlockDriverState *bs, /* Initialize the new refcount block only after updating its refcount, * update_refcount uses the refcount cache itself */ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, - (void**) refcount_block); + refcount_block); if (ret < 0) { goto fail_block; } @@ -308,7 +448,7 @@ static int alloc_refcount_block(BlockDriverState *bs, return -EAGAIN; } - ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + ret = qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); if (ret < 0) { goto fail_block; } @@ -362,7 +502,7 @@ static int alloc_refcount_block(BlockDriverState *bs, s->cluster_size; uint64_t table_offset = meta_offset + blocks_clusters * s->cluster_size; uint64_t *new_table = g_try_new0(uint64_t, table_size); - uint16_t *new_blocks = g_try_malloc0(blocks_clusters * s->cluster_size); + void *new_blocks = g_try_malloc0(blocks_clusters * s->cluster_size); assert(table_size > 0 && blocks_clusters > 0); if (new_table == NULL || new_blocks == NULL) { @@ -384,7 +524,7 @@ static int alloc_refcount_block(BlockDriverState *bs, uint64_t table_clusters = size_to_clusters(s, table_size * sizeof(uint64_t)); int block = 0; for (i = 0; i < table_clusters + blocks_clusters; i++) { - new_blocks[block++] = cpu_to_be16(1); + s->set_refcount(new_blocks, block++, 1); } /* Write refcount blocks to disk */ @@ -437,7 +577,7 @@ static int alloc_refcount_block(BlockDriverState *bs, qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t), QCOW2_DISCARD_OTHER); - ret = load_refcount_block(bs, new_block, (void**) refcount_block); + ret = load_refcount_block(bs, new_block, refcount_block); if (ret < 0) { return ret; } @@ -452,7 +592,7 @@ fail_table: g_free(new_table); fail_block: if (*refcount_block != NULL) { - qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); } return ret; } @@ -527,18 +667,25 @@ found: } /* XXX: cache several refcount block clusters ? */ +/* @addend is the absolute value of the addend; if @decrease is set, @addend + * will be subtracted from the current refcount, otherwise it will be added */ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, - int64_t offset, int64_t length, int addend, enum qcow2_discard_type type) + int64_t offset, + int64_t length, + uint64_t addend, + bool decrease, + enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset; - uint16_t *refcount_block = NULL; + void *refcount_block = NULL; int64_t old_table_index = -1; int ret; #ifdef DEBUG_ALLOC2 - fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64 " addend=%d\n", - offset, length, addend); + fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64 + " addend=%s%" PRIu64 "\n", offset, length, decrease ? "-" : "", + addend); #endif if (length < 0) { return -EINVAL; @@ -546,7 +693,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, return 0; } - if (addend < 0) { + if (decrease) { qcow2_cache_set_dependency(bs, s->refcount_block_cache, s->l2_table_cache); } @@ -556,7 +703,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, for(cluster_offset = start; cluster_offset <= last; cluster_offset += s->cluster_size) { - int block_index, refcount; + int block_index; + uint64_t refcount; int64_t cluster_index = cluster_offset >> s->cluster_bits; int64_t table_index = cluster_index >> s->refcount_block_bits; @@ -564,7 +712,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, if (table_index != old_table_index) { if (refcount_block) { ret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + &refcount_block); if (ret < 0) { goto fail; } @@ -582,16 +730,23 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, /* we can update the count and save it */ block_index = cluster_index & (s->refcount_block_size - 1); - refcount = be16_to_cpu(refcount_block[block_index]); - refcount += addend; - if (refcount < 0 || refcount > 0xffff) { + refcount = s->get_refcount(refcount_block, block_index); + if (decrease ? (refcount - addend > refcount) + : (refcount + addend < refcount || + refcount + addend > s->refcount_max)) + { ret = -EINVAL; goto fail; } + if (decrease) { + refcount -= addend; + } else { + refcount += addend; + } if (refcount == 0 && cluster_index < s->free_cluster_index) { s->free_cluster_index = cluster_index; } - refcount_block[block_index] = cpu_to_be16(refcount); + s->set_refcount(refcount_block, block_index, refcount); if (refcount == 0 && s->discard_passthrough[type]) { update_refcount_discard(bs, cluster_offset, s->cluster_size); @@ -607,8 +762,7 @@ fail: /* Write last changed block to disk */ if (refcount_block) { int wret; - wret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + wret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); if (wret < 0) { return ret < 0 ? ret : wret; } @@ -620,8 +774,8 @@ fail: */ if (ret < 0) { int dummy; - dummy = update_refcount(bs, offset, cluster_offset - offset, -addend, - QCOW2_DISCARD_NEVER); + dummy = update_refcount(bs, offset, cluster_offset - offset, addend, + !decrease, QCOW2_DISCARD_NEVER); (void)dummy; } @@ -631,24 +785,26 @@ fail: /* * Increases or decreases the refcount of a given cluster. * - * If the return value is non-negative, it is the new refcount of the cluster. - * If it is negative, it is -errno and indicates an error. + * @addend is the absolute value of the addend; if @decrease is set, @addend + * will be subtracted from the current refcount, otherwise it will be added. + * + * On success 0 is returned; on failure -errno is returned. */ int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, - int addend, + uint64_t addend, bool decrease, enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; int ret; ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend, - type); + decrease, type); if (ret < 0) { return ret; } - return qcow2_get_refcount(bs, cluster_index); + return 0; } @@ -662,17 +818,17 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) { BDRVQcowState *s = bs->opaque; - uint64_t i, nb_clusters; - int refcount; + uint64_t i, nb_clusters, refcount; + int ret; nb_clusters = size_to_clusters(s, size); retry: for(i = 0; i < nb_clusters; i++) { uint64_t next_cluster_index = s->free_cluster_index++; - refcount = qcow2_get_refcount(bs, next_cluster_index); + ret = qcow2_get_refcount(bs, next_cluster_index, &refcount); - if (refcount < 0) { - return refcount; + if (ret < 0) { + return ret; } else if (refcount != 0) { goto retry; } @@ -706,7 +862,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) return offset; } - ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER); + ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); } while (ret == -EAGAIN); if (ret < 0) { @@ -720,9 +876,9 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, int nb_clusters) { BDRVQcowState *s = bs->opaque; - uint64_t cluster_index; + uint64_t cluster_index, refcount; uint64_t i; - int refcount, ret; + int ret; assert(nb_clusters >= 0); if (nb_clusters == 0) { @@ -733,17 +889,16 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, /* Check how many clusters there are free */ cluster_index = offset >> s->cluster_bits; for(i = 0; i < nb_clusters; i++) { - refcount = qcow2_get_refcount(bs, cluster_index++); - - if (refcount < 0) { - return refcount; + ret = qcow2_get_refcount(bs, cluster_index++, &refcount); + if (ret < 0) { + return ret; } else if (refcount != 0) { break; } } /* And then allocate them */ - ret = update_refcount(bs, offset, i << s->cluster_bits, 1, + ret = update_refcount(bs, offset, i << s->cluster_bits, 1, false, QCOW2_DISCARD_NEVER); } while (ret == -EAGAIN); @@ -770,12 +925,13 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) offset = s->free_byte_offset; if (offset) { - int refcount = qcow2_get_refcount(bs, offset >> s->cluster_bits); - if (refcount < 0) { - return refcount; + uint64_t refcount; + ret = qcow2_get_refcount(bs, offset >> s->cluster_bits, &refcount); + if (ret < 0) { + return ret; } - if (refcount == 0xffff) { + if (refcount == s->refcount_max) { offset = 0; } } @@ -793,7 +949,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) } assert(offset); - ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER); + ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); if (ret < 0) { return ret; } @@ -817,7 +973,7 @@ void qcow2_free_clusters(BlockDriverState *bs, int ret; BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE); - ret = update_refcount(bs, offset, size, -1, type); + ret = update_refcount(bs, offset, size, 1, true, type); if (ret < 0) { fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret)); /* TODO Remember the clusters to free them later and avoid leaking */ @@ -876,12 +1032,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int addend) { BDRVQcowState *s = bs->opaque; - uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2; + uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount; bool l1_allocated = false; int64_t old_offset, old_l2_offset; - int i, j, l1_modified = 0, nb_csectors, refcount; + int i, j, l1_modified = 0, nb_csectors; int ret; + assert(addend >= -1 && addend <= 1); + l2_table = NULL; l1_table = NULL; l1_size2 = l1_size * sizeof(uint64_t); @@ -946,7 +1104,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (addend != 0) { ret = update_refcount(bs, (offset & s->cluster_offset_mask) & ~511, - nb_csectors * 512, addend, + nb_csectors * 512, abs(addend), addend < 0, QCOW2_DISCARD_SNAPSHOT); if (ret < 0) { goto fail; @@ -976,15 +1134,16 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, break; } if (addend != 0) { - refcount = qcow2_update_cluster_refcount(bs, - cluster_index, addend, + ret = qcow2_update_cluster_refcount(bs, + cluster_index, abs(addend), addend < 0, QCOW2_DISCARD_SNAPSHOT); - } else { - refcount = qcow2_get_refcount(bs, cluster_index); + if (ret < 0) { + goto fail; + } } - if (refcount < 0) { - ret = refcount; + ret = qcow2_get_refcount(bs, cluster_index, &refcount); + if (ret < 0) { goto fail; } break; @@ -1017,13 +1176,17 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (addend != 0) { - refcount = qcow2_update_cluster_refcount(bs, l2_offset >> - s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT); - } else { - refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); + ret = qcow2_update_cluster_refcount(bs, l2_offset >> + s->cluster_bits, + abs(addend), addend < 0, + QCOW2_DISCARD_SNAPSHOT); + if (ret < 0) { + goto fail; + } } - if (refcount < 0) { - ret = refcount; + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { goto fail; } else if (refcount == 1) { l2_offset |= QCOW_OFLAG_COPIED; @@ -1068,6 +1231,63 @@ fail: /* refcount checking functions */ +static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries) +{ + /* This assertion holds because there is no way we can address more than + * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because + * offsets have to be representable in bytes); due to every cluster + * corresponding to one refcount entry, we are well below that limit */ + assert(entries < (UINT64_C(1) << (64 - 9))); + + /* Thanks to the assertion this will not overflow, because + * s->refcount_order < 7. + * (note: x << s->refcount_order == x * s->refcount_bits) */ + return DIV_ROUND_UP(entries << s->refcount_order, 8); +} + +/** + * Reallocates *array so that it can hold new_size entries. *size must contain + * the current number of entries in *array. If the reallocation fails, *array + * and *size will not be modified and -errno will be returned. If the + * reallocation is successful, *array will be set to the new buffer, *size + * will be set to new_size and 0 will be returned. The size of the reallocated + * refcount array buffer will be aligned to a cluster boundary, and the newly + * allocated area will be zeroed. + */ +static int realloc_refcount_array(BDRVQcowState *s, void **array, + int64_t *size, int64_t new_size) +{ + size_t old_byte_size, new_byte_size; + void *new_ptr; + + /* Round to clusters so the array can be directly written to disk */ + old_byte_size = size_to_clusters(s, refcount_array_byte_size(s, *size)) + * s->cluster_size; + new_byte_size = size_to_clusters(s, refcount_array_byte_size(s, new_size)) + * s->cluster_size; + + if (new_byte_size == old_byte_size) { + *size = new_size; + return 0; + } + + assert(new_byte_size > 0); + + new_ptr = g_try_realloc(*array, new_byte_size); + if (!new_ptr) { + return -ENOMEM; + } + + if (new_byte_size > old_byte_size) { + memset((void *)((uintptr_t)new_ptr + old_byte_size), 0, + new_byte_size - old_byte_size); + } + + *array = new_ptr; + *size = new_size; + + return 0; +} /* * Increases the refcount for a range of clusters in a given refcount table. @@ -1078,12 +1298,13 @@ fail: */ static int inc_refcounts(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, + void **refcount_table, int64_t *refcount_table_size, int64_t offset, int64_t size) { BDRVQcowState *s = bs->opaque; - uint64_t start, last, cluster_offset, k; + uint64_t start, last, cluster_offset, k, refcount; + int ret; if (size <= 0) { return 0; @@ -1095,30 +1316,22 @@ static int inc_refcounts(BlockDriverState *bs, cluster_offset += s->cluster_size) { k = cluster_offset >> s->cluster_bits; if (k >= *refcount_table_size) { - int64_t old_refcount_table_size = *refcount_table_size; - uint16_t *new_refcount_table; - - *refcount_table_size = k + 1; - new_refcount_table = g_try_realloc(*refcount_table, - *refcount_table_size * - sizeof(**refcount_table)); - if (!new_refcount_table) { - *refcount_table_size = old_refcount_table_size; + ret = realloc_refcount_array(s, refcount_table, + refcount_table_size, k + 1); + if (ret < 0) { res->check_errors++; - return -ENOMEM; + return ret; } - *refcount_table = new_refcount_table; - - memset(*refcount_table + old_refcount_table_size, 0, - (*refcount_table_size - old_refcount_table_size) * - sizeof(**refcount_table)); } - if (++(*refcount_table)[k] == 0) { + refcount = s->get_refcount(*refcount_table, k); + if (refcount == s->refcount_max) { fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 "\n", cluster_offset); res->corruptions++; + continue; } + s->set_refcount(*refcount_table, k, refcount + 1); } return 0; @@ -1138,8 +1351,9 @@ enum { * error occurred. */ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, int64_t *refcount_table_size, int64_t l2_offset, - int flags) + void **refcount_table, + int64_t *refcount_table_size, int64_t l2_offset, + int flags) { BDRVQcowState *s = bs->opaque; uint64_t *l2_table, l2_entry; @@ -1256,7 +1470,7 @@ fail: */ static int check_refcounts_l1(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, + void **refcount_table, int64_t *refcount_table_size, int64_t l1_table_offset, int l1_size, int flags) @@ -1341,7 +1555,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, BDRVQcowState *s = bs->opaque; uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); int ret; - int refcount; + uint64_t refcount; int i, j; for (i = 0; i < s->l1_size; i++) { @@ -1353,14 +1567,15 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, continue; } - refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); - if (refcount < 0) { + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { /* don't print message nor increment check_errors */ continue; } if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " - "l1_entry=%" PRIx64 " refcount=%d\n", + "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i, l1_entry, refcount); @@ -1395,15 +1610,16 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((cluster_type == QCOW2_CLUSTER_NORMAL) || ((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) { - refcount = qcow2_get_refcount(bs, - data_offset >> s->cluster_bits); - if (refcount < 0) { + ret = qcow2_get_refcount(bs, + data_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { /* don't print message nor increment check_errors */ continue; } if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED data cluster: " - "l2_entry=%" PRIx64 " refcount=%d\n", + "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", l2_entry, refcount); @@ -1453,7 +1669,7 @@ fail: */ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, - uint16_t **refcount_table, int64_t *nb_clusters) + void **refcount_table, int64_t *nb_clusters) { BDRVQcowState *s = bs->opaque; int64_t i, size; @@ -1478,8 +1694,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); if (fix & BDRV_FIX_ERRORS) { - int64_t old_nb_clusters = *nb_clusters; - uint16_t *new_refcount_table; + int64_t new_nb_clusters; if (offset > INT64_MAX - s->cluster_size) { ret = -EINVAL; @@ -1496,22 +1711,15 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, goto resize_fail; } - *nb_clusters = size_to_clusters(s, size); - assert(*nb_clusters >= old_nb_clusters); + new_nb_clusters = size_to_clusters(s, size); + assert(new_nb_clusters >= *nb_clusters); - new_refcount_table = g_try_realloc(*refcount_table, - *nb_clusters * - sizeof(**refcount_table)); - if (!new_refcount_table) { - *nb_clusters = old_nb_clusters; + ret = realloc_refcount_array(s, refcount_table, + nb_clusters, new_nb_clusters); + if (ret < 0) { res->check_errors++; - return -ENOMEM; + return ret; } - *refcount_table = new_refcount_table; - - memset(*refcount_table + old_nb_clusters, 0, - (*nb_clusters - old_nb_clusters) * - sizeof(**refcount_table)); if (cluster >= *nb_clusters) { ret = -EINVAL; @@ -1546,9 +1754,10 @@ resize_fail: if (ret < 0) { return ret; } - if ((*refcount_table)[cluster] != 1) { + if (s->get_refcount(*refcount_table, cluster) != 1) { fprintf(stderr, "ERROR refcount block %" PRId64 - " refcount=%d\n", i, (*refcount_table)[cluster]); + " refcount=%" PRIu64 "\n", i, + s->get_refcount(*refcount_table, cluster)); res->corruptions++; *rebuild = true; } @@ -1563,7 +1772,7 @@ resize_fail: */ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, - uint16_t **refcount_table, int64_t *nb_clusters) + void **refcount_table, int64_t *nb_clusters) { BDRVQcowState *s = bs->opaque; int64_t i; @@ -1571,10 +1780,12 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, int ret; if (!*refcount_table) { - *refcount_table = g_try_new0(uint16_t, *nb_clusters); - if (*nb_clusters && *refcount_table == NULL) { + int64_t old_size = 0; + ret = realloc_refcount_array(s, refcount_table, + &old_size, *nb_clusters); + if (ret < 0) { res->check_errors++; - return -ENOMEM; + return ret; } } @@ -1625,22 +1836,23 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, int64_t *highest_cluster, - uint16_t *refcount_table, int64_t nb_clusters) + void *refcount_table, int64_t nb_clusters) { BDRVQcowState *s = bs->opaque; int64_t i; - int refcount1, refcount2, ret; + uint64_t refcount1, refcount2; + int ret; for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) { - refcount1 = qcow2_get_refcount(bs, i); - if (refcount1 < 0) { + ret = qcow2_get_refcount(bs, i, &refcount1); + if (ret < 0) { fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", - i, strerror(-refcount1)); + i, strerror(-ret)); res->check_errors++; continue; } - refcount2 = refcount_table[i]; + refcount2 = s->get_refcount(refcount_table, i); if (refcount1 > 0 || refcount2 > 0) { *highest_cluster = i; @@ -1657,7 +1869,8 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, num_fixed = &res->corruptions_fixed; } - fprintf(stderr, "%s cluster %" PRId64 " refcount=%d reference=%d\n", + fprintf(stderr, "%s cluster %" PRId64 " refcount=%" PRIu64 + " reference=%" PRIu64 "\n", num_fixed != NULL ? "Repairing" : refcount1 < refcount2 ? "ERROR" : "Leaked", @@ -1665,7 +1878,8 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, if (num_fixed) { ret = update_refcount(bs, i << s->cluster_bits, 1, - refcount2 - refcount1, + refcount_diff(refcount1, refcount2), + refcount1 > refcount2, QCOW2_DISCARD_ALWAYS); if (ret >= 0) { (*num_fixed)++; @@ -1697,7 +1911,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, */ static int64_t alloc_clusters_imrt(BlockDriverState *bs, int cluster_count, - uint16_t **refcount_table, + void **refcount_table, int64_t *imrt_nb_clusters, int64_t *first_free_cluster) { @@ -1705,6 +1919,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, int64_t cluster = *first_free_cluster, i; bool first_gap = true; int contiguous_free_clusters; + int ret; /* Starting at *first_free_cluster, find a range of at least cluster_count * continuously free clusters */ @@ -1713,7 +1928,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, contiguous_free_clusters < cluster_count; cluster++) { - if (!(*refcount_table)[cluster]) { + if (!s->get_refcount(*refcount_table, cluster)) { contiguous_free_clusters++; if (first_gap) { /* If this is the first free cluster found, update @@ -1734,34 +1949,24 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, /* If no such range could be found, grow the in-memory refcount table * accordingly to append free clusters at the end of the image */ if (contiguous_free_clusters < cluster_count) { - int64_t old_imrt_nb_clusters = *imrt_nb_clusters; - uint16_t *new_refcount_table; - /* contiguous_free_clusters clusters are already empty at the image end; * we need cluster_count clusters; therefore, we have to allocate * cluster_count - contiguous_free_clusters new clusters at the end of * the image (which is the current value of cluster; note that cluster * may exceed old_imrt_nb_clusters if *first_free_cluster pointed beyond * the image end) */ - *imrt_nb_clusters = cluster + cluster_count - contiguous_free_clusters; - new_refcount_table = g_try_realloc(*refcount_table, - *imrt_nb_clusters * - sizeof(**refcount_table)); - if (!new_refcount_table) { - *imrt_nb_clusters = old_imrt_nb_clusters; - return -ENOMEM; + ret = realloc_refcount_array(s, refcount_table, imrt_nb_clusters, + cluster + cluster_count + - contiguous_free_clusters); + if (ret < 0) { + return ret; } - *refcount_table = new_refcount_table; - - memset(*refcount_table + old_imrt_nb_clusters, 0, - (*imrt_nb_clusters - old_imrt_nb_clusters) * - sizeof(**refcount_table)); } /* Go back to the first free cluster */ cluster -= contiguous_free_clusters; for (i = 0; i < cluster_count; i++) { - (*refcount_table)[cluster + i] = 1; + s->set_refcount(*refcount_table, cluster + i, 1); } return cluster << s->cluster_bits; @@ -1777,7 +1982,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, */ static int rebuild_refcount_structure(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, + void **refcount_table, int64_t *nb_clusters) { BDRVQcowState *s = bs->opaque; @@ -1785,8 +1990,8 @@ static int rebuild_refcount_structure(BlockDriverState *bs, int64_t refblock_offset, refblock_start, refblock_index; uint32_t reftable_size = 0; uint64_t *on_disk_reftable = NULL; - uint16_t *on_disk_refblock; - int i, ret = 0; + void *on_disk_refblock; + int ret = 0; struct { uint64_t reftable_offset; uint32_t reftable_clusters; @@ -1796,7 +2001,7 @@ static int rebuild_refcount_structure(BlockDriverState *bs, write_refblocks: for (; cluster < *nb_clusters; cluster++) { - if (!(*refcount_table)[cluster]) { + if (!s->get_refcount(*refcount_table, cluster)) { continue; } @@ -1869,17 +2074,13 @@ write_refblocks: goto fail; } - on_disk_refblock = qemu_blockalign0(bs->file, s->cluster_size); - for (i = 0; i < s->refcount_block_size && - refblock_start + i < *nb_clusters; i++) - { - on_disk_refblock[i] = - cpu_to_be16((*refcount_table)[refblock_start + i]); - } + /* The size of *refcount_table is always cluster-aligned, therefore the + * write operation will not overflow */ + on_disk_refblock = (void *)((char *) *refcount_table + + refblock_index * s->cluster_size); ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE, - (void *)on_disk_refblock, s->cluster_sectors); - qemu_vfree(on_disk_refblock); + on_disk_refblock, s->cluster_sectors); if (ret < 0) { fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); goto fail; @@ -1974,7 +2175,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BDRVQcowState *s = bs->opaque; BdrvCheckResult pre_compare_res; int64_t size, highest_cluster, nb_clusters; - uint16_t *refcount_table = NULL; + void *refcount_table = NULL; bool rebuild = false; int ret; @@ -2023,7 +2224,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* Because the old reftable has been exchanged for a new one the * references have to be recalculated */ rebuild = false; - memset(refcount_table, 0, nb_clusters * sizeof(uint16_t)); + memset(refcount_table, 0, refcount_array_byte_size(s, nb_clusters)); ret = calculate_refcounts(bs, res, 0, &rebuild, &refcount_table, &nb_clusters); if (ret < 0) { diff --git a/block/qcow2.c b/block/qcow2.c index 50e0a94..8bfb094 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -677,13 +677,16 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } /* Check support for various header values */ - if (header.refcount_order != 4) { - report_unsupported(bs, errp, "%d bit reference counts", - 1 << header.refcount_order); - ret = -ENOTSUP; + if (header.refcount_order > 6) { + error_setg(errp, "Reference count entry width too large; may not " + "exceed 64 bits"); + ret = -EINVAL; goto fail; } s->refcount_order = header.refcount_order; + s->refcount_bits = 1 << s->refcount_order; + s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); + s->refcount_max += s->refcount_max - 1; if (header.crypt_method > QCOW_CRYPT_AES) { error_setg(errp, "Unsupported encryption method: %" PRIu32, @@ -1780,7 +1783,7 @@ static int preallocate(BlockDriverState *bs) static int qcow2_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, int flags, size_t cluster_size, PreallocMode prealloc, - QemuOpts *opts, int version, + QemuOpts *opts, int version, int refcount_order, Error **errp) { /* Calculate cluster_bits */ @@ -1813,9 +1816,21 @@ static int qcow2_create2(const char *filename, int64_t total_size, int ret; if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) { + /* Note: The following calculation does not need to be exact; if it is a + * bit off, either some bytes will be "leaked" (which is fine) or we + * will need to increase the file size by some bytes (which is fine, + * too, as long as the bulk is allocated here). Therefore, using + * floating point arithmetic is fine. */ int64_t meta_size = 0; uint64_t nreftablee, nrefblocke, nl1e, nl2e; int64_t aligned_total_size = align_offset(total_size, cluster_size); + int refblock_bits, refblock_size; + /* refcount entry size in bytes */ + double rces = (1 << refcount_order) / 8.; + + /* see qcow2_open() */ + refblock_bits = cluster_bits - (refcount_order - 3); + refblock_size = 1 << refblock_bits; /* header: 1 cluster */ meta_size += cluster_size; @@ -1840,20 +1855,20 @@ static int qcow2_create2(const char *filename, int64_t total_size, * c = cluster size * y1 = number of refcount blocks entries * y2 = meta size including everything + * rces = refcount entry size in bytes * then, * y1 = (y2 + a)/c - * y2 = y1 * sizeof(u16) + y1 * sizeof(u16) * sizeof(u64) / c + m + * y2 = y1 * rces + y1 * rces * sizeof(u64) / c + m * we can get y1: - * y1 = (a + m) / (c - sizeof(u16) - sizeof(u16) * sizeof(u64) / c) + * y1 = (a + m) / (c - rces - rces * sizeof(u64) / c) */ - nrefblocke = (aligned_total_size + meta_size + cluster_size) / - (cluster_size - sizeof(uint16_t) - - 1.0 * sizeof(uint16_t) * sizeof(uint64_t) / cluster_size); - nrefblocke = align_offset(nrefblocke, cluster_size / sizeof(uint16_t)); - meta_size += nrefblocke * sizeof(uint16_t); + nrefblocke = (aligned_total_size + meta_size + cluster_size) + / (cluster_size - rces - rces * sizeof(uint64_t) + / cluster_size); + meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size; /* total size of refcount tables */ - nreftablee = nrefblocke * sizeof(uint16_t) / cluster_size; + nreftablee = nrefblocke / refblock_size; nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t)); meta_size += nreftablee * sizeof(uint64_t); @@ -1889,7 +1904,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, .l1_size = cpu_to_be32(0), .refcount_table_offset = cpu_to_be64(cluster_size), .refcount_table_clusters = cpu_to_be32(1), - .refcount_order = cpu_to_be32(4), + .refcount_order = cpu_to_be32(refcount_order), .header_length = cpu_to_be32(sizeof(*header)), }; @@ -2008,6 +2023,8 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) size_t cluster_size = DEFAULT_CLUSTER_SIZE; PreallocMode prealloc; int version = 3; + uint64_t refcount_bits = 16; + int refcount_order; Error *local_err = NULL; int ret; @@ -2062,8 +2079,28 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) goto finish; } + refcount_bits = qemu_opt_get_number_del(opts, BLOCK_OPT_REFCOUNT_BITS, + refcount_bits); + if (refcount_bits > 64 || !is_power_of_2(refcount_bits)) { + error_setg(errp, "Refcount width must be a power of two and may not " + "exceed 64 bits"); + ret = -EINVAL; + goto finish; + } + + if (version < 3 && refcount_bits != 16) { + error_setg(errp, "Different refcount widths than 16 bits require " + "compatibility level 1.1 or above (use compat=1.1 or " + "greater)"); + ret = -EINVAL; + goto finish; + } + + refcount_order = ffs(refcount_bits) - 1; + ret = qcow2_create2(filename, size, backing_file, backing_fmt, flags, - cluster_size, prealloc, opts, version, &local_err); + cluster_size, prealloc, opts, version, refcount_order, + &local_err); if (local_err) { error_propagate(errp, local_err); } @@ -2479,7 +2516,8 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) }; if (s->qcow_version == 2) { *spec_info->qcow2 = (ImageInfoSpecificQCow2){ - .compat = g_strdup("0.10"), + .compat = g_strdup("0.10"), + .refcount_bits = s->refcount_bits, }; } else if (s->qcow_version == 3) { *spec_info->qcow2 = (ImageInfoSpecificQCow2){ @@ -2490,6 +2528,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) .corrupt = s->incompatible_features & QCOW2_INCOMPAT_CORRUPT, .has_corrupt = true, + .refcount_bits = s->refcount_bits, }; } @@ -2642,8 +2681,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, continue; } - if (!strcmp(desc->name, "compat")) { - compat = qemu_opt_get(opts, "compat"); + if (!strcmp(desc->name, BLOCK_OPT_COMPAT_LEVEL)) { + compat = qemu_opt_get(opts, BLOCK_OPT_COMPAT_LEVEL); if (!compat) { /* preserve default */ } else if (!strcmp(compat, "0.10")) { @@ -2654,33 +2693,37 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, fprintf(stderr, "Unknown compatibility level %s.\n", compat); return -EINVAL; } - } else if (!strcmp(desc->name, "preallocation")) { + } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { fprintf(stderr, "Cannot change preallocation mode.\n"); return -ENOTSUP; - } else if (!strcmp(desc->name, "size")) { - new_size = qemu_opt_get_size(opts, "size", 0); - } else if (!strcmp(desc->name, "backing_file")) { - backing_file = qemu_opt_get(opts, "backing_file"); - } else if (!strcmp(desc->name, "backing_fmt")) { - backing_format = qemu_opt_get(opts, "backing_fmt"); - } else if (!strcmp(desc->name, "encryption")) { - encrypt = qemu_opt_get_bool(opts, "encryption", s->crypt_method); + } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { + new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) { + backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) { + backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); + } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) { + encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, + s->crypt_method); if (encrypt != !!s->crypt_method) { fprintf(stderr, "Changing the encryption flag is not " "supported.\n"); return -ENOTSUP; } - } else if (!strcmp(desc->name, "cluster_size")) { - cluster_size = qemu_opt_get_size(opts, "cluster_size", + } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { + cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, cluster_size); if (cluster_size != s->cluster_size) { fprintf(stderr, "Changing the cluster size is not " "supported.\n"); return -ENOTSUP; } - } else if (!strcmp(desc->name, "lazy_refcounts")) { - lazy_refcounts = qemu_opt_get_bool(opts, "lazy_refcounts", + } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { + lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, lazy_refcounts); + } else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) { + error_report("Cannot change refcount entry width"); + return -ENOTSUP; } else { /* if this assertion fails, this probably means a new option was * added without having it covered here */ @@ -2850,6 +2893,12 @@ static QemuOptsList qcow2_create_opts = { .help = "Postpone refcount updates", .def_value_str = "off" }, + { + .name = BLOCK_OPT_REFCOUNT_BITS, + .type = QEMU_OPT_NUMBER, + .help = "Width of a reference count entry in bits", + .def_value_str = "16" + }, { /* end of list */ } } }; diff --git a/block/qcow2.h b/block/qcow2.h index 6e39a1b..aa6d367 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -213,6 +213,11 @@ typedef struct Qcow2DiscardRegion { QTAILQ_ENTRY(Qcow2DiscardRegion) next; } Qcow2DiscardRegion; +typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array, + uint64_t index); +typedef void Qcow2SetRefcountFunc(void *refcount_array, + uint64_t index, uint64_t value); + typedef struct BDRVQcowState { int cluster_bits; int cluster_size; @@ -258,6 +263,11 @@ typedef struct BDRVQcowState { int qcow_version; bool use_lazy_refcounts; int refcount_order; + int refcount_bits; + uint64_t refcount_max; + + Qcow2GetRefcountFunc *get_refcount; + Qcow2SetRefcountFunc *set_refcount; bool discard_passthrough[QCOW2_DISCARD_MAX]; @@ -275,17 +285,6 @@ typedef struct BDRVQcowState { bool cache_discards; } BDRVQcowState; -/* XXX: use std qcow open function ? */ -typedef struct QCowCreateState { - int cluster_size; - int cluster_bits; - uint16_t *refcount_block; - uint64_t *refcount_table; - int64_t l1_table_offset; - int64_t refcount_table_offset; - int64_t refcount_block_offset; -} QCowCreateState; - struct QCowAIOCB; typedef struct Qcow2COWRegion { @@ -468,6 +467,11 @@ static inline uint64_t l2meta_cow_end(QCowL2Meta *m) + (m->cow_end.nb_sectors << BDRV_SECTOR_BITS); } +static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2) +{ + return r1 > r2 ? r1 - r2 : r2 - r1; +} + // FIXME Need qcow2_ prefix to global functions /* qcow2.c functions */ @@ -487,10 +491,12 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, int qcow2_refcount_init(BlockDriverState *bs); void qcow2_refcount_close(BlockDriverState *bs); -int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index); +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t *refcount); int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, - int addend, enum qcow2_discard_type type); + uint64_t addend, bool decrease, + enum qcow2_discard_type type); int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, diff --git a/block/raw-posix.c b/block/raw-posix.c index b5f077a..f0b4488 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -56,6 +56,10 @@ #include <linux/cdrom.h> #include <linux/fd.h> #include <linux/fs.h> +#include <linux/hdreg.h> +#ifdef __s390__ +#include <asm/dasd.h> +#endif #ifndef FS_NOCOW_FL #define FS_NOCOW_FL 0x00800000 /* Do not cow file */ #endif @@ -218,39 +222,100 @@ static int raw_normalize_devicepath(const char **filename) } #endif -static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) +/* + * Get logical block size via ioctl. On success store it in @sector_size_p. + */ +static int probe_logical_blocksize(int fd, unsigned int *sector_size_p) { - BDRVRawState *s = bs->opaque; - char *buf; unsigned int sector_size; + bool success = false; - /* For /dev/sg devices the alignment is not really used. - With buffered I/O, we don't have any restrictions. */ - if (bs->sg || !s->needs_alignment) { - bs->request_alignment = 1; - s->buf_align = 1; - return; - } + errno = ENOTSUP; /* Try a few ioctls to get the right size */ - bs->request_alignment = 0; - s->buf_align = 0; - #ifdef BLKSSZGET if (ioctl(fd, BLKSSZGET, §or_size) >= 0) { - bs->request_alignment = sector_size; + *sector_size_p = sector_size; + success = true; } #endif #ifdef DKIOCGETBLOCKSIZE if (ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) >= 0) { - bs->request_alignment = sector_size; + *sector_size_p = sector_size; + success = true; } #endif #ifdef DIOCGSECTORSIZE if (ioctl(fd, DIOCGSECTORSIZE, §or_size) >= 0) { - bs->request_alignment = sector_size; + *sector_size_p = sector_size; + success = true; + } +#endif + + return success ? 0 : -errno; +} + +/** + * Get physical block size of @fd. + * On success, store it in @blk_size and return 0. + * On failure, return -errno. + */ +static int probe_physical_blocksize(int fd, unsigned int *blk_size) +{ +#ifdef BLKPBSZGET + if (ioctl(fd, BLKPBSZGET, blk_size) < 0) { + return -errno; + } + return 0; +#else + return -ENOTSUP; +#endif +} + +/* Check if read is allowed with given memory buffer and length. + * + * This function is used to check O_DIRECT memory buffer and request alignment. + */ +static bool raw_is_io_aligned(int fd, void *buf, size_t len) +{ + ssize_t ret = pread(fd, buf, len, 0); + + if (ret >= 0) { + return true; + } + +#ifdef __linux__ + /* The Linux kernel returns EINVAL for misaligned O_DIRECT reads. Ignore + * other errors (e.g. real I/O error), which could happen on a failed + * drive, since we only care about probing alignment. + */ + if (errno != EINVAL) { + return true; } #endif + + return false; +} + +static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) +{ + BDRVRawState *s = bs->opaque; + char *buf; + + /* For /dev/sg devices the alignment is not really used. + With buffered I/O, we don't have any restrictions. */ + if (bs->sg || !s->needs_alignment) { + bs->request_alignment = 1; + s->buf_align = 1; + return; + } + + bs->request_alignment = 0; + s->buf_align = 0; + /* Let's try to use the logical blocksize for the alignment. */ + if (probe_logical_blocksize(fd, &bs->request_alignment) < 0) { + bs->request_alignment = 0; + } #ifdef CONFIG_XFS if (s->is_xfs) { struct dioattr da; @@ -267,7 +332,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) size_t align; buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE); for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { - if (pread(fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) { + if (raw_is_io_aligned(fd, buf + align, MAX_BLOCKSIZE)) { s->buf_align = align; break; } @@ -279,7 +344,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) size_t align; buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE); for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { - if (pread(fd, buf, align, 0) >= 0) { + if (raw_is_io_aligned(fd, buf, align)) { bs->request_alignment = align; break; } @@ -655,6 +720,86 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.opt_mem_alignment = s->buf_align; } +static int check_for_dasd(int fd) +{ +#ifdef BIODASDINFO2 + struct dasd_information2_t info = {0}; + + return ioctl(fd, BIODASDINFO2, &info); +#else + return -1; +#endif +} + +/** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz and return zero. + * On failure, return negative errno. + */ +static int hdev_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + BDRVRawState *s = bs->opaque; + int ret; + + /* If DASD, get blocksizes */ + if (check_for_dasd(s->fd) < 0) { + return -ENOTSUP; + } + ret = probe_logical_blocksize(s->fd, &bsz->log); + if (ret < 0) { + return ret; + } + return probe_physical_blocksize(s->fd, &bsz->phys); +} + +/** + * Try to get @bs's geometry: cyls, heads, sectors. + * On success, store them in @geo and return 0. + * On failure return -errno. + * (Allows block driver to assign default geometry values that guest sees) + */ +#ifdef __linux__ +static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + BDRVRawState *s = bs->opaque; + struct hd_geometry ioctl_geo = {0}; + uint32_t blksize; + + /* If DASD, get its geometry */ + if (check_for_dasd(s->fd) < 0) { + return -ENOTSUP; + } + if (ioctl(s->fd, HDIO_GETGEO, &ioctl_geo) < 0) { + return -errno; + } + /* HDIO_GETGEO may return success even though geo contains zeros + (e.g. certain multipath setups) */ + if (!ioctl_geo.heads || !ioctl_geo.sectors || !ioctl_geo.cylinders) { + return -ENOTSUP; + } + /* Do not return a geometry for partition */ + if (ioctl_geo.start != 0) { + return -ENOTSUP; + } + geo->heads = ioctl_geo.heads; + geo->sectors = ioctl_geo.sectors; + if (!probe_physical_blocksize(s->fd, &blksize)) { + /* overwrite cyls: HDIO_GETGEO result is incorrect for big drives */ + geo->cylinders = bdrv_nb_sectors(bs) / (blksize / BDRV_SECTOR_SIZE) + / (geo->heads * geo->sectors); + return 0; + } + geo->cylinders = ioctl_geo.cylinders; + + return 0; +} +#else /* __linux__ */ +static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + return -ENOTSUP; +} +#endif + static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb) { int ret; @@ -944,7 +1089,9 @@ static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) { +#if defined(CONFIG_FALLOCATE) || defined(CONFIG_XFS) BDRVRawState *s = aiocb->bs->opaque; +#endif if (aiocb->aio_type & QEMU_AIO_BLKDEV) { return handle_aiocb_write_zeroes_block(aiocb); @@ -2194,6 +2341,8 @@ static BlockDriver bdrv_host_device = { .bdrv_get_info = raw_get_info, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, + .bdrv_probe_blocksizes = hdev_probe_blocksizes, + .bdrv_probe_geometry = hdev_probe_geometry, .bdrv_detach_aio_context = raw_detach_aio_context, .bdrv_attach_aio_context = raw_attach_aio_context, diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 05b02c7..e3d2d04 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -235,6 +235,16 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) return 1; } +static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + return bdrv_probe_blocksizes(bs->file, bsz); +} + +static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + return bdrv_probe_geometry(bs->file, geo); +} + BlockDriver bdrv_raw = { .format_name = "raw", .bdrv_probe = &raw_probe, @@ -252,6 +262,8 @@ BlockDriver bdrv_raw = { .has_variable_length = true, .bdrv_get_info = &raw_get_info, .bdrv_refresh_limits = &raw_refresh_limits, + .bdrv_probe_blocksizes = &raw_probe_blocksizes, + .bdrv_probe_geometry = &raw_probe_geometry, .bdrv_is_inserted = &raw_is_inserted, .bdrv_media_changed = &raw_media_changed, .bdrv_eject = &raw_eject, diff --git a/block/sheepdog.c b/block/sheepdog.c index d17ee36..c14172c 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -37,6 +37,7 @@ #define SD_OP_READ_VDIS 0x15 #define SD_OP_FLUSH_VDI 0x16 #define SD_OP_DEL_VDI 0x17 +#define SD_OP_GET_CLUSTER_DEFAULT 0x18 #define SD_FLAG_CMD_WRITE 0x01 #define SD_FLAG_CMD_COW 0x02 @@ -91,6 +92,7 @@ #define SD_NR_VDIS (1U << 24) #define SD_DATA_OBJ_SIZE (UINT64_C(1) << 22) #define SD_MAX_VDI_SIZE (SD_DATA_OBJ_SIZE * MAX_DATA_OBJS) +#define SD_DEFAULT_BLOCK_SIZE_SHIFT 22 /* * For erasure coding, we use at most SD_EC_MAX_STRIP for data strips and * (SD_EC_MAX_STRIP - 1) for parity strips @@ -167,7 +169,8 @@ typedef struct SheepdogVdiReq { uint32_t base_vdi_id; uint8_t copies; uint8_t copy_policy; - uint8_t reserved[2]; + uint8_t store_policy; + uint8_t block_size_shift; uint32_t snapid; uint32_t type; uint32_t pad[2]; @@ -186,6 +189,21 @@ typedef struct SheepdogVdiRsp { uint32_t pad[5]; } SheepdogVdiRsp; +typedef struct SheepdogClusterRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint8_t nr_copies; + uint8_t copy_policy; + uint8_t block_size_shift; + uint8_t __pad1; + uint32_t __pad2[6]; +} SheepdogClusterRsp; + typedef struct SheepdogInode { char name[SD_MAX_VDI_LEN]; char tag[SD_MAX_VDI_TAG_LEN]; @@ -527,6 +545,7 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, return acb; } +/* Return -EIO in case of error, file descriptor on success */ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) { int fd; @@ -546,11 +565,14 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) if (fd >= 0) { qemu_set_nonblock(fd); + } else { + fd = -EIO; } return fd; } +/* Return 0 on success and -errno in case of error */ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, unsigned int *wlen) { @@ -559,11 +581,13 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, ret = qemu_co_send(sockfd, hdr, sizeof(*hdr)); if (ret != sizeof(*hdr)) { error_report("failed to send a req, %s", strerror(errno)); + ret = -socket_error(); return ret; } ret = qemu_co_send(sockfd, data, *wlen); if (ret != *wlen) { + ret = -socket_error(); error_report("failed to send a req, %s", strerror(errno)); } @@ -638,6 +662,11 @@ out: srco->finished = true; } +/* + * Send the request to the sheep in a synchronous manner. + * + * Return 0 on success, -errno in case of error. + */ static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr, void *data, unsigned int *wlen, unsigned int *rlen) { @@ -1541,6 +1570,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, hdr.vdi_size = s->inode.vdi_size; hdr.copy_policy = s->inode.copy_policy; hdr.copies = s->inode.nr_copies; + hdr.block_size_shift = s->inode.block_size_shift; ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen); @@ -1566,9 +1596,12 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, static int sd_prealloc(const char *filename, Error **errp) { BlockDriverState *bs = NULL; + BDRVSheepdogState *base = NULL; + unsigned long buf_size; uint32_t idx, max_idx; + uint32_t object_size; int64_t vdi_size; - void *buf = g_malloc0(SD_DATA_OBJ_SIZE); + void *buf = NULL; int ret; ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, @@ -1582,18 +1615,24 @@ static int sd_prealloc(const char *filename, Error **errp) ret = vdi_size; goto out; } - max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE); + + base = bs->opaque; + object_size = (UINT32_C(1) << base->inode.block_size_shift); + buf_size = MIN(object_size, SD_DATA_OBJ_SIZE); + buf = g_malloc0(buf_size); + + max_idx = DIV_ROUND_UP(vdi_size, buf_size); for (idx = 0; idx < max_idx; idx++) { /* * The created image can be a cloned image, so we need to read * a data from the source image. */ - ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + ret = bdrv_pread(bs, idx * buf_size, buf, buf_size); if (ret < 0) { goto out; } - ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + ret = bdrv_pwrite(bs, idx * buf_size, buf, buf_size); if (ret < 0) { goto out; } @@ -1666,6 +1705,27 @@ static int parse_redundancy(BDRVSheepdogState *s, const char *opt) return 0; } +static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt) +{ + struct SheepdogInode *inode = &s->inode; + uint64_t object_size; + int obj_order; + + object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0); + if (object_size) { + if ((object_size - 1) & object_size) { /* not a power of 2? */ + return -EINVAL; + } + obj_order = ffs(object_size) - 1; + if (obj_order < 20 || obj_order > 31) { + return -EINVAL; + } + inode->block_size_shift = (uint8_t)obj_order; + } + + return 0; +} + static int sd_create(const char *filename, QemuOpts *opts, Error **errp) { @@ -1676,6 +1736,7 @@ static int sd_create(const char *filename, QemuOpts *opts, BDRVSheepdogState *s; char tag[SD_MAX_VDI_TAG_LEN]; uint32_t snapid; + uint64_t max_vdi_size; bool prealloc = false; s = g_new0(BDRVSheepdogState, 1); @@ -1714,10 +1775,11 @@ static int sd_create(const char *filename, QemuOpts *opts, goto out; } } - - if (s->inode.vdi_size > SD_MAX_VDI_SIZE) { - error_setg(errp, "too big image size"); - ret = -EINVAL; + ret = parse_block_size_shift(s, opts); + if (ret < 0) { + error_setg(errp, "Invalid object_size." + " obect_size needs to be power of 2" + " and be limited from 2^20 to 2^31"); goto out; } @@ -1754,6 +1816,51 @@ static int sd_create(const char *filename, QemuOpts *opts, } s->aio_context = qemu_get_aio_context(); + + /* if block_size_shift is not specified, get cluster default value */ + if (s->inode.block_size_shift == 0) { + SheepdogVdiReq hdr; + SheepdogClusterRsp *rsp = (SheepdogClusterRsp *)&hdr; + Error *local_err = NULL; + int fd; + unsigned int wlen = 0, rlen = 0; + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report("%s", error_get_pretty(local_err)); + error_free(local_err); + ret = -EIO; + goto out; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.opcode = SD_OP_GET_CLUSTER_DEFAULT; + hdr.proto_ver = SD_PROTO_VER; + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + NULL, &wlen, &rlen); + closesocket(fd); + if (ret) { + error_setg_errno(errp, -ret, "failed to get cluster default"); + goto out; + } + if (rsp->result == SD_RES_SUCCESS) { + s->inode.block_size_shift = rsp->block_size_shift; + } else { + s->inode.block_size_shift = SD_DEFAULT_BLOCK_SIZE_SHIFT; + } + } + + max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; + + if (s->inode.vdi_size > max_vdi_size) { + error_setg(errp, "An image is too large." + " The maximum image size is %"PRIu64 "GB", + max_vdi_size / 1024 / 1024 / 1024); + ret = -EINVAL; + goto out; + } + ret = do_sd_create(s, &vid, 0, errp); if (ret) { goto out; @@ -1823,11 +1930,13 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset) BDRVSheepdogState *s = bs->opaque; int ret, fd; unsigned int datalen; + uint64_t max_vdi_size; + max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; if (offset < s->inode.vdi_size) { error_report("shrinking is not supported"); return -EINVAL; - } else if (offset > SD_MAX_VDI_SIZE) { + } else if (offset > max_vdi_size) { error_report("too big image size"); return -EINVAL; } @@ -2005,9 +2114,10 @@ static int coroutine_fn sd_co_rw_vector(void *p) SheepdogAIOCB *acb = p; int ret = 0; unsigned long len, done = 0, total = acb->nb_sectors * BDRV_SECTOR_SIZE; - unsigned long idx = acb->sector_num * BDRV_SECTOR_SIZE / SD_DATA_OBJ_SIZE; + unsigned long idx; + uint32_t object_size; uint64_t oid; - uint64_t offset = (acb->sector_num * BDRV_SECTOR_SIZE) % SD_DATA_OBJ_SIZE; + uint64_t offset; BDRVSheepdogState *s = acb->common.bs->opaque; SheepdogInode *inode = &s->inode; AIOReq *aio_req; @@ -2024,6 +2134,10 @@ static int coroutine_fn sd_co_rw_vector(void *p) } } + object_size = (UINT32_C(1) << inode->block_size_shift); + idx = acb->sector_num * BDRV_SECTOR_SIZE / object_size; + offset = (acb->sector_num * BDRV_SECTOR_SIZE) % object_size; + /* * Make sure we don't free the aiocb before we are done with all requests. * This additional reference is dropped at the end of this function. @@ -2037,7 +2151,7 @@ static int coroutine_fn sd_co_rw_vector(void *p) oid = vid_to_data_oid(inode->data_vdi_id[idx], idx); - len = MIN(total - done, SD_DATA_OBJ_SIZE - offset); + len = MIN(total - done, object_size - offset); switch (acb->aiocb_type) { case AIOCB_READ_UDATA: @@ -2061,7 +2175,7 @@ static int coroutine_fn sd_co_rw_vector(void *p) * We discard the object only when the whole object is * 1) allocated 2) trimmed. Otherwise, simply skip it. */ - if (len != SD_DATA_OBJ_SIZE || inode->data_vdi_id[idx] == 0) { + if (len != object_size || inode->data_vdi_id[idx] == 0) { goto done; } break; @@ -2225,9 +2339,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) ret = do_sd_create(s, &new_vid, 1, &local_err); if (ret < 0) { - error_report_err(local_err); - error_report("failed to create inode for snapshot. %s", - strerror(errno)); + error_report("failed to create inode for snapshot: %s", + error_get_pretty(local_err)); goto cleanup; } @@ -2414,6 +2527,7 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, uint64_t offset; uint32_t vdi_index; uint32_t vdi_id = load ? s->inode.parent_vdi_id : s->inode.vdi_id; + uint32_t object_size = (UINT32_C(1) << s->inode.block_size_shift); fd = connect_to_sdog(s, &local_err); if (fd < 0) { @@ -2422,10 +2536,10 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, } while (remaining) { - vdi_index = pos / SD_DATA_OBJ_SIZE; - offset = pos % SD_DATA_OBJ_SIZE; + vdi_index = pos / object_size; + offset = pos % object_size; - data_len = MIN(remaining, SD_DATA_OBJ_SIZE - offset); + data_len = MIN(remaining, object_size - offset); vmstate_oid = vid_to_vmstate_oid(vdi_id, vdi_index); @@ -2512,10 +2626,11 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, { BDRVSheepdogState *s = bs->opaque; SheepdogInode *inode = &s->inode; + uint32_t object_size = (UINT32_C(1) << inode->block_size_shift); uint64_t offset = sector_num * BDRV_SECTOR_SIZE; - unsigned long start = offset / SD_DATA_OBJ_SIZE, + unsigned long start = offset / object_size, end = DIV_ROUND_UP((sector_num + nb_sectors) * - BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE); + BDRV_SECTOR_SIZE, object_size); unsigned long idx; int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; @@ -2534,7 +2649,7 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, } } - *pnum = (idx - start) * SD_DATA_OBJ_SIZE / BDRV_SECTOR_SIZE; + *pnum = (idx - start) * object_size / BDRV_SECTOR_SIZE; if (*pnum > nb_sectors) { *pnum = nb_sectors; } @@ -2545,14 +2660,15 @@ static int64_t sd_get_allocated_file_size(BlockDriverState *bs) { BDRVSheepdogState *s = bs->opaque; SheepdogInode *inode = &s->inode; - unsigned long i, last = DIV_ROUND_UP(inode->vdi_size, SD_DATA_OBJ_SIZE); + uint32_t object_size = (UINT32_C(1) << inode->block_size_shift); + unsigned long i, last = DIV_ROUND_UP(inode->vdi_size, object_size); uint64_t size = 0; for (i = 0; i < last; i++) { if (inode->data_vdi_id[i] == 0) { continue; } - size += SD_DATA_OBJ_SIZE; + size += object_size; } return size; } @@ -2581,6 +2697,11 @@ static QemuOptsList sd_create_opts = { .type = QEMU_OPT_STRING, .help = "Redundancy of the image" }, + { + .name = BLOCK_OPT_OBJECT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Object size of the image" + }, { /* end of list */ } } }; diff --git a/block/vdi.c b/block/vdi.c index 74030c6..53bd02f 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -53,6 +53,7 @@ #include "block/block_int.h" #include "qemu/module.h" #include "migration/migration.h" +#include "block/coroutine.h" #if defined(CONFIG_UUID) #include <uuid/uuid.h> @@ -196,6 +197,8 @@ typedef struct { /* VDI header (converted to host endianness). */ VdiHeader header; + CoMutex write_lock; + Error *migration_blocker; } BDRVVdiState; @@ -504,6 +507,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, "vdi", bdrv_get_device_name(bs), "live migration"); migrate_add_blocker(s->migration_blocker); + qemu_co_mutex_init(&s->write_lock); + return 0; fail_free_bmap: @@ -639,11 +644,31 @@ static int vdi_co_write(BlockDriverState *bs, buf, n_sectors * SECTOR_SIZE); memset(block + (sector_in_block + n_sectors) * SECTOR_SIZE, 0, (s->block_sectors - n_sectors - sector_in_block) * SECTOR_SIZE); + + /* Note that this coroutine does not yield anywhere from reading the + * bmap entry until here, so in regards to all the coroutines trying + * to write to this cluster, the one doing the allocation will + * always be the first to try to acquire the lock. + * Therefore, it is also the first that will actually be able to + * acquire the lock and thus the padded cluster is written before + * the other coroutines can write to the affected area. */ + qemu_co_mutex_lock(&s->write_lock); ret = bdrv_write(bs->file, offset, block, s->block_sectors); + qemu_co_mutex_unlock(&s->write_lock); } else { uint64_t offset = s->header.offset_data / SECTOR_SIZE + (uint64_t)bmap_entry * s->block_sectors + sector_in_block; + qemu_co_mutex_lock(&s->write_lock); + /* This lock is only used to make sure the following write operation + * is executed after the write issued by the coroutine allocating + * this cluster, therefore we do not need to keep it locked. + * As stated above, the allocating coroutine will always try to lock + * the mutex before all the other concurrent accesses to that + * cluster, therefore at this point we can be absolutely certain + * that that write operation has returned (there may be other writes + * in flight, but they do not concern this very operation). */ + qemu_co_mutex_unlock(&s->write_lock); ret = bdrv_write(bs->file, offset, buf, n_sectors); } diff --git a/block/vpc.c b/block/vpc.c index 46803b1..1533b6a 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -597,6 +597,51 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num, return ret; } +static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) +{ + BDRVVPCState *s = bs->opaque; + VHDFooter *footer = (VHDFooter*) s->footer_buf; + int64_t start, offset, next; + bool allocated; + int n; + + if (be32_to_cpu(footer->type) == VHD_FIXED) { + *pnum = nb_sectors; + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | + (sector_num << BDRV_SECTOR_BITS); + } + + offset = get_sector_offset(bs, sector_num, 0); + start = offset; + allocated = (offset != -1); + *pnum = 0; + + do { + /* All sectors in a block are contiguous (without using the bitmap) */ + n = ROUND_UP(sector_num + 1, s->block_size / BDRV_SECTOR_SIZE) + - sector_num; + n = MIN(n, nb_sectors); + + *pnum += n; + sector_num += n; + nb_sectors -= n; + next = start + (*pnum * BDRV_SECTOR_SIZE); + + if (nb_sectors == 0) { + break; + } + + offset = get_sector_offset(bs, sector_num, 0); + } while ((allocated && offset == next) || (!allocated && offset == -1)); + + if (allocated) { + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + } else { + return 0; + } +} + /* * Calculates the number of cylinders, heads and sectors per cylinder * based on a given number of sectors. This is the algorithm described @@ -801,6 +846,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) } total_sectors = (int64_t) cyls * heads * secs_per_cyl; + total_size = total_sectors * BDRV_SECTOR_SIZE; /* Prepare the Hard Disk Footer */ memset(buf, 0, 1024); @@ -822,13 +868,8 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) /* Version of Virtual PC 2007 */ footer->major = cpu_to_be16(0x0005); footer->minor = cpu_to_be16(0x0003); - if (disk_type == VHD_DYNAMIC) { - footer->orig_size = cpu_to_be64(total_sectors * 512); - footer->size = cpu_to_be64(total_sectors * 512); - } else { - footer->orig_size = cpu_to_be64(total_size); - footer->size = cpu_to_be64(total_size); - } + footer->orig_size = cpu_to_be64(total_size); + footer->size = cpu_to_be64(total_size); footer->cyls = cpu_to_be16(cyls); footer->heads = heads; footer->secs_per_cyl = secs_per_cyl; @@ -907,8 +948,9 @@ static BlockDriver bdrv_vpc = { .bdrv_reopen_prepare = vpc_reopen_prepare, .bdrv_create = vpc_create, - .bdrv_read = vpc_co_read, - .bdrv_write = vpc_co_write, + .bdrv_read = vpc_co_read, + .bdrv_write = vpc_co_write, + .bdrv_co_get_block_status = vpc_co_get_block_status, .bdrv_get_info = vpc_get_info, @@ -1474,6 +1474,7 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, uint32_t l; CPUState *cpu; uint8_t buf[1024]; + int64_t orig_addr = addr, orig_size = size; if (!has_cpu) { cpu_index = 0; @@ -1497,7 +1498,8 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, if (l > size) l = size; if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { - error_setg(errp, "Invalid addr 0x%016" PRIx64 "specified", addr); + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64 + " specified", orig_addr, orig_size); goto exit; } if (fwrite(buf, 1, l, f) != l) { diff --git a/disas/arm.c b/disas/arm.c index 76e97a8..6165246 100644 --- a/disas/arm.c +++ b/disas/arm.c @@ -1549,10 +1549,6 @@ enum map_type { MAP_DATA }; -enum map_type last_type; -int last_mapping_sym = -1; -bfd_vma last_mapping_addr = 0; - /* Decode a bitfield of the form matching regexp (N(-N)?,)*N(-N)?. Returns pointer to following character of the format string and fills in *VALUEP and *WIDTHP with the extracted value and number of @@ -3878,135 +3874,11 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) int is_data = false; unsigned int size = 4; void (*printer) (bfd_vma, struct disassemble_info *, long); -#if 0 - bfd_boolean found = false; - - if (info->disassembler_options) - { - parse_disassembler_options (info->disassembler_options); - - /* To avoid repeated parsing of these options, we remove them here. */ - info->disassembler_options = NULL; - } - - /* First check the full symtab for a mapping symbol, even if there - are no usable non-mapping symbols for this address. */ - if (info->symtab != NULL - && bfd_asymbol_flavour (*info->symtab) == bfd_target_elf_flavour) - { - bfd_vma addr; - int n; - int last_sym = -1; - enum map_type type = MAP_ARM; - - if (pc <= last_mapping_addr) - last_mapping_sym = -1; - is_thumb = (last_type == MAP_THUMB); - found = false; - /* Start scanning at the start of the function, or wherever - we finished last time. */ - n = info->symtab_pos + 1; - if (n < last_mapping_sym) - n = last_mapping_sym; - - /* Scan up to the location being disassembled. */ - for (; n < info->symtab_size; n++) - { - addr = bfd_asymbol_value (info->symtab[n]); - if (addr > pc) - break; - if ((info->section == NULL - || info->section == info->symtab[n]->section) - && get_sym_code_type (info, n, &type)) - { - last_sym = n; - found = true; - } - } - - if (!found) - { - n = info->symtab_pos; - if (n < last_mapping_sym - 1) - n = last_mapping_sym - 1; - - /* No mapping symbol found at this address. Look backwards - for a preceding one. */ - for (; n >= 0; n--) - { - if (get_sym_code_type (info, n, &type)) - { - last_sym = n; - found = true; - break; - } - } - } - - last_mapping_sym = last_sym; - last_type = type; - is_thumb = (last_type == MAP_THUMB); - is_data = (last_type == MAP_DATA); - - /* Look a little bit ahead to see if we should print out - two or four bytes of data. If there's a symbol, - mapping or otherwise, after two bytes then don't - print more. */ - if (is_data) - { - size = 4 - (pc & 3); - for (n = last_sym + 1; n < info->symtab_size; n++) - { - addr = bfd_asymbol_value (info->symtab[n]); - if (addr > pc) - { - if (addr - pc < size) - size = addr - pc; - break; - } - } - /* If the next symbol is after three bytes, we need to - print only part of the data, so that we can use either - .byte or .short. */ - if (size == 3) - size = (pc & 1) ? 1 : 2; - } - } - - if (info->symbols != NULL) - { - if (bfd_asymbol_flavour (*info->symbols) == bfd_target_coff_flavour) - { - coff_symbol_type * cs; - - cs = coffsymbol (*info->symbols); - is_thumb = ( cs->native->u.syment.n_sclass == C_THUMBEXT - || cs->native->u.syment.n_sclass == C_THUMBSTAT - || cs->native->u.syment.n_sclass == C_THUMBLABEL - || cs->native->u.syment.n_sclass == C_THUMBEXTFUNC - || cs->native->u.syment.n_sclass == C_THUMBSTATFUNC); - } - else if (bfd_asymbol_flavour (*info->symbols) == bfd_target_elf_flavour - && !found) - { - /* If no mapping symbol has been found then fall back to the type - of the function symbol. */ - elf_symbol_type * es; - unsigned int type; - - es = *(elf_symbol_type **)(info->symbols); - type = ELF_ST_TYPE (es->internal_elf_sym.st_info); - - is_thumb = (type == STT_ARM_TFUNC) || (type == STT_ARM_16BIT); - } - } -#else int little; little = (info->endian == BFD_ENDIAN_LITTLE); is_thumb |= (pc & 1); pc &= ~(bfd_vma)1; -#endif if (force_thumb) is_thumb = true; diff --git a/disas/microblaze.c b/disas/microblaze.c index ec91af3..c14ab89 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -275,7 +275,7 @@ enum microblaze_instr_type { #define MAX_OPCODES 280 -struct op_code_struct { +static struct op_code_struct { const char *name; short inst_type; /* registers and immediate values involved */ short inst_offset_type; /* immediate vals offset from PC? (= 1 for branches) */ @@ -567,10 +567,9 @@ struct op_code_struct { }; /* prefix for register names */ -char register_prefix[] = "r"; -char special_register_prefix[] = "spr"; -char fsl_register_prefix[] = "rfsl"; -char pvr_register_prefix[] = "rpvr"; +static const char register_prefix[] = "r"; +static const char fsl_register_prefix[] = "rfsl"; +static const char pvr_register_prefix[] = "rpvr"; /* #defines for valid immediate range */ @@ -738,7 +737,9 @@ get_field_special (long instr, struct op_code_struct * op) default : { if ( ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000) == REG_PVR_MASK) { - sprintf(tmpstr, "%spvr%d", register_prefix, (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ^ REG_PVR_MASK); + sprintf(tmpstr, "%s%u", pvr_register_prefix, + (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ + op->immval_mask) ^ REG_PVR_MASK); return(strdup(tmpstr)); } else { strcpy(spr, "pc"); @@ -548,6 +548,7 @@ void cpu_exec_init(CPUArchState *env) #ifndef CONFIG_USER_ONLY cpu->as = &address_space_memory; cpu->thread_id = qemu_get_thread_id(); + cpu_reload_memory_map(cpu); #endif QTAILQ_INSERT_TAIL(&cpus, cpu, node); #if defined(CONFIG_USER_ONLY) @@ -1188,7 +1189,7 @@ static void *file_ram_alloc(RAMBlock *block, error: if (mem_prealloc) { - error_report("%s\n", error_get_pretty(*errp)); + error_report("%s", error_get_pretty(*errp)); exit(1); } return NULL; @@ -1443,15 +1443,17 @@ void gdb_exit(CPUArchState *env, int code) if (gdbserver_fd < 0 || s->fd < 0) { return; } +#else + if (!s->chr) { + return; + } #endif snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); put_packet(s, buf); #ifndef CONFIG_USER_ONLY - if (s->chr) { - qemu_chr_delete(s->chr); - } + qemu_chr_delete(s->chr); #endif } diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c index a183eee..d05c917 100644 --- a/hw/9pfs/virtio-9p-local.c +++ b/hw/9pfs/virtio-9p-local.c @@ -332,7 +332,6 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, tsize = read(fd, (void *)buf, bufsz); } while (tsize == -1 && errno == EINTR); close(fd); - return tsize; } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || (fs_ctx->export_flags & V9FS_SM_NONE)) { buffer = rpath(fs_ctx, path); diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index 2a4b872..7114c36 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -65,7 +65,7 @@ static void digic4_board_init(DigicBoard *board) s->digic = DIGIC(object_new(TYPE_DIGIC)); object_property_set_bool(OBJECT(s->digic), true, "realized", &err); if (err != NULL) { - error_report("Couldn't realize DIGIC SoC: %s\n", + error_report("Couldn't realize DIGIC SoC: %s", error_get_pretty(err)); exit(1); } @@ -104,13 +104,13 @@ static void digic_load_rom(DigicBoardState *s, hwaddr addr, char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); if (!fn) { - error_report("Couldn't find rom image '%s'.\n", filename); + error_report("Couldn't find rom image '%s'.", filename); exit(1); } rom_size = load_image_targphys(fn, addr, max_size); if (rom_size < 0 || rom_size > max_size) { - error_report("Couldn't load rom image '%s'.\n", filename); + error_report("Couldn't load rom image '%s'.", filename); exit(1); } } diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 5933454..8496c16 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -515,9 +515,9 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, { DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); - if (di && qdev_prop_set_drive(dev, "drive", - blk_by_legacy_dinfo(di))) { - abort(); + if (di) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di), + &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 69f51ac..93b7605 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -522,9 +522,9 @@ static void create_one_flash(const char *name, hwaddr flashbase, DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); const uint64_t sectorlength = 256 * 1024; - if (dinfo && qdev_prop_set_drive(dev, "drive", - blk_by_legacy_dinfo(dinfo))) { - abort(); + if (dinfo) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", flashsize / sectorlength); diff --git a/hw/block/block.c b/hw/block/block.c index a625773..f7243e5 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -25,6 +25,30 @@ void blkconf_serial(BlockConf *conf, char **serial) } } +void blkconf_blocksizes(BlockConf *conf) +{ + BlockBackend *blk = conf->blk; + BlockSizes blocksizes; + int backend_ret; + + backend_ret = blk_probe_blocksizes(blk, &blocksizes); + /* fill in detected values if they are not defined via qemu command line */ + if (!conf->physical_block_size) { + if (!backend_ret) { + conf->physical_block_size = blocksizes.phys; + } else { + conf->physical_block_size = BDRV_SECTOR_SIZE; + } + } + if (!conf->logical_block_size) { + if (!backend_ret) { + conf->logical_block_size = blocksizes.log; + } else { + conf->logical_block_size = BDRV_SECTOR_SIZE; + } + } +} + void blkconf_geometry(BlockConf *conf, int *ptrans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp) diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index 6fcf74d..b187878 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -121,8 +121,16 @@ void hd_geometry_guess(BlockBackend *blk, int *ptrans) { int cylinders, heads, secs, translation; + HDGeometry geo; - if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) { + /* Try to probe the backing device geometry, otherwise fallback + to the old logic. (as of 12/2014 probing only succeeds on DASDs) */ + if (blk_probe_geometry(blk, &geo) == 0) { + *pcyls = geo.cylinders; + *psecs = geo.sectors; + *pheads = geo.heads; + translation = BIOS_ATA_TRANSLATION_NONE; + } else if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) { /* no LCHS guess: use a standard physical disk geometry */ guess_chs_for_size(blk, pcyls, pheads, psecs); translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs); diff --git a/hw/block/nand.c b/hw/block/nand.c index 1882a0c..61d2cec 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -393,7 +393,7 @@ static void nand_realize(DeviceState *dev, Error **errp) nand_init_2048(s); break; default: - error_setg(errp, "Unsupported NAND block size %#x\n", + error_setg(errp, "Unsupported NAND block size %#x", 1 << s->page_shift); return; } diff --git a/hw/block/nvme.c b/hw/block/nvme.c index ce079ae..0f3dfb9 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -765,6 +765,7 @@ static int nvme_init(PCIDevice *pci_dev) if (!n->serial) { return -1; } + blkconf_blocksizes(&n->conf); pci_conf = pci_dev->config; pci_conf[PCI_INTERRUPT_PIN] = 1; diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 89d380e..d282695 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -969,8 +969,8 @@ pflash_t *pflash_cfi01_register(hwaddr base, { DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); - if (blk && qdev_prop_set_drive(dev, "drive", blk)) { - abort(); + if (blk) { + qdev_prop_set_drive(dev, "drive", blk, &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); qdev_prop_set_uint64(dev, "sector-length", sector_len); diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 389b4aa..074a005 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -773,8 +773,8 @@ pflash_t *pflash_cfi02_register(hwaddr base, { DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02); - if (blk && qdev_prop_set_drive(dev, "drive", blk)) { - abort(); + if (blk) { + qdev_prop_set_drive(dev, "drive", blk, &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); qdev_prop_set_uint32(dev, "sector-length", sector_len); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 1e5b918..000c38d 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -201,6 +201,7 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) #ifdef __linux__ int i; VirtIOBlockIoctlReq *ioctl_req; + BlockAIOCB *acb; #endif /* @@ -278,8 +279,13 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) ioctl_req->hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; ioctl_req->hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; - blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr, - virtio_blk_ioctl_complete, ioctl_req); + acb = blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr, + virtio_blk_ioctl_complete, ioctl_req); + if (!acb) { + g_free(ioctl_req); + status = VIRTIO_BLK_S_UNSUPP; + goto fail; + } return -EINPROGRESS; #else abort(); @@ -591,12 +597,6 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) if (mrb.num_reqs) { virtio_blk_submit_multireq(s->blk, &mrb); } - - /* - * FIXME: Want to check for completions before returning to guest mode, - * so cached reads and writes are reported as quickly as possible. But - * that should be done in the generic block layer. - */ } static void virtio_blk_dma_restart_bh(void *opaque) @@ -884,6 +884,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } + blkconf_blocksizes(&conf->conf); virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config)); diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index a2e44bd..c413226 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -341,27 +341,25 @@ PropertyInfo qdev_prop_vlan = { .set = set_vlan, }; -int qdev_prop_set_drive(DeviceState *dev, const char *name, - BlockBackend *value) +void qdev_prop_set_drive(DeviceState *dev, const char *name, + BlockBackend *value, Error **errp) { - Error *err = NULL; - object_property_set_str(OBJECT(dev), - value ? blk_name(value) : "", name, &err); - if (err) { - qerror_report_err(err); - error_free(err); - return -1; - } - return 0; + object_property_set_str(OBJECT(dev), value ? blk_name(value) : "", + name, errp); } void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockBackend *value) { - if (qdev_prop_set_drive(dev, name, value) < 0) { + Error *err = NULL; + + qdev_prop_set_drive(dev, name, value, &err); + if (err) { + error_report_err(err); exit(1); } } + void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value) { diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 5a4e4d5..570d5f0 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -580,7 +580,8 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque, error_propagate(errp, local_err); return; } - if (value < min || value > max) { + /* value of 0 means "unset" */ + if (value && (value < min || value > max)) { error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, dev->id?:"", name, (int64_t)value, min, max); return; diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 84af593..b53c351 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -91,6 +91,8 @@ bool sysbus_has_irq(SysBusDevice *dev, int n) ObjectProperty *r; r = object_property_find(OBJECT(dev), prop, NULL); + g_free(prop); + return (r != NULL); } diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index 16cf77e..5be3df5 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -1566,7 +1566,7 @@ static void pl330_realize(DeviceState *dev, Error **errp) s->cfg[1] |= 5; break; default: - error_setg(errp, "Bad value for i-cache_len property: %" PRIx8 "\n", + error_setg(errp, "Bad value for i-cache_len property: %" PRIx8, s->i_cache_len); return; } @@ -1601,7 +1601,7 @@ static void pl330_realize(DeviceState *dev, Error **errp) s->cfg[CFG_CRD] |= 0x4; break; default: - error_setg(errp, "Bad value for data_width property: %" PRIx8 "\n", + error_setg(errp, "Bad value for data_width property: %" PRIx8, s->data_width); return; } diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 5651372..e1ae36f 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1160,6 +1160,11 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s, dma_cb(s, 0); } +static void ahci_restart_dma(IDEDMA *dma) +{ + /* Nothing to do, ahci_start_dma already resets s->io_buffer_offset. */ +} + /** * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist. * Not currently invoked by PIO R/W chains, @@ -1226,12 +1231,6 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) return 1; } -static int ahci_dma_set_unit(IDEDMA *dma, int unit) -{ - /* only a single unit per link */ - return 0; -} - static void ahci_cmd_done(IDEDMA *dma) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); @@ -1252,19 +1251,14 @@ static void ahci_irq_set(void *opaque, int n, int level) { } -static void ahci_dma_restart_cb(void *opaque, int running, RunState state) -{ -} - static const IDEDMAOps ahci_dma_ops = { .start_dma = ahci_start_dma, + .restart_dma = ahci_restart_dma, .start_transfer = ahci_start_transfer, .prepare_buf = ahci_dma_prepare_buf, .commit_buf = ahci_commit_buf, .rw_buf = ahci_dma_rw_buf, - .set_unit = ahci_dma_set_unit, .cmd_done = ahci_cmd_done, - .restart_cb = ahci_dma_restart_cb, }; void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) @@ -1294,6 +1288,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) ad->port_no = i; ad->port.dma = &ad->dma; ad->port.dma->ops = &ahci_dma_ops; + ide_register_restart_cb(&ad->port); } } @@ -1333,6 +1328,7 @@ static const VMStateDescription vmstate_ahci_device = { .version_id = 1, .fields = (VMStateField[]) { VMSTATE_IDE_BUS(port, AHCIDevice), + VMSTATE_IDE_DRIVE(port.ifs[0], AHCIDevice), VMSTATE_UINT32(port_state, AHCIDevice), VMSTATE_UINT32(finished, AHCIDevice), VMSTATE_UINT32(port_regs.lst_addr, AHCIDevice), @@ -1371,16 +1367,23 @@ static int ahci_state_post_load(void *opaque, int version_id) map_page(s->as, &ad->res_fis, ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); /* - * All pending i/o should be flushed out on a migrate. However, - * we might not have cleared the busy_slot since this is done - * in a bh. Also, issue i/o against any slots that are pending. + * If an error is present, ad->busy_slot will be valid and not -1. + * In this case, an operation is waiting to resume and will re-check + * for additional AHCI commands to execute upon completion. + * + * In the case where no error was present, busy_slot will be -1, + * and we should check to see if there are additional commands waiting. */ - if ((ad->busy_slot != -1) && - !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { - pr->cmd_issue &= ~(1 << ad->busy_slot); - ad->busy_slot = -1; + if (ad->busy_slot == -1) { + check_cmd(s, i); + } else { + /* We are in the middle of a command, and may need to access + * the command header in guest memory again. */ + if (ad->busy_slot < 0 || ad->busy_slot >= AHCI_MAX_CMDS) { + return -1; + } + ad->cur_cmd = &((AHCICmdHdr *)ad->lst)[ad->busy_slot]; } - check_cmd(s, i); } return 0; diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 1bf8b34..950e311 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -252,7 +252,6 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) s->packet_transfer_size = size; s->io_buffer_size = size; /* dma: send the reply data as one chunk */ s->elementary_transfer_size = 0; - s->io_buffer_index = 0; if (s->atapi_dma) { block_acct_start(blk_get_stats(s->blk), &s->acct, size, @@ -261,6 +260,7 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) ide_start_dma(s, ide_atapi_cmd_read_dma_cb); } else { s->status = READY_STAT | SEEK_STAT; + s->io_buffer_index = 0; ide_atapi_cmd_reply_end(s); } } @@ -368,7 +368,6 @@ static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, { s->lba = lba; s->packet_transfer_size = nb_sectors * sector_size; - s->io_buffer_index = 0; s->io_buffer_size = 0; s->cd_sector_size = sector_size; diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index dafa9ec..66fb9d9 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -368,8 +368,7 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, - &d->bmdma[i].dma); + ide_register_restart_cb(&d->bus[i]); } vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); diff --git a/hw/ide/core.c b/hw/ide/core.c index ac3f015..ef52f35 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -561,6 +561,8 @@ static bool ide_sect_range_ok(IDEState *s, return true; } +static void ide_sector_read(IDEState *s); + static void ide_sector_read_cb(void *opaque, int ret) { IDEState *s = opaque; @@ -595,7 +597,7 @@ static void ide_sector_read_cb(void *opaque, int ret) s->io_buffer_offset += 512 * n; } -void ide_sector_read(IDEState *s) +static void ide_sector_read(IDEState *s) { int64_t sector_num; int n; @@ -646,6 +648,9 @@ static void dma_buf_commit(IDEState *s, uint32_t tx_bytes) void ide_set_inactive(IDEState *s, bool more) { s->bus->dma->aiocb = NULL; + s->bus->retry_unit = -1; + s->bus->retry_sector_num = 0; + s->bus->retry_nsector = 0; if (s->bus->dma->ops->set_inactive) { s->bus->dma->ops->set_inactive(s->bus->dma, more); } @@ -666,7 +671,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) BlockErrorAction action = blk_get_error_action(s->blk, is_read, error); if (action == BLOCK_ERROR_ACTION_STOP) { - s->bus->dma->ops->set_unit(s->bus->dma, s->unit); + assert(s->bus->retry_unit == s->unit); s->bus->error_status = op; } else if (action == BLOCK_ERROR_ACTION_REPORT) { if (op & IDE_RETRY_DMA) { @@ -679,7 +684,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) return action != BLOCK_ERROR_ACTION_IGNORE; } -void ide_dma_cb(void *opaque, int ret) +static void ide_dma_cb(void *opaque, int ret) { IDEState *s = opaque; int n; @@ -777,7 +782,6 @@ eot: static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) { s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; - s->io_buffer_index = 0; s->io_buffer_size = 0; s->dma_cmd = dma_cmd; @@ -799,11 +803,17 @@ static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) void ide_start_dma(IDEState *s, BlockCompletionFunc *cb) { + s->io_buffer_index = 0; + s->bus->retry_unit = s->unit; + s->bus->retry_sector_num = ide_get_sector(s); + s->bus->retry_nsector = s->nsector; if (s->bus->dma->ops->start_dma) { s->bus->dma->ops->start_dma(s->bus->dma, s, cb); } } +static void ide_sector_write(IDEState *s); + static void ide_sector_write_timer_cb(void *opaque) { IDEState *s = opaque; @@ -863,7 +873,7 @@ static void ide_sector_write_cb(void *opaque, int ret) } } -void ide_sector_write(IDEState *s) +static void ide_sector_write(IDEState *s) { int64_t sector_num; int n; @@ -917,7 +927,7 @@ static void ide_flush_cb(void *opaque, int ret) ide_set_irq(s->bus); } -void ide_flush_cache(IDEState *s) +static void ide_flush_cache(IDEState *s) { if (s->blk == NULL) { ide_flush_cb(s, 0); @@ -2314,22 +2324,101 @@ static int ide_nop_int(IDEDMA *dma, int x) return 0; } -static int32_t ide_nop_int32(IDEDMA *dma, int x) +static void ide_nop(IDEDMA *dma) { - return 0; } -static void ide_nop_restart(void *opaque, int x, RunState y) +static int32_t ide_nop_int32(IDEDMA *dma, int x) { + return 0; } static const IDEDMAOps ide_dma_nop_ops = { .prepare_buf = ide_nop_int32, + .restart_dma = ide_nop, .rw_buf = ide_nop_int, - .set_unit = ide_nop_int, - .restart_cb = ide_nop_restart, }; +static void ide_restart_dma(IDEState *s, enum ide_dma_cmd dma_cmd) +{ + s->unit = s->bus->retry_unit; + ide_set_sector(s, s->bus->retry_sector_num); + s->nsector = s->bus->retry_nsector; + s->bus->dma->ops->restart_dma(s->bus->dma); + s->io_buffer_size = 0; + s->dma_cmd = dma_cmd; + ide_start_dma(s, ide_dma_cb); +} + +static void ide_restart_bh(void *opaque) +{ + IDEBus *bus = opaque; + IDEState *s; + bool is_read; + int error_status; + + qemu_bh_delete(bus->bh); + bus->bh = NULL; + + error_status = bus->error_status; + if (bus->error_status == 0) { + return; + } + + s = idebus_active_if(bus); + is_read = (bus->error_status & IDE_RETRY_READ) != 0; + + /* The error status must be cleared before resubmitting the request: The + * request may fail again, and this case can only be distinguished if the + * called function can set a new error status. */ + bus->error_status = 0; + + if (error_status & IDE_RETRY_DMA) { + if (error_status & IDE_RETRY_TRIM) { + ide_restart_dma(s, IDE_DMA_TRIM); + } else { + ide_restart_dma(s, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); + } + } else if (error_status & IDE_RETRY_PIO) { + if (is_read) { + ide_sector_read(s); + } else { + ide_sector_write(s); + } + } else if (error_status & IDE_RETRY_FLUSH) { + ide_flush_cache(s); + } else { + /* + * We've not got any bits to tell us about ATAPI - but + * we do have the end_transfer_func that tells us what + * we're trying to do. + */ + if (s->end_transfer_func == ide_atapi_cmd) { + ide_atapi_dma_restart(s); + } + } +} + +static void ide_restart_cb(void *opaque, int running, RunState state) +{ + IDEBus *bus = opaque; + + if (!running) + return; + + if (!bus->bh) { + bus->bh = qemu_bh_new(ide_restart_bh, bus); + qemu_bh_schedule(bus->bh); + } +} + +void ide_register_restart_cb(IDEBus *bus) +{ + if (bus->dma->ops->restart_dma) { + qemu_add_vm_change_state_handler(ide_restart_cb, bus); + } +} + static IDEDMA ide_dma_nop = { .ops = &ide_dma_nop_ops, .aiocb = NULL, @@ -2557,10 +2646,13 @@ const VMStateDescription vmstate_ide_drive = { static const VMStateDescription vmstate_ide_error_status = { .name ="ide_bus/error", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_INT32(error_status, IDEBus), + VMSTATE_INT64_V(retry_sector_num, IDEBus, 2), + VMSTATE_UINT32_V(retry_nsector, IDEBus, 2), + VMSTATE_UINT8_V(retry_unit, IDEBus, 2), VMSTATE_END_OF_LIST() } }; diff --git a/hw/ide/internal.h b/hw/ide/internal.h index ee9a57f..965cc55 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -436,10 +436,9 @@ struct IDEDMAOps { DMAInt32Func *prepare_buf; DMAu32Func *commit_buf; DMAIntFunc *rw_buf; - DMAIntFunc *set_unit; + DMAVoidFunc *restart_dma; DMAStopFunc *set_inactive; DMAVoidFunc *cmd_done; - DMARestartFunc *restart_cb; DMAVoidFunc *reset; }; @@ -455,6 +454,8 @@ struct IDEBus { IDEDevice *master; IDEDevice *slave; IDEState ifs[2]; + QEMUBH *bh; + int bus_id; int max_units; IDEDMA *dma; @@ -463,6 +464,9 @@ struct IDEBus { qemu_irq irq; int error_status; + uint8_t retry_unit; + int64_t retry_sector_num; + uint32_t retry_nsector; }; #define TYPE_IDE_DEVICE "ide-device" @@ -522,6 +526,9 @@ extern const VMStateDescription vmstate_ide_drive; #define VMSTATE_IDE_DRIVES(_field, _state) \ VMSTATE_STRUCT_ARRAY(_field, _state, 2, 3, vmstate_ide_drive, IDEState) +#define VMSTATE_IDE_DRIVE(_field, _state) \ + VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_drive, IDEState) + void ide_bus_reset(IDEBus *bus); int64_t ide_get_sector(IDEState *s); void ide_set_sector(IDEState *s, int64_t sector_num); @@ -550,12 +557,9 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, int chs_trans); void ide_init2(IDEBus *bus, qemu_irq irq); void ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); +void ide_register_restart_cb(IDEBus *bus); void ide_exec_cmd(IDEBus *bus, uint32_t val); -void ide_dma_cb(void *opaque, int ret); -void ide_sector_write(IDEState *s); -void ide_sector_read(IDEState *s); -void ide_flush_cache(IDEState *s); void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func); diff --git a/hw/ide/isa.c b/hw/ide/isa.c index c0c4e1b..9f80503 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -74,7 +74,8 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) isa_init_irq(isadev, &s->irq, s->isairq); ide_init2(&s->bus, s->irq); vmstate_register(dev, 0, &vmstate_ide_isa, s); -}; + ide_register_restart_cb(&s->bus); +} ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, DriveInfo *hd0, DriveInfo *hd1) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index f6074f2..a009674 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -558,10 +558,6 @@ static int32_t ide_nop_int32(IDEDMA *dma, int x) return 0; } -static void ide_nop_restart(void *opaque, int x, RunState y) -{ -} - static void ide_dbdma_start(IDEDMA *dma, IDEState *s, BlockCompletionFunc *cb) { @@ -576,8 +572,6 @@ static const IDEDMAOps dbdma_ops = { .start_dma = ide_dbdma_start, .prepare_buf = ide_nop_int32, .rw_buf = ide_nop_int, - .set_unit = ide_nop_int, - .restart_cb = ide_nop_restart, }; static void macio_ide_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index e3f2054..1b3d1c1 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -42,13 +42,10 @@ static void bmdma_start_dma(IDEDMA *dma, IDEState *s, { BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - bm->unit = s->unit; bm->dma_cb = dma_cb; bm->cur_prd_last = 0; bm->cur_prd_addr = 0; bm->cur_prd_len = 0; - bm->sector_num = ide_get_sector(s); - bm->nsector = s->nsector; if (bm->status & BM_STATUS_DMAING) { bm->dma_cb(bmdma_active_if(bm), 0); @@ -99,7 +96,7 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int is_write) * This should accommodate the largest ATA transaction * for LBA48 (65,536 sectors) and 32K sector sizes. */ if (s->sg.size > INT32_MAX) { - error_report("IDE: sglist describes more than 2GiB.\n"); + error_report("IDE: sglist describes more than 2GiB."); break; } bm->cur_prd_addr += l; @@ -163,20 +160,11 @@ static int bmdma_rw_buf(IDEDMA *dma, int is_write) return 1; } -static int bmdma_set_unit(IDEDMA *dma, int unit) -{ - BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - bm->unit = unit; - - return 0; -} - static void bmdma_set_inactive(IDEDMA *dma, bool more) { BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); bm->dma_cb = NULL; - bm->unit = -1; if (more) { bm->status |= BM_STATUS_DMAING; } else { @@ -184,83 +172,11 @@ static void bmdma_set_inactive(IDEDMA *dma, bool more) } } -static void bmdma_restart_dma(BMDMAState *bm, enum ide_dma_cmd dma_cmd) -{ - IDEState *s = bmdma_active_if(bm); - - ide_set_sector(s, bm->sector_num); - s->io_buffer_index = 0; - s->io_buffer_size = 0; - s->nsector = bm->nsector; - s->dma_cmd = dma_cmd; - bm->cur_addr = bm->addr; - bm->dma_cb = ide_dma_cb; - bmdma_start_dma(&bm->dma, s, bm->dma_cb); -} - -/* TODO This should be common IDE code */ -static void bmdma_restart_bh(void *opaque) -{ - BMDMAState *bm = opaque; - IDEBus *bus = bm->bus; - bool is_read; - int error_status; - - qemu_bh_delete(bm->bh); - bm->bh = NULL; - - if (bm->unit == (uint8_t) -1) { - return; - } - - is_read = (bus->error_status & IDE_RETRY_READ) != 0; - - /* The error status must be cleared before resubmitting the request: The - * request may fail again, and this case can only be distinguished if the - * called function can set a new error status. */ - error_status = bus->error_status; - bus->error_status = 0; - - if (error_status & IDE_RETRY_DMA) { - if (error_status & IDE_RETRY_TRIM) { - bmdma_restart_dma(bm, IDE_DMA_TRIM); - } else { - bmdma_restart_dma(bm, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); - } - } else if (error_status & IDE_RETRY_PIO) { - if (is_read) { - ide_sector_read(bmdma_active_if(bm)); - } else { - ide_sector_write(bmdma_active_if(bm)); - } - } else if (error_status & IDE_RETRY_FLUSH) { - ide_flush_cache(bmdma_active_if(bm)); - } else { - IDEState *s = bmdma_active_if(bm); - - /* - * We've not got any bits to tell us about ATAPI - but - * we do have the end_transfer_func that tells us what - * we're trying to do. - */ - if (s->end_transfer_func == ide_atapi_cmd) { - ide_atapi_dma_restart(s); - } - } -} - -static void bmdma_restart_cb(void *opaque, int running, RunState state) +static void bmdma_restart_dma(IDEDMA *dma) { - IDEDMA *dma = opaque; BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - if (!running) - return; - - if (!bm->bh) { - bm->bh = qemu_bh_new(bmdma_restart_bh, &bm->dma); - qemu_bh_schedule(bm->bh); - } + bm->cur_addr = bm->addr; } static void bmdma_cancel(BMDMAState *bm) @@ -286,8 +202,6 @@ static void bmdma_reset(IDEDMA *dma) bm->cur_prd_last = 0; bm->cur_prd_addr = 0; bm->cur_prd_len = 0; - bm->sector_num = 0; - bm->nsector = 0; } static void bmdma_irq(void *opaque, int n, int level) @@ -404,6 +318,9 @@ static void ide_bmdma_pre_save(void *opaque) BMDMAState *bm = opaque; uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS; + bm->migration_retry_unit = bm->bus->retry_unit; + bm->migration_retry_sector_num = bm->bus->retry_sector_num; + bm->migration_retry_nsector = bm->bus->retry_nsector; bm->migration_compat_status = (bm->status & ~abused_bits) | (bm->bus->error_status & abused_bits); } @@ -420,6 +337,11 @@ static int ide_bmdma_post_load(void *opaque, int version_id) bm->status = bm->migration_compat_status & ~abused_bits; bm->bus->error_status |= bm->migration_compat_status & abused_bits; } + if (bm->bus->error_status) { + bm->bus->retry_sector_num = bm->migration_retry_sector_num; + bm->bus->retry_nsector = bm->migration_retry_nsector; + bm->bus->retry_unit = bm->migration_retry_unit; + } return 0; } @@ -456,9 +378,9 @@ static const VMStateDescription vmstate_bmdma = { VMSTATE_UINT8(cmd, BMDMAState), VMSTATE_UINT8(migration_compat_status, BMDMAState), VMSTATE_UINT32(addr, BMDMAState), - VMSTATE_INT64(sector_num, BMDMAState), - VMSTATE_UINT32(nsector, BMDMAState), - VMSTATE_UINT8(unit, BMDMAState), + VMSTATE_INT64(migration_retry_sector_num, BMDMAState), + VMSTATE_UINT32(migration_retry_nsector, BMDMAState), + VMSTATE_UINT8(migration_retry_unit, BMDMAState), VMSTATE_END_OF_LIST() }, .subsections = (VMStateSubsection []) { @@ -482,7 +404,7 @@ static int ide_pci_post_load(void *opaque, int version_id) for(i = 0; i < 2; i++) { /* current versions always store 0/1, but older version stored bigger values. We only need last bit */ - d->bmdma[i].unit &= 1; + d->bmdma[i].migration_retry_unit &= 1; ide_bmdma_post_load(&d->bmdma[i], -1); } @@ -523,9 +445,8 @@ static const struct IDEDMAOps bmdma_ops = { .start_dma = bmdma_start_dma, .prepare_buf = bmdma_prepare_buf, .rw_buf = bmdma_rw_buf, - .set_unit = bmdma_set_unit, + .restart_dma = bmdma_restart_dma, .set_inactive = bmdma_set_inactive, - .restart_cb = bmdma_restart_cb, .reset = bmdma_reset, }; diff --git a/hw/ide/pci.h b/hw/ide/pci.h index 2e9314a..0f2d4b9 100644 --- a/hw/ide/pci.h +++ b/hw/ide/pci.h @@ -22,18 +22,18 @@ typedef struct BMDMAState { uint32_t cur_prd_last; uint32_t cur_prd_addr; uint32_t cur_prd_len; - uint8_t unit; BlockCompletionFunc *dma_cb; - int64_t sector_num; - uint32_t nsector; MemoryRegion addr_ioport; MemoryRegion extra_io; - QEMUBH *bh; qemu_irq irq; /* Bit 0-2 and 7: BM status register * Bit 3-6: bus->error_status */ uint8_t migration_compat_status; + uint8_t migration_retry_unit; + int64_t migration_retry_sector_num; + uint32_t migration_retry_nsector; + struct PCIIDEState *pci_dev; } BMDMAState; @@ -62,8 +62,8 @@ typedef struct PCIIDEState { static inline IDEState *bmdma_active_if(BMDMAState *bmdma) { - assert(bmdma->unit != (uint8_t)-1); - return bmdma->bus->ifs + bmdma->unit; + assert(bmdma->bus->retry_unit != (uint8_t)-1); + return bmdma->bus->ifs + bmdma->bus->retry_unit; } diff --git a/hw/ide/piix.c b/hw/ide/piix.c index ab93084..adb6649 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -143,8 +143,7 @@ static void pci_piix_init_ports(PCIIDEState *d) { bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, - &d->bmdma[i].dma); + ide_register_restart_cb(&d->bus[i]); } } diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index b4103fa..788b361 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -163,6 +163,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) return -1; } + blkconf_blocksizes(&dev->conf); if (dev->conf.logical_block_size != 512) { error_report("logical_block_size must be 512 for IDE"); return -1; diff --git a/hw/ide/via.c b/hw/ide/via.c index 7a5151c..e2da9ef 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -166,8 +166,7 @@ static void vt82c686b_init_ports(PCIIDEState *d) { bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, - &d->bmdma[i].dma); + ide_register_restart_cb(&d->bus[i]); } } diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index 256c102..7f62261 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -155,6 +155,7 @@ milkymist_init(MachineState *machine) bios_name); exit(1); } + g_free(bios_filename); milkymist_uart_create(0x60000000, irq[0]); milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3], diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index a2843cd..38c59db 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -185,7 +185,7 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, ram_size - initrd_offset); } if (initrd_size < 0) { - error_report("qemu: could not load initrd '%s'\n", + error_report("qemu: could not load initrd '%s'", initrd_filename); exit(EXIT_FAILURE); } diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 9bc3f2d..063ad80 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -273,7 +273,7 @@ static int macio_newworld_initfn(PCIDevice *d) MacIOState *s = MACIO(d); NewWorldMacIOState *ns = NEWWORLD_MACIO(d); SysBusDevice *sysbus_dev; - MemoryRegion *timer_memory = g_new(MemoryRegion, 1); + MemoryRegion *timer_memory = NULL; int i; int cur_irq = 0; int ret = macio_common_initfn(d); @@ -301,6 +301,7 @@ static int macio_newworld_initfn(PCIDevice *d) } /* Timer */ + timer_memory = g_new(MemoryRegion, 1); memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer", 0x1000); memory_region_add_subregion(&s->bar, 0x15000, timer_memory); diff --git a/hw/misc/milkymist-pfpu.c b/hw/misc/milkymist-pfpu.c index 609f33f..08b604f 100644 --- a/hw/misc/milkymist-pfpu.c +++ b/hw/misc/milkymist-pfpu.c @@ -362,7 +362,7 @@ static void pfpu_start(MilkymistPFPUState *s) i = 0; while (pfpu_decode_insn(s)) { /* decode at most MICROCODE_WORDS instructions */ - if (i++ >= MICROCODE_WORDS) { + if (++i >= MICROCODE_WORDS) { error_report("milkymist_pfpu: too many instructions " "executed in microcode. No VECTOUT?"); break; diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 2ea1ef1..cf23335 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -56,7 +56,7 @@ static const int kernel_feature_bits[] = { }; /* Features supported by others. */ -const int user_feature_bits[] = { +static const int user_feature_bits[] = { VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 1187ab8..27adcc5 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -120,8 +120,8 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) return; } - if (!!n->vhost_started == - (virtio_net_started(n, status) && !nc->peer->link_down)) { + if ((virtio_net_started(n, status) && !nc->peer->link_down) == + !!n->vhost_started) { return; } if (!n->vhost_started) { diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 9536f64..ad6b553 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -146,6 +146,7 @@ eth_write(void *opaque, hwaddr addr, if (!(value & CTRL_S)) { qemu_flush_queued_packets(qemu_get_queue(s->nic)); } + /* fall through */ case R_TX_LEN0: case R_TX_LEN1: case R_TX_GIE0: diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index fd0d138..d51fb60 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -308,6 +308,7 @@ static int ppce500_load_device_tree(MachineState *machine, } fdt = load_device_tree(filename, &fdt_size); + g_free(filename); if (!fdt) { goto out; } diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index 00b7297..8d2242d 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -342,13 +342,12 @@ static const struct SCSIBusInfo esp_pci_scsi_info = { .cancel = esp_request_cancelled, }; -static int esp_pci_scsi_init(PCIDevice *dev) +static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) { PCIESPState *pci = PCI_ESP(dev); DeviceState *d = DEVICE(dev); ESPState *s = &pci->esp; uint8_t *pci_conf; - Error *err = NULL; pci_conf = dev->config; @@ -367,13 +366,8 @@ static int esp_pci_scsi_init(PCIDevice *dev) scsi_bus_new(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info, NULL); if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - return 0; } static void esp_pci_scsi_uninit(PCIDevice *d) @@ -388,7 +382,7 @@ static void esp_pci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = esp_pci_scsi_init; + k->realize = esp_pci_scsi_realize; k->exit = esp_pci_scsi_uninit; k->vendor_id = PCI_VENDOR_ID_AMD; k->device_id = PCI_DEVICE_ID_AMD_SCSI; @@ -466,17 +460,19 @@ static void dc390_write_config(PCIDevice *dev, } } -static int dc390_scsi_init(PCIDevice *dev) +static void dc390_scsi_realize(PCIDevice *dev, Error **errp) { DC390State *pci = DC390(dev); + Error *err = NULL; uint8_t *contents; uint16_t chksum = 0; - int i, ret; + int i; /* init base class */ - ret = esp_pci_scsi_init(dev); - if (ret < 0) { - return ret; + esp_pci_scsi_realize(dev, &err); + if (err) { + error_propagate(errp, err); + return; } /* EEPROM */ @@ -503,8 +499,6 @@ static int dc390_scsi_init(PCIDevice *dev) chksum = 0x1234 - chksum; contents[EE_CHKSUM1] = chksum & 0xff; contents[EE_CHKSUM2] = chksum >> 8; - - return 0; } static void dc390_class_init(ObjectClass *klass, void *data) @@ -512,7 +506,7 @@ static void dc390_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = dc390_scsi_init; + k->realize = dc390_scsi_realize; k->config_read = dc390_read_config; k->config_write = dc390_write_config; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index db7d4b8..c5b0cc5 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -19,7 +19,6 @@ #include "hw/pci/pci.h" #include "hw/scsi/scsi.h" #include "sysemu/dma.h" -#include "qemu/error-report.h" //#define DEBUG_LSI //#define DEBUG_LSI_REG @@ -2089,12 +2088,11 @@ static const struct SCSIBusInfo lsi_scsi_info = { .cancel = lsi_request_cancelled }; -static int lsi_scsi_init(PCIDevice *dev) +static void lsi_scsi_realize(PCIDevice *dev, Error **errp) { LSIState *s = LSI53C895A(dev); DeviceState *d = DEVICE(dev); uint8_t *pci_conf; - Error *err = NULL; pci_conf = dev->config; @@ -2117,13 +2115,8 @@ static int lsi_scsi_init(PCIDevice *dev) scsi_bus_new(&s->bus, sizeof(s->bus), d, &lsi_scsi_info, NULL); if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - return 0; } static void lsi_class_init(ObjectClass *klass, void *data) @@ -2131,7 +2124,7 @@ static void lsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = lsi_scsi_init; + k->realize = lsi_scsi_realize; k->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; k->device_id = PCI_DEVICE_ID_LSI_53C895A; k->class_id = PCI_CLASS_STORAGE_SCSI; diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 4852237..bf83b65 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2320,14 +2320,13 @@ static const struct SCSIBusInfo megasas_scsi_info = { .cancel = megasas_command_cancel, }; -static int megasas_scsi_init(PCIDevice *dev) +static void megasas_scsi_realize(PCIDevice *dev, Error **errp) { DeviceState *d = DEVICE(dev); MegasasState *s = MEGASAS(dev); MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s); uint8_t *pci_conf; int i, bar_type; - Error *err = NULL; pci_conf = dev->config; @@ -2407,13 +2406,8 @@ static int megasas_scsi_init(PCIDevice *dev) scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), &megasas_scsi_info, NULL); if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - return 0; } static void @@ -2507,7 +2501,7 @@ static void megasas_class_init(ObjectClass *oc, void *data) MegasasBaseClass *e = MEGASAS_DEVICE_CLASS(oc); const MegasasInfo *info = data; - pc->init = megasas_scsi_init; + pc->realize = megasas_scsi_realize; pc->exit = megasas_scsi_uninit; pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; pc->device_id = info->device_id; diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index dca9576..bd2c0e4 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -242,8 +242,9 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, if (serial && object_property_find(OBJECT(dev), "serial", NULL)) { qdev_prop_set_string(dev, "serial", serial); } - if (qdev_prop_set_drive(dev, "drive", blk) < 0) { - error_setg(errp, "Setting drive property failed"); + qdev_prop_set_drive(dev, "drive", blk, &err); + if (err) { + error_propagate(errp, err); object_unparent(OBJECT(dev)); return NULL; } @@ -273,7 +274,6 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, Error **errp) scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), unit, false, -1, NULL, &err); if (err != NULL) { - error_report("%s", error_get_pretty(err)); error_propagate(errp, err); break; } diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index f65618d..54d71f4 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2251,6 +2251,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) } blkconf_serial(&s->qdev.conf, &s->serial); + blkconf_blocksizes(&s->qdev.conf); if (dev->type == TYPE_DISK) { blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, &err); if (err) { @@ -2290,6 +2291,12 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) static void scsi_hd_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + /* can happen for devices without drive. The error message for missing + * backend will be issued in scsi_realize + */ + if (s->qdev.conf.blk) { + blkconf_blocksizes(&s->qdev.conf); + } s->qdev.blocksize = s->qdev.conf.logical_block_size; s->qdev.type = TYPE_DISK; if (!s->product) { diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 618b0af..335f442 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -83,7 +83,7 @@ static int vhost_scsi_start(VHostSCSI *s) if (abi_version > VHOST_SCSI_ABI_VERSION) { error_report("vhost-scsi: The running tcm_vhost kernel abi_version:" " %d is greater than vhost_scsi userspace supports: %d, please" - " upgrade your version of QEMU\n", abi_version, + " upgrade your version of QEMU", abi_version, VHOST_SCSI_ABI_VERSION); return -ENOSYS; } @@ -141,7 +141,7 @@ static void vhost_scsi_stop(VHostSCSI *s) if (k->set_guest_notifiers) { ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); if (ret < 0) { - error_report("vhost guest notifier cleanup failed: %d\n", ret); + error_report("vhost guest notifier cleanup failed: %d", ret); } } assert(ret >= 0); @@ -186,7 +186,7 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) ret = vhost_scsi_start(s); if (ret < 0) { - error_report("virtio-scsi: unable to start vhost: %s\n", + error_report("virtio-scsi: unable to start vhost: %s", strerror(-ret)); /* There is no userspace virtio-scsi fallback so exit */ diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 751392e..e41ec0b 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -186,6 +186,7 @@ static void leon3_generic_hw_init(MachineState *machine) fprintf(stderr, "Can't read bios image %s\n", filename); exit(1); } + g_free(filename); /* Can directly load an application. */ if (kernel_filename != NULL) { diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 435142a..b087bbdd 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -289,7 +289,7 @@ static void a9_gtimer_realize(DeviceState *dev, Error **errp) int i; if (s->num_cpu < 1 || s->num_cpu > A9_GTIMER_MAX_CPUS) { - error_setg(errp, "%s: num-cpu must be between 1 and %d\n", + error_setg(errp, "%s: num-cpu must be between 1 and %d", __func__, A9_GTIMER_MAX_CPUS); return; } diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index a94c7c5..2a45071 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -143,7 +143,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, if (!tpm_pt->tpm_op_canceled || (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { error_report("tpm_passthrough: error while transmitting data " - "to TPM: %s (%i)\n", + "to TPM: %s (%i)", strerror(errno), errno); } goto err_exit; @@ -156,14 +156,14 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, if (!tpm_pt->tpm_op_canceled || (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { error_report("tpm_passthrough: error while reading data from " - "TPM: %s (%i)\n", + "TPM: %s (%i)", strerror(errno), errno); } } else if (ret < sizeof(struct tpm_resp_hdr) || tpm_passthrough_get_size_from_buffer(out) != ret) { ret = -1; error_report("tpm_passthrough: received invalid response " - "packet from TPM\n"); + "packet from TPM"); } if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) { @@ -309,7 +309,7 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb) if (tpm_pt->cancel_fd >= 0) { n = write(tpm_pt->cancel_fd, "-", 1); if (n != 1) { - error_report("Canceling TPM command failed: %s\n", + error_report("Canceling TPM command failed: %s", strerror(errno)); } else { tpm_pt->tpm_op_canceled = true; @@ -440,13 +440,13 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); if (tpm_pt->tpm_fd < 0) { - error_report("Cannot access TPM device using '%s': %s\n", + error_report("Cannot access TPM device using '%s': %s", tpm_pt->tpm_dev, strerror(errno)); goto err_free_parameters; } if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) { - error_report("'%s' is not a TPM device.\n", + error_report("'%s' is not a TPM device.", tpm_pt->tpm_dev); goto err_close_tpmdev; } diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 65d9aa6..dacefd7 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -611,6 +611,7 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) } blkconf_serial(&s->conf, &dev->serial); + blkconf_blocksizes(&s->conf); /* * Hack alert: this pretends to be a block device, but it's really @@ -663,6 +664,7 @@ static void usb_msd_realize_bot(USBDevice *dev, Error **errp) static USBDevice *usb_msd_init(USBBus *bus, const char *filename) { static int nr=0; + Error *err = NULL; char id[8]; QemuOpts *opts; DriveInfo *dinfo; @@ -706,8 +708,10 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename) /* create guest device */ dev = usb_create(bus, "usb-storage"); - if (qdev_prop_set_drive(&dev->qdev, "drive", - blk_by_legacy_dinfo(dinfo)) < 0) { + qdev_prop_set_drive(&dev->qdev, "drive", blk_by_legacy_dinfo(dinfo), + &err); + if (err) { + error_report_err(err); object_unparent(OBJECT(dev)); return NULL; } diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 148eb53..b012620 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -201,7 +201,7 @@ static int vfio_dma_unmap(VFIOContainer *container, }; if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { - error_report("VFIO_UNMAP_DMA: %d\n", -errno); + error_report("VFIO_UNMAP_DMA: %d", -errno); return -errno; } @@ -234,7 +234,7 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova, return 0; } - error_report("VFIO_MAP_DMA: %d\n", -errno); + error_report("VFIO_MAP_DMA: %d", -errno); return -errno; } @@ -274,7 +274,7 @@ static void vfio_iommu_map_notify(Notifier *n, void *data) iotlb->translated_addr, &xlat, &len, iotlb->perm & IOMMU_WO); if (!memory_region_is_ram(mr)) { - error_report("iommu map to non memory area %"HWADDR_PRIx"\n", + error_report("iommu map to non memory area %"HWADDR_PRIx"", xlat); return; } @@ -283,7 +283,7 @@ static void vfio_iommu_map_notify(Notifier *n, void *data) * check that it did not truncate too much. */ if (len & iotlb->addr_mask) { - error_report("iommu has granularity incompatible with target AS\n"); + error_report("iommu has granularity incompatible with target AS"); return; } @@ -566,7 +566,7 @@ static void vfio_kvm_device_add_group(VFIOGroup *group) }; if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) { - error_report("Failed to create KVM VFIO device: %m\n"); + error_report("Failed to create KVM VFIO device: %m"); return; } diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index ff4f200..4d68a27 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -61,7 +61,7 @@ int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) dev->vhost_ops = &user_ops; break; default: - error_report("Unknown vhost backend type\n"); + error_report("Unknown vhost backend type"); r = -1; } diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index de9a20f..d99c22e 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -360,15 +360,13 @@ static uint64_t xen_pt_get_bar_size(PCIIORegion *r) } static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s, - XenPTRegInfo *reg) + int index) { PCIDevice *d = &s->dev; XenPTRegion *region = NULL; PCIIORegion *r; - int index = 0; /* check 64bit BAR */ - index = xen_pt_bar_offset_to_index(reg->offset); if ((0 < index) && (index < PCI_ROM_SLOT)) { int type = s->real_device.io_regions[index - 1].type; @@ -422,7 +420,7 @@ static int xen_pt_bar_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, } /* set BAR flag */ - s->bases[index].bar_flag = xen_pt_bar_reg_parse(s, reg); + s->bases[index].bar_flag = xen_pt_bar_reg_parse(s, index); if (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED) { reg_field = XEN_PT_INVALID_REG; } @@ -440,7 +438,7 @@ static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, /* get BAR index */ index = xen_pt_bar_offset_to_index(reg->offset); - if (index < 0 || index >= PCI_NUM_REGIONS) { + if (index < 0 || index >= PCI_NUM_REGIONS - 1) { XEN_PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); return -1; } diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 37ea9ae..328d209 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -64,7 +64,7 @@ static void xtensa_sim_init(MachineState *machine) for (n = 0; n < smp_cpus; n++) { cpu = cpu_xtensa_init(cpu_model); if (cpu == NULL) { - error_report("unable to find CPU definition '%s'\n", + error_report("unable to find CPU definition '%s'", cpu_model); exit(EXIT_FAILURE); } diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index d441c02..ab4d0e4 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -207,7 +207,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) for (n = 0; n < smp_cpus; n++) { cpu = cpu_xtensa_init(cpu_model); if (cpu == NULL) { - error_report("unable to find CPU definition '%s'\n", + error_report("unable to find CPU definition '%s'", cpu_model); exit(EXIT_FAILURE); } @@ -253,7 +253,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) board->flash_size / board->flash_sector_size, 4, 0x0000, 0x0000, 0x0000, 0x0000, be); if (flash == NULL) { - error_report("unable to mount pflash\n"); + error_report("unable to mount pflash"); exit(EXIT_FAILURE); } } @@ -305,7 +305,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) uint32_t dtb_addr = tswap32(cur_lowmem); if (!fdt) { - error_report("could not load DTB '%s'\n", dtb_filename); + error_report("could not load DTB '%s'", dtb_filename); exit(EXIT_FAILURE); } @@ -325,7 +325,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) lowmem_end - cur_lowmem); } if (initrd_size < 0) { - error_report("could not load initrd '%s'\n", initrd_filename); + error_report("could not load initrd '%s'", initrd_filename); exit(EXIT_FAILURE); } initrd_location.start = tswap32(cur_lowmem); @@ -351,7 +351,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) if (success > 0 && is_linux) { entry_point = ep; } else { - error_report("could not load kernel '%s'\n", + error_report("could not load kernel '%s'", kernel_filename); exit(EXIT_FAILURE); } diff --git a/include/block/block.h b/include/block/block.h index 649c269..473c746 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -60,6 +60,17 @@ typedef enum { BDRV_REQ_MAY_UNMAP = 0x4, } BdrvRequestFlags; +typedef struct BlockSizes { + uint32_t phys; + uint32_t log; +} BlockSizes; + +typedef struct HDGeometry { + uint32_t heads; + uint32_t sectors; + uint32_t cylinders; +} HDGeometry; + #define BDRV_O_RDWR 0x0002 #define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */ #define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */ @@ -550,6 +561,8 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs); * This function must be called with iothread lock held. */ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context); +int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); +int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); void bdrv_io_plug(BlockDriverState *bs); void bdrv_io_unplug(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index b340e7e..dccb092 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -56,6 +56,8 @@ #define BLOCK_OPT_ADAPTER_TYPE "adapter_type" #define BLOCK_OPT_REDUNDANCY "redundancy" #define BLOCK_OPT_NOCOW "nocow" +#define BLOCK_OPT_OBJECT_SIZE "object_size" +#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits" #define BLOCK_PROBE_BUF_SIZE 512 @@ -273,6 +275,21 @@ struct BlockDriver { void (*bdrv_io_unplug)(BlockDriverState *bs); void (*bdrv_flush_io_queue)(BlockDriverState *bs); + /** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz and return zero. + * On failure, return negative errno. + */ + int (*bdrv_probe_blocksizes)(BlockDriverState *bs, BlockSizes *bsz); + /** + * Try to get @bs's geometry (cyls, heads, sectors) + * On success, store them in @geo and return 0. + * On failure return -errno. + * Only drivers that want to override guest geometry implement this + * callback; see hd_geometry_guess(). + */ + int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); + QLIST_ENTRY(BlockDriver) list; }; diff --git a/include/block/coroutine_int.h b/include/block/coroutine_int.h index f133d65..9aa1aae 100644 --- a/include/block/coroutine_int.h +++ b/include/block/coroutine_int.h @@ -31,6 +31,7 @@ typedef enum { COROUTINE_YIELD = 1, COROUTINE_TERMINATE = 2, + COROUTINE_ENTER = 3, } CoroutineAction; struct Coroutine { diff --git a/include/hw/block/block.h b/include/hw/block/block.h index 0d0ce9a..8d7c4b4 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -44,9 +44,9 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) #define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ DEFINE_PROP_DRIVE("drive", _state, _conf.blk), \ DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \ - _conf.logical_block_size, 512), \ + _conf.logical_block_size), \ DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \ - _conf.physical_block_size, 512), \ + _conf.physical_block_size), \ DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \ DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \ DEFINE_PROP_UINT32("discard_granularity", _state, \ @@ -63,6 +63,7 @@ void blkconf_serial(BlockConf *conf, char **serial); void blkconf_geometry(BlockConf *conf, int *trans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp); +void blkconf_blocksizes(BlockConf *conf); /* Hard disk geometry */ diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 57ee363..d67dad5 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -149,8 +149,8 @@ extern PropertyInfo qdev_prop_arraylen; LostTickPolicy) #define DEFINE_PROP_BIOS_CHS_TRANS(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_bios_chs_trans, int) -#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f, _d) \ - DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_blocksize, uint16_t) +#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, 0, qdev_prop_blocksize, uint16_t) #define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress) @@ -168,8 +168,8 @@ void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value); void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value); void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value); void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value); -int qdev_prop_set_drive(DeviceState *dev, const char *name, - BlockBackend *value) QEMU_WARN_UNUSED_RESULT; +void qdev_prop_set_drive(DeviceState *dev, const char *name, + BlockBackend *value, Error **errp); void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockBackend *value); void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value); diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index b0ed04c..4356af4 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -32,7 +32,6 @@ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); void xen_piix3_set_irq(void *opaque, int irq_num, int level); void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); void xen_hvm_inject_msi(uint64_t addr, uint32_t data); -void xen_cmos_set_s3_resume(void *opaque, int irq, int level); qemu_irq *xen_interrupt_controller_init(void); diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 986260f..57a62d4 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -37,12 +37,12 @@ void qerror_report_err(Error *err); #define QERR_BASE_NOT_FOUND \ ERROR_CLASS_GENERIC_ERROR, "Base '%s' not found" -#define QERR_BLOCK_JOB_NOT_READY \ - ERROR_CLASS_GENERIC_ERROR, "The active block job for device '%s' cannot be completed" - #define QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED \ ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by device '%s' does not support feature '%s'" +#define QERR_BLOCK_JOB_NOT_READY \ + ERROR_CLASS_GENERIC_ERROR, "The active block job for device '%s' cannot be completed" + #define QERR_BUS_NO_HOTPLUG \ ERROR_CLASS_GENERIC_ERROR, "Bus '%s' does not support hotplugging" diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 3ff9aee..77e9b9c 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -164,5 +164,7 @@ int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors); int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int64_t pos, int size); int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); +int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); +int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); #endif diff --git a/include/ui/console.h b/include/ui/console.h index 0f97d86..2f5b9f0 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -36,7 +36,6 @@ typedef struct QEMUPutLEDEntry QEMUPutLEDEntry; QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque); -void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry); QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute, const char *name); @@ -194,7 +193,6 @@ DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height, pixman_format_code_t format, int linesize, uint64_t addr); -PixelFormat qemu_different_endianness_pixelformat(int bpp); PixelFormat qemu_default_pixelformat(int bpp); DisplaySurface *qemu_create_displaysurface(int width, int height); @@ -322,7 +320,6 @@ void qemu_console_resize(QemuConsole *con, int width, int height); void qemu_console_copy(QemuConsole *con, int src_x, int src_y, int dst_x, int dst_y, int w, int h); DisplaySurface *qemu_console_surface(QemuConsole *con); -DisplayState *qemu_console_displaystate(QemuConsole *console); /* sdl.c */ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); @@ -335,7 +332,6 @@ void vnc_display_init(const char *id); void vnc_display_open(const char *id, Error **errp); void vnc_display_add_client(const char *id, int csock, bool skipauth); char *vnc_display_local_addr(const char *id); -void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts); #ifdef CONFIG_VNC int vnc_display_password(const char *id, const char *password); int vnc_display_pw_expire(const char *id, time_t expires); @@ -366,7 +366,7 @@ static void kvm_log_stop(MemoryListener *listener, } } -static int kvm_set_migration_log(int enable) +static int kvm_set_migration_log(bool enable) { KVMState *s = kvm_state; KVMSlot *mem; diff --git a/migration/qemu-file-buf.c b/migration/qemu-file-buf.c index e97e0bd..e56a8ad 100644 --- a/migration/qemu-file-buf.c +++ b/migration/qemu-file-buf.c @@ -2,6 +2,10 @@ * QEMU System Emulator * * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 IBM Corp. + * + * Authors: + * Stefan Berger <stefanb@linux.vnet.ibm.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/migration/rdma.c b/migration/rdma.c index 17d0035..42d443c 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -702,7 +702,7 @@ static void qemu_rdma_dump_id(const char *who, struct ibv_context *verbs) verbs->device->ibdev_path, port.link_layer, (port.link_layer == IBV_LINK_LAYER_INFINIBAND) ? "Infiniband" : - ((port.link_layer == IBV_LINK_LAYER_ETHERNET) + ((port.link_layer == IBV_LINK_LAYER_ETHERNET) ? "Ethernet" : "Unknown")); } @@ -737,7 +737,7 @@ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) * and validate what time of hardware it is. * * Unfortunately, this puts the user in a fix: - * + * * If the source VM connects with an IPv4 address without knowing that the * destination has bound to '[::]' the migration will unconditionally fail * unless the management software is explicitly listening on the the IPv4 @@ -745,13 +745,13 @@ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) * * If the source VM connects with an IPv6 address, then we're OK because we can * throw an error on the source (and similarly on the destination). - * + * * But in mixed environments, this will be broken for a while until it is fixed * inside linux. * * We do provide a *tiny* bit of help in this function: We can list all of the * devices in the system and check to see if all the devices are RoCE or - * Infiniband. + * Infiniband. * * If we detect that we have a *pure* RoCE environment, then we can safely * thrown an error even if the management software has specified '[::]' as the @@ -770,17 +770,17 @@ static int qemu_rdma_broken_ipv6_kernel(Error **errp, struct ibv_context *verbs) /* This bug only exists in linux, to our knowledge. */ #ifdef CONFIG_LINUX - /* + /* * Verbs are only NULL if management has bound to '[::]'. - * + * * Let's iterate through all the devices and see if there any pure IB * devices (non-ethernet). - * + * * If not, then we can safely proceed with the migration. * Otherwise, there are no guarantees until the bug is fixed in linux. */ if (!verbs) { - int num_devices, x; + int num_devices, x; struct ibv_device ** dev_list = ibv_get_device_list(&num_devices); bool roce_found = false; bool ib_found = false; @@ -825,8 +825,8 @@ static int qemu_rdma_broken_ipv6_kernel(Error **errp, struct ibv_context *verbs) /* * If we have a verbs context, that means that some other than '[::]' was - * used by the management software for binding. In which case we can actually - * warn the user about a potential broken kernel; + * used by the management software for binding. In which case we can + * actually warn the user about a potentially broken kernel. */ /* IB ports start with 1, not 0 */ @@ -1626,7 +1626,7 @@ static int qemu_rdma_exchange_get_response(RDMAContext *rdma, return -EIO; } if (head->len > RDMA_CONTROL_MAX_BUFFER - sizeof(*head)) { - error_report("too long length: %d\n", head->len); + error_report("too long length: %d", head->len); return -EINVAL; } if (sizeof(*head) + head->len != byte_len) { diff --git a/net/vhost-user.c b/net/vhost-user.c index 2435b0f..1d86a2b 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -121,12 +121,12 @@ static void net_vhost_user_event(void *opaque, int event) case CHR_EVENT_OPENED: vhost_user_start(s); net_vhost_link_down(s, false); - error_report("chardev \"%s\" went up\n", s->chr->label); + error_report("chardev \"%s\" went up", s->chr->label); break; case CHR_EVENT_CLOSED: net_vhost_link_down(s, true); vhost_user_stop(s); - error_report("chardev \"%s\" went down\n", s->chr->label); + error_report("chardev \"%s\" went down", s->chr->label); break; } } @@ -66,7 +66,7 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) if (nodenr >= MAX_NODES) { error_setg(errp, "Max number of NUMA nodes reached: %" - PRIu16 "\n", nodenr); + PRIu16 "", nodenr); return; } @@ -85,7 +85,7 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) } if (node->has_mem && node->has_memdev) { - error_setg(errp, "qemu: cannot specify both mem= and memdev=\n"); + error_setg(errp, "qemu: cannot specify both mem= and memdev="); return; } @@ -94,7 +94,7 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) } if (node->has_memdev != have_memdevs) { error_setg(errp, "qemu: memdev option must be specified for either " - "all or no nodes\n"); + "all or no nodes"); return; } diff --git a/pc-bios/keymaps/ru b/pc-bios/keymaps/ru index b3e7d24..8f652d5 100644 --- a/pc-bios/keymaps/ru +++ b/pc-bios/keymaps/ru @@ -4,7 +4,7 @@ map 0x419 exclam 0x02 shift at 0x03 shift quotedbl 0x03 shift altgr -numbersign 0x04 shift +numerosign 0x04 shift dollar 0x05 shift asterisk 0x05 shift altgr percent 0x06 shift diff --git a/qapi-schema.json b/qapi-schema.json index e16f8eb..8141f71 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -871,9 +871,9 @@ # @connection-id: SPICE connection id number. All channels with the same id # belong to the same SPICE session. # -# @connection-type: SPICE channel type number. "1" is the main control -# channel, filter for this one if you want to track spice -# sessions only +# @channel-type: SPICE channel type number. "1" is the main control +# channel, filter for this one if you want to track spice +# sessions only # # @channel-id: SPICE channel ID number. Usually "0", might be different when # multiple channels of the same type exist, such as multiple diff --git a/qapi/block-core.json b/qapi/block-core.json index a3fdaf0..90586a5 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -41,13 +41,16 @@ # @corrupt: #optional true if the image has been marked corrupt; only valid for # compat >= 1.1 (since 2.2) # +# @refcount-bits: width of a refcount entry in bits (since 2.3) +# # Since: 1.7 ## { 'type': 'ImageInfoSpecificQCow2', 'data': { 'compat': 'str', '*lazy-refcounts': 'bool', - '*corrupt': 'bool' + '*corrupt': 'bool', + 'refcount-bits': 'int' } } ## diff --git a/qemu-coroutine.c b/qemu-coroutine.c index 525247b..c17a92b 100644 --- a/qemu-coroutine.c +++ b/qemu-coroutine.c @@ -99,29 +99,10 @@ static void coroutine_delete(Coroutine *co) qemu_coroutine_delete(co); } -static void coroutine_swap(Coroutine *from, Coroutine *to) -{ - CoroutineAction ret; - - ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD); - - qemu_co_queue_run_restart(to); - - switch (ret) { - case COROUTINE_YIELD: - return; - case COROUTINE_TERMINATE: - trace_qemu_coroutine_terminate(to); - coroutine_delete(to); - return; - default: - abort(); - } -} - void qemu_coroutine_enter(Coroutine *co, void *opaque) { Coroutine *self = qemu_coroutine_self(); + CoroutineAction ret; trace_qemu_coroutine_enter(self, co, opaque); @@ -132,7 +113,20 @@ void qemu_coroutine_enter(Coroutine *co, void *opaque) co->caller = self; co->entry_arg = opaque; - coroutine_swap(self, co); + ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER); + + qemu_co_queue_run_restart(co); + + switch (ret) { + case COROUTINE_YIELD: + return; + case COROUTINE_TERMINATE: + trace_qemu_coroutine_terminate(co); + coroutine_delete(co); + return; + default: + abort(); + } } void coroutine_fn qemu_coroutine_yield(void) @@ -148,5 +142,5 @@ void coroutine_fn qemu_coroutine_yield(void) } self->caller = NULL; - coroutine_swap(self, to); + qemu_coroutine_switch(self, to, COROUTINE_YIELD); } @@ -976,7 +976,8 @@ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n, int *pnum) { - int res, i; + bool res; + int i; if (n <= 0) { *pnum = 0; @@ -1645,7 +1646,7 @@ static int img_convert(int argc, char **argv) if (skip_create) { int64_t output_sectors = blk_nb_sectors(out_blk); if (output_sectors < 0) { - error_report("unable to get output image length: %s\n", + error_report("unable to get output image length: %s", strerror(-output_sectors)); ret = -1; goto out; diff --git a/qemu-options.hx b/qemu-options.hx index b0345ae..837624d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1376,11 +1376,25 @@ ETEXI DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, "-smbios file=binary\n" " load SMBIOS entry from binary file\n" - "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d][,uefi=on|off]\n" + "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]\n" + " [,uefi=on|off]\n" " specify SMBIOS type 0 fields\n" "-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]\n" " [,uuid=uuid][,sku=str][,family=str]\n" - " specify SMBIOS type 1 fields\n", QEMU_ARCH_I386) + " specify SMBIOS type 1 fields\n" + "-smbios type=2[,manufacturer=str][,product=str][,version=str][,serial=str]\n" + " [,asset=str][,location=str]\n" + " specify SMBIOS type 2 fields\n" + "-smbios type=3[,manufacturer=str][,version=str][,serial=str][,asset=str]\n" + " [,sku=str]\n" + " specify SMBIOS type 3 fields\n" + "-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str]\n" + " [,asset=str][,part=str]\n" + " specify SMBIOS type 4 fields\n" + "-smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str]\n" + " [,asset=str][,part=str]\n" + " specify SMBIOS type 17 fields\n", + QEMU_ARCH_I386) STEXI @item -smbios file=@var{binary} @findex -smbios @@ -1389,8 +1403,20 @@ Load SMBIOS entry from binary file. @item -smbios type=0[,vendor=@var{str}][,version=@var{str}][,date=@var{str}][,release=@var{%d.%d}][,uefi=on|off] Specify SMBIOS type 0 fields -@item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}] [,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}] [,family=@var{str}] +@item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}][,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}][,family=@var{str}] Specify SMBIOS type 1 fields + +@item -smbios type=2[,manufacturer=@var{str}][,product=@var{str}][,version=@var{str}][,serial=@var{str}][,asset=@var{str}][,location=@var{str}][,family=@var{str}] +Specify SMBIOS type 2 fields + +@item -smbios type=3[,manufacturer=@var{str}][,version=@var{str}][,serial=@var{str}][,asset=@var{str}][,sku=@var{str}] +Specify SMBIOS type 3 fields + +@item -smbios type=4[,sock_pfx=@var{str}][,manufacturer=@var{str}][,version=@var{str}][,serial=@var{str}][,asset=@var{str}][,part=@var{str}] +Specify SMBIOS type 4 fields + +@item -smbios type=17[,loc_pfx=@var{str}][,bank=@var{str}][,manufacturer=@var{str}][,serial=@var{str}][,asset=@var{str}][,part=@var{str}] +Specify SMBIOS type 17 fields ETEXI STEXI @@ -3181,12 +3207,30 @@ Set TB size. ETEXI DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \ - "-incoming p prepare for incoming migration, listen on port p\n", + "-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6]\n" \ + "-incoming rdma:host:port[,ipv4][,ipv6]\n" \ + "-incoming unix:socketpath\n" \ + " prepare for incoming migration, listen on\n" \ + " specified protocol and socket address\n" \ + "-incoming fd:fd\n" \ + "-incoming exec:cmdline\n" \ + " accept incoming migration on given file descriptor\n" \ + " or from given external command\n", QEMU_ARCH_ALL) STEXI -@item -incoming @var{port} +@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6] +@item -incoming rdma:@var{host}:@var{port}[,ipv4][,ipv6] @findex -incoming -Prepare for incoming migration, listen on @var{port}. +Prepare for incoming migration, listen on a given tcp port. + +@item -incoming unix:@var{socketpath} +Prepare for incoming migration, listen on a given unix socket. + +@item -incoming fd:@var{fd} +Accept incoming migration from a given filedescriptor. + +@item -incoming exec:@var{cmdline} +Accept incoming migration as an output from specified external command. ETEXI DEF("nodefaults", 0, QEMU_OPTION_nodefaults, \ diff --git a/qmp-commands.hx b/qmp-commands.hx index a85d847..c12334a 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1,5 +1,5 @@ HXCOMM QMP dispatch table and documentation -HXCOMM Text between SQMP and EQMP is copied to the QMP documention file and +HXCOMM Text between SQMP and EQMP is copied to the QMP documentation file and HXCOMM does not show up in the other formats. SQMP @@ -1767,7 +1767,7 @@ Arguments: - "protocol": protocol name (json-string) - "password": password (json-string) -- "connected": [ keep | disconnect | fail ] (josn-string, optional) +- "connected": [ keep | disconnect | fail ] (json-string, optional) Example: @@ -2922,7 +2922,7 @@ Channels are described by a json-object, each one contain the following: - "channel-id": channel id. Usually "0", might be different needed when multiple channels of the same type exist, such as multiple display channels in a multihead setup (json-int) -- "tls": whevener the channel is encrypted (json-bool) +- "tls": whether the channel is encrypted (json-bool) Example: @@ -391,7 +391,6 @@ static void qmp_change_vnc_listen(const char *target, Error **errp) return; } - vnc_auto_assign_id(olist, opts); vnc_display_open("default", errp); } diff --git a/scripts/kvm/kvm_stat b/scripts/kvm/kvm_stat index c65cabd..7e5d256 100755 --- a/scripts/kvm/kvm_stat +++ b/scripts/kvm/kvm_stat @@ -519,7 +519,10 @@ def tui(screen, stats): def refresh(sleeptime): screen.erase() screen.addstr(0, 0, 'kvm statistics') - row = 2 + screen.addstr(2, 1, 'Event') + screen.addstr(2, 1 + label_width + number_width - len('Total'), 'Total') + screen.addstr(2, 1 + label_width + number_width + 8 - len('Current'), 'Current') + row = 3 s = stats.get() def sortkey(x): if s[x][1]: diff --git a/scripts/kvm/kvm_stat.texi b/scripts/kvm/kvm_stat.texi new file mode 100644 index 0000000..6ce00d8 --- /dev/null +++ b/scripts/kvm/kvm_stat.texi @@ -0,0 +1,55 @@ +@example +@c man begin SYNOPSIS +usage: kvm_stat [OPTION]... +@c man end +@end example + +@c man begin DESCRIPTION + +kvm_stat prints counts of KVM kernel module trace events. These events signify +state transitions such as guest mode entry and exit. + +This tool is useful for observing guest behavior from the host perspective. +Often conclusions about performance or buggy behavior can be drawn from the +output. + +The set of KVM kernel module trace events may be specific to the kernel version +or architecture. It is best to check the KVM kernel module source code for the +meaning of events. + +Note that trace events are counted globally across all running guests. + +@c man end + +@c man begin OPTIONS +@table @option +@item -1, --once, --batch + run in batch mode for one second +@item -l, --log + run in logging mode (like vmstat) +@item -t, --tracepoints + retrieve statistics from tracepoints +@item -d, --debugfs + retrieve statistics from debugfs +@item -f, --fields=@var{fields} + fields to display (regex) +@item -h, --help + show help message +@end table + +@c man end + +@ignore + +@setfilename kvm_stat +@settitle Report KVM kernel module event counters. + +@c man begin AUTHOR +Stefan Hajnoczi <stefanha@redhat.com> +@c man end + +@c man begin SEEALSO +perf(1), trace-cmd(1) +@c man end + +@end ignore diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c index fa374d0..2bc757a 100644 --- a/target-i386/seg_helper.c +++ b/target-i386/seg_helper.c @@ -1043,7 +1043,7 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); env->eip = (uint32_t)env->regs[R_ECX]; } - cpu_x86_load_seg_cache(env, R_SS, selector + 8, + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | @@ -1056,7 +1056,7 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); env->eip = (uint32_t)env->regs[R_ECX]; - cpu_x86_load_seg_cache(env, R_SS, selector + 8, + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index d7c57d9..508cc0a 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -211,7 +211,7 @@ void kvm_s390_reset_vcpu(S390CPU *cpu) * Before this ioctl cpu_synchronize_state() is called in common kvm * code (kvm-all) */ if (kvm_vcpu_ioctl(cs, KVM_S390_INITIAL_RESET, NULL)) { - error_report("Initial CPU reset failed on CPU %i\n", cs->cpu_index); + error_report("Initial CPU reset failed on CPU %i", cs->cpu_index); } } diff --git a/tests/.gitignore b/tests/.gitignore index e2e4957..0dcb618 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,6 +5,7 @@ check-qjson check-qlist check-qstring check-qom-interface +rcutorture test-aio test-bitops test-coroutine @@ -26,6 +27,7 @@ test-qmp-input-strict test-qmp-input-visitor test-qmp-marshal.c test-qmp-output-visitor +test-rcu-list test-rfifolock test-string-input-visitor test-string-output-visitor @@ -33,6 +35,7 @@ test-thread-pool test-throttle test-visitor-serialization test-vmstate +test-write-threshold test-x86-cpuid test-xbzrle *-test diff --git a/tests/Makefile b/tests/Makefile index 307035c..fed8096 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -191,6 +191,8 @@ gcov-files-sparc-y += hw/timer/m48t59.c gcov-files-sparc64-y += hw/timer/m48t59.c check-qtest-arm-y = tests/tmp105-test$(EXESUF) gcov-files-arm-y += hw/misc/tmp105.c +check-qtest-arm-y += tests/virtio-blk-test$(EXESUF) +gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c check-qtest-ppc-y += tests/boot-order-test$(EXESUF) check-qtest-ppc64-y += tests/boot-order-test$(EXESUF) check-qtest-ppc64-y += tests/spapr-phb-test$(EXESUF) @@ -315,8 +317,8 @@ libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o libqos-pc-obj-y += tests/libqos/ahci.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o -libqos-virtio-obj-y = $(libqos-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o +libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o tests/rtc-test$(EXESUF): tests/rtc-test.o tests/m48t59-test$(EXESUF): tests/m48t59-test.o diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 53fd068..cf0b98b 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -107,6 +107,21 @@ static void ahci_shutdown(AHCIQState *ahci) qtest_shutdown(qs); } +/** + * Boot and fully enable the HBA device. + * @see ahci_boot, ahci_pci_enable and ahci_hba_enable. + */ +static AHCIQState *ahci_boot_and_enable(void) +{ + AHCIQState *ahci; + ahci = ahci_boot(); + + ahci_pci_enable(ahci); + ahci_hba_enable(ahci); + + return ahci; +} + /*** Specification Adherence Tests ***/ /** @@ -716,12 +731,12 @@ static void ahci_test_identify(AHCIQState *ahci) g_assert_cmphex(sect_size, ==, 0x200); } -static void ahci_test_dma_rw_simple(AHCIQState *ahci) +static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, + uint8_t read_cmd, uint8_t write_cmd) { uint64_t ptr; uint8_t port; unsigned i; - const unsigned bufsize = 4096; unsigned char *tx = g_malloc(bufsize); unsigned char *rx = g_malloc0(bufsize); @@ -736,16 +751,16 @@ static void ahci_test_dma_rw_simple(AHCIQState *ahci) ptr = ahci_alloc(ahci, bufsize); g_assert(ptr); - /* Write some indicative pattern to our 4K buffer. */ + /* Write some indicative pattern to our buffer. */ for (i = 0; i < bufsize; i++) { tx[i] = (bufsize - i); } memwrite(ptr, tx, bufsize); /* Write this buffer to disk, then read it back to the DMA buffer. */ - ahci_guest_io(ahci, port, CMD_WRITE_DMA, ptr, bufsize); + ahci_guest_io(ahci, port, write_cmd, ptr, bufsize); qmemset(ptr, 0x00, bufsize); - ahci_guest_io(ahci, port, CMD_READ_DMA, ptr, bufsize); + ahci_guest_io(ahci, port, read_cmd, ptr, bufsize); /*** Read back the Data ***/ memread(ptr, rx, bufsize); @@ -831,25 +846,204 @@ static void test_identify(void) { AHCIQState *ahci; - ahci = ahci_boot(); - ahci_pci_enable(ahci); - ahci_hba_enable(ahci); + ahci = ahci_boot_and_enable(); ahci_test_identify(ahci); ahci_shutdown(ahci); } /** - * Perform a simple DMA R/W test, using a single PRD and non-NCQ commands. + * Fragmented DMA test: Perform a standard 4K DMA read/write + * test, but make sure the physical regions are fragmented to + * be very small, each just 32 bytes, to see how AHCI performs + * with chunks defined to be much less than a sector. */ -static void test_dma_rw_simple(void) +static void test_dma_fragmented(void) { AHCIQState *ahci; + AHCICommand *cmd; + uint8_t px; + size_t bufsize = 4096; + unsigned char *tx = g_malloc(bufsize); + unsigned char *rx = g_malloc0(bufsize); + unsigned i; + uint64_t ptr; + + ahci = ahci_boot_and_enable(); + px = ahci_port_select(ahci); + ahci_port_clear(ahci, px); + + /* create pattern */ + for (i = 0; i < bufsize; i++) { + tx[i] = (bufsize - i); + } + + /* Create a DMA buffer in guest memory, and write our pattern to it. */ + ptr = guest_alloc(ahci->parent->alloc, bufsize); + g_assert(ptr); + memwrite(ptr, tx, bufsize); + + cmd = ahci_command_create(CMD_WRITE_DMA); + ahci_command_adjust(cmd, 0, ptr, bufsize, 32); + ahci_command_commit(ahci, cmd, px); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + g_free(cmd); + + cmd = ahci_command_create(CMD_READ_DMA); + ahci_command_adjust(cmd, 0, ptr, bufsize, 32); + ahci_command_commit(ahci, cmd, px); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + g_free(cmd); + + /* Read back the guest's receive buffer into local memory */ + memread(ptr, rx, bufsize); + guest_free(ahci->parent->alloc, ptr); + + g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); - ahci = ahci_boot(); - ahci_pci_enable(ahci); - ahci_hba_enable(ahci); - ahci_test_dma_rw_simple(ahci); ahci_shutdown(ahci); + + g_free(rx); + g_free(tx); +} + +/******************************************************************************/ +/* AHCI I/O Test Matrix Definitions */ + +enum BuffLen { + LEN_BEGIN = 0, + LEN_SIMPLE = LEN_BEGIN, + LEN_DOUBLE, + LEN_LONG, + LEN_SHORT, + NUM_LENGTHS +}; + +static const char *buff_len_str[NUM_LENGTHS] = { "simple", "double", + "long", "short" }; + +enum AddrMode { + ADDR_MODE_BEGIN = 0, + ADDR_MODE_LBA28 = ADDR_MODE_BEGIN, + ADDR_MODE_LBA48, + NUM_ADDR_MODES +}; + +static const char *addr_mode_str[NUM_ADDR_MODES] = { "lba28", "lba48" }; + +enum IOMode { + MODE_BEGIN = 0, + MODE_PIO = MODE_BEGIN, + MODE_DMA, + NUM_MODES +}; + +static const char *io_mode_str[NUM_MODES] = { "pio", "dma" }; + +enum IOOps { + IO_BEGIN = 0, + IO_READ = IO_BEGIN, + IO_WRITE, + NUM_IO_OPS +}; + +typedef struct AHCIIOTestOptions { + enum BuffLen length; + enum AddrMode address_type; + enum IOMode io_type; +} AHCIIOTestOptions; + +/** + * Table of possible I/O ATA commands given a set of enumerations. + */ +static const uint8_t io_cmds[NUM_MODES][NUM_ADDR_MODES][NUM_IO_OPS] = { + [MODE_PIO] = { + [ADDR_MODE_LBA28] = { + [IO_READ] = CMD_READ_PIO, + [IO_WRITE] = CMD_WRITE_PIO }, + [ADDR_MODE_LBA48] = { + [IO_READ] = CMD_READ_PIO_EXT, + [IO_WRITE] = CMD_WRITE_PIO_EXT } + }, + [MODE_DMA] = { + [ADDR_MODE_LBA28] = { + [IO_READ] = CMD_READ_DMA, + [IO_WRITE] = CMD_WRITE_DMA }, + [ADDR_MODE_LBA48] = { + [IO_READ] = CMD_READ_DMA_EXT, + [IO_WRITE] = CMD_WRITE_DMA_EXT } + } +}; + +/** + * Test a Read/Write pattern using various commands, addressing modes, + * transfer modes, and buffer sizes. + */ +static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma, + unsigned bufsize) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(); + ahci_test_io_rw_simple(ahci, bufsize, + io_cmds[dma][lba48][IO_READ], + io_cmds[dma][lba48][IO_WRITE]); + ahci_shutdown(ahci); +} + +/** + * Demultiplex the test data and invoke the actual test routine. + */ +static void test_io_interface(gconstpointer opaque) +{ + AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque; + unsigned bufsize; + + switch (opts->length) { + case LEN_SIMPLE: + bufsize = 4096; + break; + case LEN_DOUBLE: + bufsize = 8192; + break; + case LEN_LONG: + bufsize = 4096 * 64; + break; + case LEN_SHORT: + bufsize = 512; + break; + default: + g_assert_not_reached(); + } + + test_io_rw_interface(opts->address_type, opts->io_type, bufsize); + g_free(opts); + return; +} + +static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, + enum BuffLen len) +{ + static const char *arch; + char *name; + AHCIIOTestOptions *opts = g_malloc(sizeof(AHCIIOTestOptions)); + + opts->length = len; + opts->address_type = addr; + opts->io_type = type; + + if (!arch) { + arch = qtest_get_arch(); + } + + name = g_strdup_printf("/%s/ahci/io/%s/%s/%s", arch, + io_mode_str[type], + addr_mode_str[addr], + buff_len_str[len]); + + g_test_add_data_func(name, opts, test_io_interface); + g_free(name); } /******************************************************************************/ @@ -860,6 +1054,7 @@ int main(int argc, char **argv) int fd; int ret; int c; + int i, j, k; static struct option long_options[] = { {"pedantic", no_argument, 0, 'p' }, @@ -907,7 +1102,16 @@ int main(int argc, char **argv) qtest_add_func("/ahci/hba_spec", test_hba_spec); qtest_add_func("/ahci/hba_enable", test_hba_enable); qtest_add_func("/ahci/identify", test_identify); - qtest_add_func("/ahci/dma/simple", test_dma_rw_simple); + + for (i = MODE_BEGIN; i < NUM_MODES; i++) { + for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) { + for (k = LEN_BEGIN; k < NUM_LENGTHS; k++) { + create_ahci_io_test(i, j, k); + } + } + } + + qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented); ret = g_test_run(); diff --git a/tests/ide-test.c b/tests/ide-test.c index 29f4039..b28a302 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -118,7 +118,6 @@ static void ide_test_start(const char *cmdline_fmt, ...) va_end(ap); qtest_start(cmdline); - qtest_irq_intercept_in(global_qtest, "ioapic"); guest_malloc = pc_alloc_init(); g_free(cmdline); @@ -388,6 +387,7 @@ static void test_bmdma_setup(void) "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " "-global ide-hd.ver=%s", tmp_path, "testdisk", "version"); + qtest_irq_intercept_in(global_qtest, "ioapic"); } static void test_bmdma_teardown(void) @@ -516,7 +516,7 @@ static void prepare_blkdebug_script(const char *debug_fn, const char *event) g_assert(ret == 0); } -static void test_retry_flush(void) +static void test_retry_flush(const char *machine) { uint8_t data; const char *s; @@ -580,6 +580,16 @@ static void test_flush_nodev(void) ide_test_quit(); } +static void test_pci_retry_flush(const char *machine) +{ + test_retry_flush("pc"); +} + +static void test_isa_retry_flush(const char *machine) +{ + test_retry_flush("isapc"); +} + int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -617,9 +627,9 @@ int main(int argc, char **argv) qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown); qtest_add_func("/ide/flush", test_flush); - qtest_add_func("/ide/flush_nodev", test_flush_nodev); - - qtest_add_func("/ide/retry/flush", test_retry_flush); + qtest_add_func("/ide/flush/nodev", test_flush_nodev); + qtest_add_func("/ide/flush/retry_pci", test_pci_retry_flush); + qtest_add_func("/ide/flush/retry_isa", test_isa_retry_flush); ret = g_test_run(); diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index a6105c7..b0f39a5 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -487,7 +487,7 @@ void ahci_get_command_header(AHCIQState *ahci, uint8_t port, void ahci_set_command_header(AHCIQState *ahci, uint8_t port, uint8_t slot, AHCICommandHeader *cmd) { - AHCICommandHeader tmp; + AHCICommandHeader tmp = { .flags = 0 }; uint64_t ba = ahci->port[port].clb; ba += slot * sizeof(AHCICommandHeader); @@ -713,6 +713,40 @@ void ahci_command_free(AHCICommand *cmd) g_free(cmd); } +void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags) +{ + cmd->header.flags |= cmdh_flags; +} + +void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags) +{ + cmd->header.flags &= ~cmdh_flags; +} + +void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect) +{ + RegH2DFIS *fis = &(cmd->fis); + if (cmd->props->lba28) { + g_assert_cmphex(lba_sect, <=, 0xFFFFFFF); + } else if (cmd->props->lba48) { + g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF); + } else { + /* Can't set offset if we don't know the format. */ + g_assert_not_reached(); + } + + /* LBA28 uses the low nibble of the device/control register for LBA24:27 */ + fis->lba_lo[0] = (lba_sect & 0xFF); + fis->lba_lo[1] = (lba_sect >> 8) & 0xFF; + fis->lba_lo[2] = (lba_sect >> 16) & 0xFF; + if (cmd->props->lba28) { + fis->device = (fis->device & 0xF0) || (lba_sect >> 24) & 0x0F; + } + fis->lba_hi[0] = (lba_sect >> 24) & 0xFF; + fis->lba_hi[1] = (lba_sect >> 32) & 0xFF; + fis->lba_hi[2] = (lba_sect >> 40) & 0xFF; +} + void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer) { cmd->buffer = buffer; @@ -740,6 +774,14 @@ void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size) ahci_command_set_sizes(cmd, cmd->xbytes, prd_size); } +void ahci_command_adjust(AHCICommand *cmd, uint64_t offset, uint64_t buffer, + uint64_t xbytes, unsigned prd_size) +{ + ahci_command_set_sizes(cmd, xbytes, prd_size); + ahci_command_set_buffer(cmd, buffer); + ahci_command_set_offset(cmd, offset); +} + void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port) { uint16_t i, prdtl; diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index 39b99d3..888545d 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -537,11 +537,16 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd); void ahci_command_free(AHCICommand *cmd); /* Command adjustments */ +void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags); +void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags); +void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect); void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer); void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes); void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size); void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, unsigned prd_size); +void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer, + uint64_t xbytes, unsigned prd_size); /* Command Misc */ uint8_t ahci_command_slot(AHCICommand *cmd); diff --git a/tests/libqos/malloc-generic.c b/tests/libqos/malloc-generic.c new file mode 100644 index 0000000..d30a2f4 --- /dev/null +++ b/tests/libqos/malloc-generic.c @@ -0,0 +1,39 @@ +/* + * Basic libqos generic malloc support + * + * Copyright (c) 2014 Marc Marà + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include "libqos/malloc-generic.h" +#include "libqos/malloc.h" + +/* + * Mostly for valgrind happiness, but it does offer + * a chokepoint for debugging guest memory leaks, too. + */ +void generic_alloc_uninit(QGuestAllocator *allocator) +{ + alloc_uninit(allocator); +} + +QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, + uint32_t page_size, QAllocOpts flags) +{ + QGuestAllocator *s; + uint64_t start = base_addr + (1 << 20); /* Start at 1MB */ + + s = alloc_init_flags(flags, start, start + size); + alloc_set_page_size(s, page_size); + + return s; +} + +inline QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, + uint32_t page_size) +{ + return generic_alloc_init_flags(base_addr, size, page_size, ALLOC_NO_FLAGS); +} diff --git a/tests/libqos/malloc-generic.h b/tests/libqos/malloc-generic.h new file mode 100644 index 0000000..90104ec --- /dev/null +++ b/tests/libqos/malloc-generic.h @@ -0,0 +1,21 @@ +/* + * Basic libqos generic malloc support + * + * Copyright (c) 2014 Marc Marà + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef LIBQOS_MALLOC_GENERIC_H +#define LIBQOS_MALLOC_GENERIC_H + +#include "libqos/malloc.h" + +QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, + uint32_t page_size); +QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, + uint32_t page_size, QAllocOpts flags); +void generic_alloc_uninit(QGuestAllocator *allocator); + +#endif diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c new file mode 100644 index 0000000..b3e62e7 --- /dev/null +++ b/tests/libqos/virtio-mmio.c @@ -0,0 +1,198 @@ +/* + * libqos virtio MMIO driver + * + * Copyright (c) 2014 Marc Marà + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <stdio.h> +#include "libqtest.h" +#include "libqos/virtio.h" +#include "libqos/virtio-mmio.h" +#include "libqos/malloc.h" +#include "libqos/malloc-generic.h" + +static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readb(dev->addr + addr); +} + +static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readw(dev->addr + addr); +} + +static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readl(dev->addr + addr); +} + +static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readq(dev->addr + addr); +} + +static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0); + return readl(dev->addr + QVIRTIO_MMIO_HOST_FEATURES); +} + +static void qvirtio_mmio_set_features(QVirtioDevice *d, uint32_t features) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + dev->features = features; + writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0); + writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features); +} + +static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return dev->features; +} + +static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return (uint8_t)readl(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS); +} + +static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status); +} + +static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + uint32_t isr; + + isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1; + if (isr != 0) { + writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1); + return true; + } + + return false; +} + +static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + uint32_t isr; + + isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2; + if (isr != 0) { + writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2); + return true; + } + + return false; +} + +static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index); + + g_assert_cmphex(readl(dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0); +} + +static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return (uint16_t)readl(dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX); +} + +static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, uint32_t pfn) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn); +} + +static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtQueue *vq; + uint64_t addr; + + vq = g_malloc0(sizeof(*vq)); + qvirtio_mmio_queue_select(d, index); + writel(dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size); + + vq->index = index; + vq->size = qvirtio_mmio_get_queue_size(d); + vq->free_head = 0; + vq->num_free = vq->size; + vq->align = dev->page_size; + vq->indirect = (dev->features & QVIRTIO_F_RING_INDIRECT_DESC) != 0; + vq->event = (dev->features & QVIRTIO_F_RING_EVENT_IDX) != 0; + + writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size); + + /* Check different than 0 */ + g_assert_cmpint(vq->size, !=, 0); + + /* Check power of 2 */ + g_assert_cmpint(vq->size & (vq->size - 1), ==, 0); + + addr = guest_alloc(alloc, qvring_size(vq->size, dev->page_size)); + qvring_init(alloc, vq, addr); + qvirtio_mmio_set_queue_address(d, vq->desc / dev->page_size); + + return vq; +} + +static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index); +} + +const QVirtioBus qvirtio_mmio = { + .config_readb = qvirtio_mmio_config_readb, + .config_readw = qvirtio_mmio_config_readw, + .config_readl = qvirtio_mmio_config_readl, + .config_readq = qvirtio_mmio_config_readq, + .get_features = qvirtio_mmio_get_features, + .set_features = qvirtio_mmio_set_features, + .get_guest_features = qvirtio_mmio_get_guest_features, + .get_status = qvirtio_mmio_get_status, + .set_status = qvirtio_mmio_set_status, + .get_queue_isr_status = qvirtio_mmio_get_queue_isr_status, + .get_config_isr_status = qvirtio_mmio_get_config_isr_status, + .queue_select = qvirtio_mmio_queue_select, + .get_queue_size = qvirtio_mmio_get_queue_size, + .set_queue_address = qvirtio_mmio_set_queue_address, + .virtqueue_setup = qvirtio_mmio_virtqueue_setup, + .virtqueue_kick = qvirtio_mmio_virtqueue_kick, +}; + +QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size) +{ + QVirtioMMIODevice *dev; + uint32_t magic; + dev = g_malloc0(sizeof(*dev)); + + magic = readl(addr + QVIRTIO_MMIO_MAGIC_VALUE); + g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)); + + dev->addr = addr; + dev->page_size = page_size; + dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID); + + writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); + + return dev; +} diff --git a/tests/libqos/virtio-mmio.h b/tests/libqos/virtio-mmio.h new file mode 100644 index 0000000..e3e52b9 --- /dev/null +++ b/tests/libqos/virtio-mmio.h @@ -0,0 +1,46 @@ +/* + * libqos virtio MMIO definitions + * + * Copyright (c) 2014 Marc Marà + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef LIBQOS_VIRTIO_MMIO_H +#define LIBQOS_VIRTIO_MMIO_H + +#include "libqos/virtio.h" + +#define QVIRTIO_MMIO_MAGIC_VALUE 0x000 +#define QVIRTIO_MMIO_VERSION 0x004 +#define QVIRTIO_MMIO_DEVICE_ID 0x008 +#define QVIRTIO_MMIO_VENDOR_ID 0x00C +#define QVIRTIO_MMIO_HOST_FEATURES 0x010 +#define QVIRTIO_MMIO_HOST_FEATURES_SEL 0x014 +#define QVIRTIO_MMIO_GUEST_FEATURES 0x020 +#define QVIRTIO_MMIO_GUEST_FEATURES_SEL 0x024 +#define QVIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 +#define QVIRTIO_MMIO_QUEUE_SEL 0x030 +#define QVIRTIO_MMIO_QUEUE_NUM_MAX 0x034 +#define QVIRTIO_MMIO_QUEUE_NUM 0x038 +#define QVIRTIO_MMIO_QUEUE_ALIGN 0x03C +#define QVIRTIO_MMIO_QUEUE_PFN 0x040 +#define QVIRTIO_MMIO_QUEUE_NOTIFY 0x050 +#define QVIRTIO_MMIO_INTERRUPT_STATUS 0x060 +#define QVIRTIO_MMIO_INTERRUPT_ACK 0x064 +#define QVIRTIO_MMIO_DEVICE_STATUS 0x070 +#define QVIRTIO_MMIO_DEVICE_SPECIFIC 0x100 + +typedef struct QVirtioMMIODevice { + QVirtioDevice vdev; + uint64_t addr; + uint32_t page_size; + uint32_t features; /* As it cannot be read later, save it */ +} QVirtioMMIODevice; + +extern const QVirtioBus qvirtio_mmio; + +QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size); + +#endif diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 788ebaf..f9fb924 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -60,25 +60,25 @@ static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) *vpcidev = (QVirtioPCIDevice *)d; } -static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, void *addr) +static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, addr); + return qpci_io_readb(dev->pdev, (void *)(uintptr_t)addr); } -static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, void *addr) +static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, addr); + return qpci_io_readw(dev->pdev, (void *)(uintptr_t)addr); } -static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, void *addr) +static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, addr); + return qpci_io_readl(dev->pdev, (void *)(uintptr_t)addr); } -static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) +static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; int i; @@ -86,11 +86,13 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) if (qtest_big_endian()) { for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << (7 - i) * 8; + u64 |= (uint64_t)qpci_io_readb(dev->pdev, + (void *)(uintptr_t)addr + i) << (7 - i) * 8; } } else { for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << i * 8; + u64 |= (uint64_t)qpci_io_readb(dev->pdev, + (void *)(uintptr_t)addr + i) << i * 8; } } @@ -100,31 +102,31 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_DEVICE_FEATURES); + return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_FEATURES); } static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES, features); + qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES, features); } static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES); + return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES); } static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS); + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS); } static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS, status); + qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS, status); } static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) @@ -140,11 +142,15 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) return qpci_msix_pending(dev->pdev, vqpci->msix_entry); } else { data = readl(vqpci->msix_addr); - writel(vqpci->msix_addr, 0); - return data == vqpci->msix_data; + if (data == vqpci->msix_data) { + writel(vqpci->msix_addr, 0); + return true; + } else { + return false; + } } } else { - return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 1; + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 1; } } @@ -160,30 +166,34 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) return qpci_msix_pending(dev->pdev, dev->config_msix_entry); } else { data = readl(dev->config_msix_addr); - writel(dev->config_msix_addr, 0); - return data == dev->config_msix_data; + if (data == dev->config_msix_data) { + writel(dev->config_msix_addr, 0); + return true; + } else { + return false; + } } } else { - return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 2; + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 2; } } static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_QUEUE_SELECT, index); + qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SELECT, index); } static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_QUEUE_SIZE); + return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SIZE); } static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_QUEUE_ADDRESS, pfn); + qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_ADDRESS, pfn); } static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, @@ -225,7 +235,7 @@ static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_QUEUE_NOTIFY, vq->index); + qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_NOTIFY, vq->index); } const QVirtioBus qvirtio_pci = { @@ -305,8 +315,8 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index); - qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR); + qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR); g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); } @@ -337,7 +347,7 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); - qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR); + qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR); g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); } diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index 883f7ff..8f0e52a 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -13,18 +13,18 @@ #include "libqos/virtio.h" #include "libqos/pci.h" -#define QVIRTIO_DEVICE_FEATURES 0x00 -#define QVIRTIO_GUEST_FEATURES 0x04 -#define QVIRTIO_QUEUE_ADDRESS 0x08 -#define QVIRTIO_QUEUE_SIZE 0x0C -#define QVIRTIO_QUEUE_SELECT 0x0E -#define QVIRTIO_QUEUE_NOTIFY 0x10 -#define QVIRTIO_DEVICE_STATUS 0x12 -#define QVIRTIO_ISR_STATUS 0x13 -#define QVIRTIO_MSIX_CONF_VECTOR 0x14 -#define QVIRTIO_MSIX_QUEUE_VECTOR 0x16 -#define QVIRTIO_DEVICE_SPECIFIC_MSIX 0x18 -#define QVIRTIO_DEVICE_SPECIFIC_NO_MSIX 0x14 +#define QVIRTIO_PCI_DEVICE_FEATURES 0x00 +#define QVIRTIO_PCI_GUEST_FEATURES 0x04 +#define QVIRTIO_PCI_QUEUE_ADDRESS 0x08 +#define QVIRTIO_PCI_QUEUE_SIZE 0x0C +#define QVIRTIO_PCI_QUEUE_SELECT 0x0E +#define QVIRTIO_PCI_QUEUE_NOTIFY 0x10 +#define QVIRTIO_PCI_DEVICE_STATUS 0x12 +#define QVIRTIO_PCI_ISR_STATUS 0x13 +#define QVIRTIO_PCI_MSIX_CONF_VECTOR 0x14 +#define QVIRTIO_PCI_MSIX_QUEUE_VECTOR 0x16 +#define QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX 0x18 +#define QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX 0x14 #define QVIRTIO_PCI_ALIGN 4096 diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index a061289..3205b88 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -12,25 +12,25 @@ #include "libqos/virtio.h" uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readb(d, addr); } uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readw(d, addr); } uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readl(d, addr); } uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readq(d, addr); } diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 29fbacb..2449fee 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -93,10 +93,10 @@ typedef struct QVRingIndirectDesc { } QVRingIndirectDesc; typedef struct QVirtioBus { - uint8_t (*config_readb)(QVirtioDevice *d, void *addr); - uint16_t (*config_readw)(QVirtioDevice *d, void *addr); - uint32_t (*config_readl)(QVirtioDevice *d, void *addr); - uint64_t (*config_readq)(QVirtioDevice *d, void *addr); + uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr); + uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr); + uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr); + uint64_t (*config_readq)(QVirtioDevice *d, uint64_t addr); /* Get features of the device */ uint32_t (*get_features)(QVirtioDevice *d); @@ -144,13 +144,13 @@ static inline uint32_t qvring_size(uint32_t num, uint32_t align) } uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d); void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, uint32_t features); diff --git a/tests/qemu-iotests/.gitignore b/tests/qemu-iotests/.gitignore index 0541f80..0711cbd 100644 --- a/tests/qemu-iotests/.gitignore +++ b/tests/qemu-iotests/.gitignore @@ -1,5 +1,6 @@ check.log check.time +common.env *.out.bad *.notrun socket_scm_helper diff --git a/tests/qemu-iotests/004 b/tests/qemu-iotests/004 index 651072e..2ad77ed 100755 --- a/tests/qemu-iotests/004 +++ b/tests/qemu-iotests/004 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt generic +_supported_fmt raw qcow qcow2 qed vdi vmdk vhdx _supported_proto generic _supported_os Linux diff --git a/tests/qemu-iotests/006.out b/tests/qemu-iotests/006.out deleted file mode 100644 index d135841..0000000 --- a/tests/qemu-iotests/006.out +++ /dev/null @@ -1,6 +0,0 @@ -QA output created by 006 - -creating 128GB image -qemu-img: The image size is too large for file format 'vpc' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=137438953472 -*** done diff --git a/tests/qemu-iotests/007 b/tests/qemu-iotests/007 index fe1a743..7b5aff5 100755 --- a/tests/qemu-iotests/007 +++ b/tests/qemu-iotests/007 @@ -43,6 +43,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# refcount_bits must be at least 4 so we can create ten internal snapshots +# (1 bit supports none, 2 bits support two, 4 bits support 14) +_unsupported_imgopts 'refcount_bits=\(1\|2\)[^0-9]' echo echo "creating image" diff --git a/tests/qemu-iotests/015 b/tests/qemu-iotests/015 index 099d757..6f26095 100755 --- a/tests/qemu-iotests/015 +++ b/tests/qemu-iotests/015 @@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' echo echo "creating image" diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index df2884b..0fc3244 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -46,6 +46,13 @@ _supported_proto file _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" "none" +# The refcount table tests expect a certain minimum width for refcount entries +# (so that the refcount table actually needs to grow); that minimum is 16 bits, +# being the default refcount entry width. +# 32 and 64 bits do not work either, however, due to different leaked cluster +# count on error. +# Thus, the only remaining option is refcount_bits=16. +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' echo "Errors while writing 128 kB" echo diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out index 524f7ee..5e964fb 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -140,19 +140,13 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write -Failed to flush the L2 table cache: Input/output error write failed: Input/output error - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write -b -Failed to flush the L2 table cache: Input/output error write failed: Input/output error - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write @@ -174,19 +168,13 @@ This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write -Failed to flush the L2 table cache: No space left on device write failed: No space left on device - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write -b -Failed to flush the L2 table cache: No space left on device write failed: No space left on device - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write @@ -356,13 +344,11 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: on; write -Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: on; write -b -Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -382,13 +368,11 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: on; write -Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: on; write -b -Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index fa46ace..b9cd826 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -44,6 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' offset_size=24 offset_l1_size=36 diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index b6311b0..765afdd 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -4,90 +4,90 @@ QA output created by 049 == 1. Traditional size parameter == qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == 2. Specifying size via -o == qemu-img create -f qcow2 -o size=1024 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024.0 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024.0b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == 3. Invalid sizes == @@ -97,7 +97,7 @@ qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 qemu-img: qcow2 doesn't support shrinking images yet qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k qemu-img: Image size must be less than 8 EiB! @@ -105,14 +105,14 @@ qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 qemu-img: qcow2 doesn't support shrinking images yet qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for @@ -125,84 +125,84 @@ qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' == Check correct interpretation of suffixes for cluster size == qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1048576 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1048576 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024.0 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024.0b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=524288 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=524288 lazy_refcounts=off refcount_bits=16 == Check compat level option == qemu-img create -f qcow2 -o compat=0.10 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check preallocation option == qemu-img create -f qcow2 -o preallocation=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off refcount_bits=16 == Check encryption option == qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o encryption=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check lazy_refcounts option (only with v3) == qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 *** done diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 27138a2..0360f37 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# A compat=0.10 image is created in this test which does not support anything +# other than refcount_bits=16 +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' function do_run_qemu() { @@ -96,6 +99,12 @@ run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2 run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2 echo +echo === Device without drive === +echo + +run_qemu -device virtio-scsi-pci -device scsi-hd + +echo echo === Overriding backing file === echo diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index bf52bf0..09895e6 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -47,6 +47,14 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' +=== Device without drive === + +Testing: -device virtio-scsi-pci -device scsi-hd +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device scsi-hd: drive property not set +QEMU_PROG: -device scsi-hd: Device 'scsi-hd' could not be initialized + + === Overriding backing file === Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig -nodefaults @@ -115,8 +123,7 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive if=ide QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: Device needs media, but drive is empty -QEMU_PROG: Device initialization failed. -QEMU_PROG: Initialization of device ide-hd failed +QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. Testing: -drive if=virtio QEMU X.Y.Z monitor - type 'help' for more information @@ -127,8 +134,7 @@ QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized Testing: -drive if=scsi QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty -QEMU_PROG: Device initialization failed. -QEMU_PROG: Initialization of device lsi53c895a failed +QEMU_PROG: Initialization of device lsi53c895a failed: Device initialization failed. Testing: -drive if=none,id=disk -device ide-cd,drive=disk QEMU X.Y.Z monitor - type 'help' for more information @@ -178,8 +184,7 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: Can't use a read-only drive -QEMU_PROG: Device initialization failed. -QEMU_PROG: Initialization of device ide-hd failed +QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on QEMU X.Y.Z monitor - type 'help' for more information diff --git a/tests/qemu-iotests/058 b/tests/qemu-iotests/058 index a60b34b..f2bdd0b 100755 --- a/tests/qemu-iotests/058 +++ b/tests/qemu-iotests/058 @@ -89,6 +89,8 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux _require_command QEMU_NBD +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' # Use -f raw instead of -f $IMGFMT for the NBD connection QEMU_IO_NBD="$QEMU_IO -f raw --cache=$CACHEMODE" diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index dc9f6b7..7511189 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -18,6 +18,7 @@ cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: true qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write read 512/512 bytes at offset 0 diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index 8d3a9c9..72aa970 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -88,34 +88,41 @@ class TestQMP(TestImageInfoSpecific): class TestQCow2(TestQemuImgInfo): '''Testing a qcow2 version 2 image''' img_options = 'compat=0.10' - json_compare = { 'compat': '0.10' } - human_compare = [ 'compat: 0.10' ] + json_compare = { 'compat': '0.10', 'refcount-bits': 16 } + human_compare = [ 'compat: 0.10', 'refcount bits: 16' ] class TestQCow3NotLazy(TestQemuImgInfo): '''Testing a qcow2 version 3 image with lazy refcounts disabled''' img_options = 'compat=1.1,lazy_refcounts=off' - json_compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False } - human_compare = [ 'compat: 1.1', 'lazy refcounts: false', 'corrupt: false' ] + json_compare = { 'compat': '1.1', 'lazy-refcounts': False, + 'refcount-bits': 16, 'corrupt': False } + human_compare = [ 'compat: 1.1', 'lazy refcounts: false', + 'refcount bits: 16', 'corrupt: false' ] class TestQCow3Lazy(TestQemuImgInfo): '''Testing a qcow2 version 3 image with lazy refcounts enabled''' img_options = 'compat=1.1,lazy_refcounts=on' - json_compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False } - human_compare = [ 'compat: 1.1', 'lazy refcounts: true', 'corrupt: false' ] + json_compare = { 'compat': '1.1', 'lazy-refcounts': True, + 'refcount-bits': 16, 'corrupt': False } + human_compare = [ 'compat: 1.1', 'lazy refcounts: true', + 'refcount bits: 16', 'corrupt: false' ] class TestQCow3NotLazyQMP(TestQMP): '''Testing a qcow2 version 3 image with lazy refcounts disabled, opening with lazy refcounts enabled''' img_options = 'compat=1.1,lazy_refcounts=off' qemu_options = 'lazy-refcounts=on' - compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False } + compare = { 'compat': '1.1', 'lazy-refcounts': False, + 'refcount-bits': 16, 'corrupt': False } + class TestQCow3LazyQMP(TestQMP): '''Testing a qcow2 version 3 image with lazy refcounts enabled, opening with lazy refcounts disabled''' img_options = 'compat=1.1,lazy_refcounts=on' qemu_options = 'lazy-refcounts=off' - compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False } + compare = { 'compat': '1.1', 'lazy-refcounts': True, + 'refcount-bits': 16, 'corrupt': False } TestImageInfoSpecific = None TestQemuImgInfo = None diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 index 0508c69..83eefa3 100755 --- a/tests/qemu-iotests/067 +++ b/tests/qemu-iotests/067 @@ -35,6 +35,8 @@ status=1 # failure is the default! _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Because anything other than 16 would change the output of query-block +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' function do_run_qemu() { diff --git a/tests/qemu-iotests/067.out b/tests/qemu-iotests/067.out index 00b3eae..6ff41bc 100644 --- a/tests/qemu-iotests/067.out +++ b/tests/qemu-iotests/067.out @@ -32,6 +32,7 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virti "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, @@ -208,6 +209,7 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, @@ -414,6 +416,7 @@ Testing: "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, @@ -599,6 +602,7 @@ Testing: "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, @@ -710,6 +714,7 @@ Testing: "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, diff --git a/tests/qemu-iotests/079 b/tests/qemu-iotests/079 index 6613cfb..ade6efa 100755 --- a/tests/qemu-iotests/079 +++ b/tests/qemu-iotests/079 @@ -42,19 +42,13 @@ _supported_fmt qcow2 _supported_proto file nfs _supported_os Linux -function test_qemu_img() -{ - echo qemu-img "$@" | _filter_testdir - $QEMU_IMG "$@" 2>&1 | _filter_testdir - echo -} - echo "=== Check option preallocation and cluster_size ===" echo cluster_sizes="16384 32768 65536 131072 262144 524288 1048576 2097152 4194304" for s in $cluster_sizes; do - test_qemu_img create -f $IMGFMT -o preallocation=metadata,cluster_size=$s "$TEST_IMG" 4G + IMGOPTS=$(_optstr_add "$IMGOPTS" "preallocation=metadata,cluster_size=$s") \ + _make_test_img 4G done # success, all done diff --git a/tests/qemu-iotests/079.out b/tests/qemu-iotests/079.out index ef4b8c9..6dc5d57 100644 --- a/tests/qemu-iotests/079.out +++ b/tests/qemu-iotests/079.out @@ -1,32 +1,14 @@ QA output created by 079 === Check option preallocation and cluster_size === -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=16384 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=16384 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=32768 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=32768 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=65536 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=131072 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=131072 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=262144 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=262144 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=524288 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=524288 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=1048576 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=1048576 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=2097152 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=2097152 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=4194304 TEST_DIR/t.qcow2 4G -qemu-img: TEST_DIR/t.qcow2: Cluster size must be a power of two between 512 and 2048k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=4194304 preallocation='metadata' lazy_refcounts=off - +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +qemu-img: TEST_DIR/t.IMGFMT: Cluster size must be a power of two between 512 and 2048k +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' *** done diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 index 73795f1..a2c58ae 100755 --- a/tests/qemu-iotests/080 +++ b/tests/qemu-iotests/080 @@ -42,6 +42,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' header_size=104 diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index d0234e6..3a749b8 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -3,14 +3,14 @@ QA output created by 082 === create: Options specified more than once === Testing: create -f foo -f qcow2 TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) cluster_size: 65536 Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) @@ -18,10 +18,11 @@ cluster_size: 4096 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) @@ -29,10 +30,11 @@ cluster_size: 8192 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: create -f qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) @@ -50,6 +52,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o ? TEST_DIR/t.qcow2 128M @@ -62,6 +65,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 128M @@ -74,6 +78,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 128M @@ -86,6 +91,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 128M @@ -98,6 +104,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 128M @@ -110,6 +117,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 128M @@ -122,6 +130,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 128M @@ -134,13 +143,14 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2, @@ -161,6 +171,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits Testing: create -o help Supported options: @@ -169,7 +180,7 @@ size Virtual disk size === convert: Options specified more than once === Testing: create -f qcow2 TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 Testing: convert -f foo -f qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base image: TEST_DIR/t.IMGFMT.base @@ -190,6 +201,7 @@ cluster_size: 4096 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -200,6 +212,7 @@ cluster_size: 8192 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: convert -O qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -220,6 +233,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -232,6 +246,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -244,6 +259,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -256,6 +272,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -268,6 +285,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -280,6 +298,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -292,6 +311,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -304,6 +324,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -331,6 +352,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits Testing: convert -o help Supported options: @@ -346,6 +368,7 @@ cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: amend -f qcow2 -o size=130M -o lazy_refcounts=off TEST_DIR/t.qcow2 @@ -356,6 +379,7 @@ cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: false Testing: amend -f qcow2 -o size=8M -o lazy_refcounts=on -o size=132M TEST_DIR/t.qcow2 @@ -366,6 +390,7 @@ cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: amend -f qcow2 -o size=4M,size=148M TEST_DIR/t.qcow2 @@ -386,6 +411,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 @@ -398,6 +424,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 @@ -410,6 +437,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 @@ -422,6 +450,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 @@ -434,6 +463,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 @@ -446,6 +476,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 @@ -458,6 +489,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 @@ -470,6 +502,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 @@ -499,6 +532,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits Testing: convert -o help Supported options: diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out index 0f2b17f..5eb8b94 100644 --- a/tests/qemu-iotests/085.out +++ b/tests/qemu-iotests/085.out @@ -11,7 +11,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 === Create a single snapshot on virtio0 === -Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} === Invalid command - missing device and nodename === @@ -25,31 +25,31 @@ Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file === Create several transactional group snapshots === -Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} *** done diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089 index 6f74cec..3e0038d 100755 --- a/tests/qemu-iotests/089 +++ b/tests/qemu-iotests/089 @@ -41,6 +41,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Because anything other than 16 would change the output of qemu_io -c info +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' # Using an image filename containing quotation marks will render the JSON data # below invalid. In that case, we have little choice but simply not to run this diff --git a/tests/qemu-iotests/089.out b/tests/qemu-iotests/089.out index a6d3711..5b541a3 100644 --- a/tests/qemu-iotests/089.out +++ b/tests/qemu-iotests/089.out @@ -43,6 +43,7 @@ vm state offset: 512 MiB Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: false format name: IMGFMT cluster size: 64 KiB @@ -50,5 +51,6 @@ vm state offset: 512 MiB Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: false *** done diff --git a/tests/qemu-iotests/104 b/tests/qemu-iotests/104 index f32752b..2e35ea8 100755 --- a/tests/qemu-iotests/104 +++ b/tests/qemu-iotests/104 @@ -34,7 +34,7 @@ trap "exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt generic +_supported_fmt raw qcow qcow2 qed vdi vmdk vhdx _supported_proto generic _supported_os Linux diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 index 12fc92a..ce44749 100755 --- a/tests/qemu-iotests/108 +++ b/tests/qemu-iotests/108 @@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# This test directly modifies a refblock so it relies on refcount_bits being 16 +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' echo echo '=== Repairing an image without any refcount table ===' diff --git a/tests/qemu-iotests/112 b/tests/qemu-iotests/112 new file mode 100755 index 0000000..3f054a3 --- /dev/null +++ b/tests/qemu-iotests/112 @@ -0,0 +1,187 @@ +#!/bin/bash +# +# Test cases for different refcount_bits values +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This tests qcow2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux +# This test will set refcount_bits on its own which would conflict with the +# manual setting; compat will be overridden as well +_unsupported_imgopts refcount_bits 'compat=0.10' + +function print_refcount_bits() +{ + $QEMU_IMG info "$TEST_IMG" | sed -n '/refcount bits:/ s/^ *//p' +} + +echo +echo '=== refcount_bits limits ===' +echo + +# Must be positive (non-zero) +IMGOPTS="$IMGOPTS,refcount_bits=0" _make_test_img 64M +# Must be positive (non-negative) +IMGOPTS="$IMGOPTS,refcount_bits=-1" _make_test_img 64M +# May not exceed 64 +IMGOPTS="$IMGOPTS,refcount_bits=128" _make_test_img 64M +# Must be a power of two +IMGOPTS="$IMGOPTS,refcount_bits=42" _make_test_img 64M + +# 1 is the minimum +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +# 64 is the maximum +IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +print_refcount_bits + +# 16 is the default +_make_test_img 64M +print_refcount_bits + +echo +echo '=== refcount_bits and compat=0.10 ===' +echo + +# Should work +IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=16" _make_test_img 64M +print_refcount_bits + +# Should not work +IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=1" _make_test_img 64M +IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=64" _make_test_img 64M + + +echo +echo '=== Snapshot limit on refcount_bits=1 ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Should fail for now; in the future, this might be supported by automatically +# copying all clusters with overflowing refcount +$QEMU_IMG snapshot -c foo "$TEST_IMG" + +# The new L1 table could/should be leaked +_check_test_img + +echo +echo '=== Snapshot limit on refcount_bits=2 ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=2" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Should succeed +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IMG snapshot -c bar "$TEST_IMG" +# Should fail (4th reference) +$QEMU_IMG snapshot -c baz "$TEST_IMG" + +# The new L1 table could/should be leaked +_check_test_img + +echo +echo '=== Compressed clusters with refcount_bits=1 ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +# Both should fit into a single host cluster; instead of failing to increase the +# refcount of that cluster, qemu should just allocate a new cluster and make +# this operation succeed +$QEMU_IO -c 'write -P 0 -c 0 64k' \ + -c 'write -P 1 -c 64k 64k' \ + "$TEST_IMG" | _filter_qemu_io + +_check_test_img + +echo +echo '=== MSb set in 64 bit refcount ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Set the MSb in the refblock entry of the data cluster +poke_file "$TEST_IMG" $((0x20028)) "\x80\x00\x00\x00\x00\x00\x00\x00" + +# Clear OFLAG_COPIED in the L2 entry of the data cluster +poke_file "$TEST_IMG" $((0x40000)) "\x00\x00\x00\x00\x00\x05\x00\x00" + +# Try to write to that cluster (should work, even though the MSb is set) +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +echo +echo '=== Snapshot on maximum 64 bit refcount value ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Set the refblock entry to the maximum value possible +poke_file "$TEST_IMG" $((0x20028)) "\xff\xff\xff\xff\xff\xff\xff\xff" + +# Clear OFLAG_COPIED in the L2 entry of the data cluster +poke_file "$TEST_IMG" $((0x40000)) "\x00\x00\x00\x00\x00\x05\x00\x00" + +# Try a snapshot (should correctly identify the overflow; may work in the future +# by falling back to COW) +$QEMU_IMG snapshot -c foo "$TEST_IMG" + +# The new L1 table could/should be leaked; and obviously the data cluster is +# leaked (refcount=UINT64_MAX reference=1) +_check_test_img + + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out new file mode 100644 index 0000000..9a98633 --- /dev/null +++ b/tests/qemu-iotests/112.out @@ -0,0 +1,84 @@ +QA output created by 112 + +=== refcount_bits limits === + +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount_bits=-1 +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 64 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 16 + +=== refcount_bits and compat=0.10 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 16 +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + +=== Snapshot limit on refcount_bits=1 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'foo': -22 (Invalid argument) +Leaked cluster 6 refcount=1 reference=0 + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. + +=== Snapshot limit on refcount_bits=2 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 2 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'baz': -22 (Invalid argument) +Leaked cluster 7 refcount=1 reference=0 + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. + +=== Compressed clusters with refcount_bits=1 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +=== MSb set in 64 bit refcount === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 64 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Snapshot on maximum 64 bit refcount value === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 64 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'foo': -22 (Invalid argument) +Leaked cluster 5 refcount=18446744073709551615 reference=1 +Leaked cluster 6 refcount=1 reference=0 + +2 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +*** done diff --git a/tests/qemu-iotests/006 b/tests/qemu-iotests/128 index 0c0cf5d..249a865 100755 --- a/tests/qemu-iotests/006 +++ b/tests/qemu-iotests/128 @@ -1,9 +1,8 @@ #!/bin/bash # -# Make sure qemu-img rejects > 127GB images for the vpc format as the format -# doesn't support this. +# Test that opening O_DIRECT succeeds when image file I/O produces EIO # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright (C) 2015 Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,7 +19,7 @@ # # creator -owner=hch@lst.de +owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" @@ -29,9 +28,34 @@ here=`pwd` tmp=/tmp/$$ status=1 # failure is the default! +devname="eiodev$$" + +_setup_eiodev() +{ + # This test should either be run as root or with passwordless sudo + for cmd in "" "sudo -n"; do + echo "0 $((1024 * 1024 * 1024 / 512)) error" | \ + $cmd dmsetup create "$devname" 2>/dev/null + if [ "$?" -eq 0 ]; then + return + fi + done + _notrun "root privileges required to run dmsetup" +} + +_cleanup_eiodev() +{ + for cmd in "" "sudo -n"; do + $cmd dmsetup remove "$devname" 2>/dev/null + if [ "$?" -eq 0 ]; then + return + fi + done +} + _cleanup() { - _cleanup_test_img + _cleanup_eiodev } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -39,14 +63,18 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt vpc -_supported_proto generic +_supported_fmt raw +_supported_proto file _supported_os Linux +_setup_eiodev + +TEST_IMG="/dev/mapper/$devname" echo -echo "creating 128GB image" -_make_test_img 128G +echo "== reading from error device ==" +# Opening image should succeed but the read operation should fail +$QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/128.out b/tests/qemu-iotests/128.out new file mode 100644 index 0000000..4e43f5f --- /dev/null +++ b/tests/qemu-iotests/128.out @@ -0,0 +1,5 @@ +QA output created by 128 + +== reading from error device == +read failed: Input/output error +*** done diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 06e1bb0..012a812 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -192,7 +192,8 @@ _filter_img_create() -e "s# block_size=[0-9]\\+##g" \ -e "s# block_state_zero=\\(on\\|off\\)##g" \ -e "s# log_size=[0-9]\\+##g" \ - -e "s/archipelago:a/TEST_DIR\//g" + -e "s/archipelago:a/TEST_DIR\//g" \ + -e "s# refcount_bits=[0-9]\\+##g" } _filter_img_info() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 0d3b95c..71f19d4 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -12,7 +12,7 @@ 003 rw auto 004 rw auto quick 005 img auto quick -006 img auto +# 006 was removed, do not reuse 007 snapshot auto 008 rw auto quick 009 rw auto quick @@ -116,7 +116,9 @@ 109 rw auto 110 rw auto backing quick 111 rw auto quick +112 rw auto 113 rw auto quick 114 rw auto quick 116 rw auto quick 123 rw auto quick +128 rw auto quick diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c index 27d1b6f..b552d9f 100644 --- a/tests/test-coroutine.c +++ b/tests/test-coroutine.c @@ -13,6 +13,7 @@ #include <glib.h> #include "block/coroutine.h" +#include "block/coroutine_int.h" /* * Check that qemu_in_coroutine() works @@ -122,6 +123,30 @@ static void test_yield(void) g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */ } +static void coroutine_fn c2_fn(void *opaque) +{ + qemu_coroutine_yield(); +} + +static void coroutine_fn c1_fn(void *opaque) +{ + Coroutine *c2 = opaque; + qemu_coroutine_enter(c2, NULL); +} + +static void test_co_queue(void) +{ + Coroutine *c1; + Coroutine *c2; + + c1 = qemu_coroutine_create(c1_fn); + c2 = qemu_coroutine_create(c2_fn); + + qemu_coroutine_enter(c1, c2); + memset(c1, 0xff, sizeof(Coroutine)); + qemu_coroutine_enter(c2, NULL); +} + /* * Check that creation, enter, and return work */ @@ -343,6 +368,7 @@ static void perf_cost(void) int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + g_test_add_func("/basic/co_queue", test_co_queue); g_test_add_func("/basic/lifecycle", test_lifecycle); g_test_add_func("/basic/yield", test_yield); g_test_add_func("/basic/nesting", test_nesting); diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 89d7cbf..4078321 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -16,9 +16,11 @@ #include "libqtest.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" +#include "libqos/virtio-mmio.h" #include "libqos/pci-pc.h" #include "libqos/malloc.h" #include "libqos/malloc-pc.h" +#include "libqos/malloc-generic.h" #include "qemu/bswap.h" #define QVIRTIO_BLK_F_BARRIER 0x00000001 @@ -42,10 +44,14 @@ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) +#define PCI_SLOT_HP 0x06 #define PCI_SLOT 0x04 #define PCI_FN 0x00 -#define PCI_SLOT_HP 0x06 +#define MMIO_PAGE_SIZE 4096 +#define MMIO_DEV_BASE_ADDR 0x0A003E00 +#define MMIO_RAM_ADDR 0x40000000 +#define MMIO_RAM_SIZE 0x20000000 typedef struct QVirtioBlkReq { uint32_t type; @@ -55,11 +61,10 @@ typedef struct QVirtioBlkReq { uint8_t status; } QVirtioBlkReq; -static QPCIBus *test_start(void) +static char *drive_create(void) { - char *cmdline; - char tmp_path[] = "/tmp/qtest.XXXXXX"; int fd, ret; + char *tmp_path = g_strdup("/tmp/qtest.XXXXXX"); /* Create a temporary raw image */ fd = mkstemp(tmp_path); @@ -68,24 +73,52 @@ static QPCIBus *test_start(void) g_assert_cmpint(ret, ==, 0); close(fd); + return tmp_path; +} + +static QPCIBus *pci_test_start(void) +{ + char *cmdline; + char *tmp_path; + + tmp_path = drive_create(); + cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s,format=raw " - "-drive if=none,id=drive1,file=/dev/null,format=raw " - "-device virtio-blk-pci,id=drv0,drive=drive0," - "addr=%x.%x", - tmp_path, PCI_SLOT, PCI_FN); + "-drive if=none,id=drive1,file=/dev/null,format=raw " + "-device virtio-blk-pci,id=drv0,drive=drive0," + "addr=%x.%x", + tmp_path, PCI_SLOT, PCI_FN); qtest_start(cmdline); unlink(tmp_path); + g_free(tmp_path); g_free(cmdline); return qpci_init_pc(); } +static void arm_test_start(void) +{ + char *cmdline; + char *tmp_path; + + tmp_path = drive_create(); + + cmdline = g_strdup_printf("-machine virt " + "-drive if=none,id=drive0,file=%s,format=raw " + "-device virtio-blk-device,drive=drive0", + tmp_path); + qtest_start(cmdline); + unlink(tmp_path); + g_free(tmp_path); + g_free(cmdline); +} + static void test_end(void) { qtest_end(); } -static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus, int slot) +static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot) { QVirtioPCIDevice *dev; @@ -135,14 +168,10 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req, return addr; } -static void pci_basic(void) +static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, uint64_t device_specific) { - QVirtioPCIDevice *dev; - QPCIBus *bus; - QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; QVirtioBlkReq req; - void *addr; uint64_t req_addr; uint64_t capacity; uint32_t features; @@ -150,29 +179,19 @@ static void pci_basic(void) uint8_t status; char *data; - bus = test_start(); + capacity = qvirtio_config_readq(bus, dev, device_specific); - dev = virtio_blk_init(bus, PCI_SLOT); - - /* MSI-X is not enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; - - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(bus, dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | QVIRTIO_F_RING_INDIRECT_DESC | QVIRTIO_F_RING_EVENT_IDX | QVIRTIO_BLK_F_SCSI); - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); - - alloc = pc_alloc_init(); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); + qvirtio_set_features(bus, dev, features); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(bus, dev); - /* Write and read with 2 descriptor layout */ + /* Write and read with 3 descriptor layout */ /* Write request */ req.type = QVIRTIO_BLK_T_OUT; req.ioprio = 1; @@ -184,12 +203,13 @@ static void pci_basic(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtqueue_kick(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -205,13 +225,13 @@ static void pci_basic(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, true, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(bus, dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -222,61 +242,84 @@ static void pci_basic(void) guest_free(alloc, req_addr); - /* Write and read with 3 descriptor layout */ - /* Write request */ - req.type = QVIRTIO_BLK_T_OUT; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); + if (features & QVIRTIO_F_ANY_LAYOUT) { + /* Write and read with 2 descriptor layout */ + /* Write request */ + req.type = QVIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, &req, 512); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + g_free(req.data); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 528, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(bus, dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); - guest_free(alloc, req_addr); + guest_free(alloc, req_addr); - /* Read request */ - req.type = QVIRTIO_BLK_T_IN; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); + /* Read request */ + req.type = QVIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, &req, 512); - g_free(req.data); + g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 513, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(bus, dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); - data = g_malloc0(512); - memread(req_addr + 16, data, 512); - g_assert_cmpstr(data, ==, "TEST"); - g_free(data); + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); - guest_free(alloc, req_addr); + guest_free(alloc, req_addr); + } +} + +static void pci_basic(void) +{ + QVirtioPCIDevice *dev; + QPCIBus *bus; + QVirtQueuePCI *vqpci; + QGuestAllocator *alloc; + void *addr; + + bus = pci_test_start(); + dev = virtio_blk_pci_init(bus, PCI_SLOT); + + alloc = pc_alloc_init(); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 0); + + /* MSI-X is not enabled */ + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; + + test_basic(&qvirtio_pci, &dev->vdev, alloc, &vqpci->vq, + (uint64_t)(uintptr_t)addr); /* End test */ guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -296,14 +339,15 @@ static void pci_indirect(void) uint8_t status; char *data; - bus = test_start(); + bus = pci_test_start(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); /* MSI-X is not enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); @@ -374,8 +418,10 @@ static void pci_indirect(void) /* End test */ guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -387,14 +433,15 @@ static void pci_config(void) void *addr; uint64_t capacity; - bus = test_start(); + bus = pci_test_start(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); /* MSI-X is not enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); @@ -403,11 +450,13 @@ static void pci_config(void) " 'size': %d } }", n_size); qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, n_size / 512); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -427,18 +476,19 @@ static void pci_msix(void) uint8_t status; char *data; - bus = test_start(); + bus = pci_test_start(); alloc = pc_alloc_init(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); qpci_msix_enable(dev->pdev); qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); /* MSI-X is enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); @@ -458,7 +508,8 @@ static void pci_msix(void) qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, n_size / 512); /* Write request */ @@ -472,7 +523,8 @@ static void pci_msix(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -495,7 +547,8 @@ static void pci_msix(void) g_free(req.data); free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -514,10 +567,12 @@ static void pci_msix(void) guest_free(alloc, req_addr); /* End test */ - guest_free(alloc, (uint64_t)vqpci->vq.desc); + guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -536,18 +591,19 @@ static void pci_idx(void) uint8_t status; char *data; - bus = test_start(); + bus = pci_test_start(); alloc = pc_alloc_init(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); qpci_msix_enable(dev->pdev); qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); /* MSI-X is enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); @@ -573,7 +629,8 @@ static void pci_idx(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -593,7 +650,8 @@ static void pci_idx(void) /* Notify after processing the third request */ qvirtqueue_set_used_event(&vqpci->vq, 2); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -616,11 +674,11 @@ static void pci_idx(void) g_free(req.data); free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); @@ -636,45 +694,94 @@ static void pci_idx(void) /* End test */ guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } -static void hotplug(void) +static void pci_hotplug(void) { QPCIBus *bus; QVirtioPCIDevice *dev; - bus = test_start(); + bus = pci_test_start(); /* plug secondary disk */ qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, "'drive': 'drive1'"); - dev = virtio_blk_init(bus, PCI_SLOT_HP); + dev = virtio_blk_pci_init(bus, PCI_SLOT_HP); g_assert(dev); qvirtio_pci_device_disable(dev); g_free(dev); /* unplug secondary disk */ qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); + qpci_free_pc(bus); + test_end(); +} + +static void mmio_basic(void) +{ + QVirtioMMIODevice *dev; + QVirtQueue *vq; + QGuestAllocator *alloc; + int n_size = TEST_IMAGE_SIZE / 2; + uint64_t capacity; + + arm_test_start(); + + dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); + g_assert(dev != NULL); + g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID); + + qvirtio_reset(&qvirtio_mmio, &dev->vdev); + qvirtio_set_acknowledge(&qvirtio_mmio, &dev->vdev); + qvirtio_set_driver(&qvirtio_mmio, &dev->vdev); + + alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); + vq = qvirtqueue_setup(&qvirtio_mmio, &dev->vdev, alloc, 0); + + test_basic(&qvirtio_mmio, &dev->vdev, alloc, vq, + QVIRTIO_MMIO_DEVICE_SPECIFIC); + + qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); + + qvirtio_wait_queue_isr(&qvirtio_mmio, &dev->vdev, vq, + QVIRTIO_BLK_TIMEOUT_US); + + capacity = qvirtio_config_readq(&qvirtio_mmio, &dev->vdev, + QVIRTIO_MMIO_DEVICE_SPECIFIC); + g_assert_cmpint(capacity, ==, n_size / 512); + + /* End test */ + guest_free(alloc, vq->desc); + generic_alloc_uninit(alloc); + g_free(dev); test_end(); } int main(int argc, char **argv) { int ret; + const char *arch = qtest_get_arch(); g_test_init(&argc, &argv, NULL); - g_test_add_func("/virtio/blk/pci/basic", pci_basic); - g_test_add_func("/virtio/blk/pci/indirect", pci_indirect); - g_test_add_func("/virtio/blk/pci/config", pci_config); - g_test_add_func("/virtio/blk/pci/msix", pci_msix); - g_test_add_func("/virtio/blk/pci/idx", pci_idx); - g_test_add_func("/virtio/blk/pci/hotplug", hotplug); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("/virtio/blk/pci/basic", pci_basic); + qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); + qtest_add_func("/virtio/blk/pci/config", pci_config); + qtest_add_func("/virtio/blk/pci/msix", pci_msix); + qtest_add_func("/virtio/blk/pci/idx", pci_idx); + qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); + } else if (strcmp(arch, "arm") == 0) { + qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); + } ret = g_test_run(); @@ -134,7 +134,7 @@ static int configure_tpm(QemuOpts *opts) Error *local_err = NULL; if (!QLIST_EMPTY(&tpm_backends)) { - error_report("Only one TPM is allowed.\n"); + error_report("Only one TPM is allowed."); return 1; } diff --git a/trace/control.c b/trace/control.c index 0d30801..995beb3 100644 --- a/trace/control.c +++ b/trace/control.c @@ -126,7 +126,7 @@ static void trace_init_events(const char *fname) error_report("WARNING: trace event '%s' does not exist", line_ptr); } else if (!trace_event_get_state_static(ev)) { - error_report("WARNING: trace event '%s' is not traceable\n", + error_report("WARNING: trace event '%s' is not traceable", line_ptr); } else { trace_event_set_state_dynamic(ev, enable); diff --git a/ui/console.c b/ui/console.c index 87574a7..87af6b5 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2005,18 +2005,6 @@ DisplaySurface *qemu_console_surface(QemuConsole *console) return console->surface; } -DisplayState *qemu_console_displaystate(QemuConsole *console) -{ - return console->ds; -} - -PixelFormat qemu_different_endianness_pixelformat(int bpp) -{ - pixman_format_code_t fmt = qemu_default_pixman_format(bpp, false); - PixelFormat pf = qemu_pixelformat_from_pixman(fmt); - return pf; -} - PixelFormat qemu_default_pixelformat(int bpp) { pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); @@ -121,15 +121,6 @@ static void cookey(register unsigned long *raw1) return; } -void cpkey(register unsigned long *into) -{ - register unsigned long *from, *endp; - - from = KnL, endp = &KnL[32]; - while( from < endp ) *into++ = *from++; - return; - } - void usekey(register unsigned long *from) { register unsigned long *to, *endp; @@ -36,12 +36,6 @@ void usekey(unsigned long *); * Loads the internal key register with the data in cookedkey. */ -void cpkey(unsigned long *); -/* cookedkey[32] - * Copies the contents of the internal key register into the storage - * located at &cookedkey[0]. - */ - void des(unsigned char *, unsigned char *); /* from[8] to[8] * Encrypts/Decrypts (according to the key currently loaded in the diff --git a/ui/input-legacy.c b/ui/input-legacy.c index a698a34..2d4ca19 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -143,12 +143,6 @@ QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) return entry; } -void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry) -{ - qemu_input_handler_unregister(entry->s); - g_free(entry); -} - static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 68f3d77..c8ee203 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -342,16 +342,3 @@ void vnc_start_worker_thread(void) QEMU_THREAD_DETACHED); queue = q; /* Set global queue */ } - -void vnc_stop_worker_thread(void) -{ - if (!vnc_worker_thread_running()) - return ; - - /* Remove all jobs and wake up the thread */ - vnc_lock_queue(queue); - queue->exit = true; - vnc_unlock_queue(queue); - vnc_jobs_clear(NULL); - qemu_cond_broadcast(&queue->cond); -} diff --git a/ui/vnc-jobs.h b/ui/vnc-jobs.h index 31da103..044bf9f 100644 --- a/ui/vnc-jobs.h +++ b/ui/vnc-jobs.h @@ -40,7 +40,6 @@ void vnc_jobs_join(VncState *vs); void vnc_jobs_consume_buffer(VncState *vs); void vnc_start_worker_thread(void); -void vnc_stop_worker_thread(void); /* Locks */ static inline int vnc_trylock_display(VncDisplay *vd) diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index e304baf..d75950d 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -207,8 +207,7 @@ static void vncws_send_handshake_response(VncState *vs, const char* key) } response = g_strdup_printf(WS_HANDSHAKE, accept); - vnc_write(vs, response, strlen(response)); - vnc_flush(vs); + vnc_client_write_buf(vs, (const uint8_t *)response, strlen(response)); g_free(accept); g_free(response); @@ -3687,12 +3687,7 @@ void vnc_display_add_client(const char *id, int csock, bool skipauth) vnc_connect(vs, csock, skipauth, false); } -QemuOpts *vnc_parse_func(const char *str) -{ - return qemu_opts_parse(qemu_find_opts("vnc"), str, 1); -} - -void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) +static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) { int i = 2; char *id; @@ -3705,18 +3700,25 @@ void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) qemu_opts_set_id(opts, id); } -int vnc_init_func(QemuOpts *opts, void *opaque) +QemuOpts *vnc_parse_func(const char *str) { - Error *local_err = NULL; QemuOptsList *olist = qemu_find_opts("vnc"); - char *id = (char *)qemu_opts_id(opts); + QemuOpts *opts = qemu_opts_parse(olist, str, 1); + const char *id = qemu_opts_id(opts); if (!id) { /* auto-assign id if not present */ vnc_auto_assign_id(olist, opts); - id = (char *)qemu_opts_id(opts); } + return opts; +} + +int vnc_init_func(QemuOpts *opts, void *opaque) +{ + Error *local_err = NULL; + char *id = (char *)qemu_opts_id(opts); + assert(id); vnc_display_init(id); vnc_display_open(id, &local_err); if (local_err != NULL) { diff --git a/ui/vnc_keysym.h b/ui/vnc_keysym.h index 1dc039f..7fa2bc1 100644 --- a/ui/vnc_keysym.h +++ b/ui/vnc_keysym.h @@ -404,6 +404,7 @@ static const name2keysym_t name2keysym[]={ {"breve", 0x01a2}, /* U+02D8 BREVE */ {"caron", 0x01b7}, /* U+02C7 CARON */ {"Ccaron", 0x01c8}, /* U+010C LATIN CAPITAL LETTER C WITH CARON */ +{"numerosign", 0x06b0}, /* U+2116 NUMERO SIGN */ {"Cyrillic_a", 0x06c1}, /* U+0430 CYRILLIC SMALL LETTER A */ {"Cyrillic_A", 0x06e1}, /* U+0410 CYRILLIC CAPITAL LETTER A */ {"Cyrillic_be", 0x06c2}, /* U+0431 CYRILLIC SMALL LETTER BE */ diff --git a/util/cutils.c b/util/cutils.c index c2250d1..144b25c 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -537,16 +537,17 @@ int parse_debug_env(const char *name, int max, int initial) { char *debug_env = getenv(name); char *inv = NULL; - int debug; + long debug; if (!debug_env) { return initial; } + errno = 0; debug = strtol(debug_env, &inv, 10); if (inv == debug_env) { return initial; } - if (debug < 0 || debug > max) { + if (debug < 0 || debug > max || errno != 0) { fprintf(stderr, "warning: %s not in [0, %d]", name, max); return initial; } diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 16fcec2..37ffd96 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -399,10 +399,10 @@ void os_mem_prealloc(int fd, char *area, size_t memory) } else { int i; size_t hpagesize = fd_getpagesize(fd); + size_t numpages = DIV_ROUND_UP(memory, hpagesize); /* MAP_POPULATE silently ignores failures */ - memory = (memory + hpagesize - 1) & -hpagesize; - for (i = 0; i < (memory / hpagesize); i++) { + for (i = 0; i < numpages; i++) { memset(area + (hpagesize * i), 0, 1); } diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index 50a29d8..ba67cec 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -51,12 +51,8 @@ static void error_exit(int err, const char *msg) void qemu_mutex_init(QemuMutex *mutex) { int err; - pthread_mutexattr_t mutexattr; - pthread_mutexattr_init(&mutexattr); - pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK); - err = pthread_mutex_init(&mutex->lock, &mutexattr); - pthread_mutexattr_destroy(&mutexattr); + err = pthread_mutex_init(&mutex->lock, NULL); if (err) error_exit(err, __func__); } @@ -283,7 +283,7 @@ void rcu_unregister_thread(void) qemu_mutex_unlock(&rcu_gp_lock); } -static void __attribute__((__constructor__)) rcu_init(void) +static void rcu_init_complete(void) { QemuThread thread; @@ -291,8 +291,39 @@ static void __attribute__((__constructor__)) rcu_init(void) qemu_event_init(&rcu_gp_event, true); qemu_event_init(&rcu_call_ready_event, false); + + /* The caller is assumed to have iothread lock, so the call_rcu thread + * must have been quiescent even after forking, just recreate it. + */ qemu_thread_create(&thread, "call_rcu", call_rcu_thread, NULL, QEMU_THREAD_DETACHED); rcu_register_thread(); } + +#ifdef CONFIG_POSIX +static void rcu_init_lock(void) +{ + qemu_mutex_lock(&rcu_gp_lock); +} + +static void rcu_init_unlock(void) +{ + qemu_mutex_unlock(&rcu_gp_lock); +} + +static void rcu_init_child(void) +{ + qemu_mutex_unlock(&rcu_gp_lock); + memset(®istry, 0, sizeof(registry)); + rcu_init_complete(); +} +#endif + +static void __attribute__((__constructor__)) rcu_init(void) +{ +#ifdef CONFIG_POSIX + pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child); +#endif + rcu_init_complete(); +} @@ -2001,7 +2001,6 @@ static DisplayType select_display(const char *p) } else if (strstart(p, "vnc", &opts)) { #ifdef CONFIG_VNC if (*opts == '=') { - display_remote++; if (vnc_parse_func(opts+1) == NULL) { exit(1); } @@ -3477,7 +3476,6 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_vnc: #ifdef CONFIG_VNC - display_remote++; if (vnc_parse_func(optarg) == NULL) { exit(1); } @@ -3970,6 +3968,11 @@ int main(int argc, char **argv, char **envp) } } +#if defined(CONFIG_VNC) + if (!QTAILQ_EMPTY(&(qemu_find_opts("vnc")->head))) { + display_remote++; + } +#endif if (display_type == DT_DEFAULT && !display_remote) { #if defined(CONFIG_GTK) display_type = DT_GTK; diff --git a/xen-hvm-stub.c b/xen-hvm-stub.c index 2d98696..46867d8 100644 --- a/xen-hvm-stub.c +++ b/xen-hvm-stub.c @@ -30,10 +30,6 @@ void xen_hvm_inject_msi(uint64_t addr, uint32_t data) { } -void xen_cmos_set_s3_resume(void *opaque, int irq, int level) -{ -} - void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr) { } |