diff options
85 files changed, 1281 insertions, 931 deletions
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 7879d13ddb..909f0517f8 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -52,8 +52,6 @@ struct BdrvDirtyBitmap { Such operations must fail and both the image and this bitmap must remain unchanged while this flag is set. */ - bool autoload; /* For persistent bitmaps: bitmap must be - autoloaded on image opening */ bool persistent; /* bitmap must be saved to owner disk image */ QLIST_ENTRY(BdrvDirtyBitmap) list; }; @@ -104,7 +102,6 @@ void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) g_free(bitmap->name); bitmap->name = NULL; bitmap->persistent = false; - bitmap->autoload = false; } /* Called with BQL taken. */ @@ -261,8 +258,6 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, bitmap->successor = NULL; successor->persistent = bitmap->persistent; bitmap->persistent = false; - successor->autoload = bitmap->autoload; - bitmap->autoload = false; bdrv_release_dirty_bitmap(bs, bitmap); return successor; @@ -667,19 +662,6 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs) } /* Called with BQL taken. */ -void bdrv_dirty_bitmap_set_autoload(BdrvDirtyBitmap *bitmap, bool autoload) -{ - qemu_mutex_lock(bitmap->mutex); - bitmap->autoload = autoload; - qemu_mutex_unlock(bitmap->mutex); -} - -bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap) -{ - return bitmap->autoload; -} - -/* Called with BQL taken. */ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) { qemu_mutex_lock(bitmap->mutex); diff --git a/block/gluster.c b/block/gluster.c index d8decc41ad..3f17b7819d 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -965,12 +965,68 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs, } #endif +static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset, + PreallocMode prealloc, Error **errp) +{ + int64_t current_length; + + current_length = glfs_lseek(fd, 0, SEEK_END); + if (current_length < 0) { + error_setg_errno(errp, errno, "Failed to determine current size"); + return -errno; + } + + if (current_length > offset && prealloc != PREALLOC_MODE_OFF) { + error_setg(errp, "Cannot use preallocation for shrinking files"); + return -ENOTSUP; + } + + if (current_length == offset) { + return 0; + } + + switch (prealloc) { +#ifdef CONFIG_GLUSTERFS_FALLOCATE + case PREALLOC_MODE_FALLOC: + if (glfs_fallocate(fd, 0, current_length, offset - current_length)) { + error_setg_errno(errp, errno, "Could not preallocate data"); + return -errno; + } + break; +#endif /* CONFIG_GLUSTERFS_FALLOCATE */ +#ifdef CONFIG_GLUSTERFS_ZEROFILL + case PREALLOC_MODE_FULL: + if (glfs_ftruncate(fd, offset)) { + error_setg_errno(errp, errno, "Could not resize file"); + return -errno; + } + if (glfs_zerofill(fd, current_length, offset - current_length)) { + error_setg_errno(errp, errno, "Could not zerofill the new area"); + return -errno; + } + break; +#endif /* CONFIG_GLUSTERFS_ZEROFILL */ + case PREALLOC_MODE_OFF: + if (glfs_ftruncate(fd, offset)) { + error_setg_errno(errp, errno, "Could not resize file"); + return -errno; + } + break; + default: + error_setg(errp, "Unsupported preallocation mode: %s", + PreallocMode_str(prealloc)); + return -EINVAL; + } + + return 0; +} + static int qemu_gluster_create(const char *filename, QemuOpts *opts, Error **errp) { BlockdevOptionsGluster *gconf; struct glfs *glfs; - struct glfs_fd *fd; + struct glfs_fd *fd = NULL; int ret = 0; PreallocMode prealloc; int64_t total_size = 0; @@ -1019,45 +1075,14 @@ static int qemu_gluster_create(const char *filename, goto out; } - switch (prealloc) { -#ifdef CONFIG_GLUSTERFS_FALLOCATE - case PREALLOC_MODE_FALLOC: - if (glfs_fallocate(fd, 0, 0, total_size)) { - error_setg(errp, "Could not preallocate data for the new file"); - ret = -errno; - } - break; -#endif /* CONFIG_GLUSTERFS_FALLOCATE */ -#ifdef CONFIG_GLUSTERFS_ZEROFILL - case PREALLOC_MODE_FULL: - if (!glfs_ftruncate(fd, total_size)) { - if (glfs_zerofill(fd, 0, total_size)) { - error_setg(errp, "Could not zerofill the new file"); - ret = -errno; - } - } else { - error_setg(errp, "Could not resize file"); - ret = -errno; - } - break; -#endif /* CONFIG_GLUSTERFS_ZEROFILL */ - case PREALLOC_MODE_OFF: - if (glfs_ftruncate(fd, total_size) != 0) { + ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp); + +out: + if (fd) { + if (glfs_close(fd) != 0 && ret == 0) { ret = -errno; - error_setg(errp, "Could not resize file"); } - break; - default: - ret = -EINVAL; - error_setg(errp, "Unsupported preallocation mode: %s", - PreallocMode_str(prealloc)); - break; - } - - if (glfs_close(fd) != 0) { - ret = -errno; } -out: qapi_free_BlockdevOptionsGluster(gconf); glfs_clear_preopened(glfs); return ret; @@ -1097,23 +1122,8 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, PreallocMode prealloc, Error **errp) { - int ret; BDRVGlusterState *s = bs->opaque; - - if (prealloc != PREALLOC_MODE_OFF) { - error_setg(errp, "Unsupported preallocation mode '%s'", - PreallocMode_str(prealloc)); - return -ENOTSUP; - } - - ret = glfs_ftruncate(s->fd, offset); - if (ret < 0) { - ret = -errno; - error_setg_errno(errp, -ret, "Failed to truncate file"); - return ret; - } - - return 0; + return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); } static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index efa10c6663..4f6fd863ea 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -933,14 +933,14 @@ static void set_readonly_helper(gpointer bitmap, gpointer value) bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value); } -/* qcow2_load_autoloading_dirty_bitmaps() +/* qcow2_load_dirty_bitmaps() * Return value is a hint for caller: true means that the Qcow2 header was * updated. (false doesn't mean that the header should be updated by the * caller, it just means that updating was not needed or the image cannot be * written to). * On failure the function returns false. */ -bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp) +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) { BDRVQcow2State *s = bs->opaque; Qcow2BitmapList *bm_list; @@ -960,14 +960,16 @@ bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp) } QSIMPLEQ_FOREACH(bm, bm_list, entry) { - if ((bm->flags & BME_FLAG_AUTO) && !(bm->flags & BME_FLAG_IN_USE)) { + if (!(bm->flags & BME_FLAG_IN_USE)) { BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp); if (bitmap == NULL) { goto fail; } + if (!(bm->flags & BME_FLAG_AUTO)) { + bdrv_disable_dirty_bitmap(bitmap); + } bdrv_dirty_bitmap_set_persistance(bitmap, true); - bdrv_dirty_bitmap_set_autoload(bitmap, true); bm->flags |= BME_FLAG_IN_USE; created_dirty_bitmaps = g_slist_append(created_dirty_bitmaps, bitmap); @@ -1369,7 +1371,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) bm->table.size = 0; QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry); } - bm->flags = bdrv_dirty_bitmap_get_autoload(bitmap) ? BME_FLAG_AUTO : 0; + bm->flags = bdrv_dirty_bitmap_enabled(bitmap) ? BME_FLAG_AUTO : 0; bm->granularity_bits = ctz32(bdrv_dirty_bitmap_granularity(bitmap)); bm->dirty_bitmap = bitmap; } diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index c48ffebd8f..d9dafa31e5 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -39,26 +39,23 @@ struct Qcow2Cache { Qcow2CachedTable *entries; struct Qcow2Cache *depends; int size; + int table_size; bool depends_on_flush; void *table_array; uint64_t lru_counter; uint64_t cache_clean_lru_counter; }; -static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs, - Qcow2Cache *c, int table) +static inline void *qcow2_cache_get_table_addr(Qcow2Cache *c, int table) { - BDRVQcow2State *s = bs->opaque; - return (uint8_t *) c->table_array + (size_t) table * s->cluster_size; + return (uint8_t *) c->table_array + (size_t) table * c->table_size; } -static inline int qcow2_cache_get_table_idx(BlockDriverState *bs, - Qcow2Cache *c, void *table) +static inline int qcow2_cache_get_table_idx(Qcow2Cache *c, void *table) { - BDRVQcow2State *s = bs->opaque; ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; - int idx = table_offset / s->cluster_size; - assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0); + int idx = table_offset / c->table_size; + assert(idx >= 0 && idx < c->size && table_offset % c->table_size == 0); return idx; } @@ -74,15 +71,13 @@ static inline const char *qcow2_cache_get_name(BDRVQcow2State *s, Qcow2Cache *c) } } -static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, - int i, int num_tables) +static void qcow2_cache_table_release(Qcow2Cache *c, int i, int num_tables) { /* Using MADV_DONTNEED to discard memory is a Linux-specific feature */ #ifdef CONFIG_LINUX - BDRVQcow2State *s = bs->opaque; - void *t = qcow2_cache_get_table_addr(bs, c, i); + void *t = qcow2_cache_get_table_addr(c, i); int align = getpagesize(); - size_t mem_size = (size_t) s->cluster_size * num_tables; + size_t mem_size = (size_t) c->table_size * num_tables; size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); if (mem_size > offset && length > 0) { @@ -98,7 +93,7 @@ static inline bool can_clean_entry(Qcow2Cache *c, int i) t->lru_counter <= c->cache_clean_lru_counter; } -void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c) +void qcow2_cache_clean_unused(Qcow2Cache *c) { int i = 0; while (i < c->size) { @@ -118,23 +113,30 @@ void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c) } if (to_clean > 0) { - qcow2_cache_table_release(bs, c, i - to_clean, to_clean); + qcow2_cache_table_release(c, i - to_clean, to_clean); } } c->cache_clean_lru_counter = c->lru_counter; } -Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, + unsigned table_size) { BDRVQcow2State *s = bs->opaque; Qcow2Cache *c; + assert(num_tables > 0); + assert(is_power_of_2(table_size)); + assert(table_size >= (1 << MIN_CLUSTER_BITS)); + assert(table_size <= s->cluster_size); + c = g_new0(Qcow2Cache, 1); c->size = num_tables; + c->table_size = table_size; c->entries = g_try_new0(Qcow2CachedTable, num_tables); c->table_array = qemu_try_blockalign(bs->file->bs, - (size_t) num_tables * s->cluster_size); + (size_t) num_tables * c->table_size); if (!c->entries || !c->table_array) { qemu_vfree(c->table_array); @@ -146,7 +148,7 @@ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) return c; } -int qcow2_cache_destroy(BlockDriverState *bs, Qcow2Cache *c) +int qcow2_cache_destroy(Qcow2Cache *c) { int i; @@ -203,13 +205,13 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) if (c == s->refcount_block_cache) { ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK, - c->entries[i].offset, s->cluster_size); + c->entries[i].offset, c->table_size); } else if (c == s->l2_table_cache) { ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, - c->entries[i].offset, s->cluster_size); + c->entries[i].offset, c->table_size); } else { ret = qcow2_pre_write_overlap_check(bs, 0, - c->entries[i].offset, s->cluster_size); + c->entries[i].offset, c->table_size); } if (ret < 0) { @@ -223,7 +225,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) } ret = bdrv_pwrite(bs->file, c->entries[i].offset, - qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); + qcow2_cache_get_table_addr(c, i), c->table_size); if (ret < 0) { return ret; } @@ -309,7 +311,7 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) c->entries[i].lru_counter = 0; } - qcow2_cache_table_release(bs, c, 0, c->size); + qcow2_cache_table_release(c, 0, c->size); c->lru_counter = 0; @@ -331,7 +333,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, offset, read_from_disk); - if (offset_into_cluster(s, offset)) { + if (!QEMU_IS_ALIGNED(offset, c->table_size)) { qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s " "cache: Offset %#" PRIx64 " is unaligned", qcow2_cache_get_name(s, c), offset); @@ -339,7 +341,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, } /* Check if the table is already cached */ - i = lookup_index = (offset / s->cluster_size * 4) % c->size; + i = lookup_index = (offset / c->table_size * 4) % c->size; do { const Qcow2CachedTable *t = &c->entries[i]; if (t->offset == offset) { @@ -379,8 +381,8 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, } ret = bdrv_pread(bs->file, offset, - qcow2_cache_get_table_addr(bs, c, i), - s->cluster_size); + qcow2_cache_get_table_addr(c, i), + c->table_size); if (ret < 0) { return ret; } @@ -391,7 +393,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, /* And return the right table */ found: c->entries[i].ref++; - *table = qcow2_cache_get_table_addr(bs, c, i); + *table = qcow2_cache_get_table_addr(c, i); trace_qcow2_cache_get_done(qemu_coroutine_self(), c == s->l2_table_cache, i); @@ -411,9 +413,9 @@ int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, return qcow2_cache_do_get(bs, c, offset, table, false); } -void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) +void qcow2_cache_put(Qcow2Cache *c, void **table) { - int i = qcow2_cache_get_table_idx(bs, c, *table); + int i = qcow2_cache_get_table_idx(c, *table); c->entries[i].ref--; *table = NULL; @@ -425,30 +427,28 @@ void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) assert(c->entries[i].ref >= 0); } -void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, - void *table) +void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) { - int i = qcow2_cache_get_table_idx(bs, c, table); + int i = qcow2_cache_get_table_idx(c, table); assert(c->entries[i].offset != 0); c->entries[i].dirty = true; } -void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, - uint64_t offset) +void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset) { int i; for (i = 0; i < c->size; i++) { if (c->entries[i].offset == offset) { - return qcow2_cache_get_table_addr(bs, c, i); + return qcow2_cache_get_table_addr(c, i); } } return NULL; } -void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table) +void qcow2_cache_discard(Qcow2Cache *c, void *table) { - int i = qcow2_cache_get_table_idx(bs, c, table); + int i = qcow2_cache_get_table_idx(c, table); assert(c->entries[i].ref == 0); @@ -456,5 +456,5 @@ void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table) c->entries[i].lru_counter = 0; c->entries[i].dirty = false; - qcow2_cache_table_release(bs, c, i, 1); + qcow2_cache_table_release(c, i, 1); } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 3a979bcd82..e406b0f3b9 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -195,20 +195,26 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, /* * l2_load * - * Loads a L2 table into memory. If the table is in the cache, the cache - * is used; otherwise the L2 table is loaded from the image file. + * @bs: The BlockDriverState + * @offset: A guest offset, used to calculate what slice of the L2 + * table to load. + * @l2_offset: Offset to the L2 table in the image file. + * @l2_slice: Location to store the pointer to the L2 slice. * - * Returns a pointer to the L2 table on success, or NULL if the read from - * the image file failed. + * Loads a L2 slice into memory (L2 slices are the parts of L2 tables + * that are loaded by the qcow2 cache). If the slice is in the cache, + * the cache is used; otherwise the L2 slice is loaded from the image + * file. */ - -static int l2_load(BlockDriverState *bs, uint64_t l2_offset, - uint64_t **l2_table) +static int l2_load(BlockDriverState *bs, uint64_t offset, + uint64_t l2_offset, uint64_t **l2_slice) { BDRVQcow2State *s = bs->opaque; + int start_of_slice = sizeof(uint64_t) * + (offset_to_l2_index(s, offset) - offset_to_l2_slice_index(s, offset)); - return qcow2_cache_get(bs, s->l2_table_cache, l2_offset, - (void **)l2_table); + return qcow2_cache_get(bs, s->l2_table_cache, l2_offset + start_of_slice, + (void **)l2_slice); } /* @@ -257,11 +263,12 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) * */ -static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) +static int l2_allocate(BlockDriverState *bs, int l1_index) { BDRVQcow2State *s = bs->opaque; uint64_t old_l2_offset; - uint64_t *l2_table = NULL; + uint64_t *l2_slice = NULL; + unsigned slice, slice_size2, n_slices; int64_t l2_offset; int ret; @@ -292,39 +299,47 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) /* allocate a new entry in the l2 cache */ + slice_size2 = s->l2_slice_size * sizeof(uint64_t); + n_slices = s->cluster_size / slice_size2; + trace_qcow2_l2_allocate_get_empty(bs, l1_index); - ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table); - if (ret < 0) { - goto fail; - } + for (slice = 0; slice < n_slices; slice++) { + ret = qcow2_cache_get_empty(bs, s->l2_table_cache, + l2_offset + slice * slice_size2, + (void **) &l2_slice); + if (ret < 0) { + goto fail; + } - l2_table = *table; + if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { + /* if there was no old l2 table, clear the new slice */ + memset(l2_slice, 0, slice_size2); + } else { + uint64_t *old_slice; + uint64_t old_l2_slice_offset = + (old_l2_offset & L1E_OFFSET_MASK) + slice * slice_size2; + + /* if there was an old l2 table, read a slice from the disk */ + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); + ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_slice_offset, + (void **) &old_slice); + if (ret < 0) { + goto fail; + } - if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { - /* if there was no old l2 table, clear the new table */ - memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); - } else { - uint64_t* old_table; + memcpy(l2_slice, old_slice, slice_size2); - /* if there was an old l2 table, read it from the disk */ - BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); - ret = qcow2_cache_get(bs, s->l2_table_cache, - old_l2_offset & L1E_OFFSET_MASK, - (void**) &old_table); - if (ret < 0) { - goto fail; + qcow2_cache_put(s->l2_table_cache, (void **) &old_slice); } - memcpy(l2_table, old_table, s->cluster_size); + /* write the l2 slice to the file */ + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); - qcow2_cache_put(bs, s->l2_table_cache, (void **) &old_table); + trace_qcow2_l2_allocate_write_l2(bs, l1_index); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); } - /* write the l2 table to the file */ - BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); - - trace_qcow2_l2_allocate_write_l2(bs, l1_index); - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); ret = qcow2_cache_flush(bs, s->l2_table_cache); if (ret < 0) { goto fail; @@ -338,14 +353,13 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) goto fail; } - *table = l2_table; trace_qcow2_l2_allocate_done(bs, l1_index, 0); return 0; fail: trace_qcow2_l2_allocate_done(bs, l1_index, ret); - if (l2_table != NULL) { - qcow2_cache_put(bs, s->l2_table_cache, (void**) table); + if (l2_slice != NULL) { + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); } s->l1_table[l1_index] = old_l2_offset; if (l2_offset > 0) { @@ -356,19 +370,19 @@ fail: } /* - * Checks how many clusters in a given L2 table are contiguous in the image + * Checks how many clusters in a given L2 slice are contiguous in the image * file. As soon as one of the flags in the bitmask stop_flags changes compared * to the first cluster, the search is stopped and the cluster is not counted * as contiguous. (This allows it, for example, to stop at the first compressed * cluster which may require a different handling) */ static int count_contiguous_clusters(int nb_clusters, int cluster_size, - uint64_t *l2_table, uint64_t stop_flags) + uint64_t *l2_slice, uint64_t stop_flags) { int i; QCow2ClusterType first_cluster_type; uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED; - uint64_t first_entry = be64_to_cpu(l2_table[0]); + uint64_t first_entry = be64_to_cpu(l2_slice[0]); uint64_t offset = first_entry & mask; if (!offset) { @@ -381,7 +395,7 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC); for (i = 0; i < nb_clusters; i++) { - uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask; + uint64_t l2_entry = be64_to_cpu(l2_slice[i]) & mask; if (offset + (uint64_t) i * cluster_size != l2_entry) { break; } @@ -392,10 +406,10 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, /* * Checks how many consecutive unallocated clusters in a given L2 - * table have the same cluster type. + * slice have the same cluster type. */ static int count_contiguous_clusters_unallocated(int nb_clusters, - uint64_t *l2_table, + uint64_t *l2_slice, QCow2ClusterType wanted_type) { int i; @@ -403,7 +417,7 @@ static int count_contiguous_clusters_unallocated(int nb_clusters, assert(wanted_type == QCOW2_CLUSTER_ZERO_PLAIN || wanted_type == QCOW2_CLUSTER_UNALLOCATED); for (i = 0; i < nb_clusters; i++) { - uint64_t entry = be64_to_cpu(l2_table[i]); + uint64_t entry = be64_to_cpu(l2_slice[i]); QCow2ClusterType type = qcow2_get_cluster_type(entry); if (type != wanted_type) { @@ -515,8 +529,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, { BDRVQcow2State *s = bs->opaque; unsigned int l2_index; - uint64_t l1_index, l2_offset, *l2_table; - int l1_bits, c; + uint64_t l1_index, l2_offset, *l2_slice; + int c; unsigned int offset_in_cluster; uint64_t bytes_available, bytes_needed, nb_clusters; QCow2ClusterType type; @@ -525,12 +539,12 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, offset_in_cluster = offset_into_cluster(s, offset); bytes_needed = (uint64_t) *bytes + offset_in_cluster; - l1_bits = s->l2_bits + s->cluster_bits; - /* compute how many bytes there are between the start of the cluster - * containing offset and the end of the l1 entry */ - bytes_available = (1ULL << l1_bits) - (offset & ((1ULL << l1_bits) - 1)) - + offset_in_cluster; + * containing offset and the end of the l2 slice that contains + * the entry pointing to it */ + bytes_available = + ((uint64_t) (s->l2_slice_size - offset_to_l2_slice_index(s, offset))) + << s->cluster_bits; if (bytes_needed > bytes_available) { bytes_needed = bytes_available; @@ -540,7 +554,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, /* seek to the l2 offset in the l1 table */ - l1_index = offset >> l1_bits; + l1_index = offset_to_l1_index(s, offset); if (l1_index >= s->l1_size) { type = QCOW2_CLUSTER_UNALLOCATED; goto out; @@ -559,17 +573,17 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, return -EIO; } - /* load the l2 table in memory */ + /* load the l2 slice in memory */ - ret = l2_load(bs, l2_offset, &l2_table); + ret = l2_load(bs, offset, l2_offset, &l2_slice); if (ret < 0) { return ret; } /* find the cluster offset for the given disk offset */ - l2_index = offset_to_l2_index(s, offset); - *cluster_offset = be64_to_cpu(l2_table[l2_index]); + l2_index = offset_to_l2_slice_index(s, offset); + *cluster_offset = be64_to_cpu(l2_slice[l2_index]); nb_clusters = size_to_clusters(s, bytes_needed); /* bytes_needed <= *bytes + offset_in_cluster, both of which are unsigned @@ -596,14 +610,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, case QCOW2_CLUSTER_UNALLOCATED: /* how many empty clusters ? */ c = count_contiguous_clusters_unallocated(nb_clusters, - &l2_table[l2_index], type); + &l2_slice[l2_index], type); *cluster_offset = 0; break; case QCOW2_CLUSTER_ZERO_ALLOC: case QCOW2_CLUSTER_NORMAL: /* how many allocated clusters ? */ c = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], QCOW_OFLAG_ZERO); + &l2_slice[l2_index], QCOW_OFLAG_ZERO); *cluster_offset &= L2E_OFFSET_MASK; if (offset_into_cluster(s, *cluster_offset)) { qcow2_signal_corruption(bs, true, -1, -1, @@ -619,7 +633,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, abort(); } - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); bytes_available = (int64_t)c * s->cluster_size; @@ -637,7 +651,7 @@ out: return type; fail: - qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); + qcow2_cache_put(s->l2_table_cache, (void **)&l2_slice); return ret; } @@ -645,26 +659,25 @@ fail: * get_cluster_table * * for a given disk offset, load (and allocate if needed) - * the l2 table. + * the appropriate slice of its l2 table. * - * the l2 table offset in the qcow2 file and the cluster index - * in the l2 table are given to the caller. + * the cluster index in the l2 slice is given to the caller. * * Returns 0 on success, -errno in failure case */ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, - uint64_t **new_l2_table, + uint64_t **new_l2_slice, int *new_l2_index) { BDRVQcow2State *s = bs->opaque; unsigned int l2_index; uint64_t l1_index, l2_offset; - uint64_t *l2_table = NULL; + uint64_t *l2_slice = NULL; int ret; /* seek to the l2 offset in the l1 table */ - l1_index = offset >> (s->l2_bits + s->cluster_bits); + l1_index = offset_to_l1_index(s, offset); if (l1_index >= s->l1_size) { ret = qcow2_grow_l1_table(bs, l1_index + 1, false); if (ret < 0) { @@ -681,17 +694,9 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, return -EIO; } - /* seek the l2 table of the given l2 offset */ - - if (s->l1_table[l1_index] & QCOW_OFLAG_COPIED) { - /* load the l2 table in memory */ - ret = l2_load(bs, l2_offset, &l2_table); - if (ret < 0) { - return ret; - } - } else { + if (!(s->l1_table[l1_index] & QCOW_OFLAG_COPIED)) { /* First allocate a new L2 table (and do COW if needed) */ - ret = l2_allocate(bs, l1_index, &l2_table); + ret = l2_allocate(bs, l1_index); if (ret < 0) { return ret; } @@ -701,13 +706,23 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t), QCOW2_DISCARD_OTHER); } + + /* Get the offset of the newly-allocated l2 table */ + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; + assert(offset_into_cluster(s, l2_offset) == 0); + } + + /* load the l2 slice in memory */ + ret = l2_load(bs, offset, l2_offset, &l2_slice); + if (ret < 0) { + return ret; } /* find the cluster offset for the given disk offset */ - l2_index = offset_to_l2_index(s, offset); + l2_index = offset_to_l2_slice_index(s, offset); - *new_l2_table = l2_table; + *new_l2_slice = l2_slice; *new_l2_index = l2_index; return 0; @@ -732,26 +747,26 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, { BDRVQcow2State *s = bs->opaque; int l2_index, ret; - uint64_t *l2_table; + uint64_t *l2_slice; int64_t cluster_offset; int nb_csectors; - ret = get_cluster_table(bs, offset, &l2_table, &l2_index); + ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); if (ret < 0) { return 0; } /* Compression can't overwrite anything. Fail if the cluster was already * allocated. */ - cluster_offset = be64_to_cpu(l2_table[l2_index]); + cluster_offset = be64_to_cpu(l2_slice[l2_index]); if (cluster_offset & L2E_OFFSET_MASK) { - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); return 0; } cluster_offset = qcow2_alloc_bytes(bs, compressed_size); if (cluster_offset < 0) { - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); return 0; } @@ -766,9 +781,9 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, /* compressed clusters never have the copied flag */ BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); - l2_table[l2_index] = cpu_to_be64(cluster_offset); - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); + l2_slice[l2_index] = cpu_to_be64(cluster_offset); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); return cluster_offset; } @@ -907,7 +922,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) { BDRVQcow2State *s = bs->opaque; int i, j = 0, l2_index, ret; - uint64_t *old_cluster, *l2_table; + uint64_t *old_cluster, *l2_slice; uint64_t cluster_offset = m->alloc_offset; trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); @@ -934,13 +949,13 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) s->refcount_block_cache); } - ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index); + ret = get_cluster_table(bs, m->offset, &l2_slice, &l2_index); if (ret < 0) { goto err; } - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); - assert(l2_index + m->nb_clusters <= s->l2_size); + assert(l2_index + m->nb_clusters <= s->l2_slice_size); for (i = 0; i < m->nb_clusters; i++) { /* if two concurrent writes happen to the same unallocated cluster * each write allocates separate cluster and writes data concurrently. @@ -948,16 +963,16 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) * cluster the second one has to do RMW (which is done above by * perform_cow()), update l2 table with its cluster pointer and free * old cluster. This is what this loop does */ - if (l2_table[l2_index + i] != 0) { - old_cluster[j++] = l2_table[l2_index + i]; + if (l2_slice[l2_index + i] != 0) { + old_cluster[j++] = l2_slice[l2_index + i]; } - l2_table[l2_index + i] = cpu_to_be64((cluster_offset + + l2_slice[l2_index + i] = cpu_to_be64((cluster_offset + (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); } - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); /* * If this was a COW, we need to decrease the refcount of the old cluster. @@ -984,12 +999,12 @@ err: * which must copy from the backing file) */ static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters, - uint64_t *l2_table, int l2_index) + uint64_t *l2_slice, int l2_index) { int i; for (i = 0; i < nb_clusters; i++) { - uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]); + uint64_t l2_entry = be64_to_cpu(l2_slice[l2_index + i]); QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry); switch(cluster_type) { @@ -1104,7 +1119,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, BDRVQcow2State *s = bs->opaque; int l2_index; uint64_t cluster_offset; - uint64_t *l2_table; + uint64_t *l2_slice; uint64_t nb_clusters; unsigned int keep_clusters; int ret; @@ -1116,23 +1131,23 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, == offset_into_cluster(s, *host_offset)); /* - * Calculate the number of clusters to look for. We stop at L2 table + * Calculate the number of clusters to look for. We stop at L2 slice * boundaries to keep things simple. */ nb_clusters = size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); - l2_index = offset_to_l2_index(s, guest_offset); - nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + l2_index = offset_to_l2_slice_index(s, guest_offset); + nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); assert(nb_clusters <= INT_MAX); /* Find L2 entry for the first involved cluster */ - ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); + ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); if (ret < 0) { return ret; } - cluster_offset = be64_to_cpu(l2_table[l2_index]); + cluster_offset = be64_to_cpu(l2_slice[l2_index]); /* Check how many clusters are already allocated and don't need COW */ if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL @@ -1160,7 +1175,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, /* We keep all QCOW_OFLAG_COPIED clusters */ keep_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], + &l2_slice[l2_index], QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); assert(keep_clusters <= nb_clusters); @@ -1175,7 +1190,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, /* Cleanup */ out: - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); /* Only return a host offset if we actually made progress. Otherwise we * would make requirements for handle_alloc() that it can't fulfill */ @@ -1259,7 +1274,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, { BDRVQcow2State *s = bs->opaque; int l2_index; - uint64_t *l2_table; + uint64_t *l2_slice; uint64_t entry; uint64_t nb_clusters; int ret; @@ -1272,29 +1287,29 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, assert(*bytes > 0); /* - * Calculate the number of clusters to look for. We stop at L2 table + * Calculate the number of clusters to look for. We stop at L2 slice * boundaries to keep things simple. */ nb_clusters = size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); - l2_index = offset_to_l2_index(s, guest_offset); - nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + l2_index = offset_to_l2_slice_index(s, guest_offset); + nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); assert(nb_clusters <= INT_MAX); /* Find L2 entry for the first involved cluster */ - ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); + ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); if (ret < 0) { return ret; } - entry = be64_to_cpu(l2_table[l2_index]); + entry = be64_to_cpu(l2_slice[l2_index]); /* For the moment, overwrite compressed clusters one by one */ if (entry & QCOW_OFLAG_COMPRESSED) { nb_clusters = 1; } else { - nb_clusters = count_cow_clusters(s, nb_clusters, l2_table, l2_index); + nb_clusters = count_cow_clusters(s, nb_clusters, l2_slice, l2_index); } /* This function is only called when there were no non-COW clusters, so if @@ -1323,7 +1338,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, * nb_clusters already to a range of COW clusters */ preallocated_nb_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], QCOW_OFLAG_COPIED); + &l2_slice[l2_index], QCOW_OFLAG_COPIED); assert(preallocated_nb_clusters > 0); nb_clusters = preallocated_nb_clusters; @@ -1334,7 +1349,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, keep_old_clusters = true; } - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); if (!alloc_cluster_offset) { /* Allocate, if necessary at a given offset in the image file */ @@ -1616,32 +1631,32 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) /* * This discards as many clusters of nb_clusters as possible at once (i.e. - * all clusters in the same L2 table) and returns the number of discarded + * all clusters in the same L2 slice) and returns the number of discarded * clusters. */ -static int discard_single_l2(BlockDriverState *bs, uint64_t offset, - uint64_t nb_clusters, enum qcow2_discard_type type, - bool full_discard) +static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, + uint64_t nb_clusters, + enum qcow2_discard_type type, bool full_discard) { BDRVQcow2State *s = bs->opaque; - uint64_t *l2_table; + uint64_t *l2_slice; int l2_index; int ret; int i; - ret = get_cluster_table(bs, offset, &l2_table, &l2_index); + ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); if (ret < 0) { return ret; } - /* Limit nb_clusters to one L2 table */ - nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + /* Limit nb_clusters to one L2 slice */ + nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); assert(nb_clusters <= INT_MAX); for (i = 0; i < nb_clusters; i++) { uint64_t old_l2_entry; - old_l2_entry = be64_to_cpu(l2_table[l2_index + i]); + old_l2_entry = be64_to_cpu(l2_slice[l2_index + i]); /* * If full_discard is false, make sure that a discarded area reads back @@ -1679,18 +1694,18 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, } /* First remove L2 entries */ - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); if (!full_discard && s->qcow_version >= 3) { - l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); + l2_slice[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); } else { - l2_table[l2_index + i] = cpu_to_be64(0); + l2_slice[l2_index + i] = cpu_to_be64(0); } /* Then decrease the refcount */ qcow2_free_any_clusters(bs, old_l2_entry, 1, type); } - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); return nb_clusters; } @@ -1714,10 +1729,10 @@ int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, s->cache_discards = true; - /* Each L2 table is handled by its own loop iteration */ + /* Each L2 slice is handled by its own loop iteration */ while (nb_clusters > 0) { - cleared = discard_single_l2(bs, offset, nb_clusters, type, - full_discard); + cleared = discard_in_l2_slice(bs, offset, nb_clusters, type, + full_discard); if (cleared < 0) { ret = cleared; goto fail; @@ -1737,33 +1752,33 @@ fail: /* * This zeroes as many clusters of nb_clusters as possible at once (i.e. - * all clusters in the same L2 table) and returns the number of zeroed + * all clusters in the same L2 slice) and returns the number of zeroed * clusters. */ -static int zero_single_l2(BlockDriverState *bs, uint64_t offset, - uint64_t nb_clusters, int flags) +static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, + uint64_t nb_clusters, int flags) { BDRVQcow2State *s = bs->opaque; - uint64_t *l2_table; + uint64_t *l2_slice; int l2_index; int ret; int i; bool unmap = !!(flags & BDRV_REQ_MAY_UNMAP); - ret = get_cluster_table(bs, offset, &l2_table, &l2_index); + ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); if (ret < 0) { return ret; } - /* Limit nb_clusters to one L2 table */ - nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + /* Limit nb_clusters to one L2 slice */ + nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); assert(nb_clusters <= INT_MAX); for (i = 0; i < nb_clusters; i++) { uint64_t old_offset; QCow2ClusterType cluster_type; - old_offset = be64_to_cpu(l2_table[l2_index + i]); + old_offset = be64_to_cpu(l2_slice[l2_index + i]); /* * Minimize L2 changes if the cluster already reads back as @@ -1775,16 +1790,16 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, continue; } - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); if (cluster_type == QCOW2_CLUSTER_COMPRESSED || unmap) { - l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); + l2_slice[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); } else { - l2_table[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO); + l2_slice[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO); } } - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); return nb_clusters; } @@ -1808,13 +1823,13 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset, return -ENOTSUP; } - /* Each L2 table is handled by its own loop iteration */ + /* Each L2 slice is handled by its own loop iteration */ nb_clusters = size_to_clusters(s, bytes); s->cache_discards = true; while (nb_clusters > 0) { - cleared = zero_single_l2(bs, offset, nb_clusters, flags); + cleared = zero_in_l2_slice(bs, offset, nb_clusters, flags); if (cleared < 0) { ret = cleared; goto fail; @@ -1848,22 +1863,25 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, { BDRVQcow2State *s = bs->opaque; bool is_active_l1 = (l1_table == s->l1_table); - uint64_t *l2_table = NULL; + uint64_t *l2_slice = NULL; + unsigned slice, slice_size2, n_slices; int ret; int i, j; + slice_size2 = s->l2_slice_size * sizeof(uint64_t); + n_slices = s->cluster_size / slice_size2; + if (!is_active_l1) { /* inactive L2 tables require a buffer to be stored in when loading * them from disk */ - l2_table = qemu_try_blockalign(bs->file->bs, s->cluster_size); - if (l2_table == NULL) { + l2_slice = qemu_try_blockalign(bs->file->bs, slice_size2); + if (l2_slice == NULL) { return -ENOMEM; } } for (i = 0; i < l1_size; i++) { uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; - bool l2_dirty = false; uint64_t l2_refcount; if (!l2_offset) { @@ -1883,124 +1901,131 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - if (is_active_l1) { - /* get active L2 tables from cache */ - ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, - (void **)&l2_table); - } else { - /* load inactive L2 tables from disk */ - ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, - (void *)l2_table, s->cluster_sectors); - } - if (ret < 0) { - goto fail; - } - ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, &l2_refcount); if (ret < 0) { goto fail; } - for (j = 0; j < s->l2_size; j++) { - uint64_t l2_entry = be64_to_cpu(l2_table[j]); - int64_t offset = l2_entry & L2E_OFFSET_MASK; - QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry); - - if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN && - cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) { - continue; + for (slice = 0; slice < n_slices; slice++) { + uint64_t slice_offset = l2_offset + slice * slice_size2; + bool l2_dirty = false; + if (is_active_l1) { + /* get active L2 tables from cache */ + ret = qcow2_cache_get(bs, s->l2_table_cache, slice_offset, + (void **)&l2_slice); + } else { + /* load inactive L2 tables from disk */ + ret = bdrv_pread(bs->file, slice_offset, l2_slice, slice_size2); + } + if (ret < 0) { + goto fail; } - if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { - if (!bs->backing) { - /* not backed; therefore we can simply deallocate the - * cluster */ - l2_table[j] = 0; - l2_dirty = true; + for (j = 0; j < s->l2_slice_size; j++) { + uint64_t l2_entry = be64_to_cpu(l2_slice[j]); + int64_t offset = l2_entry & L2E_OFFSET_MASK; + QCow2ClusterType cluster_type = + qcow2_get_cluster_type(l2_entry); + + if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN && + cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) { continue; } - offset = qcow2_alloc_clusters(bs, s->cluster_size); - if (offset < 0) { - ret = offset; - goto fail; - } + if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { + if (!bs->backing) { + /* not backed; therefore we can simply deallocate the + * cluster */ + l2_slice[j] = 0; + l2_dirty = true; + continue; + } + + offset = qcow2_alloc_clusters(bs, s->cluster_size); + if (offset < 0) { + ret = offset; + goto fail; + } - if (l2_refcount > 1) { - /* 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, + if (l2_refcount > 1) { + /* 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, refcount_diff(1, l2_refcount), false, QCOW2_DISCARD_OTHER); - if (ret < 0) { - qcow2_free_clusters(bs, offset, s->cluster_size, - QCOW2_DISCARD_OTHER); - goto fail; + if (ret < 0) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_OTHER); + goto fail; + } } } - } - if (offset_into_cluster(s, offset)) { - qcow2_signal_corruption(bs, true, -1, -1, - "Cluster allocation offset " - "%#" PRIx64 " unaligned (L2 offset: %#" - PRIx64 ", L2 index: %#x)", offset, - l2_offset, j); - if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { - qcow2_free_clusters(bs, offset, s->cluster_size, - QCOW2_DISCARD_ALWAYS); + if (offset_into_cluster(s, offset)) { + int l2_index = slice * s->l2_slice_size + j; + qcow2_signal_corruption( + bs, true, -1, -1, + "Cluster allocation offset " + "%#" PRIx64 " unaligned (L2 offset: %#" + PRIx64 ", L2 index: %#x)", offset, + l2_offset, l2_index); + if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + } + ret = -EIO; + goto fail; } - ret = -EIO; - goto fail; - } - ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); - if (ret < 0) { - if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { - qcow2_free_clusters(bs, offset, s->cluster_size, - QCOW2_DISCARD_ALWAYS); + ret = qcow2_pre_write_overlap_check(bs, 0, offset, + s->cluster_size); + if (ret < 0) { + if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + } + goto fail; } - goto fail; - } - ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0); - if (ret < 0) { - if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { - qcow2_free_clusters(bs, offset, s->cluster_size, - QCOW2_DISCARD_ALWAYS); + ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0); + if (ret < 0) { + if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + } + goto fail; } - goto fail; - } - if (l2_refcount == 1) { - l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); - } else { - l2_table[j] = cpu_to_be64(offset); + if (l2_refcount == 1) { + l2_slice[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); + } else { + l2_slice[j] = cpu_to_be64(offset); + } + l2_dirty = true; } - l2_dirty = true; - } - if (is_active_l1) { - if (l2_dirty) { - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); - qcow2_cache_depends_on_flush(s->l2_table_cache); - } - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); - } else { - if (l2_dirty) { - ret = qcow2_pre_write_overlap_check(bs, - QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, l2_offset, - s->cluster_size); - if (ret < 0) { - goto fail; + if (is_active_l1) { + if (l2_dirty) { + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); + qcow2_cache_depends_on_flush(s->l2_table_cache); } + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); + } else { + if (l2_dirty) { + ret = qcow2_pre_write_overlap_check( + bs, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, + slice_offset, slice_size2); + if (ret < 0) { + goto fail; + } - ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, - (void *)l2_table, s->cluster_sectors); - if (ret < 0) { - goto fail; + ret = bdrv_pwrite(bs->file, slice_offset, + l2_slice, slice_size2); + if (ret < 0) { + goto fail; + } } } } @@ -2014,11 +2039,11 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, ret = 0; fail: - if (l2_table) { + if (l2_slice) { if (!is_active_l1) { - qemu_vfree(l2_table); + qemu_vfree(l2_slice); } else { - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); } } return ret; @@ -2070,7 +2095,15 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, int l1_sectors = DIV_ROUND_UP(s->snapshots[i].l1_size * sizeof(uint64_t), BDRV_SECTOR_SIZE); - l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); + uint64_t *new_l1_table = + g_try_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); + + if (!new_l1_table) { + ret = -ENOMEM; + goto fail; + } + + l1_table = new_l1_table; ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE, diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 92701ab7af..d46b69d7f3 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -277,7 +277,7 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, block_index = cluster_index & (s->refcount_block_size - 1); *refcount = s->get_refcount(refcount_block, block_index); - qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); + qcow2_cache_put(s->refcount_block_cache, &refcount_block); return 0; } @@ -421,7 +421,7 @@ static int alloc_refcount_block(BlockDriverState *bs, /* Now the new refcount block needs to be written to disk */ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, *refcount_block); + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block); ret = qcow2_cache_flush(bs, s->refcount_block_cache); if (ret < 0) { goto fail; @@ -449,7 +449,7 @@ static int alloc_refcount_block(BlockDriverState *bs, return -EAGAIN; } - qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); + qcow2_cache_put(s->refcount_block_cache, refcount_block); /* * If we come here, we need to grow the refcount table. Again, a new @@ -501,7 +501,7 @@ static int alloc_refcount_block(BlockDriverState *bs, fail: if (*refcount_block != NULL) { - qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); + qcow2_cache_put(s->refcount_block_cache, refcount_block); } return ret; } @@ -623,7 +623,7 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, goto fail; } memset(refblock_data, 0, s->cluster_size); - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refblock_data); new_table[i] = block_offset; @@ -656,11 +656,11 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, s->set_refcount(refblock_data, j, 1); } - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refblock_data); } - qcow2_cache_put(bs, s->refcount_block_cache, &refblock_data); + qcow2_cache_put(s->refcount_block_cache, &refblock_data); } assert(block_offset == table_offset); @@ -836,7 +836,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, /* Load the refcount block and allocate it if needed */ if (table_index != old_table_index) { if (refcount_block) { - qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); + qcow2_cache_put(s->refcount_block_cache, &refcount_block); } ret = alloc_refcount_block(bs, cluster_index, &refcount_block); if (ret < 0) { @@ -845,8 +845,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, } old_table_index = table_index; - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, - refcount_block); + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block); /* we can update the count and save it */ block_index = cluster_index & (s->refcount_block_size - 1); @@ -872,16 +871,16 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, if (refcount == 0) { void *table; - table = qcow2_cache_is_table_offset(bs, s->refcount_block_cache, + table = qcow2_cache_is_table_offset(s->refcount_block_cache, offset); if (table != NULL) { - qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); - qcow2_cache_discard(bs, s->refcount_block_cache, table); + qcow2_cache_put(s->refcount_block_cache, &refcount_block); + qcow2_cache_discard(s->refcount_block_cache, table); } - table = qcow2_cache_is_table_offset(bs, s->l2_table_cache, offset); + table = qcow2_cache_is_table_offset(s->l2_table_cache, offset); if (table != NULL) { - qcow2_cache_discard(bs, s->l2_table_cache, table); + qcow2_cache_discard(s->l2_table_cache, table); } if (s->discard_passthrough[type]) { @@ -898,7 +897,7 @@ fail: /* Write last changed block to disk */ if (refcount_block) { - qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); + qcow2_cache_put(s->refcount_block_cache, &refcount_block); } /* @@ -1184,17 +1183,20 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int addend) { BDRVQcow2State *s = bs->opaque; - uint64_t *l1_table, *l2_table, l2_offset, entry, l1_size2, refcount; + uint64_t *l1_table, *l2_slice, l2_offset, entry, l1_size2, refcount; bool l1_allocated = false; int64_t old_entry, old_l2_offset; + unsigned slice, slice_size2, n_slices; int i, j, l1_modified = 0, nb_csectors; int ret; assert(addend >= -1 && addend <= 1); - l2_table = NULL; + l2_slice = NULL; l1_table = NULL; l1_size2 = l1_size * sizeof(uint64_t); + slice_size2 = s->l2_slice_size * sizeof(uint64_t); + n_slices = s->cluster_size / slice_size2; s->cache_discards = true; @@ -1237,91 +1239,97 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, goto fail; } - ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, - (void**) &l2_table); - if (ret < 0) { - goto fail; - } + for (slice = 0; slice < n_slices; slice++) { + ret = qcow2_cache_get(bs, s->l2_table_cache, + l2_offset + slice * slice_size2, + (void **) &l2_slice); + if (ret < 0) { + goto fail; + } - for (j = 0; j < s->l2_size; j++) { - uint64_t cluster_index; - uint64_t offset; - - entry = be64_to_cpu(l2_table[j]); - old_entry = entry; - entry &= ~QCOW_OFLAG_COPIED; - offset = entry & L2E_OFFSET_MASK; - - switch (qcow2_get_cluster_type(entry)) { - case QCOW2_CLUSTER_COMPRESSED: - nb_csectors = ((entry >> s->csize_shift) & - s->csize_mask) + 1; - if (addend != 0) { - ret = update_refcount(bs, - (entry & s->cluster_offset_mask) & ~511, + for (j = 0; j < s->l2_slice_size; j++) { + uint64_t cluster_index; + uint64_t offset; + + entry = be64_to_cpu(l2_slice[j]); + old_entry = entry; + entry &= ~QCOW_OFLAG_COPIED; + offset = entry & L2E_OFFSET_MASK; + + switch (qcow2_get_cluster_type(entry)) { + case QCOW2_CLUSTER_COMPRESSED: + nb_csectors = ((entry >> s->csize_shift) & + s->csize_mask) + 1; + if (addend != 0) { + ret = update_refcount( + bs, (entry & s->cluster_offset_mask) & ~511, nb_csectors * 512, abs(addend), addend < 0, QCOW2_DISCARD_SNAPSHOT); - if (ret < 0) { + if (ret < 0) { + goto fail; + } + } + /* compressed clusters are never modified */ + refcount = 2; + break; + + case QCOW2_CLUSTER_NORMAL: + case QCOW2_CLUSTER_ZERO_ALLOC: + if (offset_into_cluster(s, offset)) { + /* Here l2_index means table (not slice) index */ + int l2_index = slice * s->l2_slice_size + j; + qcow2_signal_corruption( + bs, true, -1, -1, "Cluster " + "allocation offset %#" PRIx64 + " unaligned (L2 offset: %#" + PRIx64 ", L2 index: %#x)", + offset, l2_offset, l2_index); + ret = -EIO; goto fail; } - } - /* compressed clusters are never modified */ - refcount = 2; - break; - - case QCOW2_CLUSTER_NORMAL: - case QCOW2_CLUSTER_ZERO_ALLOC: - if (offset_into_cluster(s, offset)) { - qcow2_signal_corruption(bs, true, -1, -1, "Cluster " - "allocation offset %#" PRIx64 - " unaligned (L2 offset: %#" - PRIx64 ", L2 index: %#x)", - offset, l2_offset, j); - ret = -EIO; - goto fail; - } - cluster_index = offset >> s->cluster_bits; - assert(cluster_index); - if (addend != 0) { - ret = qcow2_update_cluster_refcount(bs, - cluster_index, abs(addend), addend < 0, - QCOW2_DISCARD_SNAPSHOT); + cluster_index = offset >> s->cluster_bits; + assert(cluster_index); + if (addend != 0) { + ret = qcow2_update_cluster_refcount( + bs, cluster_index, abs(addend), addend < 0, + QCOW2_DISCARD_SNAPSHOT); + if (ret < 0) { + goto fail; + } + } + + ret = qcow2_get_refcount(bs, cluster_index, &refcount); if (ret < 0) { goto fail; } - } + break; - ret = qcow2_get_refcount(bs, cluster_index, &refcount); - if (ret < 0) { - goto fail; - } - break; - - case QCOW2_CLUSTER_ZERO_PLAIN: - case QCOW2_CLUSTER_UNALLOCATED: - refcount = 0; - break; + case QCOW2_CLUSTER_ZERO_PLAIN: + case QCOW2_CLUSTER_UNALLOCATED: + refcount = 0; + break; - default: - abort(); - } + default: + abort(); + } - if (refcount == 1) { - entry |= QCOW_OFLAG_COPIED; - } - if (entry != old_entry) { - if (addend > 0) { - qcow2_cache_set_dependency(bs, s->l2_table_cache, - s->refcount_block_cache); + if (refcount == 1) { + entry |= QCOW_OFLAG_COPIED; + } + if (entry != old_entry) { + if (addend > 0) { + qcow2_cache_set_dependency(bs, s->l2_table_cache, + s->refcount_block_cache); + } + l2_slice[j] = cpu_to_be64(entry); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, + l2_slice); } - l2_table[j] = cpu_to_be64(entry); - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, - l2_table); } - } - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); + } if (addend != 0) { ret = qcow2_update_cluster_refcount(bs, l2_offset >> @@ -1348,8 +1356,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, ret = bdrv_flush(bs); fail: - if (l2_table) { - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (l2_slice) { + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); } s->cache_discards = false; @@ -2849,7 +2857,7 @@ static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, new_reftable_size, new_refblock, new_refblock_empty, allocated, errp); if (ret < 0) { - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); + qcow2_cache_put(s->refcount_block_cache, &refblock); return ret; } @@ -2862,7 +2870,7 @@ static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, if (new_refcount_bits < 64 && refcount >> new_refcount_bits) { uint64_t offset; - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); + qcow2_cache_put(s->refcount_block_cache, &refblock); offset = ((reftable_index << s->refcount_block_bits) + refblock_index) << s->cluster_bits; @@ -2883,7 +2891,7 @@ static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, new_refblock_empty = new_refblock_empty && refcount == 0; } - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); + qcow2_cache_put(s->refcount_block_cache, &refblock); } else { /* No refblock means every refcount is 0 */ for (refblock_index = 0; refblock_index < s->refcount_block_size; @@ -3175,24 +3183,24 @@ static int qcow2_discard_refcount_block(BlockDriverState *bs, offset_to_reftable_index(s, discard_block_offs), discard_block_offs, s->get_refcount(refblock, block_index)); - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); + qcow2_cache_put(s->refcount_block_cache, &refblock); return -EINVAL; } s->set_refcount(refblock, block_index, 0); - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, refblock); + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refblock); - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); + qcow2_cache_put(s->refcount_block_cache, &refblock); if (cluster_index < s->free_cluster_index) { s->free_cluster_index = cluster_index; } - refblock = qcow2_cache_is_table_offset(bs, s->refcount_block_cache, + refblock = qcow2_cache_is_table_offset(s->refcount_block_cache, discard_block_offs); if (refblock) { /* discard refblock from the cache if refblock is cached */ - qcow2_cache_discard(bs, s->refcount_block_cache, refblock); + qcow2_cache_discard(s->refcount_block_cache, refblock); } update_refcount_discard(bs, discard_block_offs, s->cluster_size); @@ -3235,7 +3243,7 @@ int qcow2_shrink_reftable(BlockDriverState *bs) } else { unused_block = buffer_is_zero(refblock, s->cluster_size); } - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); + qcow2_cache_put(s->refcount_block_cache, &refblock); reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]); } diff --git a/block/qcow2.c b/block/qcow2.c index 801e29fc56..57a517e2bd 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -676,6 +676,11 @@ static QemuOptsList qcow2_runtime_opts = { .help = "Maximum L2 table cache size", }, { + .name = QCOW2_OPT_L2_CACHE_ENTRY_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Size of each entry in the L2 cache", + }, + { .name = QCOW2_OPT_REFCOUNT_CACHE_SIZE, .type = QEMU_OPT_SIZE, .help = "Maximum refcount block cache size", @@ -706,8 +711,8 @@ static void cache_clean_timer_cb(void *opaque) { BlockDriverState *bs = opaque; BDRVQcow2State *s = bs->opaque; - qcow2_cache_clean_unused(bs, s->l2_table_cache); - qcow2_cache_clean_unused(bs, s->refcount_block_cache); + qcow2_cache_clean_unused(s->l2_table_cache); + qcow2_cache_clean_unused(s->refcount_block_cache); timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + (int64_t) s->cache_clean_interval * 1000); } @@ -747,6 +752,7 @@ static void qcow2_attach_aio_context(BlockDriverState *bs, static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, uint64_t *l2_cache_size, + uint64_t *l2_cache_entry_size, uint64_t *refcount_cache_size, Error **errp) { BDRVQcow2State *s = bs->opaque; @@ -762,6 +768,9 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, *refcount_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0); + *l2_cache_entry_size = qemu_opt_get_size( + opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE, s->cluster_size); + if (combined_cache_size_set) { if (l2_cache_size_set && refcount_cache_size_set) { error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE @@ -802,11 +811,21 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, / DEFAULT_L2_REFCOUNT_SIZE_RATIO; } } + + if (*l2_cache_entry_size < (1 << MIN_CLUSTER_BITS) || + *l2_cache_entry_size > s->cluster_size || + !is_power_of_2(*l2_cache_entry_size)) { + error_setg(errp, "L2 cache entry size must be a power of two " + "between %d and the cluster size (%d)", + 1 << MIN_CLUSTER_BITS, s->cluster_size); + return; + } } typedef struct Qcow2ReopenState { Qcow2Cache *l2_table_cache; Qcow2Cache *refcount_block_cache; + int l2_slice_size; /* Number of entries in a slice of the L2 table */ bool use_lazy_refcounts; int overlap_check; bool discard_passthrough[QCOW2_DISCARD_MAX]; @@ -823,7 +842,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, QemuOpts *opts = NULL; const char *opt_overlap_check, *opt_overlap_check_template; int overlap_check_template = 0; - uint64_t l2_cache_size, refcount_cache_size; + uint64_t l2_cache_size, l2_cache_entry_size, refcount_cache_size; int i; const char *encryptfmt; QDict *encryptopts = NULL; @@ -842,15 +861,15 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } /* get L2 table/refcount block cache size from command line options */ - read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size, - &local_err); + read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, + &refcount_cache_size, &local_err); if (local_err) { error_propagate(errp, local_err); ret = -EINVAL; goto fail; } - l2_cache_size /= s->cluster_size; + l2_cache_size /= l2_cache_entry_size; if (l2_cache_size < MIN_L2_CACHE_SIZE) { l2_cache_size = MIN_L2_CACHE_SIZE; } @@ -888,8 +907,11 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } } - r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size); - r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size); + r->l2_slice_size = l2_cache_entry_size / sizeof(uint64_t); + r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size, + l2_cache_entry_size); + r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size, + s->cluster_size); if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) { error_setg(errp, "Could not allocate metadata caches"); ret = -ENOMEM; @@ -1044,13 +1066,14 @@ static void qcow2_update_options_commit(BlockDriverState *bs, int i; if (s->l2_table_cache) { - qcow2_cache_destroy(bs, s->l2_table_cache); + qcow2_cache_destroy(s->l2_table_cache); } if (s->refcount_block_cache) { - qcow2_cache_destroy(bs, s->refcount_block_cache); + qcow2_cache_destroy(s->refcount_block_cache); } s->l2_table_cache = r->l2_table_cache; s->refcount_block_cache = r->refcount_block_cache; + s->l2_slice_size = r->l2_slice_size; s->overlap_check = r->overlap_check; s->use_lazy_refcounts = r->use_lazy_refcounts; @@ -1073,10 +1096,10 @@ static void qcow2_update_options_abort(BlockDriverState *bs, Qcow2ReopenState *r) { if (r->l2_table_cache) { - qcow2_cache_destroy(bs, r->l2_table_cache); + qcow2_cache_destroy(r->l2_table_cache); } if (r->refcount_block_cache) { - qcow2_cache_destroy(bs, r->refcount_block_cache); + qcow2_cache_destroy(r->refcount_block_cache); } qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); } @@ -1460,7 +1483,7 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, s->autoclear_features &= QCOW2_AUTOCLEAR_MASK; } - if (qcow2_load_autoloading_dirty_bitmaps(bs, &local_err)) { + if (qcow2_load_dirty_bitmaps(bs, &local_err)) { update_header = false; } if (local_err != NULL) { @@ -1514,10 +1537,10 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, s->l1_table = NULL; cache_clean_timer_del(bs); if (s->l2_table_cache) { - qcow2_cache_destroy(bs, s->l2_table_cache); + qcow2_cache_destroy(s->l2_table_cache); } if (s->refcount_block_cache) { - qcow2_cache_destroy(bs, s->refcount_block_cache); + qcow2_cache_destroy(s->refcount_block_cache); } qcrypto_block_free(s->crypto); qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); @@ -2065,8 +2088,8 @@ static void qcow2_close(BlockDriverState *bs) } cache_clean_timer_del(bs); - qcow2_cache_destroy(bs, s->l2_table_cache); - qcow2_cache_destroy(bs, s->refcount_block_cache); + qcow2_cache_destroy(s->l2_table_cache); + qcow2_cache_destroy(s->refcount_block_cache); qcrypto_block_free(s->crypto); s->crypto = NULL; @@ -3259,9 +3282,9 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, host_offset = allocation_start; guest_offset = old_length; while (nb_new_data_clusters) { - int64_t guest_cluster = guest_offset >> s->cluster_bits; - int64_t nb_clusters = MIN(nb_new_data_clusters, - s->l2_size - guest_cluster % s->l2_size); + int64_t nb_clusters = MIN( + nb_new_data_clusters, + s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset)); QCowL2Meta allocation = { .offset = guest_offset, .alloc_offset = host_offset, diff --git a/block/qcow2.h b/block/qcow2.h index 46c8cf44ec..883802241f 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -68,7 +68,7 @@ #define MAX_CLUSTER_BITS 21 /* Must be at least 2 to cover COW */ -#define MIN_L2_CACHE_SIZE 2 /* clusters */ +#define MIN_L2_CACHE_SIZE 2 /* cache entries */ /* Must be at least 4 to cover all cases of refcount table growth */ #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ @@ -100,6 +100,7 @@ #define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" #define QCOW2_OPT_CACHE_SIZE "cache-size" #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" +#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" #define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size" #define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval" @@ -251,6 +252,7 @@ typedef struct BDRVQcow2State { int cluster_bits; int cluster_size; int cluster_sectors; + int l2_slice_size; int l2_bits; int l2_size; int l1_size; @@ -463,11 +465,21 @@ static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size) return (size + (1ULL << shift) - 1) >> shift; } +static inline int offset_to_l1_index(BDRVQcow2State *s, uint64_t offset) +{ + return offset >> (s->l2_bits + s->cluster_bits); +} + static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset) { return (offset >> s->cluster_bits) & (s->l2_size - 1); } +static inline int offset_to_l2_slice_index(BDRVQcow2State *s, int64_t offset) +{ + return (offset >> s->cluster_bits) & (s->l2_slice_size - 1); +} + static inline int64_t align_offset(int64_t offset, int n) { offset = (offset + n - 1) & ~(n - 1); @@ -636,34 +648,33 @@ void qcow2_free_snapshots(BlockDriverState *bs); int qcow2_read_snapshots(BlockDriverState *bs); /* qcow2-cache.c functions */ -Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); -int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, + unsigned table_size); +int qcow2_cache_destroy(Qcow2Cache *c); -void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, - void *table); +void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c); int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, Qcow2Cache *dependency); void qcow2_cache_depends_on_flush(Qcow2Cache *c); -void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c); +void qcow2_cache_clean_unused(Qcow2Cache *c); int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, void **table); int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, void **table); -void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); -void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, - uint64_t offset); -void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table); +void qcow2_cache_put(Qcow2Cache *c, void **table); +void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset); +void qcow2_cache_discard(Qcow2Cache *c, void *table); /* qcow2-bitmap.c functions */ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size); -bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp); +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); diff --git a/block/sheepdog.c b/block/sheepdog.c index af125a2c8d..ac02b10fe0 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1826,40 +1826,34 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, return 0; } -static int sd_prealloc(const char *filename, Error **errp) +static int sd_prealloc(BlockDriverState *bs, int64_t old_size, int64_t new_size, + Error **errp) { BlockBackend *blk = NULL; - BDRVSheepdogState *base = NULL; + BDRVSheepdogState *base = bs->opaque; unsigned long buf_size; uint32_t idx, max_idx; uint32_t object_size; - int64_t vdi_size; void *buf = NULL; int ret; - blk = blk_new_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); - if (blk == NULL) { - ret = -EIO; + blk = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE, + BLK_PERM_ALL); + + ret = blk_insert_bs(blk, bs, errp); + if (ret < 0) { goto out_with_err_set; } blk_set_allow_write_beyond_eof(blk, true); - vdi_size = blk_getlength(blk); - if (vdi_size < 0) { - ret = vdi_size; - goto out; - } - - base = blk_bs(blk)->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); + max_idx = DIV_ROUND_UP(new_size, buf_size); - for (idx = 0; idx < max_idx; idx++) { + for (idx = old_size / buf_size; idx < max_idx; idx++) { /* * The created image can be a cloned image, so we need to read * a data from the source image. @@ -2108,7 +2102,20 @@ static int sd_create(const char *filename, QemuOpts *opts, } if (prealloc) { - ret = sd_prealloc(filename, errp); + BlockDriverState *bs; + QDict *opts; + + opts = qdict_new(); + qdict_put_str(opts, "driver", "sheepdog"); + bs = bdrv_open(filename, NULL, opts, BDRV_O_PROTOCOL | BDRV_O_RDWR, + errp); + if (!bs) { + goto out; + } + + ret = sd_prealloc(bs, 0, s->inode.vdi_size, errp); + + bdrv_unref(bs); } out: g_free(backing_file); @@ -2173,15 +2180,16 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset, int ret, fd; unsigned int datalen; uint64_t max_vdi_size; + int64_t old_size = s->inode.vdi_size; - if (prealloc != PREALLOC_MODE_OFF) { + if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_FULL) { error_setg(errp, "Unsupported preallocation mode '%s'", PreallocMode_str(prealloc)); return -ENOTSUP; } max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; - if (offset < s->inode.vdi_size) { + if (offset < old_size) { error_setg(errp, "shrinking is not supported"); return -EINVAL; } else if (offset > max_vdi_size) { @@ -2204,9 +2212,17 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset, if (ret < 0) { error_setg_errno(errp, -ret, "failed to update an inode"); + return ret; } - return ret; + if (prealloc == PREALLOC_MODE_FULL) { + ret = sd_prealloc(bs, old_size, offset, errp); + if (ret < 0) { + return ret; + } + } + + return 0; } /* diff --git a/blockdev.c b/blockdev.c index bdbdeae7e4..3fb1ca803c 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2825,14 +2825,9 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, if (!has_persistent) { persistent = false; } - if (!has_autoload) { - autoload = false; - } - if (has_autoload && !persistent) { - error_setg(errp, "Autoload flag must be used only for persistent " - "bitmaps"); - return; + if (has_autoload) { + warn_report("Autoload option is deprecated and its value is ignored"); } if (persistent && @@ -2847,7 +2842,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, } bdrv_dirty_bitmap_set_persistance(bitmap, persistent); - bdrv_dirty_bitmap_set_autoload(bitmap, autoload); } void qmp_block_dirty_bitmap_remove(const char *node, const char *name, @@ -3569,6 +3563,11 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) return; } + /* Early check to avoid creating target */ + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) { + return; + } + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi index cd74767ed3..f1793692bb 100644 --- a/docs/qemu-block-drivers.texi +++ b/docs/qemu-block-drivers.texi @@ -845,6 +845,16 @@ QEMU transparently handles lock handover during shared storage migration. For shared virtual disk images between multiple VMs, the "share-rw" device option should be used. +By default, the guest has exclusive write access to its disk image. If the +guest can safely share the disk image with other writers the @code{-device +...,share-rw=on} parameter can be used. This is only safe if the guest is +running software, such as a cluster file system, that coordinates disk accesses +to avoid corruption. + +Note that share-rw=on only declares the guest's ability to share the disk. +Some QEMU features, such as image file formats, require exclusive write access +to the disk image and this is unaffected by the share-rw=on option. + Alternatively, locking can be fully disabled by "locking=off" block device option. In the command line, the option is usually in the form of "file.locking=off" as the protocol driver is normally placed as a "file" child diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 3da8486ab1..e3f4bbf51d 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -66,7 +66,6 @@ void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); -void bdrv_dirty_bitmap_set_autoload(BdrvDirtyBitmap *bitmap, bool autoload); void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent); diff --git a/qapi/block-core.json b/qapi/block-core.json index 8046c2da23..5c5921bfb7 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1593,9 +1593,9 @@ # Qcow2 disks support persistent bitmaps. Default is false for # block-dirty-bitmap-add. (Since: 2.10) # -# @autoload: the bitmap will be automatically loaded when the image it is stored -# in is opened. This flag may only be specified for persistent -# bitmaps. Default is false for block-dirty-bitmap-add. (Since: 2.10) +# @autoload: ignored and deprecated since 2.12. +# Currently, all dirty tracking bitmaps are loaded from Qcow2 on +# open. # # Since: 2.4 ## @@ -2521,6 +2521,11 @@ # @l2-cache-size: the maximum size of the L2 table cache in # bytes (since 2.2) # +# @l2-cache-entry-size: the size of each entry in the L2 cache in +# bytes. It must be a power of two between 512 +# and the cluster size. The default value is +# the cluster size (since 2.12) +# # @refcount-cache-size: the maximum size of the refcount block cache # in bytes (since 2.2) # @@ -2542,6 +2547,7 @@ '*overlap-check': 'Qcow2OverlapChecks', '*cache-size': 'int', '*l2-cache-size': 'int', + '*l2-cache-entry-size': 'int', '*refcount-cache-size': 'int', '*cache-clean-interval': 'int', '*encrypt': 'BlockdevQcow2Encryption' } } diff --git a/qemu-doc.texi b/qemu-doc.texi index 769968aba4..137f5814a8 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2757,6 +2757,13 @@ used and it will be removed with no replacement. The ``convert -s snapshot_id_or_name'' argument is obsoleted by the ``convert -l snapshot_param'' argument instead. +@section QEMU Machine Protocol (QMP) commands + +@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) + +"autoload" parameter is now ignored. All bitmaps are automatically loaded +from qcow2 images. + @section System emulator human monitor commands @subsection host_net_add (since 2.10.0) diff --git a/qemu-img.texi b/qemu-img.texi index fdcf120f36..8a26400adb 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -33,38 +33,14 @@ The following commands are supported: Command parameters: @table @var -@item filename - is a disk image filename - -@item --object @var{objectdef} - -is a QEMU user creatable object definition. See the @code{qemu(1)} manual -page for a description of the object properties. The most common object -type is a @code{secret}, which is used to supply passwords and/or encryption -keys. - -@item --image-opts - -Indicates that the source @var{filename} parameter is to be interpreted as a -full option string, not a plain filename. This parameter is mutually -exclusive with the @var{-f} parameter. - -@item --target-image-opts -Indicates that the @var{output_filename} parameter(s) are to be interpreted as -a full option string, not a plain filename. This parameter is mutually -exclusive with the @var{-O} parameters. It is currently required to also use -the @var{-n} parameter to skip image creation. This restriction may be relaxed -in a future release. +@item filename +is a disk image filename @item fmt is the disk image format. It is guessed automatically in most cases. See below for a description of the supported disk formats. -@item --backing-chain -will enumerate information about backing files in a disk image chain. Refer -below for further description. - @item size is the disk image size in bytes. Optional suffixes @code{k} or @code{K} (kilobyte, 1024) @code{M} (megabyte, 1024k) and @code{G} (gigabyte, 1024M) @@ -74,42 +50,86 @@ and T (terabyte, 1024G) are supported. @code{b} is ignored. is the destination disk image filename @item output_fmt - is the destination format +is the destination format + @item options is a comma separated list of format specific options in a name=value format. Use @code{-o ?} for an overview of the options supported by the used format or see the format descriptions below for details. + @item snapshot_param is param used for internal snapshot, format is 'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' + @item snapshot_id_or_name is deprecated, use snapshot_param instead +@end table + +@table @option + +@item --object @var{objectdef} +is a QEMU user creatable object definition. See the @code{qemu(1)} manual +page for a description of the object properties. The most common object +type is a @code{secret}, which is used to supply passwords and/or encryption +keys. + +@item --image-opts +Indicates that the source @var{filename} parameter is to be interpreted as a +full option string, not a plain filename. This parameter is mutually +exclusive with the @var{-f} parameter. + +@item --target-image-opts +Indicates that the @var{output_filename} parameter(s) are to be interpreted as +a full option string, not a plain filename. This parameter is mutually +exclusive with the @var{-O} parameters. It is currently required to also use +the @var{-n} parameter to skip image creation. This restriction may be relaxed +in a future release. + +@item --force-share (-U) +If specified, @code{qemu-img} will open the image in shared mode, allowing +other QEMU processes to open it in write mode. For example, this can be used to +get the image information (with 'info' subcommand) when the image is used by a +running guest. Note that this could produce inconsistent results because of +concurrent metadata changes, etc. This option is only allowed when opening +images in read-only mode. + +@item --backing-chain +will enumerate information about backing files in a disk image chain. Refer +below for further description. + @item -c indicates that target image must be compressed (qcow format only) + @item -h with or without a command shows help and lists the supported formats + @item -p display progress bar (compare, convert and rebase commands only). If the @var{-p} option is not used for a command that supports it, the progress is reported when the process receives a @code{SIGUSR1} or @code{SIGINFO} signal. + @item -q Quiet mode - do not print any output (except errors). There's no progress bar in case both @var{-q} and @var{-p} options are used. + @item -S @var{size} indicates the consecutive number of bytes that must contain only zeros for qemu-img to create a sparse image during conversion. This value is rounded down to the nearest 512 bytes. You may use the common size suffixes like @code{k} for kilobytes. + @item -t @var{cache} specifies the cache mode that should be used with the (destination) file. See the documentation of the emulator's @code{-drive cache=...} option for allowed values. + @item -T @var{src_cache} specifies the cache mode that should be used with the source file(s). See the documentation of the emulator's @code{-drive cache=...} option for allowed values. + @end table Parameters to snapshot subcommand: @@ -11,6 +11,9 @@ #include "qemu/osdep.h" #include <getopt.h> #include <libgen.h> +#ifndef _WIN32 +#include <termios.h> +#endif #include "qapi/error.h" #include "qemu-io.h" @@ -42,6 +45,26 @@ static bool imageOpts; static ReadLineState *readline_state; +static int ttyEOF; + +static int get_eof_char(void) +{ +#ifdef _WIN32 + return 0x4; /* Ctrl-D */ +#else + struct termios tty; + if (tcgetattr(STDIN_FILENO, &tty) != 0) { + if (errno == ENOTTY) { + return 0x0; /* just expect read() == 0 */ + } else { + return 0x4; /* Ctrl-D */ + } + } + + return tty.c_cc[VEOF]; +#endif +} + static int close_f(BlockBackend *blk, int argc, char **argv) { blk_unref(qemuio_blk); @@ -323,7 +346,8 @@ static char *fetchline_readline(void) readline_start(readline_state, get_prompt(), 0, readline_func, &line); while (!line) { int ch = getchar(); - if (ch == EOF) { + if (ttyEOF != 0x0 && ch == ttyEOF) { + printf("\n"); break; } readline_handle_byte(readline_state, ch); @@ -593,6 +617,7 @@ int main(int argc, char **argv) qemuio_add_command(&close_cmd); if (isatty(STDIN_FILENO)) { + ttyEOF = get_eof_char(); readline_state = readline_init(readline_printf_func, readline_flush_func, NULL, diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 6a80be009b..3026714471 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -113,6 +113,7 @@ static void m68000_cpu_initfn(Object *obj) m68k_set_feature(env, M68K_FEATURE_M68000); m68k_set_feature(env, M68K_FEATURE_USP); m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); + m68k_set_feature(env, M68K_FEATURE_MOVEP); } static void m68020_cpu_initfn(Object *obj) @@ -135,6 +136,7 @@ static void m68020_cpu_initfn(Object *obj) m68k_set_feature(env, M68K_FEATURE_BKPT); m68k_set_feature(env, M68K_FEATURE_RTD); m68k_set_feature(env, M68K_FEATURE_CHK2); + m68k_set_feature(env, M68K_FEATURE_MOVEP); } #define m68030_cpu_initfn m68020_cpu_initfn diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 627fb787b6..1d79885222 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -492,6 +492,7 @@ enum m68k_features { M68K_FEATURE_RTD, M68K_FEATURE_CHK2, M68K_FEATURE_M68040, /* instructions specific to MC68040 */ + M68K_FEATURE_MOVEP, }; static inline int m68k_feature(CPUM68KState *env, int feature) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 34db97b8a0..70c7583621 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -2078,6 +2078,51 @@ DISAS_INSN(movem) tcg_temp_free(addr); } +DISAS_INSN(movep) +{ + uint8_t i; + int16_t displ; + TCGv reg; + TCGv addr; + TCGv abuf; + TCGv dbuf; + + displ = read_im16(env, s); + + addr = AREG(insn, 0); + reg = DREG(insn, 9); + + abuf = tcg_temp_new(); + tcg_gen_addi_i32(abuf, addr, displ); + dbuf = tcg_temp_new(); + + if (insn & 0x40) { + i = 4; + } else { + i = 2; + } + + if (insn & 0x80) { + for ( ; i > 0 ; i--) { + tcg_gen_shri_i32(dbuf, reg, (i - 1) * 8); + tcg_gen_qemu_st8(dbuf, abuf, IS_USER(s)); + if (i > 1) { + tcg_gen_addi_i32(abuf, abuf, 2); + } + } + } else { + for ( ; i > 0 ; i--) { + tcg_gen_qemu_ld8u(dbuf, abuf, IS_USER(s)); + tcg_gen_deposit_i32(reg, reg, dbuf, (i - 1) * 8, 8); + if (i > 1) { + tcg_gen_addi_i32(abuf, abuf, 2); + } + } + } + tcg_temp_free(abuf); + tcg_temp_free(dbuf); +} + DISAS_INSN(bitop_im) { int opsize; @@ -5678,6 +5723,7 @@ void register_m68k_insns (CPUM68KState *env) BASE(bitop_reg, 0140, f1c0); BASE(bitop_reg, 0180, f1c0); BASE(bitop_reg, 01c0, f1c0); + INSN(movep, 0108, f138, MOVEP); INSN(arith_im, 0280, fff8, CF_ISA_A); INSN(arith_im, 0200, ff00, M68000); INSN(undef, 02c0, ffc0, M68000); diff --git a/tests/Makefile.include b/tests/Makefile.include index 278c13aa93..a1bcbffe12 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -319,16 +319,15 @@ check-qtest-ppc-y += tests/boot-order-test$(EXESUF) check-qtest-ppc-y += tests/prom-env-test$(EXESUF) check-qtest-ppc-y += tests/drive_del-test$(EXESUF) check-qtest-ppc-y += tests/boot-serial-test$(EXESUF) +check-qtest-ppc-y += tests/m48t59-test$(EXESUF) +gcov-files-ppc-y += hw/timer/m48t59.c -check-qtest-ppc64-y = tests/spapr-phb-test$(EXESUF) -gcov-files-ppc64-y = ppc64-softmmu/hw/ppc/spapr_pci.c -check-qtest-ppc64-y += tests/endianness-test$(EXESUF) -check-qtest-ppc64-y += tests/boot-order-test$(EXESUF) -check-qtest-ppc64-y += tests/prom-env-test$(EXESUF) +check-qtest-ppc64-y = $(check-qtest-ppc-y) +gcov-files-ppc64-y = $(subst ppc-softmmu/,ppc64-softmmu/,$(gcov-files-ppc-y)) +check-qtest-ppc64-y += tests/spapr-phb-test$(EXESUF) +gcov-files-ppc64-y += ppc64-softmmu/hw/ppc/spapr_pci.c check-qtest-ppc64-y += tests/pnv-xscom-test$(EXESUF) -check-qtest-ppc64-y += tests/drive_del-test$(EXESUF) check-qtest-ppc64-y += tests/migration-test$(EXESUF) -check-qtest-ppc64-y += tests/boot-serial-test$(EXESUF) check-qtest-ppc64-y += tests/rtas-test$(EXESUF) check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) check-qtest-ppc64-y += tests/usb-hcd-ohci-test$(EXESUF) @@ -351,13 +350,13 @@ check-qtest-sh4-y = tests/endianness-test$(EXESUF) check-qtest-sh4eb-y = tests/endianness-test$(EXESUF) check-qtest-sparc-y = tests/prom-env-test$(EXESUF) -#check-qtest-sparc-y += tests/m48t59-test$(EXESUF) -#gcov-files-sparc-y = hw/timer/m48t59.c +check-qtest-sparc-y += tests/m48t59-test$(EXESUF) +gcov-files-sparc-y = hw/timer/m48t59.c +check-qtest-sparc-y += tests/boot-serial-test$(EXESUF) check-qtest-sparc64-y = tests/endianness-test$(EXESUF) -#check-qtest-sparc64-y += tests/m48t59-test$(EXESUF) -#gcov-files-sparc64-y += hw/timer/m48t59.c check-qtest-sparc64-y += tests/prom-env-test$(EXESUF) +check-qtest-sparc64-y += tests/boot-serial-test$(EXESUF) check-qtest-arm-y = tests/tmp105-test$(EXESUF) check-qtest-arm-y += tests/ds1338-test$(EXESUF) @@ -372,6 +371,7 @@ check-qtest-arm-y += tests/sdhci-test$(EXESUF) check-qtest-aarch64-y = tests/numa-test$(EXESUF) check-qtest-aarch64-y += tests/sdhci-test$(EXESUF) +check-qtest-aarch64-y += tests/boot-serial-test$(EXESUF) check-qtest-microblazeel-y = $(check-qtest-microblaze-y) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 7aa5af428c..2342fe3099 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -158,10 +158,11 @@ static AHCIQState *ahci_vboot(const char *cli, va_list ap) s = g_new0(AHCIQState, 1); s->parent = qtest_pc_vboot(cli, ap); + global_qtest = s->parent->qts; alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT); /* Verify that we have an AHCI device present. */ - s->dev = get_ahci_device(&s->fingerprint); + s->dev = get_ahci_device(s->parent->qts, &s->fingerprint); return s; } diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 2b332ed3c1..65b271a173 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -668,7 +668,7 @@ static void test_acpi_one(const char *params, test_data *data) qtest_start(args); - boot_sector_test(); + boot_sector_test(global_qtest); data->tables = g_array_new(false, true, sizeof(AcpiSdtTable)); test_acpi_rsdp_address(data); diff --git a/tests/boot-order-test.c b/tests/boot-order-test.c index 60c5545e45..e70f5dedba 100644 --- a/tests/boot-order-test.c +++ b/tests/boot-order-test.c @@ -41,7 +41,7 @@ static void test_a_boot_order(const char *machine, * system_reset only requests reset. We get a RESET event after * the actual reset completes. Need to wait for that. */ - qmp_discard_response(""); /* HACK: wait for event */ + qmp_eventwait("RESET"); actual = read_boot_order(); g_assert_cmphex(actual, ==, expected_reboot); qtest_quit(global_qtest); @@ -132,7 +132,7 @@ static void test_prep_boot_order(void) static uint64_t read_boot_order_pmac(void) { - QFWCFG *fw_cfg = mm_fw_cfg_init(0xf0000510); + QFWCFG *fw_cfg = mm_fw_cfg_init(global_qtest, 0xf0000510); return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); } @@ -157,7 +157,7 @@ static void test_pmac_newworld_boot_order(void) static uint64_t read_boot_order_sun4m(void) { - QFWCFG *fw_cfg = mm_fw_cfg_init(0xd00000510ULL); + QFWCFG *fw_cfg = mm_fw_cfg_init(global_qtest, 0xd00000510ULL); return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); } @@ -169,7 +169,7 @@ static void test_sun4m_boot_order(void) static uint64_t read_boot_order_sun4u(void) { - QFWCFG *fw_cfg = io_fw_cfg_init(0x510); + QFWCFG *fw_cfg = io_fw_cfg_init(global_qtest, 0x510); return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); } diff --git a/tests/boot-sector.c b/tests/boot-sector.c index be29d5bb9b..c373f0e715 100644 --- a/tests/boot-sector.c +++ b/tests/boot-sector.c @@ -5,7 +5,7 @@ * * Authors: * Michael S. Tsirkin <mst@redhat.com> - * Victor Kaplansky <victork@redhat.com> + * Victor Kaplansky <victork@redhat.com> * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -130,7 +130,7 @@ int boot_sector_init(char *fname) } /* Loop until signature in memory is OK. */ -void boot_sector_test(void) +void boot_sector_test(QTestState *qts) { uint8_t signature_low; uint8_t signature_high; @@ -146,8 +146,8 @@ void boot_sector_test(void) * instruction. */ for (i = 0; i < TEST_CYCLES; ++i) { - signature_low = readb(SIGNATURE_ADDR); - signature_high = readb(SIGNATURE_ADDR + 1); + signature_low = qtest_readb(qts, SIGNATURE_ADDR); + signature_high = qtest_readb(qts, SIGNATURE_ADDR + 1); signature = (signature_high << 8) | signature_low; if (signature == SIGNATURE) { break; diff --git a/tests/boot-sector.h b/tests/boot-sector.h index 35d61c7e2b..6ee6bb4d97 100644 --- a/tests/boot-sector.h +++ b/tests/boot-sector.h @@ -5,7 +5,7 @@ * * Authors: * Michael S. Tsirkin <mst@redhat.com> - * Victor Kaplansky <victork@redhat.com> + * Victor Kaplansky <victork@redhat.com> * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -14,11 +14,13 @@ #ifndef TEST_BOOT_SECTOR_H #define TEST_BOOT_SECTOR_H +#include "libqtest.h" + /* Create boot disk file. fname must be a suitable string for mkstemp() */ int boot_sector_init(char *fname); /* Loop until signature in memory is OK. */ -void boot_sector_test(void); +void boot_sector_test(QTestState *qts); /* unlink boot disk file. */ void boot_sector_cleanup(const char *fname); diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c index ea87a80be7..511bf314a3 100644 --- a/tests/boot-serial-test.c +++ b/tests/boot-serial-test.c @@ -55,6 +55,13 @@ static const uint8_t bios_raspi2[] = { 0x00, 0x10, 0x20, 0x3f, /* 0x3f201000 = UART0 base addr */ }; +static const uint8_t kernel_aarch64[] = { + 0x81, 0x0a, 0x80, 0x52, /* mov w1, #0x54 */ + 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 */ + 0x41, 0x00, 0x00, 0x39, /* strb w1, [x2] */ + 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */ +}; + typedef struct testdef { const char *arch; /* Target architecture */ const char *machine; /* Name of the machine */ @@ -69,8 +76,11 @@ static testdef_t tests[] = { { "alpha", "clipper", "", "PCI:" }, { "ppc", "ppce500", "", "U-Boot" }, { "ppc", "prep", "", "Open Hack'Ware BIOS" }, + { "ppc", "g3beige", "", "PowerPC,750" }, + { "ppc", "mac99", "", "PowerPC,G4" }, { "ppc64", "ppce500", "", "U-Boot" }, { "ppc64", "prep", "", "Open Hack'Ware BIOS" }, + { "ppc64", "mac99", "", "PowerPC,970FX" }, { "ppc64", "pseries", "", "Open Firmware" }, { "ppc64", "powernv", "-cpu POWER8", "OPAL" }, { "i386", "isapc", "-cpu qemu32 -device sga", "SGABIOS" }, @@ -78,6 +88,10 @@ static testdef_t tests[] = { { "i386", "q35", "-device sga", "SGABIOS" }, { "x86_64", "isapc", "-cpu qemu32 -device sga", "SGABIOS" }, { "x86_64", "q35", "-device sga", "SGABIOS" }, + { "sparc", "LX", "", "TMS390S10" }, + { "sparc", "SS-4", "", "MB86904" }, + { "sparc", "SS-600MP", "", "TMS390Z55" }, + { "sparc64", "sun4u", "", "UltraSPARC" }, { "s390x", "s390-ccw-virtio", "-nodefaults -device sclpconsole,chardev=serial0", "virtio device" }, { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 }, @@ -88,6 +102,8 @@ static testdef_t tests[] = { { "moxie", "moxiesim", "", "TT", sizeof(bios_moxiesim), 0, bios_moxiesim }, { "arm", "raspi2", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 }, { "hppa", "hppa", "", "SeaBIOS wants SYSTEM HALT" }, + { "aarch64", "virt", "-cpu cortex-a57", "TT", sizeof(kernel_aarch64), + kernel_aarch64 }, { NULL } }; diff --git a/tests/ds1338-test.c b/tests/ds1338-test.c index 26968bc82a..742dad9113 100644 --- a/tests/ds1338-test.c +++ b/tests/ds1338-test.c @@ -61,16 +61,14 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); s = qtest_start("-display none -machine imx25-pdk"); - i2c = imx_i2c_create(IMX25_I2C_0_BASE); + i2c = imx_i2c_create(s, IMX25_I2C_0_BASE); addr = DS1338_ADDR; qtest_add_func("/ds1338/tx-rx", send_and_receive); ret = g_test_run(); - if (s) { - qtest_quit(s); - } + qtest_quit(s); g_free(i2c); return ret; diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c index c612dc64ec..32aa738b72 100644 --- a/tests/e1000e-test.c +++ b/tests/e1000e-test.c @@ -392,12 +392,12 @@ static void data_test_init(e1000e_device *d) qtest_start(cmdline); g_free(cmdline); - test_bus = qpci_init_pc(NULL); - g_assert_nonnull(test_bus); - - test_alloc = pc_alloc_init(); + test_alloc = pc_alloc_init(global_qtest); g_assert_nonnull(test_alloc); + test_bus = qpci_init_pc(global_qtest, test_alloc); + g_assert_nonnull(test_bus); + e1000e_device_init(test_bus, d); } diff --git a/tests/fw_cfg-test.c b/tests/fw_cfg-test.c index 81f45bdfc8..1548bf14b2 100644 --- a/tests/fw_cfg-test.c +++ b/tests/fw_cfg-test.c @@ -102,12 +102,13 @@ static void test_fw_cfg_boot_menu(void) int main(int argc, char **argv) { QTestState *s; - char *cmdline; int ret; g_test_init(&argc, &argv, NULL); - fw_cfg = pc_fw_cfg_init(); + s = qtest_init("-uuid 4600cb32-38ec-4b2f-8acb-81c6ea54f2d8"); + + fw_cfg = pc_fw_cfg_init(s); qtest_add_func("fw_cfg/signature", test_fw_cfg_signature); qtest_add_func("fw_cfg/id", test_fw_cfg_id); @@ -125,15 +126,9 @@ int main(int argc, char **argv) qtest_add_func("fw_cfg/numa", test_fw_cfg_numa); qtest_add_func("fw_cfg/boot_menu", test_fw_cfg_boot_menu); - cmdline = g_strdup_printf("-uuid 4600cb32-38ec-4b2f-8acb-81c6ea54f2d8 "); - s = qtest_start(cmdline); - g_free(cmdline); - ret = g_test_run(); - if (s) { - qtest_quit(s); - } + qtest_quit(s); return ret; } diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c index e9d05c87d1..4390e5591e 100644 --- a/tests/i440fx-test.c +++ b/tests/i440fx-test.c @@ -38,7 +38,7 @@ static QPCIBus *test_start_get_bus(const TestData *s) cmdline = g_strdup_printf("-smp %d", s->num_cpus); qtest_start(cmdline); g_free(cmdline); - return qpci_init_pc(NULL); + return qpci_init_pc(global_qtest, NULL); } static void test_i440fx_defaults(gconstpointer opaque) diff --git a/tests/ide-test.c b/tests/ide-test.c index be427e41d6..2384c2c3e2 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -132,7 +132,7 @@ static void ide_test_start(const char *cmdline_fmt, ...) va_end(ap); qtest_start(cmdline); - guest_malloc = pc_alloc_init(); + guest_malloc = pc_alloc_init(global_qtest); g_free(cmdline); } @@ -150,7 +150,7 @@ static QPCIDevice *get_pci_device(QPCIBar *bmdma_bar, QPCIBar *ide_bar) uint16_t vendor_id, device_id; if (!pcibus) { - pcibus = qpci_init_pc(NULL); + pcibus = qpci_init_pc(global_qtest, NULL); } /* Find PCI device and verify it's the right one */ diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index 37763425ee..8af16ee79a 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -131,6 +131,7 @@ static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) g_printerr("ivshmem-test tests are only available on x86 or ppc64\n"); exit(EXIT_FAILURE); } + global_qtest = s->qs->qts; s->dev = get_device(s->qs->pcibus); s->reg_bar = qpci_iomap(s->dev, 0, &barsize); diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 13c0749582..bc201d762b 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -123,13 +123,13 @@ bool is_atapi(AHCIQState *ahci, uint8_t port) /** * Locate, verify, and return a handle to the AHCI device. */ -QPCIDevice *get_ahci_device(uint32_t *fingerprint) +QPCIDevice *get_ahci_device(QTestState *qts, uint32_t *fingerprint) { QPCIDevice *ahci; uint32_t ahci_fingerprint; QPCIBus *pcibus; - pcibus = qpci_init_pc(NULL); + pcibus = qpci_init_pc(qts, NULL); /* Find the AHCI PCI device and verify it's the right one. */ ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); @@ -283,7 +283,8 @@ void ahci_hba_enable(AHCIQState *ahci) /* Allocate Memory for the Command List Buffer & FIS Buffer */ /* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */ ahci->port[i].clb = ahci_alloc(ahci, num_cmd_slots * 0x20); - qmemset(ahci->port[i].clb, 0x00, num_cmd_slots * 0x20); + qtest_memset(ahci->parent->qts, ahci->port[i].clb, 0x00, + num_cmd_slots * 0x20); g_test_message("CLB: 0x%08" PRIx64, ahci->port[i].clb); ahci_px_wreg(ahci, i, AHCI_PX_CLB, ahci->port[i].clb); g_assert_cmphex(ahci->port[i].clb, ==, @@ -291,7 +292,7 @@ void ahci_hba_enable(AHCIQState *ahci) /* PxFB space ... 0x100, as in 4.2.1 p 35 */ ahci->port[i].fb = ahci_alloc(ahci, 0x100); - qmemset(ahci->port[i].fb, 0x00, 0x100); + qtest_memset(ahci->parent->qts, ahci->port[i].fb, 0x00, 0x100); g_test_message("FB: 0x%08" PRIx64, ahci->port[i].fb); ahci_px_wreg(ahci, i, AHCI_PX_FB, ahci->port[i].fb); g_assert_cmphex(ahci->port[i].fb, ==, @@ -397,7 +398,7 @@ void ahci_port_clear(AHCIQState *ahci, uint8_t port) g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0); /* Wipe the FIS-Receive Buffer */ - qmemset(ahci->port[port].fb, 0x00, 0x100); + qtest_memset(ahci->parent->qts, ahci->port[port].fb, 0x00, 0x100); } /** @@ -466,7 +467,7 @@ void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot) RegD2HFIS *d2h = g_malloc0(0x20); uint32_t reg; - memread(ahci->port[port].fb + 0x40, d2h, 0x20); + qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x40, d2h, 0x20); g_assert_cmphex(d2h->fis_type, ==, 0x34); reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); @@ -484,7 +485,7 @@ void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, /* We cannot check the Status or E_Status registers, because * the status may have again changed between the PIO Setup FIS * and the conclusion of the command with the D2H Register FIS. */ - memread(ahci->port[port].fb + 0x20, pio, 0x20); + qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x20, pio, 0x20); g_assert_cmphex(pio->fis_type, ==, 0x5f); /* BUG: PIO Setup FIS as utilized by QEMU tries to fit the entire @@ -516,7 +517,7 @@ void ahci_get_command_header(AHCIQState *ahci, uint8_t port, { uint64_t ba = ahci->port[port].clb; ba += slot * sizeof(AHCICommandHeader); - memread(ba, cmd, sizeof(AHCICommandHeader)); + qtest_memread(ahci->parent->qts, ba, cmd, sizeof(AHCICommandHeader)); cmd->flags = le16_to_cpu(cmd->flags); cmd->prdtl = le16_to_cpu(cmd->prdtl); @@ -537,7 +538,7 @@ void ahci_set_command_header(AHCIQState *ahci, uint8_t port, tmp.prdbc = cpu_to_le32(cmd->prdbc); tmp.ctba = cpu_to_le64(cmd->ctba); - memwrite(ba, &tmp, sizeof(AHCICommandHeader)); + qtest_memwrite(ahci->parent->qts, ba, &tmp, sizeof(AHCICommandHeader)); } void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot) @@ -575,7 +576,7 @@ void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd) tmp.count = cpu_to_le16(tmp.count); } - memwrite(addr, &tmp, sizeof(tmp)); + qtest_memwrite(ahci->parent->qts, addr, &tmp, sizeof(tmp)); } unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port) @@ -636,7 +637,7 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, if (opts->size && !opts->buffer) { opts->buffer = ahci_alloc(ahci, opts->size); g_assert(opts->buffer); - qmemset(opts->buffer, 0x00, opts->size); + qtest_memset(ahci->parent->qts, opts->buffer, 0x00, opts->size); } /* Command creation */ @@ -661,15 +662,15 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, ahci_command_commit(ahci, cmd, port); ahci_command_issue_async(ahci, cmd); if (opts->error) { - qmp_eventwait("STOP"); + qtest_qmp_eventwait(ahci->parent->qts, "STOP"); } if (opts->mid_cb) { rc = opts->mid_cb(ahci, cmd, opts); g_assert_cmpint(rc, ==, 0); } if (opts->error) { - qmp_async("{'execute':'cont' }"); - qmp_eventwait("RESUME"); + qtest_async_qmp(ahci->parent->qts, "{'execute':'cont' }"); + qtest_qmp_eventwait(ahci->parent->qts, "RESUME"); } /* Wait for command to complete and verify sanity */ @@ -697,7 +698,7 @@ AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, ahci_command_adjust(cmd, sector, buffer, bufsize, 0); ahci_command_commit(ahci, cmd, port); ahci_command_issue_async(ahci, cmd); - qmp_eventwait("STOP"); + qtest_qmp_eventwait(ahci->parent->qts, "STOP"); return cmd; } @@ -706,8 +707,8 @@ AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd) { /* Complete the command */ - qmp_async("{'execute':'cont' }"); - qmp_eventwait("RESUME"); + qtest_async_qmp(ahci->parent->qts, "{'execute':'cont' }"); + qtest_qmp_eventwait(ahci->parent->qts, "RESUME"); ahci_command_wait(ahci, cmd); ahci_command_verify(ahci, cmd); ahci_command_free(cmd); @@ -754,16 +755,16 @@ void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, g_assert(props); ptr = ahci_alloc(ahci, bufsize); g_assert(!bufsize || ptr); - qmemset(ptr, 0x00, bufsize); + qtest_memset(ahci->parent->qts, ptr, 0x00, bufsize); if (bufsize && props->write) { - bufwrite(ptr, buffer, bufsize); + qtest_bufwrite(ahci->parent->qts, ptr, buffer, bufsize); } ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize, sector); if (bufsize && props->read) { - bufread(ptr, buffer, bufsize); + qtest_bufread(ahci->parent->qts, ptr, buffer, bufsize); } ahci_free(ahci, ptr); @@ -901,7 +902,7 @@ static int copy_buffer(AHCIQState *ahci, AHCICommand *cmd, const AHCIOpts *opts) { unsigned char *rx = opts->opaque; - bufread(opts->buffer, rx, opts->size); + qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size); return 0; } @@ -1141,7 +1142,7 @@ void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port) ahci_write_fis(ahci, cmd); /* Then ATAPI CMD, if needed */ if (cmd->props->atapi) { - memwrite(table_ptr + 0x40, cmd->atapi_cmd, 16); + qtest_memwrite(ahci->parent->qts, table_ptr + 0x40, cmd->atapi_cmd, 16); } /* Construct and write the PRDs to the command table */ @@ -1162,8 +1163,8 @@ void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port) prd.dbc |= cpu_to_le32(0x80000000); /* Request DPS Interrupt */ /* Commit the PRD entry to the Command Table */ - memwrite(table_ptr + 0x80 + (i * sizeof(PRD)), - &prd, sizeof(PRD)); + qtest_memwrite(ahci->parent->qts, table_ptr + 0x80 + (i * sizeof(PRD)), + &prd, sizeof(PRD)); } /* Bookmark the PRDTL and CTBA values */ diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index 5f9627bb0f..715ca1e226 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -571,7 +571,7 @@ void ahci_free(AHCIQState *ahci, uint64_t addr); void ahci_clean_mem(AHCIQState *ahci); /* Device management */ -QPCIDevice *get_ahci_device(uint32_t *fingerprint); +QPCIDevice *get_ahci_device(QTestState *qts, uint32_t *fingerprint); void free_ahci_device(QPCIDevice *dev); void ahci_pci_enable(AHCIQState *ahci); void start_ahci_device(AHCIQState *ahci); diff --git a/tests/libqos/fw_cfg.c b/tests/libqos/fw_cfg.c index 4d9dc3fd0b..d0889d1e22 100644 --- a/tests/libqos/fw_cfg.c +++ b/tests/libqos/fw_cfg.c @@ -56,7 +56,7 @@ uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key) static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) { - writew(fw_cfg->base, key); + qtest_writew(fw_cfg->qts, fw_cfg->base, key); } static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) @@ -65,15 +65,16 @@ static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) int i; for (i = 0; i < len; i++) { - ptr[i] = readb(fw_cfg->base + 2); + ptr[i] = qtest_readb(fw_cfg->qts, fw_cfg->base + 2); } } -QFWCFG *mm_fw_cfg_init(uint64_t base) +QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base) { QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg)); fw_cfg->base = base; + fw_cfg->qts = qts; fw_cfg->select = mm_fw_cfg_select; fw_cfg->read = mm_fw_cfg_read; @@ -82,7 +83,7 @@ QFWCFG *mm_fw_cfg_init(uint64_t base) static void io_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) { - outw(fw_cfg->base, key); + qtest_outw(fw_cfg->qts, fw_cfg->base, key); } static void io_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) @@ -91,15 +92,16 @@ static void io_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) int i; for (i = 0; i < len; i++) { - ptr[i] = inb(fw_cfg->base + 1); + ptr[i] = qtest_inb(fw_cfg->qts, fw_cfg->base + 1); } } -QFWCFG *io_fw_cfg_init(uint16_t base) +QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base) { QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg)); fw_cfg->base = base; + fw_cfg->qts = qts; fw_cfg->select = io_fw_cfg_select; fw_cfg->read = io_fw_cfg_read; diff --git a/tests/libqos/fw_cfg.h b/tests/libqos/fw_cfg.h index e8371b2317..0353416af0 100644 --- a/tests/libqos/fw_cfg.h +++ b/tests/libqos/fw_cfg.h @@ -13,12 +13,14 @@ #ifndef LIBQOS_FW_CFG_H #define LIBQOS_FW_CFG_H +#include "libqtest.h" typedef struct QFWCFG QFWCFG; struct QFWCFG { uint64_t base; + QTestState *qts; void (*select)(QFWCFG *fw_cfg, uint16_t key); void (*read)(QFWCFG *fw_cfg, void *data, size_t len); }; @@ -30,12 +32,12 @@ uint16_t qfw_cfg_get_u16(QFWCFG *fw_cfg, uint16_t key); uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key); uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key); -QFWCFG *mm_fw_cfg_init(uint64_t base); -QFWCFG *io_fw_cfg_init(uint16_t base); +QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base); +QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base); -static inline QFWCFG *pc_fw_cfg_init(void) +static inline QFWCFG *pc_fw_cfg_init(QTestState *qts) { - return io_fw_cfg_init(0x510); + return io_fw_cfg_init(qts, 0x510); } #endif diff --git a/tests/libqos/i2c-imx.c b/tests/libqos/i2c-imx.c index 1c4b4314ba..0945f2ecdc 100644 --- a/tests/libqos/i2c-imx.c +++ b/tests/libqos/i2c-imx.c @@ -40,8 +40,8 @@ typedef struct IMXI2C { static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr, enum IMXI2CDirection direction) { - writeb(s->addr + I2DR_ADDR, (addr << 1) | - (direction == IMX_I2C_READ ? 1 : 0)); + qtest_writeb(s->parent.qts, s->addr + I2DR_ADDR, + (addr << 1) | (direction == IMX_I2C_READ ? 1 : 0)); } static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr, @@ -63,35 +63,35 @@ static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr, I2CR_MTX | I2CR_TXAK; - writeb(s->addr + I2CR_ADDR, data); - status = readb(s->addr + I2SR_ADDR); + qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IBB) != 0); /* set the slave address */ imx_i2c_set_slave_addr(s, addr, IMX_I2C_WRITE); - status = readb(s->addr + I2SR_ADDR); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) != 0); g_assert((status & I2SR_RXAK) == 0); /* ack the interrupt */ - writeb(s->addr + I2SR_ADDR, 0); - status = readb(s->addr + I2SR_ADDR); + qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) == 0); while (size < len) { /* check we are still busy */ - status = readb(s->addr + I2SR_ADDR); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IBB) != 0); /* write the data */ - writeb(s->addr + I2DR_ADDR, buf[size]); - status = readb(s->addr + I2SR_ADDR); + qtest_writeb(i2c->qts, s->addr + I2DR_ADDR, buf[size]); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) != 0); g_assert((status & I2SR_RXAK) == 0); /* ack the interrupt */ - writeb(s->addr + I2SR_ADDR, 0); - status = readb(s->addr + I2SR_ADDR); + qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) == 0); size++; @@ -99,8 +99,8 @@ static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr, /* release the bus */ data &= ~(I2CR_MSTA | I2CR_MTX); - writeb(s->addr + I2CR_ADDR, data); - status = readb(s->addr + I2SR_ADDR); + qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IBB) == 0); } @@ -123,19 +123,19 @@ static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr, I2CR_MTX | I2CR_TXAK; - writeb(s->addr + I2CR_ADDR, data); - status = readb(s->addr + I2SR_ADDR); + qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IBB) != 0); /* set the slave address */ imx_i2c_set_slave_addr(s, addr, IMX_I2C_READ); - status = readb(s->addr + I2SR_ADDR); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) != 0); g_assert((status & I2SR_RXAK) == 0); /* ack the interrupt */ - writeb(s->addr + I2SR_ADDR, 0); - status = readb(s->addr + I2SR_ADDR); + qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) == 0); /* set the bus for read */ @@ -144,23 +144,23 @@ static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr, if (len != 1) { data &= ~I2CR_TXAK; } - writeb(s->addr + I2CR_ADDR, data); - status = readb(s->addr + I2SR_ADDR); + qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IBB) != 0); /* dummy read */ - readb(s->addr + I2DR_ADDR); - status = readb(s->addr + I2SR_ADDR); + qtest_readb(i2c->qts, s->addr + I2DR_ADDR); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) != 0); /* ack the interrupt */ - writeb(s->addr + I2SR_ADDR, 0); - status = readb(s->addr + I2SR_ADDR); + qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) == 0); while (size < len) { /* check we are still busy */ - status = readb(s->addr + I2SR_ADDR); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IBB) != 0); if (size == (len - 1)) { @@ -170,30 +170,30 @@ static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr, /* ack the data read */ data |= I2CR_TXAK; } - writeb(s->addr + I2CR_ADDR, data); + qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); /* read the data */ - buf[size] = readb(s->addr + I2DR_ADDR); + buf[size] = qtest_readb(i2c->qts, s->addr + I2DR_ADDR); if (size != (len - 1)) { - status = readb(s->addr + I2SR_ADDR); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) != 0); /* ack the interrupt */ - writeb(s->addr + I2SR_ADDR, 0); + qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); } - status = readb(s->addr + I2SR_ADDR); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IIF) == 0); size++; } - status = readb(s->addr + I2SR_ADDR); + status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); g_assert((status & I2SR_IBB) == 0); } -I2CAdapter *imx_i2c_create(uint64_t addr) +I2CAdapter *imx_i2c_create(QTestState *qts, uint64_t addr) { IMXI2C *s = g_malloc0(sizeof(*s)); I2CAdapter *i2c = (I2CAdapter *)s; @@ -202,6 +202,7 @@ I2CAdapter *imx_i2c_create(uint64_t addr) i2c->send = imx_i2c_send; i2c->recv = imx_i2c_recv; + i2c->qts = qts; return i2c; } diff --git a/tests/libqos/i2c-omap.c b/tests/libqos/i2c-omap.c index f603fdf43c..1ef6e7b200 100644 --- a/tests/libqos/i2c-omap.c +++ b/tests/libqos/i2c-omap.c @@ -51,8 +51,8 @@ static void omap_i2c_set_slave_addr(OMAPI2C *s, uint8_t addr) { uint16_t data = addr; - writew(s->addr + OMAP_I2C_SA, data); - data = readw(s->addr + OMAP_I2C_SA); + qtest_writew(s->parent.qts, s->addr + OMAP_I2C_SA, data); + data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_SA); g_assert_cmphex(data, ==, addr); } @@ -65,38 +65,38 @@ static void omap_i2c_send(I2CAdapter *i2c, uint8_t addr, omap_i2c_set_slave_addr(s, addr); data = len; - writew(s->addr + OMAP_I2C_CNT, data); + qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data); data = OMAP_I2C_CON_I2C_EN | OMAP_I2C_CON_TRX | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT | OMAP_I2C_CON_STP; - writew(s->addr + OMAP_I2C_CON, data); - data = readw(s->addr + OMAP_I2C_CON); + qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON); g_assert((data & OMAP_I2C_CON_STP) != 0); - data = readw(s->addr + OMAP_I2C_STAT); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); g_assert((data & OMAP_I2C_STAT_NACK) == 0); while (len > 1) { - data = readw(s->addr + OMAP_I2C_STAT); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); g_assert((data & OMAP_I2C_STAT_XRDY) != 0); data = buf[0] | ((uint16_t)buf[1] << 8); - writew(s->addr + OMAP_I2C_DATA, data); + qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data); buf = (uint8_t *)buf + 2; len -= 2; } if (len == 1) { - data = readw(s->addr + OMAP_I2C_STAT); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); g_assert((data & OMAP_I2C_STAT_XRDY) != 0); data = buf[0]; - writew(s->addr + OMAP_I2C_DATA, data); + qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data); } - data = readw(s->addr + OMAP_I2C_CON); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON); g_assert((data & OMAP_I2C_CON_STP) == 0); } @@ -109,30 +109,30 @@ static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr, omap_i2c_set_slave_addr(s, addr); data = len; - writew(s->addr + OMAP_I2C_CNT, data); + qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data); data = OMAP_I2C_CON_I2C_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT | OMAP_I2C_CON_STP; - writew(s->addr + OMAP_I2C_CON, data); - data = readw(s->addr + OMAP_I2C_CON); + qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON); g_assert((data & OMAP_I2C_CON_STP) == 0); - data = readw(s->addr + OMAP_I2C_STAT); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); g_assert((data & OMAP_I2C_STAT_NACK) == 0); - data = readw(s->addr + OMAP_I2C_CNT); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT); g_assert_cmpuint(data, ==, len); while (len > 0) { - data = readw(s->addr + OMAP_I2C_STAT); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); g_assert((data & OMAP_I2C_STAT_RRDY) != 0); g_assert((data & OMAP_I2C_STAT_ROVR) == 0); - data = readw(s->addr + OMAP_I2C_DATA); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_DATA); - stat = readw(s->addr + OMAP_I2C_STAT); + stat = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); if (unlikely(len == 1)) { g_assert((stat & OMAP_I2C_STAT_SBD) != 0); @@ -148,11 +148,11 @@ static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr, } } - data = readw(s->addr + OMAP_I2C_CON); + data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON); g_assert((data & OMAP_I2C_CON_STP) == 0); } -I2CAdapter *omap_i2c_create(uint64_t addr) +I2CAdapter *omap_i2c_create(QTestState *qts, uint64_t addr) { OMAPI2C *s = g_malloc0(sizeof(*s)); I2CAdapter *i2c = (I2CAdapter *)s; @@ -162,9 +162,10 @@ I2CAdapter *omap_i2c_create(uint64_t addr) i2c->send = omap_i2c_send; i2c->recv = omap_i2c_recv; + i2c->qts = qts; /* verify the mmio address by looking for a known signature */ - data = readw(addr + OMAP_I2C_REV); + data = qtest_readw(qts, addr + OMAP_I2C_REV); g_assert_cmphex(data, ==, 0x34); return i2c; diff --git a/tests/libqos/i2c.h b/tests/libqos/i2c.h index 6e648f922a..eb40b808bd 100644 --- a/tests/libqos/i2c.h +++ b/tests/libqos/i2c.h @@ -9,6 +9,7 @@ #ifndef LIBQOS_I2C_H #define LIBQOS_I2C_H +#include "libqtest.h" typedef struct I2CAdapter I2CAdapter; struct I2CAdapter { @@ -16,6 +17,8 @@ struct I2CAdapter { const uint8_t *buf, uint16_t len); void (*recv)(I2CAdapter *adapter, uint8_t addr, uint8_t *buf, uint16_t len); + + QTestState *qts; }; void i2c_send(I2CAdapter *i2c, uint8_t addr, @@ -24,9 +27,9 @@ void i2c_recv(I2CAdapter *i2c, uint8_t addr, uint8_t *buf, uint16_t len); /* libi2c-omap.c */ -I2CAdapter *omap_i2c_create(uint64_t addr); +I2CAdapter *omap_i2c_create(QTestState *qts, uint64_t addr); /* libi2c-imx.c */ -I2CAdapter *imx_i2c_create(uint64_t addr); +I2CAdapter *imx_i2c_create(QTestState *qts, uint64_t addr); #endif diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c index b554758802..a9c1aceaa7 100644 --- a/tests/libqos/libqos-pc.c +++ b/tests/libqos/libqos-pc.c @@ -25,7 +25,7 @@ QOSState *qtest_pc_boot(const char *cmdline_fmt, ...) qs = qtest_vboot(&qos_ops, cmdline_fmt, ap); va_end(ap); - qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_irq_intercept_in(qs->qts, "ioapic"); return qs; } diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 306d4c06de..5124e982c1 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -18,18 +18,14 @@ QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) { char *cmdline; - struct QOSState *qs = g_new(QOSState, 1); + QOSState *qs = g_new0(QOSState, 1); cmdline = g_strdup_vprintf(cmdline_fmt, ap); - qs->qts = qtest_start(cmdline); + qs->qts = qtest_init(cmdline); qs->ops = ops; if (ops) { - if (ops->init_allocator) { - qs->alloc = ops->init_allocator(ALLOC_NO_FLAGS); - } - if (ops->qpci_init && qs->alloc) { - qs->pcibus = ops->qpci_init(qs->alloc); - } + qs->alloc = ops->init_allocator(qs->qts, ALLOC_NO_FLAGS); + qs->pcibus = ops->qpci_init(qs->qts, qs->alloc); } g_free(cmdline); @@ -85,29 +81,21 @@ void set_context(QOSState *s) global_qtest = s->qts; } -static QDict *qmp_execute(const char *command) +static QDict *qmp_execute(QTestState *qts, const char *command) { - char *fmt; - QDict *rsp; - - fmt = g_strdup_printf("{ 'execute': '%s' }", command); - rsp = qmp(fmt); - g_free(fmt); - - return rsp; + return qtest_qmp(qts, "{ 'execute': %s }", command); } void migrate(QOSState *from, QOSState *to, const char *uri) { const char *st; - char *s; QDict *rsp, *sub; bool running; set_context(from); /* Is the machine currently running? */ - rsp = qmp_execute("query-status"); + rsp = qmp_execute(from->qts, "query-status"); g_assert(qdict_haskey(rsp, "return")); sub = qdict_get_qdict(rsp, "return"); g_assert(qdict_haskey(sub, "running")); @@ -115,30 +103,28 @@ void migrate(QOSState *from, QOSState *to, const char *uri) QDECREF(rsp); /* Issue the migrate command. */ - s = g_strdup_printf("{ 'execute': 'migrate'," - "'arguments': { 'uri': '%s' } }", - uri); - rsp = qmp(s); - g_free(s); + rsp = qtest_qmp(from->qts, + "{ 'execute': 'migrate', 'arguments': { 'uri': %s }}", + uri); g_assert(qdict_haskey(rsp, "return")); QDECREF(rsp); /* Wait for STOP event, but only if we were running: */ if (running) { - qmp_eventwait("STOP"); + qtest_qmp_eventwait(from->qts, "STOP"); } /* If we were running, we can wait for an event. */ if (running) { migrate_allocator(from->alloc, to->alloc); set_context(to); - qmp_eventwait("RESUME"); + qtest_qmp_eventwait(to->qts, "RESUME"); return; } /* Otherwise, we need to wait: poll until migration is completed. */ while (1) { - rsp = qmp_execute("query-migrate"); + rsp = qmp_execute(from->qts, "query-migrate"); g_assert(qdict_haskey(rsp, "return")); sub = qdict_get_qdict(rsp, "return"); g_assert(qdict_haskey(sub, "status")); diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h index 231969766f..07d4b93d1d 100644 --- a/tests/libqos/libqos.h +++ b/tests/libqos/libqos.h @@ -8,9 +8,9 @@ typedef struct QOSState QOSState; typedef struct QOSOps { - QGuestAllocator *(*init_allocator)(QAllocOpts); + QGuestAllocator *(*init_allocator)(QTestState *qts, QAllocOpts); void (*uninit_allocator)(QGuestAllocator *); - QPCIBus *(*qpci_init)(QGuestAllocator *alloc); + QPCIBus *(*qpci_init)(QTestState *qts, QGuestAllocator *alloc); void (*qpci_free)(QPCIBus *bus); void (*shutdown)(QOSState *); } QOSOps; diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c index dd2b900c5f..634b9c288a 100644 --- a/tests/libqos/malloc-pc.c +++ b/tests/libqos/malloc-pc.c @@ -29,11 +29,11 @@ void pc_alloc_uninit(QGuestAllocator *allocator) alloc_uninit(allocator); } -QGuestAllocator *pc_alloc_init_flags(QAllocOpts flags) +QGuestAllocator *pc_alloc_init_flags(QTestState *qts, QAllocOpts flags) { QGuestAllocator *s; uint64_t ram_size; - QFWCFG *fw_cfg = pc_fw_cfg_init(); + QFWCFG *fw_cfg = pc_fw_cfg_init(qts); ram_size = qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE); s = alloc_init_flags(flags, 1 << 20, MIN(ram_size, 0xE0000000)); @@ -45,7 +45,7 @@ QGuestAllocator *pc_alloc_init_flags(QAllocOpts flags) return s; } -inline QGuestAllocator *pc_alloc_init(void) +inline QGuestAllocator *pc_alloc_init(QTestState *qts) { - return pc_alloc_init_flags(ALLOC_NO_FLAGS); + return pc_alloc_init_flags(qts, ALLOC_NO_FLAGS); } diff --git a/tests/libqos/malloc-pc.h b/tests/libqos/malloc-pc.h index 86ab9f0429..10f3da6cf2 100644 --- a/tests/libqos/malloc-pc.h +++ b/tests/libqos/malloc-pc.h @@ -15,8 +15,8 @@ #include "libqos/malloc.h" -QGuestAllocator *pc_alloc_init(void); -QGuestAllocator *pc_alloc_init_flags(QAllocOpts flags); +QGuestAllocator *pc_alloc_init(QTestState *qts); +QGuestAllocator *pc_alloc_init_flags(QTestState *qts, QAllocOpts flags); void pc_alloc_uninit(QGuestAllocator *allocator); #endif diff --git a/tests/libqos/malloc-spapr.c b/tests/libqos/malloc-spapr.c index 006404af33..1c359cea6c 100644 --- a/tests/libqos/malloc-spapr.c +++ b/tests/libqos/malloc-spapr.c @@ -22,7 +22,7 @@ void spapr_alloc_uninit(QGuestAllocator *allocator) alloc_uninit(allocator); } -QGuestAllocator *spapr_alloc_init_flags(QAllocOpts flags) +QGuestAllocator *spapr_alloc_init_flags(QTestState *qts, QAllocOpts flags) { QGuestAllocator *s; @@ -34,5 +34,5 @@ QGuestAllocator *spapr_alloc_init_flags(QAllocOpts flags) QGuestAllocator *spapr_alloc_init(void) { - return spapr_alloc_init_flags(ALLOC_NO_FLAGS); + return spapr_alloc_init_flags(NULL, ALLOC_NO_FLAGS); } diff --git a/tests/libqos/malloc-spapr.h b/tests/libqos/malloc-spapr.h index 64d0e770d1..52a9346a26 100644 --- a/tests/libqos/malloc-spapr.h +++ b/tests/libqos/malloc-spapr.h @@ -11,7 +11,7 @@ #include "libqos/malloc.h" QGuestAllocator *spapr_alloc_init(void); -QGuestAllocator *spapr_alloc_init_flags(QAllocOpts flags); +QGuestAllocator *spapr_alloc_init_flags(QTestState *qts, QAllocOpts flags); void spapr_alloc_uninit(QGuestAllocator *allocator); #endif diff --git a/tests/libqos/malloc.h b/tests/libqos/malloc.h index ae9dac8f61..828fddabdb 100644 --- a/tests/libqos/malloc.h +++ b/tests/libqos/malloc.h @@ -14,6 +14,7 @@ #define LIBQOS_MALLOC_H #include "qemu/queue.h" +#include "libqtest.h" typedef enum { ALLOC_NO_FLAGS = 0x00, diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index cd4e20e1ea..a2daf6103d 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -115,11 +115,11 @@ static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint3 outl(0xcfc, value); } -QPCIBus *qpci_init_pc(QGuestAllocator *alloc) +QPCIBus *qpci_init_pc(QTestState *qts, QGuestAllocator *alloc) { - QPCIBusPC *ret; + QPCIBusPC *ret = g_new0(QPCIBusPC, 1); - ret = g_malloc(sizeof(*ret)); + assert(qts); ret->bus.pio_readb = qpci_pc_pio_readb; ret->bus.pio_readw = qpci_pc_pio_readw; @@ -142,6 +142,7 @@ QPCIBus *qpci_init_pc(QGuestAllocator *alloc) ret->bus.config_writew = qpci_pc_config_writew; ret->bus.config_writel = qpci_pc_config_writel; + ret->bus.qts = qts; ret->bus.pio_alloc_ptr = 0xc000; ret->bus.mmio_alloc_ptr = 0xE0000000; ret->bus.mmio_limit = 0x100000000ULL; @@ -173,9 +174,5 @@ void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); - response = qmp(""); - g_assert(response); - g_assert(qdict_haskey(response, "event")); - g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); - QDECREF(response); + qmp_eventwait("DEVICE_DELETED"); } diff --git a/tests/libqos/pci-pc.h b/tests/libqos/pci-pc.h index 9479b51642..491eeac756 100644 --- a/tests/libqos/pci-pc.h +++ b/tests/libqos/pci-pc.h @@ -16,7 +16,7 @@ #include "libqos/pci.h" #include "libqos/malloc.h" -QPCIBus *qpci_init_pc(QGuestAllocator *alloc); +QPCIBus *qpci_init_pc(QTestState *qts, QGuestAllocator *alloc); void qpci_free_pc(QPCIBus *bus); #endif diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c index 2043f1e123..c0f7e6db9b 100644 --- a/tests/libqos/pci-spapr.c +++ b/tests/libqos/pci-spapr.c @@ -108,21 +108,24 @@ static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); uint32_t config_addr = (devfn << 8) | offset; - return qrtas_ibm_read_pci_config(s->alloc, s->buid, config_addr, 1); + return qrtas_ibm_read_pci_config(bus->qts, s->alloc, s->buid, + config_addr, 1); } static uint16_t qpci_spapr_config_readw(QPCIBus *bus, int devfn, uint8_t offset) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); uint32_t config_addr = (devfn << 8) | offset; - return qrtas_ibm_read_pci_config(s->alloc, s->buid, config_addr, 2); + return qrtas_ibm_read_pci_config(bus->qts, s->alloc, s->buid, + config_addr, 2); } static uint32_t qpci_spapr_config_readl(QPCIBus *bus, int devfn, uint8_t offset) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); uint32_t config_addr = (devfn << 8) | offset; - return qrtas_ibm_read_pci_config(s->alloc, s->buid, config_addr, 4); + return qrtas_ibm_read_pci_config(bus->qts, s->alloc, s->buid, + config_addr, 4); } static void qpci_spapr_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, @@ -130,7 +133,8 @@ static void qpci_spapr_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); uint32_t config_addr = (devfn << 8) | offset; - qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 1, value); + qrtas_ibm_write_pci_config(bus->qts, s->alloc, s->buid, + config_addr, 1, value); } static void qpci_spapr_config_writew(QPCIBus *bus, int devfn, uint8_t offset, @@ -138,7 +142,8 @@ static void qpci_spapr_config_writew(QPCIBus *bus, int devfn, uint8_t offset, { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); uint32_t config_addr = (devfn << 8) | offset; - qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 2, value); + qrtas_ibm_write_pci_config(bus->qts, s->alloc, s->buid, + config_addr, 2, value); } static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset, @@ -146,7 +151,8 @@ static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset, { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); uint32_t config_addr = (devfn << 8) | offset; - qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 4, value); + qrtas_ibm_write_pci_config(bus->qts, s->alloc, s->buid, + config_addr, 4, value); } #define SPAPR_PCI_BASE (1ULL << 45) @@ -154,11 +160,11 @@ static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset, #define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */ #define SPAPR_PCI_IO_WIN_SIZE 0x10000 -QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) +QPCIBus *qpci_init_spapr(QTestState *qts, QGuestAllocator *alloc) { - QPCIBusSPAPR *ret; + QPCIBusSPAPR *ret = g_new0(QPCIBusSPAPR, 1); - ret = g_malloc(sizeof(*ret)); + assert(qts); ret->alloc = alloc; @@ -197,6 +203,7 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) ret->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE; ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE; + ret->bus.qts = qts; ret->bus.pio_alloc_ptr = 0xc000; ret->bus.mmio_alloc_ptr = ret->mmio32.pci_base; ret->bus.mmio_limit = ret->mmio32.pci_base + ret->mmio32.size; diff --git a/tests/libqos/pci-spapr.h b/tests/libqos/pci-spapr.h index 4192126d86..387686dfc8 100644 --- a/tests/libqos/pci-spapr.h +++ b/tests/libqos/pci-spapr.h @@ -11,7 +11,7 @@ #include "libqos/malloc.h" #include "libqos/pci.h" -QPCIBus *qpci_init_spapr(QGuestAllocator *alloc); +QPCIBus *qpci_init_spapr(QTestState *qts, QGuestAllocator *alloc); void qpci_free_spapr(QPCIBus *bus); #endif diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index ed480614ff..429c382282 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -48,6 +48,7 @@ struct QPCIBus { void (*config_writel)(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value); + QTestState *qts; uint16_t pio_alloc_ptr; uint64_t mmio_alloc_ptr, mmio_limit; }; diff --git a/tests/libqos/rtas.c b/tests/libqos/rtas.c index 0269803ce0..d81ff4274d 100644 --- a/tests/libqos/rtas.c +++ b/tests/libqos/rtas.c @@ -7,26 +7,28 @@ #include "libqtest.h" #include "libqos/rtas.h" -static void qrtas_copy_args(uint64_t target_args, uint32_t nargs, - uint32_t *args) +static void qrtas_copy_args(QTestState *qts, uint64_t target_args, + uint32_t nargs, uint32_t *args) { int i; for (i = 0; i < nargs; i++) { - writel(target_args + i * sizeof(uint32_t), args[i]); + qtest_writel(qts, target_args + i * sizeof(uint32_t), args[i]); } } -static void qrtas_copy_ret(uint64_t target_ret, uint32_t nret, uint32_t *ret) +static void qrtas_copy_ret(QTestState *qts, uint64_t target_ret, + uint32_t nret, uint32_t *ret) { int i; for (i = 0; i < nret; i++) { - ret[i] = readl(target_ret + i * sizeof(uint32_t)); + ret[i] = qtest_readl(qts, target_ret + i * sizeof(uint32_t)); } } -static uint64_t qrtas_call(QGuestAllocator *alloc, const char *name, +static uint64_t qrtas_call(QTestState *qts, QGuestAllocator *alloc, + const char *name, uint32_t nargs, uint32_t *args, uint32_t nret, uint32_t *ret) { @@ -36,10 +38,9 @@ static uint64_t qrtas_call(QGuestAllocator *alloc, const char *name, target_args = guest_alloc(alloc, nargs * sizeof(uint32_t)); target_ret = guest_alloc(alloc, nret * sizeof(uint32_t)); - qrtas_copy_args(target_args, nargs, args); - res = qtest_rtas_call(global_qtest, name, - nargs, target_args, nret, target_ret); - qrtas_copy_ret(target_ret, nret, ret); + qrtas_copy_args(qts, target_args, nargs, args); + res = qtest_rtas_call(qts, name, nargs, target_args, nret, target_ret); + qrtas_copy_ret(qts, target_ret, nret, ret); guest_free(alloc, target_ret); guest_free(alloc, target_args); @@ -47,12 +48,13 @@ static uint64_t qrtas_call(QGuestAllocator *alloc, const char *name, return res; } -int qrtas_get_time_of_day(QGuestAllocator *alloc, struct tm *tm, uint32_t *ns) +int qrtas_get_time_of_day(QTestState *qts, QGuestAllocator *alloc, + struct tm *tm, uint32_t *ns) { int res; uint32_t ret[8]; - res = qrtas_call(alloc, "get-time-of-day", 0, NULL, 8, ret); + res = qrtas_call(qts, alloc, "get-time-of-day", 0, NULL, 8, ret); if (res != 0) { return res; } @@ -70,7 +72,8 @@ int qrtas_get_time_of_day(QGuestAllocator *alloc, struct tm *tm, uint32_t *ns) return res; } -uint32_t qrtas_ibm_read_pci_config(QGuestAllocator *alloc, uint64_t buid, +uint32_t qrtas_ibm_read_pci_config(QTestState *qts, QGuestAllocator *alloc, + uint64_t buid, uint32_t addr, uint32_t size) { int res; @@ -80,7 +83,7 @@ uint32_t qrtas_ibm_read_pci_config(QGuestAllocator *alloc, uint64_t buid, args[1] = buid >> 32; args[2] = buid & 0xffffffff; args[3] = size; - res = qrtas_call(alloc, "ibm,read-pci-config", 4, args, 2, ret); + res = qrtas_call(qts, alloc, "ibm,read-pci-config", 4, args, 2, ret); if (res != 0) { return -1; } @@ -92,7 +95,8 @@ uint32_t qrtas_ibm_read_pci_config(QGuestAllocator *alloc, uint64_t buid, return ret[1]; } -int qrtas_ibm_write_pci_config(QGuestAllocator *alloc, uint64_t buid, +int qrtas_ibm_write_pci_config(QTestState *qts, QGuestAllocator *alloc, + uint64_t buid, uint32_t addr, uint32_t size, uint32_t val) { int res; @@ -103,7 +107,7 @@ int qrtas_ibm_write_pci_config(QGuestAllocator *alloc, uint64_t buid, args[2] = buid & 0xffffffff; args[3] = size; args[4] = val; - res = qrtas_call(alloc, "ibm,write-pci-config", 5, args, 1, ret); + res = qrtas_call(qts, alloc, "ibm,write-pci-config", 5, args, 1, ret); if (res != 0) { return -1; } diff --git a/tests/libqos/rtas.h b/tests/libqos/rtas.h index 498eb19230..459e23aaf4 100644 --- a/tests/libqos/rtas.h +++ b/tests/libqos/rtas.h @@ -7,9 +7,11 @@ #define LIBQOS_RTAS_H #include "libqos/malloc.h" -int qrtas_get_time_of_day(QGuestAllocator *alloc, struct tm *tm, uint32_t *ns); -uint32_t qrtas_ibm_read_pci_config(QGuestAllocator *alloc, uint64_t buid, - uint32_t addr, uint32_t size); -int qrtas_ibm_write_pci_config(QGuestAllocator *alloc, uint64_t buid, - uint32_t addr, uint32_t size, uint32_t val); +int qrtas_get_time_of_day(QTestState *qts, QGuestAllocator *alloc, + struct tm *tm, uint32_t *ns); +uint32_t qrtas_ibm_read_pci_config(QTestState *qts, QGuestAllocator *alloc, + uint64_t buid, uint32_t addr, uint32_t size); +int qrtas_ibm_write_pci_config(QTestState *qts, QGuestAllocator *alloc, + uint64_t buid, uint32_t addr, uint32_t size, + uint32_t val); #endif /* LIBQOS_RTAS_H */ diff --git a/tests/libqtest.c b/tests/libqtest.c index f2c285374b..13c910069b 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -15,12 +15,13 @@ */ #include "qemu/osdep.h" -#include "libqtest.h" #include <sys/socket.h> #include <sys/wait.h> #include <sys/un.h> +#include "libqtest.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-streamer.h" @@ -363,12 +364,14 @@ redo: g_string_free(line, TRUE); if (strcmp(words[0], "IRQ") == 0) { - int irq; + long irq; + int ret; g_assert(words[1] != NULL); g_assert(words[2] != NULL); - irq = strtoul(words[2], NULL, 0); + ret = qemu_strtol(words[2], NULL, 0, &irq); + g_assert(!ret); g_assert_cmpint(irq, >=, 0); g_assert_cmpint(irq, <, MAX_IRQ); @@ -730,11 +733,13 @@ void qtest_outl(QTestState *s, uint16_t addr, uint32_t value) static uint32_t qtest_in(QTestState *s, const char *cmd, uint16_t addr) { gchar **args; - uint32_t value; + int ret; + unsigned long value; qtest_sendf(s, "%s 0x%x\n", cmd, addr); args = qtest_rsp(s, 2); - value = strtoul(args[1], NULL, 0); + ret = qemu_strtoul(args[1], NULL, 0, &value); + g_assert(!ret && value <= UINT32_MAX); g_strfreev(args); return value; @@ -785,11 +790,13 @@ void qtest_writeq(QTestState *s, uint64_t addr, uint64_t value) static uint64_t qtest_read(QTestState *s, const char *cmd, uint64_t addr) { gchar **args; + int ret; uint64_t value; qtest_sendf(s, "%s 0x%" PRIx64 "\n", cmd, addr); args = qtest_rsp(s, 2); - value = strtoull(args[1], NULL, 0); + ret = qemu_strtou64(args[1], NULL, 0, &value); + g_assert(!ret); g_strfreev(args); return value; diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c index 0f921ef38a..26af7d6e8e 100644 --- a/tests/m48t59-test.c +++ b/tests/m48t59-test.c @@ -28,47 +28,48 @@ static uint32_t base; static uint16_t reg_base = 0x1ff0; /* 0x7f0 for m48t02 */ static int base_year; +static const char *base_machine; static bool use_mmio; -static uint8_t cmos_read_mmio(uint8_t reg) +static uint8_t cmos_read_mmio(QTestState *s, uint8_t reg) { - return readb(base + (uint32_t)reg_base + (uint32_t)reg); + return qtest_readb(s, base + (uint32_t)reg_base + (uint32_t)reg); } -static void cmos_write_mmio(uint8_t reg, uint8_t val) +static void cmos_write_mmio(QTestState *s, uint8_t reg, uint8_t val) { uint8_t data = val; - writeb(base + (uint32_t)reg_base + (uint32_t)reg, data); + qtest_writeb(s, base + (uint32_t)reg_base + (uint32_t)reg, data); } -static uint8_t cmos_read_ioio(uint8_t reg) +static uint8_t cmos_read_ioio(QTestState *s, uint8_t reg) { - outw(base + 0, reg_base + (uint16_t)reg); - return inb(base + 3); + qtest_outw(s, base + 0, reg_base + (uint16_t)reg); + return qtest_inb(s, base + 3); } -static void cmos_write_ioio(uint8_t reg, uint8_t val) +static void cmos_write_ioio(QTestState *s, uint8_t reg, uint8_t val) { - outw(base + 0, reg_base + (uint16_t)reg); - outb(base + 3, val); + qtest_outw(s, base + 0, reg_base + (uint16_t)reg); + qtest_outb(s, base + 3, val); } -static uint8_t cmos_read(uint8_t reg) +static uint8_t cmos_read(QTestState *s, uint8_t reg) { if (use_mmio) { - return cmos_read_mmio(reg); + return cmos_read_mmio(s, reg); } else { - return cmos_read_ioio(reg); + return cmos_read_ioio(s, reg); } } -static void cmos_write(uint8_t reg, uint8_t val) +static void cmos_write(QTestState *s, uint8_t reg, uint8_t val) { if (use_mmio) { - cmos_write_mmio(reg, val); + cmos_write_mmio(s, reg, val); } else { - cmos_write_ioio(reg, val); + cmos_write_ioio(s, reg, val); } } @@ -106,18 +107,18 @@ static void print_tm(struct tm *tm) } #endif -static void cmos_get_date_time(struct tm *date) +static void cmos_get_date_time(QTestState *s, struct tm *date) { int sec, min, hour, mday, mon, year; time_t ts; struct tm dummy; - sec = cmos_read(RTC_SECONDS); - min = cmos_read(RTC_MINUTES); - hour = cmos_read(RTC_HOURS); - mday = cmos_read(RTC_DAY_OF_MONTH); - mon = cmos_read(RTC_MONTH); - year = cmos_read(RTC_YEAR); + sec = cmos_read(s, RTC_SECONDS); + min = cmos_read(s, RTC_MINUTES); + hour = cmos_read(s, RTC_HOURS); + mday = cmos_read(s, RTC_DAY_OF_MONTH); + mon = cmos_read(s, RTC_MONTH); + year = cmos_read(s, RTC_YEAR); sec = bcd2dec(sec); min = bcd2dec(min); @@ -143,11 +144,18 @@ static void cmos_get_date_time(struct tm *date) ts = mktime(date); } -static void check_time(int wiggle) +static QTestState *m48t59_qtest_start(void) +{ + return qtest_startf("-M %s -rtc clock=vm", base_machine); +} + +static void bcd_check_time(void) { struct tm start, date[4], end; struct tm *datep; time_t ts; + const int wiggle = 2; + QTestState *s = m48t59_qtest_start(); /* * This check assumes a few things. First, we cannot guarantee that we get @@ -165,10 +173,10 @@ static void check_time(int wiggle) ts = time(NULL); gmtime_r(&ts, &start); - cmos_get_date_time(&date[0]); - cmos_get_date_time(&date[1]); - cmos_get_date_time(&date[2]); - cmos_get_date_time(&date[3]); + cmos_get_date_time(s, &date[0]); + cmos_get_date_time(s, &date[1]); + cmos_get_date_time(s, &date[2]); + cmos_get_date_time(s, &date[3]); ts = time(NULL); gmtime_r(&ts, &end); @@ -198,30 +206,15 @@ static void check_time(int wiggle) g_assert_cmpint(ABS(t - s), <=, wiggle); } -} - -static int wiggle = 2; -static void bcd_check_time(void) -{ - if (strcmp(qtest_get_arch(), "sparc64") == 0) { - base = 0x74; - base_year = 1900; - use_mmio = false; - } else if (strcmp(qtest_get_arch(), "sparc") == 0) { - base = 0x71200000; - base_year = 1968; - use_mmio = true; - } else { /* PPC: need to map macio in PCI */ - g_assert_not_reached(); - } - check_time(wiggle); + qtest_quit(s); } /* success if no crash or abort */ static void fuzz_registers(void) { unsigned int i; + QTestState *s = m48t59_qtest_start(); for (i = 0; i < 1000; i++) { uint8_t reg, val; @@ -234,27 +227,47 @@ static void fuzz_registers(void) continue; } - cmos_write(reg, val); - cmos_read(reg); + cmos_write(s, reg, val); + cmos_read(s, reg); + } + + qtest_quit(s); +} + +static void base_setup(void) +{ + const char *arch = qtest_get_arch(); + + if (g_str_equal(arch, "sparc")) { + /* Note: For sparc64, we'd need to map-in the PCI bridge memory first */ + base = 0x71200000; + base_year = 1968; + base_machine = "SS-5"; + use_mmio = true; + } else if (g_str_equal(arch, "ppc") || g_str_equal(arch, "ppc64")) { + base = 0xF0000000; + base_year = 1968; + base_machine = "ref405ep"; + use_mmio = true; + } else { + g_assert_not_reached(); } } int main(int argc, char **argv) { - QTestState *s = NULL; int ret; - g_test_init(&argc, &argv, NULL); + base_setup(); - s = qtest_start("-rtc clock=vm"); + g_test_init(&argc, &argv, NULL); - qtest_add_func("/rtc/bcd/check-time", bcd_check_time); + if (g_test_slow()) { + /* Do not run this in timing-sensitive environments */ + qtest_add_func("/rtc/bcd-check-time", bcd_check_time); + } qtest_add_func("/rtc/fuzz-registers", fuzz_registers); ret = g_test_run(); - if (s) { - qtest_quit(s); - } - return ret; } diff --git a/tests/megasas-test.c b/tests/megasas-test.c index ce960e7f81..81837e14af 100644 --- a/tests/megasas-test.c +++ b/tests/megasas-test.c @@ -15,13 +15,16 @@ static QOSState *qmegasas_start(const char *extra_opts) { + QOSState *qs; const char *arch = qtest_get_arch(); const char *cmd = "-drive id=hd0,if=none,file=null-co://,format=raw " "-device megasas,id=scsi0,addr=04.0 " "-device scsi-hd,bus=scsi0.0,drive=hd0 %s"; if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - return qtest_pc_boot(cmd, extra_opts ? : ""); + qs = qtest_pc_boot(cmd, extra_opts ? : ""); + global_qtest = qs->qts; + return qs; } g_printerr("virtio-scsi tests are only available on x86 or ppc64\n"); diff --git a/tests/pxe-test.c b/tests/pxe-test.c index 5ca84805eb..6e3679672c 100644 --- a/tests/pxe-test.c +++ b/tests/pxe-test.c @@ -71,7 +71,7 @@ static void test_pxe_one(const testdef_t *test, bool ipv6) test->model); qtest_start(args); - boot_sector_test(); + boot_sector_test(global_qtest); qtest_quit(global_qtest); g_free(args); } diff --git a/tests/q35-test.c b/tests/q35-test.c index 187d68fb7e..3eaedf4b24 100644 --- a/tests/q35-test.c +++ b/tests/q35-test.c @@ -87,7 +87,7 @@ static void test_smram_lock(void) qtest_start("-M q35"); - pcibus = qpci_init_pc(NULL); + pcibus = qpci_init_pc(global_qtest, NULL); g_assert(pcibus != NULL); pcidev = qpci_device_find(pcibus, 0); @@ -146,7 +146,7 @@ static void test_tseg_size(const void *data) g_free(cmdline); /* locate the DRAM controller */ - pcibus = qpci_init_pc(NULL); + pcibus = qpci_init_pc(global_qtest, NULL); g_assert(pcibus != NULL); pcidev = qpci_device_find(pcibus, 0); g_assert(pcidev != NULL); diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out index 1ac5d56233..f6dce7947c 100644 --- a/tests/qemu-iotests/059.out +++ b/tests/qemu-iotests/059.out @@ -2358,5 +2358,5 @@ Offset Length Mapped to File 0x140000000 0x10000 0x50000 TEST_DIR/t-s003.vmdk === Testing afl image with a very large capacity === -qemu-img: Could not open 'TEST_DIR/afl9.IMGFMT': Could not open 'TEST_DIR/afl9.IMGFMT': Invalid argument +qemu-img: Can't get image size 'TEST_DIR/afl9.IMGFMT': File too large *** done diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index f5678b10c9..911b6f2894 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -54,6 +54,22 @@ $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io _check_test_img echo +echo "=== Testing version downgrade with zero expansion and 4K cache entries ===" +echo +IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M +$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 32M 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io +$PYTHON qcow2.py "$TEST_IMG" dump-header +$QEMU_IMG amend -o "compat=0.10" --image-opts \ + driver=qcow2,file.filename=$TEST_IMG,l2-cache-entry-size=4096 +$PYTHON qcow2.py "$TEST_IMG" dump-header +$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 32M 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io +_check_test_img + +echo echo "=== Testing dirty version downgrade ===" echo IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 942485de99..e857ef9a7d 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -52,6 +52,67 @@ read 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. +=== Testing version downgrade with zero expansion and 4K cache entries === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 33554432 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +128 KiB (0x20000) bytes allocated at offset 0 bytes (0x0) +31.875 MiB (0x1fe0000) bytes not allocated at offset 128 KiB (0x20000) +128 KiB (0x20000) bytes allocated at offset 32 MiB (0x2000000) +31.875 MiB (0x1fe0000) bytes not allocated at offset 32.125 MiB (0x2020000) +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x1 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Header extension: +magic 0x6803f857 +length 144 +data <binary> + +magic 0x514649fb +version 2 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 + +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 33554432 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +64 MiB (0x4000000) bytes not allocated at offset 0 bytes (0x0) +No errors were found on the image. + === Testing dirty version downgrade === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103 index d0cfab8844..2841318492 100755 --- a/tests/qemu-iotests/103 +++ b/tests/qemu-iotests/103 @@ -66,6 +66,14 @@ $QEMU_IO -c "open -o cache-size=1M,refcount-cache-size=2M $TEST_IMG" 2>&1 \ $QEMU_IO -c "open -o cache-size=0,l2-cache-size=0,refcount-cache-size=0 $TEST_IMG" \ 2>&1 | _filter_testdir | _filter_imgfmt +# Invalid cache entry sizes +$QEMU_IO -c "open -o l2-cache-entry-size=256 $TEST_IMG" \ + 2>&1 | _filter_testdir | _filter_imgfmt +$QEMU_IO -c "open -o l2-cache-entry-size=4242 $TEST_IMG" \ + 2>&1 | _filter_testdir | _filter_imgfmt +$QEMU_IO -c "open -o l2-cache-entry-size=128k $TEST_IMG" \ + 2>&1 | _filter_testdir | _filter_imgfmt + echo echo '=== Testing valid option combinations ===' echo @@ -94,6 +102,15 @@ $QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \ -c 'read -P 42 0 64k' \ | _filter_qemu_io +# Valid cache entry sizes +$QEMU_IO -c "open -o l2-cache-entry-size=512 $TEST_IMG" \ + 2>&1 | _filter_testdir | _filter_imgfmt +$QEMU_IO -c "open -o l2-cache-entry-size=16k $TEST_IMG" \ + 2>&1 | _filter_testdir | _filter_imgfmt +$QEMU_IO -c "open -o l2-cache-entry-size=64k $TEST_IMG" \ + 2>&1 | _filter_testdir | _filter_imgfmt + + echo echo '=== Testing minimal L2 cache and COW ===' echo diff --git a/tests/qemu-iotests/103.out b/tests/qemu-iotests/103.out index b7aaadf89a..bd45d3875a 100644 --- a/tests/qemu-iotests/103.out +++ b/tests/qemu-iotests/103.out @@ -9,6 +9,9 @@ can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cach can't open device TEST_DIR/t.IMGFMT: l2-cache-size may not exceed cache-size can't open device TEST_DIR/t.IMGFMT: refcount-cache-size may not exceed cache-size can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time +can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) +can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) +can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) === Testing valid option combinations === diff --git a/tests/qemu-iotests/137 b/tests/qemu-iotests/137 index 5a01250005..87965625d8 100755 --- a/tests/qemu-iotests/137 +++ b/tests/qemu-iotests/137 @@ -83,6 +83,9 @@ $QEMU_IO \ -c "reopen -o overlap-check.inactive-l2=off" \ -c "reopen -o cache-size=1M" \ -c "reopen -o l2-cache-size=512k" \ + -c "reopen -o l2-cache-entry-size=512" \ + -c "reopen -o l2-cache-entry-size=4k" \ + -c "reopen -o l2-cache-entry-size=64k" \ -c "reopen -o refcount-cache-size=128k" \ -c "reopen -o cache-clean-interval=5" \ -c "reopen -o cache-clean-interval=0" \ @@ -107,6 +110,8 @@ $QEMU_IO \ -c "reopen -o cache-size=1M,l2-cache-size=2M" \ -c "reopen -o cache-size=1M,refcount-cache-size=2M" \ -c "reopen -o l2-cache-size=256T" \ + -c "reopen -o l2-cache-entry-size=33k" \ + -c "reopen -o l2-cache-entry-size=128k" \ -c "reopen -o refcount-cache-size=256T" \ -c "reopen -o overlap-check=constant,overlap-check.template=all" \ -c "reopen -o overlap-check=blubb" \ diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out index 05efd74d17..e28e1eadba 100644 --- a/tests/qemu-iotests/137.out +++ b/tests/qemu-iotests/137.out @@ -20,6 +20,8 @@ cache-size, l2-cache-size and refcount-cache-size may not be set the same time l2-cache-size may not exceed cache-size refcount-cache-size may not exceed cache-size L2 cache size too big +L2 cache entry size must be a power of two between 512 and the cluster size (65536) +L2 cache entry size must be a power of two between 512 and the cluster size (65536) L2 cache size too big Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all') Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index fc9fa975be..42dae04c83 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -64,7 +64,7 @@ class BaseClass(iotests.QMPTestCase): 'file': {'driver': 'file', 'filename': source_img}} self.vm.add_blockdev(self.qmp_to_opts(blockdev)) - self.vm.add_device('floppy,id=qdev0,drive=source') + self.vm.add_device('virtio-blk,id=qdev0,drive=source') self.vm.launch() self.assertIntactSourceBackingChain() @@ -173,21 +173,24 @@ class MirrorBaseClass(BaseClass): def testFull(self): self.runMirror('full') - node = self.findBlockNode('target', 'qdev0') + node = self.findBlockNode('target', + '/machine/peripheral/qdev0/virtio-backend') self.assertCorrectBackingImage(node, None) self.assertIntactSourceBackingChain() def testTop(self): self.runMirror('top') - node = self.findBlockNode('target', 'qdev0') + node = self.findBlockNode('target', + '/machine/peripheral/qdev0/virtio-backend') self.assertCorrectBackingImage(node, back2_img) self.assertIntactSourceBackingChain() def testNone(self): self.runMirror('none') - node = self.findBlockNode('target', 'qdev0') + node = self.findBlockNode('target', + '/machine/peripheral/qdev0/virtio-backend') self.assertCorrectBackingImage(node, source_img) self.assertIntactSourceBackingChain() @@ -239,7 +242,8 @@ class TestCommit(BaseClass): self.vm.event_wait('BLOCK_JOB_COMPLETED') - node = self.findBlockNode(None, 'qdev0') + node = self.findBlockNode(None, + '/machine/peripheral/qdev0/virtio-backend') self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', back1_img) self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index a3932db3de..2936929627 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -64,7 +64,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): def qmpAddBitmap(self): self.vm.qmp('block-dirty-bitmap-add', node='drive0', - name='bitmap0', persistent=True, autoload=True) + name='bitmap0', persistent=True) def test_persistent(self): self.vm = self.mkVm() diff --git a/tests/qemu-iotests/176 b/tests/qemu-iotests/176 index d38b3aeb91..32baa116dd 100755 --- a/tests/qemu-iotests/176 +++ b/tests/qemu-iotests/176 @@ -95,7 +95,7 @@ case $reason in "file": { "driver": "file", "filename": "$TEST_IMG" } } } { "execute": "block-dirty-bitmap-add", "arguments": { "node": "drive0", "name": "bitmap0", - "persistent": true, "autoload": true } } + "persistent": true } } { "execute": "quit" } EOF ;; diff --git a/tests/qemu-iotests/sample_images/afl9.vmdk.bz2 b/tests/qemu-iotests/sample_images/afl9.vmdk.bz2 Binary files differindex 03615d36a1..9fcd0af45a 100644 --- a/tests/qemu-iotests/sample_images/afl9.vmdk.bz2 +++ b/tests/qemu-iotests/sample_images/afl9.vmdk.bz2 diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 908f9b981f..580848307a 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -43,32 +43,32 @@ static void test_version(QObject *version) visit_free(v); } -static void test_malformed(void) +static void test_malformed(QTestState *qts) { QDict *resp; /* Not even a dictionary */ - resp = qmp("null"); + resp = qtest_qmp(qts, "null"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); QDECREF(resp); /* No "execute" key */ - resp = qmp("{}"); + resp = qtest_qmp(qts, "{}"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); QDECREF(resp); /* "execute" isn't a string */ - resp = qmp("{ 'execute': true }"); + resp = qtest_qmp(qts, "{ 'execute': true }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); QDECREF(resp); /* "arguments" isn't a dictionary */ - resp = qmp("{ 'execute': 'no-such-cmd', 'arguments': [] }"); + resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); QDECREF(resp); /* extra key */ - resp = qmp("{ 'execute': 'no-such-cmd', 'extra': true }"); + resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); QDECREF(resp); } @@ -77,11 +77,12 @@ static void test_qmp_protocol(void) { QDict *resp, *q, *ret; QList *capabilities; + QTestState *qts; - global_qtest = qtest_init_without_qmp_handshake(common_args); + qts = qtest_init_without_qmp_handshake(common_args); /* Test greeting */ - resp = qmp_receive(); + resp = qtest_qmp_receive(qts); q = qdict_get_qdict(resp, "QMP"); g_assert(q); test_version(qdict_get(q, "version")); @@ -90,46 +91,46 @@ static void test_qmp_protocol(void) QDECREF(resp); /* Test valid command before handshake */ - resp = qmp("{ 'execute': 'query-version' }"); + resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); QDECREF(resp); /* Test malformed commands before handshake */ - test_malformed(); + test_malformed(qts); /* Test handshake */ - resp = qmp("{ 'execute': 'qmp_capabilities' }"); + resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); ret = qdict_get_qdict(resp, "return"); g_assert(ret && !qdict_size(ret)); QDECREF(resp); /* Test repeated handshake */ - resp = qmp("{ 'execute': 'qmp_capabilities' }"); + resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); QDECREF(resp); /* Test valid command */ - resp = qmp("{ 'execute': 'query-version' }"); + resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); test_version(qdict_get(resp, "return")); QDECREF(resp); /* Test malformed commands */ - test_malformed(); + test_malformed(qts); /* Test 'id' */ - resp = qmp("{ 'execute': 'query-name', 'id': 'cookie#1' }"); + resp = qtest_qmp(qts, "{ 'execute': 'query-name', 'id': 'cookie#1' }"); ret = qdict_get_qdict(resp, "return"); g_assert(ret); g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); QDECREF(resp); /* Test command failure with 'id' */ - resp = qmp("{ 'execute': 'human-monitor-command', 'id': 2 }"); + resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); QDECREF(resp); - qtest_end(); + qtest_quit(qts); } static int query_error_class(const char *cmd) diff --git a/tests/rtas-test.c b/tests/rtas-test.c index 276c87ef84..009bda6d23 100644 --- a/tests/rtas-test.c +++ b/tests/rtas-test.c @@ -14,9 +14,10 @@ static void test_rtas_get_time_of_day(void) time_t t1, t2; qs = qtest_spapr_boot("-machine pseries"); + global_qtest = qs->qts; t1 = time(NULL); - ret = qrtas_get_time_of_day(qs->alloc, &tm, &ns); + ret = qrtas_get_time_of_day(qs->qts, qs->alloc, &tm, &ns); g_assert_cmpint(ret, ==, 0); t2 = mktimegm(&tm); g_assert(t2 - t1 < 5); /* 5 sec max to run the test */ diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c index 7de7dc45ae..68bfc42178 100644 --- a/tests/rtl8139-test.c +++ b/tests/rtl8139-test.c @@ -35,7 +35,7 @@ static QPCIDevice *get_device(void) { QPCIDevice *dev; - pcibus = qpci_init_pc(NULL); + pcibus = qpci_init_pc(global_qtest, NULL); qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev); g_assert(dev != NULL); @@ -197,11 +197,12 @@ int main(int argc, char **argv) { int ret; + qtest_start("-device rtl8139"); + g_test_init(&argc, &argv, NULL); qtest_add_func("/rtl8139/nop", nop); qtest_add_func("/rtl8139/timer", test_init); - qtest_start("-device rtl8139"); ret = g_test_run(); qtest_end(); diff --git a/tests/sdhci-test.c b/tests/sdhci-test.c index 493023fd0c..6b3a5328e0 100644 --- a/tests/sdhci-test.c +++ b/tests/sdhci-test.c @@ -187,7 +187,7 @@ static QSDHCI *machine_start(const struct sdhci_t *test) global_qtest = qtest_startf("-machine %s -device sdhci-pci", test->machine); - s->pci.bus = qpci_init_pc(NULL); + s->pci.bus = qpci_init_pc(global_qtest, NULL); /* Find PCI device and verify it's the right one */ s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0)); diff --git a/tests/tco-test.c b/tests/tco-test.c index 8ab43d742a..aee17af3c1 100644 --- a/tests/tco-test.c +++ b/tests/tco-test.c @@ -64,7 +64,7 @@ static void test_init(TestData *d) global_qtest = qs; qtest_irq_intercept_in(qs, "ioapic"); - d->bus = qpci_init_pc(NULL); + d->bus = qpci_init_pc(qs, NULL); d->dev = qpci_device_find(d->bus, QPCI_DEVFN(0x1f, 0x00)); g_assert(d->dev != NULL); @@ -237,9 +237,8 @@ static void test_tco_max_timeout(void) static QDict *get_watchdog_action(void) { - QDict *ev = qmp(""); + QDict *ev = qmp_eventwait_ref("WATCHDOG"); QDict *data; - g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG")); data = qdict_get_qdict(ev, "data"); QINCREF(data); diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c index e9a3cb7ac3..66c7a0147f 100644 --- a/tests/tmp105-test.c +++ b/tests/tmp105-test.c @@ -155,15 +155,13 @@ int main(int argc, char **argv) s = qtest_start("-machine n800 " "-device tmp105,bus=i2c-bus.0,id=" TMP105_TEST_ID ",address=0x49"); - i2c = omap_i2c_create(OMAP2_I2C_1_BASE); + i2c = omap_i2c_create(s, OMAP2_I2C_1_BASE); qtest_add_func("/tmp105/tx-rx", send_and_receive); ret = g_test_run(); - if (s) { - qtest_quit(s); - } + qtest_quit(s); g_free(i2c); return ret; diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c index 944eb1c088..55d4743a2a 100644 --- a/tests/usb-hcd-ehci-test.c +++ b/tests/usb-hcd-ehci-test.c @@ -52,7 +52,7 @@ static void ehci_port_test(struct qhc *hc, int port, uint32_t expect) static void test_init(void) { - pcibus = qpci_init_pc(NULL); + pcibus = qpci_init_pc(global_qtest, NULL); g_assert(pcibus != NULL); qusb_pci_init_one(pcibus, &uhci1, QPCI_DEVFN(0x1d, 0), 4); diff --git a/tests/usb-hcd-uhci-test.c b/tests/usb-hcd-uhci-test.c index 62e0c7829d..6a7e5a2fed 100644 --- a/tests/usb-hcd-uhci-test.c +++ b/tests/usb-hcd-uhci-test.c @@ -77,6 +77,7 @@ int main(int argc, char **argv) "available on x86 or ppc64\n"); exit(EXIT_FAILURE); } + global_qtest = qs->qts; ret = g_test_run(); qtest_shutdown(qs); diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index a217353e2c..22e9202b8d 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -180,7 +180,7 @@ static void init_virtio_dev(TestServer *s, uint32_t features_mask) uint32_t features; int i; - s->bus = qpci_init_pc(NULL); + s->bus = qpci_init_pc(global_qtest, NULL); g_assert_nonnull(s->bus); s->dev = qvirtio_pci_device_find(s->bus, VIRTIO_ID_NET); @@ -191,7 +191,7 @@ static void init_virtio_dev(TestServer *s, uint32_t features_mask) qvirtio_set_acknowledge(&s->dev->vdev); qvirtio_set_driver(&s->dev->vdev); - s->alloc = pc_alloc_init(); + s->alloc = pc_alloc_init(global_qtest); for (i = 0; i < s->queues * 2; i++) { s->vq[i] = qvirtqueue_setup(&s->dev->vdev, s->alloc, i); diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index 54edcb9955..a2b31085f6 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -44,6 +44,7 @@ static QVirtIO9P *qvirtio_9p_start(const char *driver) g_printerr("virtio-9p tests are only available on x86 or ppc64\n"); exit(EXIT_FAILURE); } + global_qtest = v9p->qs->qts; return v9p; } diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 2ac64e5e25..9be9ffb378 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -77,6 +77,7 @@ static QOSState *pci_test_start(void) g_printerr("virtio-blk tests are only available on x86 or ppc64\n"); exit(EXIT_FAILURE); } + global_qtest = qs->qts; unlink(tmp_path); g_free(tmp_path); return qs; diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index 4114839457..0a3c5dd257 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -54,18 +54,21 @@ static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot) static QOSState *pci_test_start(int socket) { + QOSState *qs; const char *arch = qtest_get_arch(); const char *cmd = "-netdev socket,fd=%d,id=hs0 -device " "virtio-net-pci,netdev=hs0"; if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - return qtest_pc_boot(cmd, socket); - } - if (strcmp(arch, "ppc64") == 0) { - return qtest_spapr_boot(cmd, socket); + qs = qtest_pc_boot(cmd, socket); + } else if (strcmp(arch, "ppc64") == 0) { + qs = qtest_spapr_boot(cmd, socket); + } else { + g_printerr("virtio-net tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); } - g_printerr("virtio-net tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); + global_qtest = qs->qts; + return qs; } static void driver_init(QVirtioDevice *dev) diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index bcf408fbb6..7393d69bb2 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -34,20 +34,22 @@ typedef struct { static QOSState *qvirtio_scsi_start(const char *extra_opts) { + QOSState *qs; const char *arch = qtest_get_arch(); const char *cmd = "-drive id=drv0,if=none,file=null-co://,format=raw " "-device virtio-scsi-pci,id=vs0 " "-device scsi-hd,bus=vs0.0,drive=drv0 %s"; if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - return qtest_pc_boot(cmd, extra_opts ? : ""); - } - if (strcmp(arch, "ppc64") == 0) { - return qtest_spapr_boot(cmd, extra_opts ? : ""); + qs = qtest_pc_boot(cmd, extra_opts ? : ""); + } else if (strcmp(arch, "ppc64") == 0) { + qs = qtest_spapr_boot(cmd, extra_opts ? : ""); + } else { + g_printerr("virtio-scsi tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); } - - g_printerr("virtio-scsi tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); + global_qtest = qs->qts; + return qs; } static void qvirtio_scsi_stop(QOSState *qs) diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c index 7190e680dc..2ec274e37c 100644 --- a/tests/vmgenid-test.c +++ b/tests/vmgenid-test.c @@ -45,7 +45,7 @@ static uint32_t acpi_find_vgia(void) int i; /* Wait for guest firmware to finish and start the payload. */ - boot_sector_test(); + boot_sector_test(global_qtest); /* Tables should be initialized now. */ rsdp_offset = acpi_find_rsdp_address(); diff --git a/tests/wdt_ib700-test.c b/tests/wdt_ib700-test.c index 6062d4e942..3b5bbcf007 100644 --- a/tests/wdt_ib700-test.c +++ b/tests/wdt_ib700-test.c @@ -12,108 +12,98 @@ #include "qapi/qmp/qdict.h" #include "qemu/timer.h" -static void qmp_check_no_event(void) +static void qmp_check_no_event(QTestState *s) { - QDict *resp = qmp("{'execute':'query-status'}"); + QDict *resp = qtest_qmp(s, "{'execute':'query-status'}"); g_assert(qdict_haskey(resp, "return")); QDECREF(resp); } -static QDict *qmp_get_event(const char *name) -{ - QDict *event = qmp(""); - QDict *data; - g_assert(qdict_haskey(event, "event")); - g_assert(!strcmp(qdict_get_str(event, "event"), name)); - - if (qdict_haskey(event, "data")) { - data = qdict_get_qdict(event, "data"); - QINCREF(data); - } else { - data = NULL; - } - - QDECREF(event); - return data; -} - static QDict *ib700_program_and_wait(QTestState *s) { - clock_step(NANOSECONDS_PER_SECOND * 40); - qmp_check_no_event(); + QDict *event, *data; + + qtest_clock_step(s, NANOSECONDS_PER_SECOND * 40); + qmp_check_no_event(s); /* 2 second limit */ - outb(0x443, 14); + qtest_outb(s, 0x443, 14); /* Ping */ - clock_step(NANOSECONDS_PER_SECOND); - qmp_check_no_event(); - outb(0x443, 14); + qtest_clock_step(s, NANOSECONDS_PER_SECOND); + qmp_check_no_event(s); + qtest_outb(s, 0x443, 14); /* Disable */ - clock_step(NANOSECONDS_PER_SECOND); - qmp_check_no_event(); - outb(0x441, 1); - clock_step(3 * NANOSECONDS_PER_SECOND); - qmp_check_no_event(); + qtest_clock_step(s, NANOSECONDS_PER_SECOND); + qmp_check_no_event(s); + qtest_outb(s, 0x441, 1); + qtest_clock_step(s, 3 * NANOSECONDS_PER_SECOND); + qmp_check_no_event(s); /* Enable and let it fire */ - outb(0x443, 13); - clock_step(3 * NANOSECONDS_PER_SECOND); - qmp_check_no_event(); - clock_step(2 * NANOSECONDS_PER_SECOND); - return qmp_get_event("WATCHDOG"); + qtest_outb(s, 0x443, 13); + qtest_clock_step(s, 3 * NANOSECONDS_PER_SECOND); + qmp_check_no_event(s); + qtest_clock_step(s, 2 * NANOSECONDS_PER_SECOND); + event = qtest_qmp_eventwait_ref(s, "WATCHDOG"); + data = qdict_get_qdict(event, "data"); + QINCREF(data); + QDECREF(event); + return data; } static void ib700_pause(void) { QDict *d; - QTestState *s = qtest_start("-watchdog-action pause -device ib700"); + QTestState *s = qtest_init("-watchdog-action pause -device ib700"); + qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "pause")); QDECREF(d); - d = qmp_get_event("STOP"); - QDECREF(d); - qtest_end(); + qtest_qmp_eventwait(s, "STOP"); + qtest_quit(s); } static void ib700_reset(void) { QDict *d; - QTestState *s = qtest_start("-watchdog-action reset -device ib700"); + QTestState *s = qtest_init("-watchdog-action reset -device ib700"); + qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); QDECREF(d); - d = qmp_get_event("RESET"); - QDECREF(d); - qtest_end(); + qtest_qmp_eventwait(s, "RESET"); + qtest_quit(s); } static void ib700_shutdown(void) { QDict *d; - QTestState *s = qtest_start("-watchdog-action reset -no-reboot -device ib700"); + QTestState *s; + + s = qtest_init("-watchdog-action reset -no-reboot -device ib700"); qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); QDECREF(d); - d = qmp_get_event("SHUTDOWN"); - QDECREF(d); - qtest_end(); + qtest_qmp_eventwait(s, "SHUTDOWN"); + qtest_quit(s); } static void ib700_none(void) { QDict *d; - QTestState *s = qtest_start("-watchdog-action none -device ib700"); + QTestState *s = qtest_init("-watchdog-action none -device ib700"); + qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "none")); QDECREF(d); - qtest_end(); + qtest_quit(s); } int main(int argc, char **argv) |