diff options
71 files changed, 3275 insertions, 674 deletions
diff --git a/block/backup.c b/block/backup.c index 582bd0f7ee..02dbe48035 100644 --- a/block/backup.c +++ b/block/backup.c @@ -372,14 +372,14 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) int64_t end; int64_t last_cluster = -1; int64_t sectors_per_cluster = cluster_size_sectors(job); - HBitmapIter hbi; + BdrvDirtyBitmapIter *dbi; granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap); clusters_per_iter = MAX((granularity / job->cluster_size), 1); - bdrv_dirty_iter_init(job->sync_bitmap, &hbi); + dbi = bdrv_dirty_iter_new(job->sync_bitmap, 0); /* Find the next dirty sector(s) */ - while ((sector = hbitmap_iter_next(&hbi)) != -1) { + while ((sector = bdrv_dirty_iter_next(dbi)) != -1) { cluster = sector / sectors_per_cluster; /* Fake progress updates for any clusters we skipped */ @@ -391,7 +391,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) for (end = cluster + clusters_per_iter; cluster < end; cluster++) { do { if (yield_and_check(job)) { - return ret; + goto out; } ret = backup_do_cow(job, cluster * sectors_per_cluster, sectors_per_cluster, &error_is_read, @@ -399,7 +399,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) if ((ret < 0) && backup_error_action(job, error_is_read, -ret) == BLOCK_ERROR_ACTION_REPORT) { - return ret; + goto out; } } while (ret < 0); } @@ -407,7 +407,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) /* If the bitmap granularity is smaller than the backup granularity, * we need to advance the iterator pointer to the next cluster. */ if (granularity < job->cluster_size) { - bdrv_set_dirty_iter(&hbi, cluster * sectors_per_cluster); + bdrv_set_dirty_iter(dbi, cluster * sectors_per_cluster); } last_cluster = cluster - 1; @@ -419,6 +419,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) job->common.offset += ((end - last_cluster - 1) * job->cluster_size); } +out: + bdrv_dirty_iter_free(dbi); return ret; } diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index f2bfdcfdea..519737c8d3 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -38,13 +38,20 @@ */ struct BdrvDirtyBitmap { HBitmap *bitmap; /* Dirty sector bitmap implementation */ + HBitmap *meta; /* Meta dirty bitmap */ BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ char *name; /* Optional non-empty unique ID */ int64_t size; /* Size of the bitmap (Number of sectors) */ bool disabled; /* Bitmap is read-only */ + int active_iterators; /* How many iterators are active */ QLIST_ENTRY(BdrvDirtyBitmap) list; }; +struct BdrvDirtyBitmapIter { + HBitmapIter hbi; + BdrvDirtyBitmap *bitmap; +}; + BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) { BdrvDirtyBitmap *bm; @@ -97,6 +104,66 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, return bitmap; } +/* bdrv_create_meta_dirty_bitmap + * + * Create a meta dirty bitmap that tracks the changes of bits in @bitmap. I.e. + * when a dirty status bit in @bitmap is changed (either from reset to set or + * the other way around), its respective meta dirty bitmap bit will be marked + * dirty as well. + * + * @bitmap: the block dirty bitmap for which to create a meta dirty bitmap. + * @chunk_size: how many bytes of bitmap data does each bit in the meta bitmap + * track. + */ +void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int chunk_size) +{ + assert(!bitmap->meta); + bitmap->meta = hbitmap_create_meta(bitmap->bitmap, + chunk_size * BITS_PER_BYTE); +} + +void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap) +{ + assert(bitmap->meta); + hbitmap_free_meta(bitmap->bitmap); + bitmap->meta = NULL; +} + +int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, int64_t sector, + int nb_sectors) +{ + uint64_t i; + int sectors_per_bit = 1 << hbitmap_granularity(bitmap->meta); + + /* To optimize: we can make hbitmap to internally check the range in a + * coarse level, or at least do it word by word. */ + for (i = sector; i < sector + nb_sectors; i += sectors_per_bit) { + if (hbitmap_get(bitmap->meta, i)) { + return true; + } + } + return false; +} + +void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, int64_t sector, + int nb_sectors) +{ + hbitmap_reset(bitmap->meta, sector, nb_sectors); +} + +int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap) +{ + return bitmap->size; +} + +const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap) +{ + return bitmap->name; +} + bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) { return bitmap->successor; @@ -212,6 +279,7 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs) QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { assert(!bdrv_dirty_bitmap_frozen(bitmap)); + assert(!bitmap->active_iterators); hbitmap_truncate(bitmap->bitmap, size); bitmap->size = size; } @@ -224,7 +292,9 @@ static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bm, *next; QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) { + assert(!bm->active_iterators); assert(!bdrv_dirty_bitmap_frozen(bm)); + assert(!bm->meta); QLIST_REMOVE(bm, list); hbitmap_free(bm->bitmap); g_free(bm->name); @@ -235,6 +305,9 @@ static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs, } } } + if (bitmap) { + abort(); + } } void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) @@ -320,9 +393,43 @@ uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap) return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap); } -void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi) +uint32_t bdrv_dirty_bitmap_meta_granularity(BdrvDirtyBitmap *bitmap) +{ + return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->meta); +} + +BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap, + uint64_t first_sector) { - hbitmap_iter_init(hbi, bitmap->bitmap, 0); + BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1); + hbitmap_iter_init(&iter->hbi, bitmap->bitmap, first_sector); + iter->bitmap = bitmap; + bitmap->active_iterators++; + return iter; +} + +BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap) +{ + BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1); + hbitmap_iter_init(&iter->hbi, bitmap->meta, 0); + iter->bitmap = bitmap; + bitmap->active_iterators++; + return iter; +} + +void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) +{ + if (!iter) { + return; + } + assert(iter->bitmap->active_iterators > 0); + iter->bitmap->active_iterators--; + g_free(iter); +} + +int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) +{ + return hbitmap_iter_next(&iter->hbi); } void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, @@ -360,6 +467,43 @@ void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) hbitmap_free(tmp); } +uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, + uint64_t start, uint64_t count) +{ + return hbitmap_serialization_size(bitmap->bitmap, start, count); +} + +uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap) +{ + return hbitmap_serialization_granularity(bitmap->bitmap); +} + +void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, + uint8_t *buf, uint64_t start, + uint64_t count) +{ + hbitmap_serialize_part(bitmap->bitmap, buf, start, count); +} + +void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, + uint8_t *buf, uint64_t start, + uint64_t count, bool finish) +{ + hbitmap_deserialize_part(bitmap->bitmap, buf, start, count, finish); +} + +void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, + uint64_t start, uint64_t count, + bool finish) +{ + hbitmap_deserialize_zeroes(bitmap->bitmap, start, count, finish); +} + +void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap) +{ + hbitmap_deserialize_finish(bitmap->bitmap); +} + void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int64_t nr_sectors) { @@ -373,15 +517,19 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, } /** - * Advance an HBitmapIter to an arbitrary offset. + * Advance a BdrvDirtyBitmapIter to an arbitrary offset. */ -void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset) +void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *iter, int64_t sector_num) { - assert(hbi->hb); - hbitmap_iter_init(hbi, hbi->hb, offset); + hbitmap_iter_init(&iter->hbi, iter->hbi.hb, sector_num); } int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap) { return hbitmap_count(bitmap->bitmap); } + +int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap) +{ + return hbitmap_count(bitmap->meta); +} diff --git a/block/mirror.c b/block/mirror.c index f9d1fecaa0..a433e6848c 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -55,7 +55,7 @@ typedef struct MirrorBlockJob { int64_t bdev_length; unsigned long *cow_bitmap; BdrvDirtyBitmap *dirty_bitmap; - HBitmapIter hbi; + BdrvDirtyBitmapIter *dbi; uint8_t *buf; QSIMPLEQ_HEAD(, MirrorBuffer) buf_free; int buf_free_count; @@ -330,10 +330,10 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) int max_io_sectors = MAX((s->buf_size >> BDRV_SECTOR_BITS) / MAX_IN_FLIGHT, MAX_IO_SECTORS); - sector_num = hbitmap_iter_next(&s->hbi); + sector_num = bdrv_dirty_iter_next(s->dbi); if (sector_num < 0) { - bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); - sector_num = hbitmap_iter_next(&s->hbi); + bdrv_set_dirty_iter(s->dbi, 0); + sector_num = bdrv_dirty_iter_next(s->dbi); trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); assert(sector_num >= 0); } @@ -349,7 +349,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) /* Find the number of consective dirty chunks following the first dirty * one, and wait for in flight requests in them. */ while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) { - int64_t hbitmap_next; + int64_t next_dirty; int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk; int64_t next_chunk = next_sector / sectors_per_chunk; if (next_sector >= end || @@ -360,13 +360,13 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) break; } - hbitmap_next = hbitmap_iter_next(&s->hbi); - if (hbitmap_next > next_sector || hbitmap_next < 0) { + next_dirty = bdrv_dirty_iter_next(s->dbi); + if (next_dirty > next_sector || next_dirty < 0) { /* The bitmap iterator's cache is stale, refresh it */ - bdrv_set_dirty_iter(&s->hbi, next_sector); - hbitmap_next = hbitmap_iter_next(&s->hbi); + bdrv_set_dirty_iter(s->dbi, next_sector); + next_dirty = bdrv_dirty_iter_next(s->dbi); } - assert(hbitmap_next == next_sector); + assert(next_dirty == next_sector); nb_chunks++; } @@ -679,7 +679,8 @@ static void coroutine_fn mirror_run(void *opaque) } } - bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); + assert(!s->dbi); + s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap, 0); for (;;) { uint64_t delay_ns = 0; int64_t cnt, delta; @@ -793,6 +794,7 @@ immediate_exit: qemu_vfree(s->buf); g_free(s->cow_bitmap); g_free(s->in_flight_bitmap); + bdrv_dirty_iter_free(s->dbi); bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); data = g_malloc(sizeof(*data)); diff --git a/block/qapi.c b/block/qapi.c index 6f947e3e66..50d30907a2 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -698,6 +698,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, assert(qobject_type(obj) == QTYPE_QDICT); data = qdict_get(qobject_to_qdict(obj), "data"); dump_qobject(func_fprintf, f, 1, data); + qobject_decref(obj); visit_free(v); } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 61d1ffd223..928c1e298d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1558,7 +1558,7 @@ fail: * clusters. */ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, - uint64_t nb_clusters) + uint64_t nb_clusters, int flags) { BDRVQcow2State *s = bs->opaque; uint64_t *l2_table; @@ -1582,7 +1582,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, /* Update L2 entries */ qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); - if (old_offset & QCOW_OFLAG_COMPRESSED) { + if (old_offset & QCOW_OFLAG_COMPRESSED || flags & BDRV_REQ_MAY_UNMAP) { l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); } else { @@ -1595,7 +1595,8 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, return nb_clusters; } -int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors, + int flags) { BDRVQcow2State *s = bs->opaque; uint64_t nb_clusters; @@ -1612,7 +1613,7 @@ int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) s->cache_discards = true; while (nb_clusters > 0) { - ret = zero_single_l2(bs, offset, nb_clusters); + ret = zero_single_l2(bs, offset, nb_clusters, flags); if (ret < 0) { goto fail; } diff --git a/block/qcow2.c b/block/qcow2.c index e11c7c9d16..6d5689a23c 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1155,6 +1155,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* Initialise locks */ qemu_co_mutex_init(&s->lock); + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP; /* Repair image if dirty */ if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only && @@ -2477,7 +2478,7 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, count); /* Whatever is left can use real zero clusters */ - ret = qcow2_zero_clusters(bs, offset, count >> BDRV_SECTOR_BITS); + ret = qcow2_zero_clusters(bs, offset, count >> BDRV_SECTOR_BITS, flags); qemu_co_mutex_unlock(&s->lock); return ret; diff --git a/block/qcow2.h b/block/qcow2.h index 9ce5a37d3a..92203a8b8c 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -547,7 +547,8 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors, enum qcow2_discard_type type, bool full_discard); -int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors, + int flags); int qcow2_expand_zero_clusters(BlockDriverState *bs, BlockDriverAmendStatusCB *status_cb, diff --git a/block/quorum.c b/block/quorum.c index 9cf876fb34..d122299352 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -130,7 +130,7 @@ struct QuorumAIOCB { bool is_read; int vote_ret; - int child_iter; /* which child to read in fifo pattern */ + int children_read; /* how many children have been read from */ }; static bool quorum_vote(QuorumAIOCB *acb); @@ -156,22 +156,7 @@ static AIOCBInfo quorum_aiocb_info = { static void quorum_aio_finalize(QuorumAIOCB *acb) { - int i, ret = 0; - - if (acb->vote_ret) { - ret = acb->vote_ret; - } - - acb->common.cb(acb->common.opaque, ret); - - if (acb->is_read) { - /* on the quorum case acb->child_iter == s->num_children - 1 */ - for (i = 0; i <= acb->child_iter; i++) { - qemu_vfree(acb->qcrs[i].buf); - qemu_iovec_destroy(&acb->qcrs[i].qiov); - } - } - + acb->common.cb(acb->common.opaque, acb->vote_ret); g_free(acb->qcrs); qemu_aio_unref(acb); } @@ -283,39 +268,52 @@ static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source) } } -static void quorum_aio_cb(void *opaque, int ret) +static void quorum_report_bad_acb(QuorumChildRequest *sacb, int ret) +{ + QuorumAIOCB *acb = sacb->parent; + QuorumOpType type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE; + quorum_report_bad(type, acb->sector_num, acb->nb_sectors, + sacb->aiocb->bs->node_name, ret); +} + +static void quorum_fifo_aio_cb(void *opaque, int ret) { QuorumChildRequest *sacb = opaque; QuorumAIOCB *acb = sacb->parent; BDRVQuorumState *s = acb->common.bs->opaque; - bool rewrite = false; - if (ret == 0) { - acb->success_count++; - } else { - QuorumOpType type; - type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE; - quorum_report_bad(type, acb->sector_num, acb->nb_sectors, - sacb->aiocb->bs->node_name, ret); - } + assert(acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO); + + if (ret < 0) { + quorum_report_bad_acb(sacb, ret); - if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) { /* We try to read next child in FIFO order if we fail to read */ - if (ret < 0 && (acb->child_iter + 1) < s->num_children) { - acb->child_iter++; + if (acb->children_read < s->num_children) { read_fifo_child(acb); return; } - - if (ret == 0) { - quorum_copy_qiov(acb->qiov, &acb->qcrs[acb->child_iter].qiov); - } - acb->vote_ret = ret; - quorum_aio_finalize(acb); - return; } + acb->vote_ret = ret; + + /* FIXME: rewrite failed children if acb->children_read > 1? */ + quorum_aio_finalize(acb); +} + +static void quorum_aio_cb(void *opaque, int ret) +{ + QuorumChildRequest *sacb = opaque; + QuorumAIOCB *acb = sacb->parent; + BDRVQuorumState *s = acb->common.bs->opaque; + bool rewrite = false; + int i; + sacb->ret = ret; + if (ret == 0) { + acb->success_count++; + } else { + quorum_report_bad_acb(sacb, ret); + } acb->count++; assert(acb->count <= s->num_children); assert(acb->success_count <= s->num_children); @@ -326,6 +324,10 @@ static void quorum_aio_cb(void *opaque, int ret) /* Do the vote on read */ if (acb->is_read) { rewrite = quorum_vote(acb); + for (i = 0; i < s->num_children; i++) { + qemu_vfree(acb->qcrs[i].buf); + qemu_iovec_destroy(&acb->qcrs[i].qiov); + } } else { quorum_has_too_much_io_failed(acb); } @@ -653,6 +655,7 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb) BDRVQuorumState *s = acb->common.bs->opaque; int i; + acb->children_read = s->num_children; for (i = 0; i < s->num_children; i++) { acb->qcrs[i].buf = qemu_blockalign(s->children[i]->bs, acb->qiov->size); qemu_iovec_init(&acb->qcrs[i].qiov, acb->qiov->niov); @@ -671,16 +674,11 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb) static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->common.bs->opaque; + int n = acb->children_read++; - acb->qcrs[acb->child_iter].buf = - qemu_blockalign(s->children[acb->child_iter]->bs, acb->qiov->size); - qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov); - qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov, - acb->qcrs[acb->child_iter].buf); - acb->qcrs[acb->child_iter].aiocb = - bdrv_aio_readv(s->children[acb->child_iter], acb->sector_num, - &acb->qcrs[acb->child_iter].qiov, acb->nb_sectors, - quorum_aio_cb, &acb->qcrs[acb->child_iter]); + acb->qcrs[n].aiocb = bdrv_aio_readv(s->children[n], acb->sector_num, + acb->qiov, acb->nb_sectors, + quorum_fifo_aio_cb, &acb->qcrs[n]); return &acb->common; } @@ -696,13 +694,12 @@ static BlockAIOCB *quorum_aio_readv(BlockDriverState *bs, QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors, cb, opaque); acb->is_read = true; + acb->children_read = 0; if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { - acb->child_iter = s->num_children - 1; return read_quorum_children(acb); } - acb->child_iter = 0; return read_fifo_child(acb); } diff --git a/block/raw-posix.c b/block/raw-posix.c index 166e9d1ad5..f481e57f78 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -443,6 +443,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, fd = qemu_open(filename, s->open_flags, 0644); if (fd < 0) { ret = -errno; + error_setg_errno(errp, errno, "Could not open '%s'", filename); if (ret == -EROFS) { ret = -EACCES; } diff --git a/block/raw-win32.c b/block/raw-win32.c index 734bb105bd..800fabdd72 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -373,6 +373,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, if (s->hfile == INVALID_HANDLE_VALUE) { int err = GetLastError(); + error_setg_win32(errp, err, "Could not open '%s'", filename); if (err == ERROR_ACCESS_DENIED) { ret = -EACCES; } else { diff --git a/block/replication.c b/block/replication.c index 3bd1cf1809..8bbfc8f870 100644 --- a/block/replication.c +++ b/block/replication.c @@ -101,6 +101,11 @@ static int replication_open(BlockDriverState *bs, QDict *options, if (!strcmp(mode, "primary")) { s->mode = REPLICATION_MODE_PRIMARY; + top_id = qemu_opt_get(opts, REPLICATION_TOP_ID); + if (top_id) { + error_setg(&local_err, "The primary side does not support option top-id"); + goto fail; + } } else if (!strcmp(mode, "secondary")) { s->mode = REPLICATION_MODE_SECONDARY; top_id = qemu_opt_get(opts, REPLICATION_TOP_ID); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 59545e287e..17b2efb7c7 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -168,6 +168,22 @@ static BlockBackend *throttle_group_next_blk(BlockBackend *blk) return blk_by_public(next); } +/* + * Return whether a BlockBackend has pending requests. + * + * This assumes that tg->lock is held. + * + * @blk: the BlockBackend + * @is_write: the type of operation (read/write) + * @ret: whether the BlockBackend has pending requests. + */ +static inline bool blk_has_pending_reqs(BlockBackend *blk, + bool is_write) +{ + const BlockBackendPublic *blkp = blk_get_public(blk); + return blkp->pending_reqs[is_write]; +} + /* Return the next BlockBackend in the round-robin sequence with pending I/O * requests. * @@ -188,7 +204,7 @@ static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write) /* get next bs round in round robin style */ token = throttle_group_next_blk(token); - while (token != start && !blkp->pending_reqs[is_write]) { + while (token != start && !blk_has_pending_reqs(token, is_write)) { token = throttle_group_next_blk(token); } @@ -196,10 +212,13 @@ static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write) * then decide the token is the current bs because chances are * the current bs get the current request queued. */ - if (token == start && !blkp->pending_reqs[is_write]) { + if (token == start && !blk_has_pending_reqs(token, is_write)) { token = blk; } + /* Either we return the original BB, or one with pending requests */ + assert(token == blk || blk_has_pending_reqs(token, is_write)); + return token; } @@ -257,7 +276,7 @@ static void schedule_next_request(BlockBackend *blk, bool is_write) /* Check if there's any pending request to schedule next */ token = next_throttle_token(blk, is_write); - if (!blkp->pending_reqs[is_write]) { + if (!blk_has_pending_reqs(token, is_write)) { return; } @@ -271,7 +290,7 @@ static void schedule_next_request(BlockBackend *blk, bool is_write) qemu_co_queue_next(&blkp->throttled_reqs[is_write])) { token = blk; } else { - ThrottleTimers *tt = &blkp->throttle_timers; + ThrottleTimers *tt = &blk_get_public(token)->throttle_timers; int64_t now = qemu_clock_get_ns(tt->clock_type); timer_mod(tt->timers[is_write], now + 1); tg->any_timer_armed[is_write] = true; diff --git a/docs/qmp-commands.txt b/docs/qmp-commands.txt index 3220fb1075..284576d795 100644 --- a/docs/qmp-commands.txt +++ b/docs/qmp-commands.txt @@ -1090,11 +1090,11 @@ Arguments: Example: -> { "execute": "blockdev-add", - "arguments": { "options": { "driver": "qcow2", - "node-name": "node1534", - "file": { "driver": "file", - "filename": "hd1.qcow2" }, - "backing": "" } } } + "arguments": { "driver": "qcow2", + "node-name": "node1534", + "file": { "driver": "file", + "filename": "hd1.qcow2" }, + "backing": "" } } <- { "return": {} } @@ -3130,41 +3130,37 @@ This command is still a work in progress. It doesn't support all block drivers among other things. Stay away from it unless you want to help with its development. -Arguments: - -- "options": block driver options +For the arguments, see the QAPI schema documentation of BlockdevOptions. Example (1): -> { "execute": "blockdev-add", - "arguments": { "options" : { "driver": "qcow2", - "file": { "driver": "file", - "filename": "test.qcow2" } } } } + "arguments": { "driver": "qcow2", + "file": { "driver": "file", + "filename": "test.qcow2" } } } <- { "return": {} } Example (2): -> { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "qcow2", - "node-name": "my_disk", - "discard": "unmap", - "cache": { - "direct": true, - "writeback": true - }, - "file": { - "driver": "file", - "filename": "/tmp/test.qcow2" - }, - "backing": { - "driver": "raw", - "file": { - "driver": "file", - "filename": "/dev/fdset/4" - } - } + "driver": "qcow2", + "node-name": "my_disk", + "discard": "unmap", + "cache": { + "direct": true, + "writeback": true + }, + "file": { + "driver": "file", + "filename": "/tmp/test.qcow2" + }, + "backing": { + "driver": "raw", + "file": { + "driver": "file", + "filename": "/dev/fdset/4" + } } } } @@ -3191,13 +3187,11 @@ Example: -> { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "qcow2", - "node-name": "node0", - "file": { - "driver": "file", - "filename": "test.qcow2" - } + "driver": "qcow2", + "node-name": "node0", + "file": { + "driver": "file", + "filename": "test.qcow2" } } } @@ -3342,10 +3336,10 @@ Arguments: Example: -> { "execute": "blockdev-add", - "arguments": { "options": { "node-name": "node0", - "driver": "raw", - "file": { "driver": "file", - "filename": "fedora.iso" } } } } + "arguments": { { "node-name": "node0", + "driver": "raw", + "file": { "driver": "file", + "filename": "fedora.iso" } } } <- { "return": {} } @@ -3383,10 +3377,10 @@ Example: Add a new node to a quorum -> { "execute": "blockdev-add", - "arguments": { "options": { "driver": "raw", - "node-name": "new_node", - "file": { "driver": "file", - "filename": "test.raw" } } } } + "arguments": { "driver": "raw", + "node-name": "new_node", + "file": { "driver": "file", + "filename": "test.raw" } } } <- { "return": {} } -> { "execute": "x-blockdev-change", "arguments": { "parent": "disk1", @@ -93,6 +93,11 @@ static MemoryRegion io_mem_unassigned; #endif +#ifdef TARGET_PAGE_BITS_VARY +int target_page_bits; +bool target_page_bits_decided; +#endif + struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); /* current CPU in the current thread. It is only valid inside cpu_exec() */ @@ -102,8 +107,37 @@ __thread CPUState *current_cpu; 2 = Adaptive rate instruction counting. */ int use_icount; +bool set_preferred_target_page_bits(int bits) +{ + /* The target page size is the lowest common denominator for all + * the CPUs in the system, so we can only make it smaller, never + * larger. And we can't make it smaller once we've committed to + * a particular size. + */ +#ifdef TARGET_PAGE_BITS_VARY + assert(bits >= TARGET_PAGE_BITS_MIN); + if (target_page_bits == 0 || target_page_bits > bits) { + if (target_page_bits_decided) { + return false; + } + target_page_bits = bits; + } +#endif + return true; +} + #if !defined(CONFIG_USER_ONLY) +static void finalize_target_page_bits(void) +{ +#ifdef TARGET_PAGE_BITS_VARY + if (target_page_bits == 0) { + target_page_bits = TARGET_PAGE_BITS_MIN; + } + target_page_bits_decided = true; +#endif +} + typedef struct PhysPageEntry PhysPageEntry; struct PhysPageEntry { @@ -153,7 +187,7 @@ typedef struct subpage_t { MemoryRegion iomem; AddressSpace *as; hwaddr base; - uint16_t sub_section[TARGET_PAGE_SIZE]; + uint16_t sub_section[]; } subpage_t; #define PHYS_SECTION_UNASSIGNED 0 @@ -2217,8 +2251,7 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base) { subpage_t *mmio; - mmio = g_malloc0(sizeof(subpage_t)); - + mmio = g_malloc0(sizeof(subpage_t) + TARGET_PAGE_SIZE * sizeof(uint16_t)); mmio->as = as; mmio->base = base; memory_region_init_io(&mmio->iomem, NULL, &subpage_ops, mmio, @@ -2810,6 +2843,14 @@ void cpu_register_map_client(QEMUBH *bh) void cpu_exec_init_all(void) { qemu_mutex_init(&ram_list.mutex); + /* The data structures we set up here depend on knowing the page size, + * so no more changes can be made after this point. + * In an ideal world, nothing we did before we had finished the + * machine setup would care about the target page size, and we could + * do this much later, rather than requiring board models to state + * up front what their requirements are. + */ + finalize_target_page_bits(); io_mem_init(); memory_map_init(); qemu_mutex_init(&map_client_list_lock); diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 7527037c23..cbbca4e17a 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -384,18 +384,24 @@ static NetClientInfo net_mv88w8618_info = { .cleanup = eth_cleanup, }; -static int mv88w8618_eth_init(SysBusDevice *sbd) +static void mv88w8618_eth_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); mv88w8618_eth_state *s = MV88W8618_ETH(dev); sysbus_init_irq(sbd, &s->irq); - s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_eth_ops, s, + memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s, "mv88w8618-eth", MP_ETH_SIZE); sysbus_init_mmio(sbd, &s->iomem); - return 0; +} + +static void mv88w8618_eth_realize(DeviceState *dev, Error **errp) +{ + mv88w8618_eth_state *s = MV88W8618_ETH(dev); + + s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, + object_get_typename(OBJECT(dev)), dev->id, s); } static const VMStateDescription mv88w8618_eth_vmsd = { @@ -423,17 +429,17 @@ static Property mv88w8618_eth_properties[] = { static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mv88w8618_eth_init; dc->vmsd = &mv88w8618_eth_vmsd; dc->props = mv88w8618_eth_properties; + dc->realize = mv88w8618_eth_realize; } static const TypeInfo mv88w8618_eth_info = { .name = TYPE_MV88W8618_ETH, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_eth_state), + .instance_init = mv88w8618_eth_init, .class_init = mv88w8618_eth_class_init, }; @@ -615,23 +621,26 @@ static const GraphicHwOps musicpal_gfx_ops = { .gfx_update = lcd_refresh, }; -static int musicpal_lcd_init(SysBusDevice *sbd) +static void musicpal_lcd_realize(DeviceState *dev, Error **errp) +{ + musicpal_lcd_state *s = MUSICPAL_LCD(dev); + s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); + qemu_console_resize(s->con, 128 * 3, 64 * 3); +} + +static void musicpal_lcd_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); musicpal_lcd_state *s = MUSICPAL_LCD(dev); s->brightness = 7; - memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_lcd_ops, s, + memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s, "musicpal-lcd", MP_LCD_SIZE); sysbus_init_mmio(sbd, &s->iomem); - s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); - qemu_console_resize(s->con, 128*3, 64*3); - qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); - - return 0; } static const VMStateDescription musicpal_lcd_vmsd = { @@ -652,16 +661,16 @@ static const VMStateDescription musicpal_lcd_vmsd = { static void musicpal_lcd_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = musicpal_lcd_init; dc->vmsd = &musicpal_lcd_vmsd; + dc->realize = musicpal_lcd_realize; } static const TypeInfo musicpal_lcd_info = { .name = TYPE_MUSICPAL_LCD, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(musicpal_lcd_state), + .instance_init = musicpal_lcd_init, .class_init = musicpal_lcd_class_init, }; @@ -748,16 +757,16 @@ static const MemoryRegionOps mv88w8618_pic_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int mv88w8618_pic_init(SysBusDevice *dev) +static void mv88w8618_pic_init(Object *obj) { + SysBusDevice *dev = SYS_BUS_DEVICE(obj); mv88w8618_pic_state *s = MV88W8618_PIC(dev); qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32); sysbus_init_irq(dev, &s->parent_irq); - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pic_ops, s, + memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s, "musicpal-pic", MP_PIC_SIZE); sysbus_init_mmio(dev, &s->iomem); - return 0; } static const VMStateDescription mv88w8618_pic_vmsd = { @@ -774,9 +783,7 @@ static const VMStateDescription mv88w8618_pic_vmsd = { static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mv88w8618_pic_init; dc->reset = mv88w8618_pic_reset; dc->vmsd = &mv88w8618_pic_vmsd; } @@ -785,6 +792,7 @@ static const TypeInfo mv88w8618_pic_info = { .name = TYPE_MV88W8618_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_pic_state), + .instance_init = mv88w8618_pic_init, .class_init = mv88w8618_pic_class_init, }; @@ -913,8 +921,9 @@ static const MemoryRegionOps mv88w8618_pit_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int mv88w8618_pit_init(SysBusDevice *dev) +static void mv88w8618_pit_init(Object *obj) { + SysBusDevice *dev = SYS_BUS_DEVICE(obj); mv88w8618_pit_state *s = MV88W8618_PIT(dev); int i; @@ -924,10 +933,9 @@ static int mv88w8618_pit_init(SysBusDevice *dev) mv88w8618_timer_init(dev, &s->timer[i], 1000000); } - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pit_ops, s, + memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s, "musicpal-pit", MP_PIT_SIZE); sysbus_init_mmio(dev, &s->iomem); - return 0; } static const VMStateDescription mv88w8618_timer_vmsd = { @@ -955,9 +963,7 @@ static const VMStateDescription mv88w8618_pit_vmsd = { static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mv88w8618_pit_init; dc->reset = mv88w8618_pit_reset; dc->vmsd = &mv88w8618_pit_vmsd; } @@ -966,6 +972,7 @@ static const TypeInfo mv88w8618_pit_info = { .name = TYPE_MV88W8618_PIT, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_pit_state), + .instance_init = mv88w8618_pit_init, .class_init = mv88w8618_pit_class_init, }; @@ -1018,15 +1025,15 @@ static const MemoryRegionOps mv88w8618_flashcfg_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int mv88w8618_flashcfg_init(SysBusDevice *dev) +static void mv88w8618_flashcfg_init(Object *obj) { + SysBusDevice *dev = SYS_BUS_DEVICE(obj); mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev); s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_flashcfg_ops, s, + memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s, "musicpal-flashcfg", MP_FLASHCFG_SIZE); sysbus_init_mmio(dev, &s->iomem); - return 0; } static const VMStateDescription mv88w8618_flashcfg_vmsd = { @@ -1042,9 +1049,7 @@ static const VMStateDescription mv88w8618_flashcfg_vmsd = { static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mv88w8618_flashcfg_init; dc->vmsd = &mv88w8618_flashcfg_vmsd; } @@ -1052,6 +1057,7 @@ static const TypeInfo mv88w8618_flashcfg_info = { .name = TYPE_MV88W8618_FLASHCFG, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_flashcfg_state), + .instance_init = mv88w8618_flashcfg_init, .class_init = mv88w8618_flashcfg_class_init, }; @@ -1350,22 +1356,21 @@ static void musicpal_gpio_reset(DeviceState *d) s->isr = 0; } -static int musicpal_gpio_init(SysBusDevice *sbd) +static void musicpal_gpio_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); musicpal_gpio_state *s = MUSICPAL_GPIO(dev); sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_gpio_ops, s, + memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s, "musicpal-gpio", MP_GPIO_SIZE); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32); - - return 0; } static const VMStateDescription musicpal_gpio_vmsd = { @@ -1386,9 +1391,7 @@ static const VMStateDescription musicpal_gpio_vmsd = { static void musicpal_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = musicpal_gpio_init; dc->reset = musicpal_gpio_reset; dc->vmsd = &musicpal_gpio_vmsd; } @@ -1397,6 +1400,7 @@ static const TypeInfo musicpal_gpio_info = { .name = TYPE_MUSICPAL_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(musicpal_gpio_state), + .instance_init = musicpal_gpio_init, .class_init = musicpal_gpio_class_init, }; @@ -1516,12 +1520,13 @@ static void musicpal_key_event(void *opaque, int keycode) s->kbd_extended = 0; } -static int musicpal_key_init(SysBusDevice *sbd) +static void musicpal_key_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); musicpal_key_state *s = MUSICPAL_KEY(dev); - memory_region_init(&s->iomem, OBJECT(s), "dummy", 0); + memory_region_init(&s->iomem, obj, "dummy", 0); sysbus_init_mmio(sbd, &s->iomem); s->kbd_extended = 0; @@ -1530,8 +1535,6 @@ static int musicpal_key_init(SysBusDevice *sbd) qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); qemu_add_kbd_event_handler(musicpal_key_event, s); - - return 0; } static const VMStateDescription musicpal_key_vmsd = { @@ -1548,9 +1551,7 @@ static const VMStateDescription musicpal_key_vmsd = { static void musicpal_key_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = musicpal_key_init; dc->vmsd = &musicpal_key_vmsd; } @@ -1558,6 +1559,7 @@ static const TypeInfo musicpal_key_info = { .name = TYPE_MUSICPAL_KEY, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(musicpal_key_state), + .instance_init = musicpal_key_init, .class_init = musicpal_key_class_init, }; diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c index 576a8eb91f..521dbad039 100644 --- a/hw/arm/pxa2xx_gpio.c +++ b/hw/arm/pxa2xx_gpio.c @@ -280,23 +280,28 @@ DeviceState *pxa2xx_gpio_init(hwaddr base, return dev; } -static int pxa2xx_gpio_initfn(SysBusDevice *sbd) +static void pxa2xx_gpio_initfn(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); - s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu)); - - qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines); - qdev_init_gpio_out(dev, s->handler, s->lines); - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa_gpio_ops, s, "pxa2xx-gpio", 0x1000); + memory_region_init_io(&s->iomem, obj, &pxa_gpio_ops, + s, "pxa2xx-gpio", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq0); sysbus_init_irq(sbd, &s->irq1); sysbus_init_irq(sbd, &s->irqX); +} - return 0; +static void pxa2xx_gpio_realize(DeviceState *dev, Error **errp) +{ + PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); + + s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu)); + + qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines); + qdev_init_gpio_out(dev, s->handler, s->lines); } /* @@ -336,18 +341,18 @@ static Property pxa2xx_gpio_properties[] = { static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pxa2xx_gpio_initfn; dc->desc = "PXA2xx GPIO controller"; dc->props = pxa2xx_gpio_properties; dc->vmsd = &vmstate_pxa2xx_gpio_regs; + dc->realize = pxa2xx_gpio_realize; } static const TypeInfo pxa2xx_gpio_info = { .name = TYPE_PXA2XX_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxGPIOInfo), + .instance_init = pxa2xx_gpio_initfn, .class_init = pxa2xx_gpio_class_init, }; diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 85db1e2813..3311cc38a4 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -1236,6 +1236,11 @@ static void strongarm_uart_init(Object *obj) s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_rx_to, s); s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s); +} + +static void strongarm_uart_realize(DeviceState *dev, Error **errp) +{ + StrongARMUARTState *s = STRONGARM_UART(dev); qemu_chr_fe_set_handlers(&s->chr, strongarm_uart_can_receive, @@ -1316,6 +1321,7 @@ static void strongarm_uart_class_init(ObjectClass *klass, void *data) dc->reset = strongarm_uart_reset; dc->vmsd = &vmstate_strongarm_uart_regs; dc->props = strongarm_uart_properties; + dc->realize = strongarm_uart_realize; } static const TypeInfo strongarm_uart_info = { @@ -1516,19 +1522,19 @@ static int strongarm_ssp_post_load(void *opaque, int version_id) return 0; } -static int strongarm_ssp_init(SysBusDevice *sbd) +static void strongarm_ssp_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); StrongARMSSPState *s = STRONGARM_SSP(dev); sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_ssp_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_ssp_ops, s, "ssp", 0x1000); sysbus_init_mmio(sbd, &s->iomem); s->bus = ssi_create_bus(dev, "ssi"); - return 0; } static void strongarm_ssp_reset(DeviceState *dev) @@ -1558,9 +1564,7 @@ static const VMStateDescription vmstate_strongarm_ssp_regs = { static void strongarm_ssp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_ssp_init; dc->desc = "StrongARM SSP controller"; dc->reset = strongarm_ssp_reset; dc->vmsd = &vmstate_strongarm_ssp_regs; @@ -1570,6 +1574,7 @@ static const TypeInfo strongarm_ssp_info = { .name = TYPE_STRONGARM_SSP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMSSPState), + .instance_init = strongarm_ssp_init, .class_init = strongarm_ssp_class_init, }; diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index fa0655a775..5fc10df08e 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -384,6 +384,61 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset) } static void +build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) +{ + int iort_start = table_data->len; + AcpiIortIdMapping *idmap; + AcpiIortItsGroup *its; + AcpiIortTable *iort; + size_t node_size, iort_length; + AcpiIortRC *rc; + + iort = acpi_data_push(table_data, sizeof(*iort)); + + iort_length = sizeof(*iort); + iort->node_count = cpu_to_le32(2); /* RC and ITS nodes */ + iort->node_offset = cpu_to_le32(sizeof(*iort)); + + /* ITS group node */ + node_size = sizeof(*its) + sizeof(uint32_t); + iort_length += node_size; + its = acpi_data_push(table_data, node_size); + + its->type = ACPI_IORT_NODE_ITS_GROUP; + its->length = cpu_to_le16(node_size); + its->its_count = cpu_to_le32(1); + its->identifiers[0] = 0; /* MADT translation_id */ + + /* Root Complex Node */ + node_size = sizeof(*rc) + sizeof(*idmap); + iort_length += node_size; + rc = acpi_data_push(table_data, node_size); + + rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX; + rc->length = cpu_to_le16(node_size); + rc->mapping_count = cpu_to_le32(1); + rc->mapping_offset = cpu_to_le32(sizeof(*rc)); + + /* fully coherent device */ + rc->memory_properties.cache_coherency = cpu_to_le32(1); + rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */ + rc->pci_segment_number = 0; /* MCFG pci_segment */ + + /* Identity RID mapping covering the whole input RID range */ + idmap = &rc->id_mapping_array[0]; + idmap->input_base = 0; + idmap->id_count = cpu_to_le32(0xFFFF); + idmap->output_base = 0; + /* output IORT node is the ITS group node (the first node) */ + idmap->output_reference = cpu_to_le32(iort->node_offset); + + iort->length = cpu_to_le32(iort_length); + + build_header(linker, table_data, (void *)(table_data->data + iort_start), + "IORT", table_data->len - iort_start, 0, NULL, NULL); +} + +static void build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) { AcpiSerialPortConsoleRedirection *spcr; @@ -667,17 +722,6 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) ACPI_BUILD_TABLE_FILE, tables_blob, 64, false /* high memory */); - /* - * The ACPI v5.1 tables for Hardware-reduced ACPI platform are: - * RSDP - * RSDT - * FADT - * GTDT - * MADT - * MCFG - * DSDT - */ - /* DSDT is pointed to by FADT */ dsdt = tables_blob->len; build_dsdt(tables_blob, tables->linker, guest_info); @@ -703,6 +747,11 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) build_srat(tables_blob, tables->linker, guest_info); } + if (its_class_name() && !guest_info->no_its) { + acpi_add_table(table_offsets, tables_blob); + build_iort(tables_blob, tables->linker, guest_info); + } + /* RSDT is pointed to by RSDP */ rsdt = tables_blob->len; build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c3a1e92e51..070bbf89d4 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1499,6 +1499,8 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->pci_allow_0_address = true; + /* We know we will never create a pre-ARMv7 CPU which needs 1K pages */ + mc->minimum_page_bits = 12; } static const TypeInfo virt_machine_info = { @@ -1570,6 +1572,8 @@ static void virt_machine_2_7_options(MachineClass *mc) SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_7); /* ITS was introduced with 2.8 */ vmc->no_its = true; + /* Stick with 1K pages for migration compatibility */ + mc->minimum_page_bits = 0; } DEFINE_VIRT_MACHINE(2, 7) diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index c45c835a17..3af82afe78 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -13,6 +13,9 @@ #include "sysemu/replay.h" #include "sysemu/qtest.h" +#define DELTA_ADJUST 1 +#define DELTA_NO_ADJUST -1 + struct ptimer_state { uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */ @@ -35,16 +38,21 @@ static void ptimer_trigger(ptimer_state *s) } } -static void ptimer_reload(ptimer_state *s) +static void ptimer_reload(ptimer_state *s, int delta_adjust) { uint32_t period_frac = s->period_frac; uint64_t period = s->period; + uint64_t delta = s->delta; - if (s->delta == 0) { + if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { ptimer_trigger(s); - s->delta = s->limit; } - if (s->delta == 0 || s->period == 0) { + + if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) { + delta = s->delta = s->limit; + } + + if (s->period == 0) { if (!qtest_enabled()) { fprintf(stderr, "Timer with period zero, disabling\n"); } @@ -53,6 +61,39 @@ static void ptimer_reload(ptimer_state *s) return; } + if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) { + if (delta_adjust != DELTA_NO_ADJUST) { + delta += delta_adjust; + } + } + + if (delta == 0 && (s->policy_mask & PTIMER_POLICY_CONTINUOUS_TRIGGER)) { + if (s->enabled == 1 && s->limit == 0) { + delta = 1; + } + } + + if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { + if (delta_adjust != DELTA_NO_ADJUST) { + delta = 1; + } + } + + if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) { + if (s->enabled == 1 && s->limit != 0) { + delta = 1; + } + } + + if (delta == 0) { + if (!qtest_enabled()) { + fprintf(stderr, "Timer with delta zero, disabling\n"); + } + timer_del(s->timer); + s->enabled = 0; + return; + } + /* * Artificially limit timeout rate to something * achievable under QEMU. Otherwise, QEMU spends all @@ -62,15 +103,15 @@ static void ptimer_reload(ptimer_state *s) * on the current generation of host machines. */ - if (s->enabled == 1 && (s->delta * period < 10000) && !use_icount) { - period = 10000 / s->delta; + if (s->enabled == 1 && (delta * period < 10000) && !use_icount) { + period = 10000 / delta; period_frac = 0; } s->last_event = s->next_event; - s->next_event = s->last_event + s->delta * period; + s->next_event = s->last_event + delta * period; if (period_frac) { - s->next_event += ((int64_t)period_frac * s->delta) >> 32; + s->next_event += ((int64_t)period_frac * delta) >> 32; } timer_mod(s->timer, s->next_event); } @@ -78,12 +119,35 @@ static void ptimer_reload(ptimer_state *s) static void ptimer_tick(void *opaque) { ptimer_state *s = (ptimer_state *)opaque; - ptimer_trigger(s); - s->delta = 0; + bool trigger = true; + if (s->enabled == 2) { + s->delta = 0; s->enabled = 0; } else { - ptimer_reload(s); + int delta_adjust = DELTA_ADJUST; + + if (s->delta == 0 || s->limit == 0) { + /* If a "continuous trigger" policy is not used and limit == 0, + we should error out. delta == 0 means that this tick is + caused by a "no immediate reload" policy, so it shouldn't + be adjusted. */ + delta_adjust = DELTA_NO_ADJUST; + } + + if (!(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { + /* Avoid re-trigger on deferred reload if "no immediate trigger" + policy isn't used. */ + trigger = (delta_adjust == DELTA_ADJUST); + } + + s->delta = s->limit; + + ptimer_reload(s, delta_adjust); + } + + if (trigger) { + ptimer_trigger(s); } } @@ -91,9 +155,10 @@ uint64_t ptimer_get_count(ptimer_state *s) { uint64_t counter; - if (s->enabled) { + if (s->enabled && s->delta != 0) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); int64_t next = s->next_event; + int64_t last = s->last_event; bool expired = (now - next >= 0); bool oneshot = (s->enabled == 2); @@ -118,7 +183,7 @@ uint64_t ptimer_get_count(ptimer_state *s) /* We need to divide time by period, where time is stored in rem (64-bit integer) and period is stored in period/period_frac (64.32 fixed point). - + Doing full precision division is hard, so scale values and do a 64-bit division. The result should be rounded down, so that the rounding error never causes the timer to go @@ -145,6 +210,35 @@ uint64_t ptimer_get_count(ptimer_state *s) div += 1; } counter = rem / div; + + if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) { + /* Before wrapping around, timer should stay with counter = 0 + for a one period. */ + if (!oneshot && s->delta == s->limit) { + if (now == last) { + /* Counter == delta here, check whether it was + adjusted and if it was, then right now it is + that "one period". */ + if (counter == s->limit + DELTA_ADJUST) { + return 0; + } + } else if (counter == s->limit) { + /* Since the counter is rounded down and now != last, + the counter == limit means that delta was adjusted + by +1 and right now it is that adjusted period. */ + return 0; + } + } + } + } + + if (s->policy_mask & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) { + /* If now == last then delta == limit, i.e. the counter already + represents the correct value. It would be rounded down a 1ns + later. */ + if (now != last) { + counter += 1; + } } } else { counter = s->delta; @@ -157,7 +251,7 @@ void ptimer_set_count(ptimer_state *s, uint64_t count) s->delta = count; if (s->enabled) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } @@ -174,7 +268,7 @@ void ptimer_run(ptimer_state *s, int oneshot) s->enabled = oneshot ? 2 : 1; if (was_disabled) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } @@ -198,7 +292,7 @@ void ptimer_set_period(ptimer_state *s, int64_t period) s->period_frac = 0; if (s->enabled) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } @@ -210,7 +304,7 @@ void ptimer_set_freq(ptimer_state *s, uint32_t freq) s->period_frac = (1000000000ll << 32) / freq; if (s->enabled) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } @@ -223,7 +317,7 @@ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload) s->delta = limit; if (s->enabled && reload) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } diff --git a/hw/display/pl110.c b/hw/display/pl110.c index c069c0b7fd..8c7dcc6f0a 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -466,17 +466,16 @@ static const GraphicHwOps pl110_gfx_ops = { .gfx_update = pl110_update_display, }; -static int pl110_initfn(SysBusDevice *sbd) +static void pl110_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); PL110State *s = PL110(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); - return 0; } static void pl110_init(Object *obj) @@ -503,11 +502,10 @@ static void pl111_init(Object *obj) static void pl110_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pl110_initfn; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->vmsd = &vmstate_pl110; + dc->realize = pl110_realize; } static const TypeInfo pl110_info = { diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 4afbe0bde5..abd4c4cddb 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -88,7 +88,12 @@ int i2c_bus_busy(I2CBus *bus) return !QLIST_EMPTY(&bus->current_devs); } -/* Returns non-zero if the address is not valid. */ +/* + * Returns non-zero if the address is not valid. If this is called + * again without an intervening i2c_end_transfer(), like in the SMBus + * case where the operation is switched from write to read, this + * function will not rescan the bus and thus cannot fail. + */ /* TODO: Make this handle multiple masters. */ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) { @@ -104,15 +109,25 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) bus->broadcast = true; } - QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { - DeviceState *qdev = kid->child; - I2CSlave *candidate = I2C_SLAVE(qdev); - if ((candidate->address == address) || (bus->broadcast)) { - node = g_malloc(sizeof(struct I2CNode)); - node->elt = candidate; - QLIST_INSERT_HEAD(&bus->current_devs, node, next); - if (!bus->broadcast) { - break; + /* + * If there are already devices in the list, that means we are in + * the middle of a transaction and we shouldn't rescan the bus. + * + * This happens with any SMBus transaction, even on a pure I2C + * device. The interface does a transaction start without + * terminating the previous transaction. + */ + if (QLIST_EMPTY(&bus->current_devs)) { + QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { + DeviceState *qdev = kid->child; + I2CSlave *candidate = I2C_SLAVE(qdev); + if ((candidate->address == address) || (bus->broadcast)) { + node = g_malloc(sizeof(struct I2CNode)); + node->elt = candidate; + QLIST_INSERT_HEAD(&bus->current_devs, node, next); + if (!bus->broadcast) { + break; + } } } } @@ -137,10 +152,6 @@ void i2c_end_transfer(I2CBus *bus) I2CSlaveClass *sc; I2CNode *node, *next; - if (QLIST_EMPTY(&bus->current_devs)) { - return; - } - QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { sc = I2C_SLAVE_GET_CLASS(node->elt); if (sc->event) { diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c index 3979b3dad7..5b4dd3eba4 100644 --- a/hw/i2c/smbus.c +++ b/hw/i2c/smbus.c @@ -248,7 +248,9 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) return -1; } i2c_send(bus, command); - i2c_start_transfer(bus, addr, 1); + if (i2c_start_transfer(bus, addr, 1)) { + assert(0); + } data = i2c_recv(bus); i2c_nack(bus); i2c_end_transfer(bus); @@ -273,7 +275,9 @@ int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) return -1; } i2c_send(bus, command); - i2c_start_transfer(bus, addr, 1); + if (i2c_start_transfer(bus, addr, 1)) { + assert(0); + } data = i2c_recv(bus); data |= i2c_recv(bus) << 8; i2c_nack(bus); @@ -302,7 +306,9 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data) return -1; } i2c_send(bus, command); - i2c_start_transfer(bus, addr, 1); + if (i2c_start_transfer(bus, addr, 1)) { + assert(0); + } len = i2c_recv(bus); if (len > 32) { len = 0; diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 772f85f5fd..ce1dc63911 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -82,15 +82,15 @@ static void a9_gtimer_update(A9GTimerState *s, bool sync) if ((s->control & R_CONTROL_TIMER_ENABLE) && (gtb->control & R_CONTROL_COMP_ENABLE)) { /* R2p0+, where the compare function is >= */ - while (gtb->compare < update.new) { + if (gtb->compare < update.new) { DB_PRINT("Compare event happened for CPU %d\n", i); gtb->status = 1; - if (gtb->control & R_CONTROL_AUTO_INCREMENT) { - DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n", - gtb->inc); - gtb->compare += gtb->inc; - } else { - break; + if (gtb->control & R_CONTROL_AUTO_INCREMENT && gtb->inc) { + uint64_t inc = + QEMU_ALIGN_UP(update.new - gtb->compare, gtb->inc); + DB_PRINT("Auto incrementing timer compare by %" + PRId64 "\n", inc); + gtb->compare += inc; } } cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1; diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index d66bbf01b4..daf6c48797 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -20,22 +20,33 @@ */ #include "qemu/osdep.h" +#include "hw/ptimer.h" #include "hw/timer/arm_mptimer.h" #include "qapi/error.h" -#include "qemu/timer.h" +#include "qemu/main-loop.h" #include "qom/cpu.h" +#define PTIMER_POLICY \ + (PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | \ + PTIMER_POLICY_CONTINUOUS_TRIGGER | \ + PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | \ + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | \ + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) + /* This device implements the per-cpu private timer and watchdog block * which is used in both the ARM11MPCore and Cortex-A9MP. */ static inline int get_current_cpu(ARMMPTimerState *s) { - if (current_cpu->cpu_index >= s->num_cpu) { + int cpu_id = current_cpu ? current_cpu->cpu_index : 0; + + if (cpu_id >= s->num_cpu) { hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", - s->num_cpu, current_cpu->cpu_index); + s->num_cpu, cpu_id); } - return current_cpu->cpu_index; + + return cpu_id; } static inline void timerblock_update_irq(TimerBlock *tb) @@ -44,33 +55,42 @@ static inline void timerblock_update_irq(TimerBlock *tb) } /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ -static inline uint32_t timerblock_scale(TimerBlock *tb) +static inline uint32_t timerblock_scale(uint32_t control) { - return (((tb->control >> 8) & 0xff) + 1) * 10; + return (((control >> 8) & 0xff) + 1) * 10; } -static void timerblock_reload(TimerBlock *tb, int restart) +static inline void timerblock_set_count(struct ptimer_state *timer, + uint32_t control, uint64_t *count) { - if (tb->count == 0) { - return; + /* PTimer would trigger interrupt for periodic timer when counter set + * to 0, MPtimer under certain condition only. + */ + if ((control & 3) == 3 && (control & 0xff00) == 0 && *count == 0) { + *count = ptimer_get_limit(timer); } - if (restart) { - tb->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_set_count(timer, *count); +} + +static inline void timerblock_run(struct ptimer_state *timer, + uint32_t control, uint32_t load) +{ + if ((control & 1) && ((control & 0xff00) || load != 0)) { + ptimer_run(timer, !(control & 2)); } - tb->tick += (int64_t)tb->count * timerblock_scale(tb); - timer_mod(tb->timer, tb->tick); } static void timerblock_tick(void *opaque) { TimerBlock *tb = (TimerBlock *)opaque; - tb->status = 1; - if (tb->control & 2) { - tb->count = tb->load; - timerblock_reload(tb, 0); - } else { - tb->count = 0; + /* Periodic timer with load = 0 and prescaler != 0 would re-trigger + * IRQ after one period, otherwise it either stops or wraps around. + */ + if ((tb->control & 2) && (tb->control & 0xff00) == 0 && + ptimer_get_limit(tb->timer) == 0) { + ptimer_stop(tb->timer); } + tb->status = 1; timerblock_update_irq(tb); } @@ -78,21 +98,11 @@ static uint64_t timerblock_read(void *opaque, hwaddr addr, unsigned size) { TimerBlock *tb = (TimerBlock *)opaque; - int64_t val; switch (addr) { case 0: /* Load */ - return tb->load; + return ptimer_get_limit(tb->timer); case 4: /* Counter. */ - if (((tb->control & 1) == 0) || (tb->count == 0)) { - return 0; - } - /* Slow and ugly, but hopefully won't happen too often. */ - val = tb->tick - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - val /= timerblock_scale(tb); - if (val < 0) { - val = 0; - } - return val; + return ptimer_get_count(tb->timer); case 8: /* Control. */ return tb->control; case 12: /* Interrupt status. */ @@ -106,37 +116,45 @@ static void timerblock_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { TimerBlock *tb = (TimerBlock *)opaque; - int64_t old; + uint32_t control = tb->control; switch (addr) { case 0: /* Load */ - tb->load = value; - /* Fall through. */ - case 4: /* Counter. */ - if ((tb->control & 1) && tb->count) { - /* Cancel the previous timer. */ - timer_del(tb->timer); + /* Setting load to 0 stops the timer without doing the tick if + * prescaler = 0. + */ + if ((control & 1) && (control & 0xff00) == 0 && value == 0) { + ptimer_stop(tb->timer); } - tb->count = value; - if (tb->control & 1) { - timerblock_reload(tb, 1); + ptimer_set_limit(tb->timer, value, 1); + timerblock_run(tb->timer, control, value); + break; + case 4: /* Counter. */ + /* Setting counter to 0 stops the one-shot timer, or periodic with + * load = 0, without doing the tick if prescaler = 0. + */ + if ((control & 1) && (control & 0xff00) == 0 && value == 0 && + (!(control & 2) || ptimer_get_limit(tb->timer) == 0)) { + ptimer_stop(tb->timer); } + timerblock_set_count(tb->timer, control, &value); + timerblock_run(tb->timer, control, value); break; case 8: /* Control. */ - old = tb->control; - tb->control = value; + if ((control & 3) != (value & 3)) { + ptimer_stop(tb->timer); + } + if ((control & 0xff00) != (value & 0xff00)) { + ptimer_set_period(tb->timer, timerblock_scale(value)); + } if (value & 1) { - if ((old & 1) && (tb->count != 0)) { - /* Do nothing if timer is ticking right now. */ - break; + uint64_t count = ptimer_get_count(tb->timer); + /* Re-load periodic timer counter if needed. */ + if ((value & 2) && count == 0) { + timerblock_set_count(tb->timer, value, &count); } - if (tb->control & 2) { - tb->count = tb->load; - } - timerblock_reload(tb, 1); - } else if (old & 1) { - /* Shutdown the timer. */ - timer_del(tb->timer); + timerblock_run(tb->timer, value, count); } + tb->control = value; break; case 12: /* Interrupt status. */ tb->status &= ~value; @@ -186,13 +204,12 @@ static const MemoryRegionOps timerblock_ops = { static void timerblock_reset(TimerBlock *tb) { - tb->count = 0; - tb->load = 0; tb->control = 0; tb->status = 0; - tb->tick = 0; if (tb->timer) { - timer_del(tb->timer); + ptimer_stop(tb->timer); + ptimer_set_limit(tb->timer, 0, 1); + ptimer_set_period(tb->timer, timerblock_scale(0)); } } @@ -238,7 +255,8 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp) */ for (i = 0; i < s->num_cpu; i++) { TimerBlock *tb = &s->timerblock[i]; - tb->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timerblock_tick, tb); + QEMUBH *bh = qemu_bh_new(timerblock_tick, tb); + tb->timer = ptimer_init(bh, PTIMER_POLICY); sysbus_init_irq(sbd, &tb->irq); memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb, "arm_mptimer_timerblock", 0x20); @@ -248,26 +266,23 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_timerblock = { .name = "arm_mptimer_timerblock", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { - VMSTATE_UINT32(count, TimerBlock), - VMSTATE_UINT32(load, TimerBlock), VMSTATE_UINT32(control, TimerBlock), VMSTATE_UINT32(status, TimerBlock), - VMSTATE_INT64(tick, TimerBlock), - VMSTATE_TIMER_PTR(timer, TimerBlock), + VMSTATE_PTIMER(timer, TimerBlock), VMSTATE_END_OF_LIST() } }; static const VMStateDescription vmstate_arm_mptimer = { .name = "arm_mptimer", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu, - 2, vmstate_timerblock, TimerBlock), + 3, vmstate_timerblock, TimerBlock), VMSTATE_END_OF_LIST() } }; diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index 8c4c1f9f05..e5f5e14a90 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -217,7 +217,7 @@ static void stm32f2xx_timer_write(void *opaque, hwaddr offset, return; case TIM_PSC: timer_val = stm32f2xx_ns_to_ticks(s, now) - s->tick_offset; - s->tim_psc = value; + s->tim_psc = value & 0xFFFF; value = timer_val; break; case TIM_CNT: diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index ee3388f90d..9dea14ba03 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -8,6 +8,9 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, uint32_t granularity, const char *name, Error **errp); +void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int chunk_size); +void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap); int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, Error **errp); @@ -27,8 +30,11 @@ void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap); +uint32_t bdrv_dirty_bitmap_meta_granularity(BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap); +const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap); +int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap); DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap); int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, int64_t sector); @@ -36,9 +42,34 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, int64_t cur_sector, int64_t nr_sectors); void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, int64_t cur_sector, int64_t nr_sectors); -void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi); -void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset); +int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, int64_t sector, + int nb_sectors); +void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, int64_t sector, + int nb_sectors); +BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap); +BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap, + uint64_t first_sector); +void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter); +int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); +void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t sector_num); int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); +int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); +uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, + uint64_t start, uint64_t count); +uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap); +void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, + uint8_t *buf, uint64_t start, + uint64_t count); +void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, + uint8_t *buf, uint64_t start, + uint64_t count, bool finish); +void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, + uint64_t start, uint64_t count, + bool finish); +void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); + #endif diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index b6a705982f..861260d3db 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -189,6 +189,15 @@ void address_space_stq(AddressSpace *as, hwaddr addr, uint64_t val, /* page related stuff */ +#ifdef TARGET_PAGE_BITS_VARY +extern bool target_page_bits_decided; +extern int target_page_bits; +#define TARGET_PAGE_BITS ({ assert(target_page_bits_decided); \ + target_page_bits; }) +#else +#define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS +#endif + #define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) #define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1) #define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK) diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index fa89abc44d..d1d1d61fcb 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -638,4 +638,72 @@ typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit; /* Masks for Flags field above */ #define ACPI_DMAR_INCLUDE_PCI_ALL 1 +/* + * Input Output Remapping Table (IORT) + * Conforms to "IO Remapping Table System Software on ARM Platforms", + * Document number: ARM DEN 0049B, October 2015 + */ + +struct AcpiIortTable { + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t node_count; + uint32_t node_offset; + uint32_t reserved; +} QEMU_PACKED; +typedef struct AcpiIortTable AcpiIortTable; + +/* + * IORT node types + */ + +#define ACPI_IORT_NODE_HEADER_DEF /* Node format common fields */ \ + uint8_t type; \ + uint16_t length; \ + uint8_t revision; \ + uint32_t reserved; \ + uint32_t mapping_count; \ + uint32_t mapping_offset; + +/* Values for node Type above */ +enum { + ACPI_IORT_NODE_ITS_GROUP = 0x00, + ACPI_IORT_NODE_NAMED_COMPONENT = 0x01, + ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02, + ACPI_IORT_NODE_SMMU = 0x03, + ACPI_IORT_NODE_SMMU_V3 = 0x04 +}; + +struct AcpiIortIdMapping { + uint32_t input_base; + uint32_t id_count; + uint32_t output_base; + uint32_t output_reference; + uint32_t flags; +} QEMU_PACKED; +typedef struct AcpiIortIdMapping AcpiIortIdMapping; + +struct AcpiIortMemoryAccess { + uint32_t cache_coherency; + uint8_t hints; + uint16_t reserved; + uint8_t memory_flags; +} QEMU_PACKED; +typedef struct AcpiIortMemoryAccess AcpiIortMemoryAccess; + +struct AcpiIortItsGroup { + ACPI_IORT_NODE_HEADER_DEF + uint32_t its_count; + uint32_t identifiers[0]; +} QEMU_PACKED; +typedef struct AcpiIortItsGroup AcpiIortItsGroup; + +struct AcpiIortRC { + ACPI_IORT_NODE_HEADER_DEF + AcpiIortMemoryAccess memory_properties; + uint32_t ats_attribute; + uint32_t pci_segment_number; + AcpiIortIdMapping id_mapping_array[0]; +} QEMU_PACKED; +typedef struct AcpiIortRC AcpiIortRC; + #endif diff --git a/include/hw/boards.h b/include/hw/boards.h index e46a744bcd..a51da9c440 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -86,6 +86,12 @@ typedef struct { * Returns a @HotpluggableCPUList, which describes CPUs objects which * could be added with -device/device_add. * Caller is responsible for freeing returned list. + * @minimum_page_bits: + * If non-zero, the board promises never to create a CPU with a page size + * smaller than this, so QEMU can use a more efficient larger page + * size than the target architecture's minimum. (Attempting to create + * such a CPU will fail.) Note that changing this is a migration + * compatibility break for the machine. */ struct MachineClass { /*< private >*/ @@ -124,6 +130,7 @@ struct MachineClass { ram_addr_t default_ram_size; bool option_rom_has_mr; bool rom_file_has_mr; + int minimum_page_bits; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h index 26c7fdcd75..48cccbdb51 100644 --- a/include/hw/ptimer.h +++ b/include/hw/ptimer.h @@ -35,6 +35,26 @@ */ #define PTIMER_POLICY_DEFAULT 0 +/* Periodic timer counter stays with "0" for a one period before wrapping + * around. */ +#define PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD (1 << 0) + +/* Running periodic timer that has counter = limit = 0 would continuously + * re-trigger every period. */ +#define PTIMER_POLICY_CONTINUOUS_TRIGGER (1 << 1) + +/* Starting to run with/setting counter to "0" won't trigger immediately, + * but after a one period for both oneshot and periodic modes. */ +#define PTIMER_POLICY_NO_IMMEDIATE_TRIGGER (1 << 2) + +/* Starting to run with/setting counter to "0" won't re-load counter + * immediately, but after a one period. */ +#define PTIMER_POLICY_NO_IMMEDIATE_RELOAD (1 << 3) + +/* Make counter value of the running timer represent the actual value and + * not the one less. */ +#define PTIMER_POLICY_NO_COUNTER_ROUND_DOWN (1 << 4) + /* ptimer.c */ typedef struct ptimer_state ptimer_state; typedef void (*ptimer_cb)(void *opaque); diff --git a/include/hw/timer/arm_mptimer.h b/include/hw/timer/arm_mptimer.h index b34cba00ce..c46d8d2309 100644 --- a/include/hw/timer/arm_mptimer.h +++ b/include/hw/timer/arm_mptimer.h @@ -27,12 +27,9 @@ /* State of a single timer or watchdog block */ typedef struct { - uint32_t count; - uint32_t load; uint32_t control; uint32_t status; - int64_t tick; - QEMUTimer *timer; + struct ptimer_state *timer; qemu_irq irq; MemoryRegion iomem; } TimerBlock; diff --git a/include/qemu-common.h b/include/qemu-common.h index 9e8b0bd991..7e6e4feb4b 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -82,6 +82,18 @@ bool tcg_enabled(void); void cpu_exec_init_all(void); /** + * set_preferred_target_page_bits: + * @bits: number of bits needed to represent an address within the page + * + * Set the preferred target page size (the actual target page + * size may be smaller than any given CPU's preference). + * Returns true on success, false on failure (which can only happen + * if this is called after the system has already finalized its + * choice of page size and the requested page size is smaller than that). + */ +bool set_preferred_target_page_bits(int bits); + +/** * Sends a (part of) iovec down a socket, yielding when the socket is full, or * Receives data into a (part of) iovec from a socket, * yielding when there is no data in the socket. diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 8ab721e5aa..eb464759d5 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -146,6 +146,85 @@ void hbitmap_reset_all(HBitmap *hb); bool hbitmap_get(const HBitmap *hb, uint64_t item); /** + * hbitmap_serialization_granularity: + * @hb: HBitmap to operate on. + * + * Granularity of serialization chunks, used by other serialization functions. + * For every chunk: + * 1. Chunk start should be aligned to this granularity. + * 2. Chunk size should be aligned too, except for last chunk (for which + * start + count == hb->size) + */ +uint64_t hbitmap_serialization_granularity(const HBitmap *hb); + +/** + * hbitmap_serialization_size: + * @hb: HBitmap to operate on. + * @start: Starting bit + * @count: Number of bits + * + * Return number of bytes hbitmap_(de)serialize_part needs + */ +uint64_t hbitmap_serialization_size(const HBitmap *hb, + uint64_t start, uint64_t count); + +/** + * hbitmap_serialize_part + * @hb: HBitmap to operate on. + * @buf: Buffer to store serialized bitmap. + * @start: First bit to store. + * @count: Number of bits to store. + * + * Stores HBitmap data corresponding to given region. The format of saved data + * is linear sequence of bits, so it can be used by hbitmap_deserialize_part + * independently of endianness and size of HBitmap level array elements + */ +void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf, + uint64_t start, uint64_t count); + +/** + * hbitmap_deserialize_part + * @hb: HBitmap to operate on. + * @buf: Buffer to restore bitmap data from. + * @start: First bit to restore. + * @count: Number of bits to restore. + * @finish: Whether to call hbitmap_deserialize_finish automatically. + * + * Restores HBitmap data corresponding to given region. The format is the same + * as for hbitmap_serialize_part. + * + * If @finish is false, caller must call hbitmap_serialize_finish before using + * the bitmap. + */ +void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf, + uint64_t start, uint64_t count, + bool finish); + +/** + * hbitmap_deserialize_zeroes + * @hb: HBitmap to operate on. + * @start: First bit to restore. + * @count: Number of bits to restore. + * @finish: Whether to call hbitmap_deserialize_finish automatically. + * + * Fills the bitmap with zeroes. + * + * If @finish is false, caller must call hbitmap_serialize_finish before using + * the bitmap. + */ +void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count, + bool finish); + +/** + * hbitmap_deserialize_finish + * @hb: HBitmap to operate on. + * + * Repair HBitmap after calling hbitmap_deserialize_data. Actually, all HBitmap + * layers are restored here. + */ +void hbitmap_deserialize_finish(HBitmap *hb); + +/** * hbitmap_free: * @hb: HBitmap to operate on. * @@ -178,6 +257,27 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); */ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); +/* hbitmap_create_meta: + * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap. + * The caller owns the created bitmap and must call hbitmap_free_meta(hb) to + * free it. + * + * Currently, we only guarantee that if a bit in the hbitmap is changed it + * will be reflected in the meta bitmap, but we do not yet guarantee the + * opposite. + * + * @hb: The HBitmap to operate on. + * @chunk_size: How many bits in @hb does one bit in the meta track. + */ +HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size); + +/* hbitmap_free_meta: + * Free the meta bitmap of @hb. + * + * @hb: The HBitmap whose meta bitmap should be freed. + */ +void hbitmap_free_meta(HBitmap *hb); + /** * hbitmap_iter_next: * @hbi: HBitmapIter to operate on. diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index b113fcf156..1b8c30a7a0 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -11,6 +11,7 @@ typedef struct AioContext AioContext; typedef struct AllwinnerAHCIState AllwinnerAHCIState; typedef struct AudioState AudioState; typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; +typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter; typedef struct BlockBackend BlockBackend; typedef struct BlockBackendRootState BlockBackendRootState; typedef struct BlockDriverState BlockDriverState; diff --git a/linux-user/main.c b/linux-user/main.c index c6f2e20c09..54970bc4d9 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -806,6 +806,9 @@ void cpu_loop(CPUARMState *env) } } break; + case EXCP_SEMIHOST: + env->regs[0] = do_arm_semihosting(env); + break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; diff --git a/migration/ram.c b/migration/ram.c index bc6154fe34..d032d389c4 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -69,7 +69,7 @@ static uint64_t bitmap_sync_count; /* 0x80 is reserved in migration.h start with 0x100 next */ #define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 -static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE]; +static uint8_t *ZERO_TARGET_PAGE; static inline bool is_zero_range(uint8_t *p, uint64_t size) { @@ -1431,6 +1431,7 @@ static void ram_migration_cleanup(void *opaque) cache_fini(XBZRLE.cache); g_free(XBZRLE.encoded_buf); g_free(XBZRLE.current_buf); + g_free(ZERO_TARGET_PAGE); XBZRLE.cache = NULL; XBZRLE.encoded_buf = NULL; XBZRLE.current_buf = NULL; @@ -1889,6 +1890,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) if (migrate_use_xbzrle()) { XBZRLE_cache_lock(); + ZERO_TARGET_PAGE = g_malloc0(TARGET_PAGE_SIZE); XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() / TARGET_PAGE_SIZE, TARGET_PAGE_SIZE); diff --git a/migration/savevm.c b/migration/savevm.c index a831ec2d67..cfcbbd00d3 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -265,6 +265,7 @@ typedef struct SaveState { bool skip_configuration; uint32_t len; const char *name; + uint32_t target_page_bits; } SaveState; static SaveState savevm_state = { @@ -286,6 +287,19 @@ static void configuration_pre_save(void *opaque) state->len = strlen(current_name); state->name = current_name; + state->target_page_bits = TARGET_PAGE_BITS; +} + +static int configuration_pre_load(void *opaque) +{ + SaveState *state = opaque; + + /* If there is no target-page-bits subsection it means the source + * predates the variable-target-page-bits support and is using the + * minimum possible value for this CPU. + */ + state->target_page_bits = TARGET_PAGE_BITS_MIN; + return 0; } static int configuration_post_load(void *opaque, int version_id) @@ -298,12 +312,43 @@ static int configuration_post_load(void *opaque, int version_id) (int) state->len, state->name, current_name); return -EINVAL; } + + if (state->target_page_bits != TARGET_PAGE_BITS) { + error_report("Received TARGET_PAGE_BITS is %d but local is %d", + state->target_page_bits, TARGET_PAGE_BITS); + return -EINVAL; + } + return 0; } +/* The target-page-bits subsection is present only if the + * target page size is not the same as the default (ie the + * minimum page size for a variable-page-size guest CPU). + * If it is present then it contains the actual target page + * bits for the machine, and migration will fail if the + * two ends don't agree about it. + */ +static bool vmstate_target_page_bits_needed(void *opaque) +{ + return TARGET_PAGE_BITS > TARGET_PAGE_BITS_MIN; +} + +static const VMStateDescription vmstate_target_page_bits = { + .name = "configuration/target-page-bits", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_target_page_bits_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(target_page_bits, SaveState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_configuration = { .name = "configuration", .version_id = 1, + .pre_load = configuration_pre_load, .post_load = configuration_post_load, .pre_save = configuration_pre_save, .fields = (VMStateField[]) { @@ -311,6 +356,10 @@ static const VMStateDescription vmstate_configuration = { VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, 0, len), VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription*[]) { + &vmstate_target_page_bits, + NULL + } }; static void dump_vmstate_vmsd(FILE *out_file, diff --git a/qapi/block-core.json b/qapi/block-core.json index 1b7aa1befd..97b120532a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2197,7 +2197,8 @@ # @mode: the replication mode # # @top-id: #optional In secondary mode, node name or device ID of the root -# node who owns the replication node chain. Ignored in primary mode. +# node who owns the replication node chain. Must not be given in +# primary mode. # # Since: 2.8 ## @@ -2312,11 +2313,11 @@ # block drivers among other things. Stay away from it unless you want # to help with its development. # -# @options: block device options for the new device +# For the arguments, see the documentation of BlockdevOptions. # # Since: 1.7 ## -{ 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } } +{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } ## # @x-blockdev-del: diff --git a/qemu-img.c b/qemu-img.c index 67e851248a..ab395a9b1a 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -2956,6 +2956,7 @@ static int img_rebase(int argc, char **argv) error_reportf_err(local_err, "Could not open old backing file '%s': ", backing_name); + ret = -1; goto out; } @@ -2973,6 +2974,7 @@ static int img_rebase(int argc, char **argv) error_reportf_err(local_err, "Could not open new backing file '%s': ", out_baseimg); + ret = -1; goto out; } } diff --git a/qemu-nbd.c b/qemu-nbd.c index cca4a983b7..b757dc7621 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -48,6 +48,7 @@ #define QEMU_NBD_OPT_OBJECT 260 #define QEMU_NBD_OPT_TLSCREDS 261 #define QEMU_NBD_OPT_IMAGE_OPTS 262 +#define QEMU_NBD_OPT_FORK 263 #define MBR_SIZE 512 @@ -92,6 +93,8 @@ static void usage(const char *name) " passwords and/or encryption keys\n" " -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n" " specify tracing options\n" +" --fork fork off the server process and exit the parent\n" +" once the server is running\n" #ifdef __linux__ "Kernel NBD client support:\n" " -c, --connect=DEV connect FILE to the local NBD device DEV\n" @@ -503,6 +506,7 @@ int main(int argc, char **argv) { "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS }, { "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS }, { "trace", required_argument, NULL, 'T' }, + { "fork", no_argument, NULL, QEMU_NBD_OPT_FORK }, { NULL, 0, NULL, 0 } }; int ch; @@ -524,6 +528,8 @@ int main(int argc, char **argv) bool imageOpts = false; bool writethrough = true; char *trace_file = NULL; + bool fork_process = false; + int old_stderr = -1; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -715,6 +721,9 @@ int main(int argc, char **argv) g_free(trace_file); trace_file = trace_opt_parse(optarg); break; + case QEMU_NBD_OPT_FORK: + fork_process = true; + break; } } @@ -774,7 +783,7 @@ int main(int argc, char **argv) return 0; } - if (device && !verbose) { + if ((device && !verbose) || fork_process) { int stderr_fd[2]; pid_t pid; int ret; @@ -797,6 +806,7 @@ int main(int argc, char **argv) ret = qemu_daemon(1, 0); /* Temporarily redirect stderr to the parent's pipe... */ + old_stderr = dup(STDERR_FILENO); dup2(stderr_fd[1], STDERR_FILENO); if (ret < 0) { error_report("Failed to daemonize: %s", strerror(errno)); @@ -960,6 +970,11 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + if (fork_process) { + dup2(old_stderr, STDERR_FILENO); + close(old_stderr); + } + state = RUNNING; do { main_loop_wait(false); diff --git a/qemu-nbd.texi b/qemu-nbd.texi index 91ebf04b5b..b7a9c6d02f 100644 --- a/qemu-nbd.texi +++ b/qemu-nbd.texi @@ -86,6 +86,8 @@ the new style NBD protocol negotiation Enable mandatory TLS encryption for the server by setting the ID of the TLS credentials object previously created with the --object option. +@item --fork +Fork off the server process and exit the parent once the server is running. @item -v, --verbose Display extra debugging information @item -h, --help diff --git a/target-arm/cpu.c b/target-arm/cpu.c index fb272a86c1..2439ca57d0 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -563,6 +563,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) ARMCPU *cpu = ARM_CPU(dev); ARMCPUClass *acc = ARM_CPU_GET_CLASS(dev); CPUARMState *env = &cpu->env; + int pagebits; Error *local_err = NULL; cpu_exec_realizefn(cs, &local_err); @@ -625,6 +626,29 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) set_feature(env, ARM_FEATURE_THUMB_DSP); } + if (arm_feature(env, ARM_FEATURE_V7) && + !arm_feature(env, ARM_FEATURE_M) && + !arm_feature(env, ARM_FEATURE_MPU)) { + /* v7VMSA drops support for the old ARMv5 tiny pages, so we + * can use 4K pages. + */ + pagebits = 12; + } else { + /* For CPUs which might have tiny 1K pages, or which have an + * MPU and might have small region sizes, stick with 1K pages. + */ + pagebits = 10; + } + if (!set_preferred_target_page_bits(pagebits)) { + /* This can only ever happen for hotplugging a CPU, or if + * the board code incorrectly creates a CPU which it has + * promised via minimum_page_size that it will not. + */ + error_setg(errp, "This CPU requires a smaller page size than the " + "system is using"); + return; + } + /* This cpu-id-to-MPIDR affinity is used only for TCG; KVM will override it. * We don't support setting cluster ID ([16..23]) (known as Aff2 * in later ARM ARM versions), or any of the higher affinity level fields, diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 2218c00dad..9d75227e04 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -52,7 +52,7 @@ #define EXCP_SMC 13 /* Secure Monitor Call */ #define EXCP_VIRQ 14 #define EXCP_VFIQ 15 -#define EXCP_SEMIHOST 16 /* semihosting call (A64 only) */ +#define EXCP_SEMIHOST 16 /* semihosting call */ #define ARMV7M_EXCP_RESET 1 #define ARMV7M_EXCP_NMI 2 @@ -1766,10 +1766,11 @@ bool write_cpustate_to_list(ARMCPU *cpu); #if defined(CONFIG_USER_ONLY) #define TARGET_PAGE_BITS 12 #else -/* The ARM MMU allows 1k pages. */ -/* ??? Linux doesn't actually use these, and they're deprecated in recent - architecture revisions. Maybe a configure option to disable them. */ -#define TARGET_PAGE_BITS 10 +/* ARMv7 and later CPUs have 4K pages minimum, but ARMv5 and v6 + * have to support 1K tiny pages. + */ +#define TARGET_PAGE_BITS_VARY +#define TARGET_PAGE_BITS_MIN 10 #endif #if defined(TARGET_AARCH64) diff --git a/target-arm/helper.c b/target-arm/helper.c index cb83ee2008..25b15dc100 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -6573,12 +6573,19 @@ static inline bool check_for_semihosting(CPUState *cs) /* Only intercept calls from privileged modes, to provide some * semblance of security. */ - if (!semihosting_enabled() || - ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)) { + if (cs->exception_index != EXCP_SEMIHOST && + (!semihosting_enabled() || + ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR))) { return false; } switch (cs->exception_index) { + case EXCP_SEMIHOST: + /* This is always a semihosting call; the "is this usermode" + * and "is semihosting enabled" checks have been done at + * translate time. + */ + break; case EXCP_SWI: /* Check for semihosting interrupt. */ if (env->thumb) { diff --git a/target-arm/translate.c b/target-arm/translate.c index 164b52a0d0..ef62f8b0c4 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -28,6 +28,7 @@ #include "qemu/log.h" #include "qemu/bitops.h" #include "arm_ldst.h" +#include "exec/semihost.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -1144,6 +1145,33 @@ static inline void gen_lookup_tb(DisasContext *s) s->is_jmp = DISAS_JUMP; } +static inline void gen_hlt(DisasContext *s, int imm) +{ + /* HLT. This has two purposes. + * Architecturally, it is an external halting debug instruction. + * Since QEMU doesn't implement external debug, we treat this as + * it is required for halting debug disabled: it will UNDEF. + * Secondly, "HLT 0x3C" is a T32 semihosting trap instruction, + * and "HLT 0xF000" is an A32 semihosting syscall. These traps + * must trigger semihosting even for ARMv7 and earlier, where + * HLT was an undefined encoding. + * In system mode, we don't allow userspace access to + * semihosting, to provide some semblance of security + * (and for consistency with our 32-bit semihosting). + */ + if (semihosting_enabled() && +#ifndef CONFIG_USER_ONLY + s->current_el != 0 && +#endif + (imm == (s->thumb ? 0x3c : 0xf000))) { + gen_exception_internal_insn(s, 0, EXCP_SEMIHOST); + return; + } + + gen_exception_insn(s, s->thumb ? 2 : 4, EXCP_UDEF, syn_uncategorized(), + default_exception_el(s)); +} + static inline void gen_add_data_offset(DisasContext *s, unsigned int insn, TCGv_i32 var) { @@ -8395,6 +8423,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) { int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4); switch (op1) { + case 0: + /* HLT */ + gen_hlt(s, imm16); + break; case 1: /* bkpt */ ARCH(5); @@ -8419,7 +8451,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) gen_smc(s); break; default: - goto illegal_op; + g_assert_not_reached(); } break; } @@ -11451,19 +11483,33 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) break; } - case 0xa: /* rev */ + case 0xa: /* rev, and hlt */ + { + int op1 = extract32(insn, 6, 2); + + if (op1 == 2) { + /* HLT */ + int imm6 = extract32(insn, 0, 6); + + gen_hlt(s, imm6); + break; + } + + /* Otherwise this is rev */ ARCH(6); rn = (insn >> 3) & 0x7; rd = insn & 0x7; tmp = load_reg(s, rn); - switch ((insn >> 6) & 3) { + switch (op1) { case 0: tcg_gen_bswap32_i32(tmp, tmp); break; case 1: gen_rev16(tmp); break; case 3: gen_revsh(tmp); break; - default: goto illegal_op; + default: + g_assert_not_reached(); } store_reg(s, rd, tmp); break; + } case 6: switch ((insn >> 5) & 7) { diff --git a/tests/Makefile.include b/tests/Makefile.include index e65e9f7819..91cc308eb6 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -303,6 +303,8 @@ check-qtest-arm-y += tests/m25p80-test$(EXESUF) gcov-files-arm-y += hw/misc/tmp105.c check-qtest-arm-y += tests/virtio-blk-test$(EXESUF) gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c +check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF) +gcov-files-arm-y += hw/timer/arm_mptimer.c check-qtest-microblazeel-y = $(check-qtest-microblaze-y) @@ -684,6 +686,7 @@ tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj- tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y) +tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o tests/migration/stress$(EXESUF): tests/migration/stress.o $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") diff --git a/tests/ptimer-test-stubs.c b/tests/ptimer-test-stubs.c index e028a81a29..21d4ebb0fe 100644 --- a/tests/ptimer-test-stubs.c +++ b/tests/ptimer-test-stubs.c @@ -1,7 +1,7 @@ /* * Stubs for the ptimer-test * - * Author: Dmitry Osipenko <digetx@gmail.com> + * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.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. diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c index 7b0ddf64e0..b36a476483 100644 --- a/tests/ptimer-test.c +++ b/tests/ptimer-test.c @@ -1,7 +1,7 @@ /* * QTest testcase for the ptimer * - * Author: Dmitry Osipenko <digetx@gmail.com> + * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.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. @@ -99,6 +99,7 @@ static void check_oneshot(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -106,34 +107,46 @@ static void check_oneshot(gconstpointer arg) ptimer_set_count(ptimer, 10); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 2 + 100000); + qemu_clock_step(2000000 * 2 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); ptimer_stop(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); qemu_clock_step(2000000 * 11); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 7 + 100000); + qemu_clock_step(2000000 * 7 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); - triggered = false; + if (no_round_down) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + + triggered = false; + } qemu_clock_step(2000000); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_false(triggered); + + if (no_round_down) { + g_assert_true(triggered); + + triggered = false; + } else { + g_assert_false(triggered); + } qemu_clock_step(4000000); @@ -142,30 +155,30 @@ static void check_oneshot(gconstpointer arg) ptimer_set_count(ptimer, 10); - qemu_clock_step(20000000 + 100000); + qemu_clock_step(20000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); g_assert_false(triggered); ptimer_set_limit(ptimer, 9, 1); - qemu_clock_step(20000000 + 100000); + qemu_clock_step(20000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); g_assert_false(triggered); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); ptimer_set_count(ptimer, 20); - qemu_clock_step(2000000 * 19 + 100000); + qemu_clock_step(2000000 * 19 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); g_assert_false(triggered); qemu_clock_step(2000000); @@ -177,7 +190,7 @@ static void check_oneshot(gconstpointer arg) triggered = false; - qemu_clock_step(2000000 * 12 + 100000); + qemu_clock_step(2000000 * 12 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); g_assert_false(triggered); @@ -188,6 +201,10 @@ static void check_periodic(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -195,28 +212,70 @@ static void check_periodic(gconstpointer arg) ptimer_set_limit(ptimer, 10, 1); ptimer_run(ptimer, 0); - qemu_clock_step(2000000 * 10 + 100000); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); + g_assert_false(triggered); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 10 - 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, wrap_policy ? 0 : 10); + g_assert_true(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 10 : 9)); g_assert_true(triggered); triggered = false; qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); ptimer_set_count(ptimer, 20); - qemu_clock_step(2000000 * 11 + 100000); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 20); + g_assert_false(triggered); + + qemu_clock_step(1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 20 : 19); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 11 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 9 : 8); g_assert_false(triggered); qemu_clock_step(2000000 * 10); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); + g_assert_true(triggered); + + triggered = false; + + ptimer_set_count(ptimer, 3); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_false(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 3 : 2); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 4); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_true(triggered); ptimer_stop(ptimer); @@ -224,50 +283,82 @@ static void check_periodic(gconstpointer arg) qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); ptimer_set_count(ptimer, 3); ptimer_run(ptimer, 0); - qemu_clock_step(2000000 * 3 + 100000); + qemu_clock_step(2000000 * 3 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 10 : 9)); g_assert_true(triggered); triggered = false; qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); ptimer_set_count(ptimer, 0); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); - g_assert_true(triggered); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : 10); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } triggered = false; - qemu_clock_step(2000000 * 12 + 100000); + qemu_clock_step(1); + + if (no_immediate_reload) { + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_false(triggered); + + qemu_clock_step(2000000); + + if (no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } + + triggered = false; + } - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 12); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); g_assert_true(triggered); ptimer_stop(ptimer); triggered = false; - qemu_clock_step(2000000 * 12 + 100000); + qemu_clock_step(2000000 * 10); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); ptimer_run(ptimer, 0); ptimer_set_period(ptimer, 0); - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); } @@ -276,6 +367,8 @@ static void check_on_the_fly_mode_change(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -283,16 +376,20 @@ static void check_on_the_fly_mode_change(gconstpointer arg) ptimer_set_limit(ptimer, 10, 1); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 9 + 100000); + qemu_clock_step(2000000 * 9 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); + g_assert_false(triggered); ptimer_run(ptimer, 0); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); g_assert_false(triggered); qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 10 : 9)); g_assert_true(triggered); triggered = false; @@ -301,7 +398,8 @@ static void check_on_the_fly_mode_change(gconstpointer arg) ptimer_run(ptimer, 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 1 : 0) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); qemu_clock_step(2000000 * 3); @@ -315,6 +413,7 @@ static void check_on_the_fly_period_change(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -322,17 +421,17 @@ static void check_on_the_fly_period_change(gconstpointer arg) ptimer_set_limit(ptimer, 8, 1); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 4 + 100000); + qemu_clock_step(2000000 * 4 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); g_assert_false(triggered); ptimer_set_period(ptimer, 4000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); - qemu_clock_step(4000000 * 2 + 100000); + qemu_clock_step(4000000 * 2 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0); g_assert_false(triggered); qemu_clock_step(4000000 * 2); @@ -346,6 +445,7 @@ static void check_on_the_fly_freq_change(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -353,17 +453,17 @@ static void check_on_the_fly_freq_change(gconstpointer arg) ptimer_set_limit(ptimer, 8, 1); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 4 + 100000); + qemu_clock_step(2000000 * 4 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); g_assert_false(triggered); ptimer_set_freq(ptimer, 250); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); - qemu_clock_step(2000000 * 4 + 100000); + qemu_clock_step(2000000 * 4 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0); g_assert_false(triggered); qemu_clock_step(2000000 * 4); @@ -394,25 +494,53 @@ static void check_run_with_delta_0(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; ptimer_set_period(ptimer, 2000000); ptimer_set_limit(ptimer, 99, 0); ptimer_run(ptimer, 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99); - g_assert_true(triggered); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : 99); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } triggered = false; - qemu_clock_step(2000000 + 100000); + if (no_immediate_trigger || no_immediate_reload) { + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : (no_round_down ? 98 : 97)); + + if (no_immediate_trigger && no_immediate_reload) { + g_assert_true(triggered); + + triggered = false; + } else { + g_assert_false(triggered); + } + + ptimer_set_count(ptimer, 99); + ptimer_run(ptimer, 1); + } + + qemu_clock_step(2000000 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 97); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97); g_assert_false(triggered); qemu_clock_step(2000000 * 97); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); g_assert_false(triggered); qemu_clock_step(2000000 * 2); @@ -424,19 +552,42 @@ static void check_run_with_delta_0(gconstpointer arg) ptimer_set_count(ptimer, 0); ptimer_run(ptimer, 0); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99); - g_assert_true(triggered); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : 99); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } + + triggered = false; + + qemu_clock_step(1); + + if (no_immediate_reload) { + qemu_clock_step(2000000); + } + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 99 : 98); + + if (no_immediate_reload && no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } triggered = false; - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 97); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97); g_assert_false(triggered); qemu_clock_step(2000000 * 98); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 98); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 99 : 98)); g_assert_true(triggered); ptimer_stop(ptimer); @@ -447,6 +598,8 @@ static void check_periodic_with_load_0(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); triggered = false; @@ -454,14 +607,46 @@ static void check_periodic_with_load_0(gconstpointer arg) ptimer_run(ptimer, 0); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } + + triggered = false; + + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (continuous_trigger || no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } + + triggered = false; + + ptimer_set_count(ptimer, 10); + ptimer_run(ptimer, 0); + + qemu_clock_step(2000000 * 10 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); g_assert_true(triggered); triggered = false; - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_false(triggered); + + if (continuous_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } ptimer_stop(ptimer); } @@ -471,6 +656,7 @@ static void check_oneshot_with_load_0(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); triggered = false; @@ -478,26 +664,30 @@ static void check_oneshot_with_load_0(gconstpointer arg) ptimer_run(ptimer, 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } triggered = false; - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_false(triggered); - triggered = false; - - qemu_clock_step(2000000 + 100000); - - g_assert_false(triggered); + if (no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } } static void add_ptimer_tests(uint8_t policy) { uint8_t *ppolicy = g_malloc(1); - char *policy_name = g_malloc(64); + char *policy_name = g_malloc0(256); *ppolicy = policy; @@ -505,6 +695,26 @@ static void add_ptimer_tests(uint8_t policy) g_sprintf(policy_name, "default"); } + if (policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) { + g_strlcat(policy_name, "wrap_after_one_period,", 256); + } + + if (policy & PTIMER_POLICY_CONTINUOUS_TRIGGER) { + g_strlcat(policy_name, "continuous_trigger,", 256); + } + + if (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) { + g_strlcat(policy_name, "no_immediate_trigger,", 256); + } + + if (policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD) { + g_strlcat(policy_name, "no_immediate_reload,", 256); + } + + if (policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) { + g_strlcat(policy_name, "no_counter_rounddown,", 256); + } + g_test_add_data_func( g_strdup_printf("/ptimer/set_count policy=%s", policy_name), ppolicy, check_set_count); @@ -550,6 +760,16 @@ static void add_ptimer_tests(uint8_t policy) ppolicy, check_oneshot_with_load_0); } +static void add_all_ptimer_policies_comb_tests(void) +{ + int last_policy = PTIMER_POLICY_NO_COUNTER_ROUND_DOWN; + int policy = PTIMER_POLICY_DEFAULT; + + for (; policy < (last_policy << 1); policy++) { + add_ptimer_tests(policy); + } +} + int main(int argc, char **argv) { int i; @@ -560,7 +780,7 @@ int main(int argc, char **argv) main_loop_tlg.tl[i] = g_new0(QEMUTimerList, 1); } - add_ptimer_tests(PTIMER_POLICY_DEFAULT); + add_all_ptimer_policies_comb_tests(); qtest_allowed = true; diff --git a/tests/ptimer-test.h b/tests/ptimer-test.h index 98d9b8f347..09ac56da9e 100644 --- a/tests/ptimer-test.h +++ b/tests/ptimer-test.h @@ -1,7 +1,7 @@ /* * QTest testcase for the ptimer * - * Author: Dmitry Osipenko <digetx@gmail.com> + * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.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. diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index d1e1ad8bd2..30e628f0f7 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -194,10 +194,9 @@ class TestSingleBlockdev(TestSingleDrive): def setUp(self): TestSingleDrive.setUp(self) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) - args = {'options': - {'driver': iotests.imgfmt, - 'node-name': self.qmp_target, - 'file': { 'filename': target_img, 'driver': 'file' } } } + args = {'driver': iotests.imgfmt, + 'node-name': self.qmp_target, + 'file': { 'filename': target_img, 'driver': 'file' } } result = self.vm.qmp("blockdev-add", **args) self.assert_qmp(result, 'return', {}) @@ -782,8 +781,8 @@ class TestRepairQuorum(iotests.QMPTestCase): self.vm.launch() #assemble the quorum block device from the individual files - args = { "options" : { "driver": "quorum", "node-name": "quorum0", - "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } } + args = { "driver": "quorum", "node-name": "quorum0", + "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } if self.has_quorum(): result = self.vm.qmp("blockdev-add", **args) self.assert_qmp(result, 'return', {}) diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 index a12125bd46..38d23fce6b 100755 --- a/tests/qemu-iotests/067 +++ b/tests/qemu-iotests/067 @@ -119,13 +119,11 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 index 6d0864cff6..48b495513f 100755 --- a/tests/qemu-iotests/071 +++ b/tests/qemu-iotests/071 @@ -107,25 +107,21 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive0", - "driver": "file", - "filename": "$TEST_IMG" - } + "node-name": "drive0", + "driver": "file", + "filename": "$TEST_IMG" } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "drive0-debug", - "file": { - "driver": "blkdebug", - "image": "drive0", - "inject-error": [{ - "event": "l2_load" - }] - } + "driver": "$IMGFMT", + "node-name": "drive0-debug", + "file": { + "driver": "blkdebug", + "image": "drive0", + "inject-error": [{ + "event": "l2_load" + }] } } } @@ -145,26 +141,22 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive0", - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "node-name": "drive0", + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "blkverify", - "node-name": "drive0-verify", - "test": "drive0", - "raw": { - "driver": "file", - "filename": "$TEST_IMG.base" - } + "driver": "blkverify", + "node-name": "drive0-verify", + "test": "drive0", + "raw": { + "driver": "file", + "filename": "$TEST_IMG.base" } } } @@ -184,27 +176,23 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive0", - "driver": "file", - "filename": "$TEST_IMG.base" - } + "node-name": "drive0", + "driver": "file", + "filename": "$TEST_IMG.base" } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "blkverify", - "node-name": "drive0-verify", - "test": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } - }, - "raw": "drive0" - } + "driver": "blkverify", + "node-name": "drive0-verify", + "test": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + } + }, + "raw": "drive0" } } { "execute": "human-monitor-command", @@ -223,30 +211,26 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive0", - "driver": "file", - "filename": "$TEST_IMG" - } + "node-name": "drive0", + "driver": "file", + "filename": "$TEST_IMG" } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "drive0-debug", - "file": { - "driver": "blkdebug", - "image": "drive0", - "inject-error": [{ - "event": "read_aio", - "state": 42 - }], - "set-state": [{ - "event": "write_aio", - "new_state": 42 - }] - } + "driver": "$IMGFMT", + "node-name": "drive0-debug", + "file": { + "driver": "blkdebug", + "image": "drive0", + "inject-error": [{ + "event": "read_aio", + "state": 42 + }], + "set-state": [{ + "event": "write_aio", + "new_state": 42 + }] } } } diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 index 0a809f3499..da3fb0984b 100755 --- a/tests/qemu-iotests/081 +++ b/tests/qemu-iotests/081 @@ -105,40 +105,36 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive2", - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_DIR/2.raw" - } + "node-name": "drive2", + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_DIR/2.raw" } } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "quorum", - "node-name": "drive0-quorum", - "vote-threshold": 2, - "children": [ - { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_DIR/1.raw" - } - }, - "drive2", - { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_DIR/3.raw" - } + "driver": "quorum", + "node-name": "drive0-quorum", + "vote-threshold": 2, + "children": [ + { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_DIR/1.raw" } - ] - } + }, + "drive2", + { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_DIR/3.raw" + } + } + ] } } { "execute": "human-monitor-command", diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085 index aa77eca77d..c53e97f067 100755 --- a/tests/qemu-iotests/085 +++ b/tests/qemu-iotests/085 @@ -100,11 +100,10 @@ function add_snapshot_image() _make_test_img -b "${base_image}" "$size" mv "${TEST_IMG}" "${snapshot_file}" cmd="{ 'execute': 'blockdev-add', 'arguments': - { 'options': - { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params} - 'file': - { 'driver': 'file', 'filename': '${snapshot_file}', - 'node-name': 'file_${1}' } } } }" + { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params} + 'file': + { 'driver': 'file', 'filename': '${snapshot_file}', + 'node-name': 'file_${1}' } } }" _send_qemu_cmd $h "${cmd}" "return" } diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 index b1ac71f2b8..9de57ddf6d 100755 --- a/tests/qemu-iotests/087 +++ b/tests/qemu-iotests/087 @@ -61,12 +61,10 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } @@ -81,25 +79,21 @@ run_qemu -drive driver=$IMGFMT,id=disk,node-name=test-node,file="$TEST_IMG" <<EO { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "test-node", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "test-node", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } @@ -114,14 +108,12 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG", - "aio": "native" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG", + "aio": "native" } } } @@ -137,13 +129,11 @@ run_qemu -S <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } @@ -154,13 +144,11 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } @@ -176,9 +164,7 @@ run_qemu -S <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "disk" - } + "node-name": "disk" } } { "execute": "quit" } diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index ffcb271b36..2ed393a548 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -53,7 +53,7 @@ class ThrottleTestCase(iotests.QMPTestCase): result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) self.assert_qmp(result, 'return', {}) - def do_test_throttle(self, ndrives, seconds, params): + def do_test_throttle(self, ndrives, seconds, params, first_drive = 0): def check_limit(limit, num): # IO throttling algorithm is discrete, allow 10% error so the test # is more robust @@ -85,12 +85,14 @@ class ThrottleTestCase(iotests.QMPTestCase): # Send I/O requests to all drives for i in range(rd_nr): for drive in range(0, ndrives): - self.vm.hmp_qemu_io("drive%d" % drive, "aio_read %d %d" % + idx = first_drive + drive + self.vm.hmp_qemu_io("drive%d" % idx, "aio_read %d %d" % (i * rq_size, rq_size)) for i in range(wr_nr): for drive in range(0, ndrives): - self.vm.hmp_qemu_io("drive%d" % drive, "aio_write %d %d" % + idx = first_drive + drive + self.vm.hmp_qemu_io("drive%d" % idx, "aio_write %d %d" % (i * rq_size, rq_size)) # We'll store the I/O stats for each drive in these arrays @@ -105,15 +107,17 @@ class ThrottleTestCase(iotests.QMPTestCase): # Read the stats before advancing the clock for i in range(0, ndrives): + idx = first_drive + i start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \ - start_wr_iops[i] = self.blockstats('drive%d' % i) + start_wr_iops[i] = self.blockstats('drive%d' % idx) self.vm.qtest("clock_step %d" % ns) # Read the stats after advancing the clock for i in range(0, ndrives): + idx = first_drive + i end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \ - end_wr_iops[i] = self.blockstats('drive%d' % i) + end_wr_iops[i] = self.blockstats('drive%d' % idx) # Check that the I/O is within the limits and evenly distributed for i in range(0, ndrives): @@ -129,6 +133,7 @@ class ThrottleTestCase(iotests.QMPTestCase): self.assertTrue(check_limit(params['iops_rd'], rd_iops)) self.assertTrue(check_limit(params['iops_wr'], wr_iops)) + # Connect N drives to a VM and test I/O in all of them def test_all(self): params = {"bps": 4096, "bps_rd": 4096, @@ -146,6 +151,24 @@ class ThrottleTestCase(iotests.QMPTestCase): self.configure_throttle(ndrives, limits) self.do_test_throttle(ndrives, 5, limits) + # Connect N drives to a VM and test I/O in just one of them a time + def test_one(self): + params = {"bps": 4096, + "bps_rd": 4096, + "bps_wr": 4096, + "iops": 10, + "iops_rd": 10, + "iops_wr": 10, + } + # Repeat the test for each one of the drives + for drive in range(0, self.max_drives): + # Pick each out of all possible params and test + for tk in params: + limits = dict([(k, 0) for k in params]) + limits[tk] = params[tk] * self.max_drives + self.configure_throttle(self.max_drives, limits) + self.do_test_throttle(1, 5, limits, drive) + def test_burst(self): params = {"bps": 4096, "bps_rd": 4096, diff --git a/tests/qemu-iotests/093.out b/tests/qemu-iotests/093.out index 914e3737bd..2f7d3902f2 100644 --- a/tests/qemu-iotests/093.out +++ b/tests/qemu-iotests/093.out @@ -1,5 +1,5 @@ -..... +....... ---------------------------------------------------------------------- -Ran 5 tests +Ran 7 tests OK diff --git a/tests/qemu-iotests/117 b/tests/qemu-iotests/117 index 5b28039e17..e955d52de3 100755 --- a/tests/qemu-iotests/117 +++ b/tests/qemu-iotests/117 @@ -52,16 +52,16 @@ _send_qemu_cmd $QEMU_HANDLE \ _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'blockdev-add', - 'arguments': { 'options': { 'node-name': 'protocol', - 'driver': 'file', - 'filename': '$TEST_IMG' } } }" \ + 'arguments': { 'node-name': 'protocol', + 'driver': 'file', + 'filename': '$TEST_IMG' } }" \ 'return' _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'blockdev-add', - 'arguments': { 'options': { 'node-name': 'format', - 'driver': '$IMGFMT', - 'file': 'protocol' } } }" \ + 'arguments': { 'node-name': 'format', + 'driver': '$IMGFMT', + 'file': 'protocol' } }" \ 'return' _send_qemu_cmd $QEMU_HANDLE \ diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118 index e63a40fa94..8a9e838c90 100755 --- a/tests/qemu-iotests/118 +++ b/tests/qemu-iotests/118 @@ -229,10 +229,10 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): def test_cycle(self): result = self.vm.qmp('blockdev-add', - options={'node-name': 'new', - 'driver': iotests.imgfmt, - 'file': {'filename': new_img, - 'driver': 'file'}}) + node_name='new', + driver=iotests.imgfmt, + file={'filename': new_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) if self.device_name is not None: @@ -309,10 +309,10 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): return result = self.vm.qmp('blockdev-add', - options={'node-name': 'new', - 'driver': iotests.imgfmt, - 'file': {'filename': new_img, - 'driver': 'file'}}) + node_name='new', + driver=iotests.imgfmt, + file={'filename': new_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', @@ -341,10 +341,10 @@ class TestInitiallyFilled(GeneralChangeTestsBaseClass): def test_insert_on_filled(self): result = self.vm.qmp('blockdev-add', - options={'node-name': 'new', - 'driver': iotests.imgfmt, - 'file': {'filename': new_img, - 'driver': 'file'}}) + node_name='new', + driver=iotests.imgfmt, + file={'filename': new_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('blockdev-open-tray', device='drive0') @@ -609,11 +609,11 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) result = self.vm.qmp('blockdev-add', - options={'node-name': 'new', - 'driver': iotests.imgfmt, - 'read-only': True, - 'file': {'filename': new_img, - 'driver': 'file'}}) + node_name='new', + driver=iotests.imgfmt, + read_only=True, + file={'filename': new_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('query-block') @@ -663,10 +663,10 @@ class TestBlockJobsAfterCycle(ChangeBaseClass): self.assert_qmp_absent(result, 'return[0]/inserted') result = self.vm.qmp('blockdev-add', - options={'node-name': 'node0', - 'driver': iotests.imgfmt, - 'file': {'filename': old_img, - 'driver': 'file'}}) + node_name='node0', + driver=iotests.imgfmt, + file={'filename': old_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index 2f0bc24cd0..f06938eeb7 100644 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -416,10 +416,10 @@ class TestIncrementalBackup(TestIncrementalBackupBase): ('0xcd', '32M', '124k'))) # Create a blkdebug interface to this img as 'drive1' - result = self.vm.qmp('blockdev-add', options={ - 'node-name': drive1['id'], - 'driver': drive1['fmt'], - 'file': { + result = self.vm.qmp('blockdev-add', + node_name=drive1['id'], + driver=drive1['fmt'], + file={ 'driver': 'blkdebug', 'image': { 'driver': 'file', @@ -438,7 +438,7 @@ class TestIncrementalBackup(TestIncrementalBackupBase): 'once': True }], } - }) + ) self.assert_qmp(result, 'return', {}) # Create bitmaps and full backups for both drives @@ -560,10 +560,10 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): ''' drive0 = self.drives[0] - result = self.vm.qmp('blockdev-add', options={ - 'node-name': drive0['id'], - 'driver': drive0['fmt'], - 'file': { + result = self.vm.qmp('blockdev-add', + node_name=drive0['id'], + driver=drive0['fmt'], + file={ 'driver': 'blkdebug', 'image': { 'driver': 'file', @@ -582,7 +582,7 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): 'once': True }], } - }) + ) self.assert_qmp(result, 'return', {}) self.create_anchor_backup(drive0) diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139 index 47a4c26e29..6a0f6ca569 100644 --- a/tests/qemu-iotests/139 +++ b/tests/qemu-iotests/139 @@ -57,7 +57,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'file': {'driver': 'file', 'node-name': file_node, 'filename': base_img}} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(node) self.checkBlockDriverState(file_node) @@ -72,7 +72,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'backing': '', 'file': {'driver': 'file', 'filename': new_img}} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(node) @@ -185,7 +185,7 @@ class TestBlockdevDel(iotests.QMPTestCase): opts = {'driver': 'blkdebug', 'node-name': debug, 'image': image} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(node) self.checkBlockDriverState(debug) @@ -210,7 +210,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'node-name': blkverify, 'test': node_0, 'raw': node_1} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(test) self.checkBlockDriverState(raw) @@ -234,7 +234,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'node-name': quorum, 'vote-threshold': 1, 'children': [ child_0, child_1 ]} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(child0) self.checkBlockDriverState(child1) diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index c092d872dc..3ba79f027a 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -50,13 +50,12 @@ test_blockjob() _send_qemu_cmd $QEMU_HANDLE \ "{'execute': 'blockdev-add', 'arguments': { - 'options': { - 'node-name': 'drv0', - 'driver': '$IMGFMT', - 'file': { - 'driver': 'file', - 'filename': '$TEST_IMG' - }}}}" \ + 'node-name': 'drv0', + 'driver': '$IMGFMT', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + }}}" \ 'return' _send_qemu_cmd $QEMU_HANDLE \ diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index 4057b5e2aa..0b86ea4e5c 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -63,10 +63,10 @@ class BaseClass(iotests.QMPTestCase): # Add the BDS via blockdev-add so it stays around after the mirror block # job has been completed result = self.vm.qmp('blockdev-add', - options={'node-name': 'source', - 'driver': iotests.imgfmt, - 'file': {'driver': 'file', - 'filename': source_img}}) + node_name='source', + driver=iotests.imgfmt, + file={'driver': 'file', + 'filename': source_img}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('x-blockdev-insert-medium', @@ -90,7 +90,7 @@ class BaseClass(iotests.QMPTestCase): if self.target_blockdev_backing: options['backing'] = self.target_blockdev_backing - result = self.vm.qmp('blockdev-add', options=options) + result = self.vm.qmp('blockdev-add', **options) self.assert_qmp(result, 'return', {}) def tearDown(self): diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162 index 0b43ea3395..f8eecb325b 100755 --- a/tests/qemu-iotests/162 +++ b/tests/qemu-iotests/162 @@ -43,16 +43,26 @@ echo '=== NBD ===' $QEMU_IMG info 'json:{"driver": "nbd", "host": 42}' # And this should not treat @port as if it had not been specified -# (We cannot use localhost with an invalid port here, but we need to use a -# non-existing domain, because otherwise the error message will not contain -# the port) -$QEMU_IMG info 'json:{"driver": "nbd", "host": "does.not.exist.example.com", "port": 42}' +# (We need to set up a server here, because the error message for "Connection +# refused" does not contain the destination port) + +# Launching qemu-nbd is done in a loop: We try to set up an NBD server on some +# random port and continue until success, i.e. until we have found a port that +# is not in use yet. +while true; do + port=$((RANDOM + 32768)) + if $QEMU_NBD -p $port -f raw --fork null-co:// 2> /dev/null; then + break + fi +done + +$QEMU_IMG info "json:{'driver': 'nbd', 'host': 'localhost', 'port': $port}" \ + | grep '^image' | sed -e "s/$port/PORT/" # This is a test for NBD's bdrv_refresh_filename() implementation: It expects # either host or path to be set, but it must not assume that they are set to # strings in the options QDict -$QEMU_NBD -k "$PWD/42" -f raw null-co:// & -sleep 0.5 +$QEMU_NBD -k "$PWD/42" -f raw --fork null-co:// $QEMU_IMG info 'json:{"driver": "nbd", "path": 42}' | grep '^image' rm -f 42 diff --git a/tests/qemu-iotests/162.out b/tests/qemu-iotests/162.out index 9bba72353a..3c5be2c569 100644 --- a/tests/qemu-iotests/162.out +++ b/tests/qemu-iotests/162.out @@ -2,7 +2,7 @@ QA output created by 162 === NBD === qemu-img: Could not open 'json:{"driver": "nbd", "host": 42}': Failed to connect socket: Invalid argument -qemu-img: Could not open 'json:{"driver": "nbd", "host": "does.not.exist.example.com", "port": 42}': address resolution failed for does.not.exist.example.com:42: Name or service not known +image: nbd://localhost:PORT image: nbd+unix://?socket=42 === SSH === diff --git a/tests/test-arm-mptimer.c b/tests/test-arm-mptimer.c new file mode 100644 index 0000000000..cb8f2df914 --- /dev/null +++ b/tests/test-arm-mptimer.c @@ -0,0 +1,1105 @@ +/* + * QTest testcase for the ARM MPTimer + * + * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.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. + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "libqtest.h" + +#define TIMER_BLOCK_SCALE(s) ((((s) & 0xff) + 1) * 10) + +#define TIMER_BLOCK_STEP(scaler, steps_nb) \ + clock_step(TIMER_BLOCK_SCALE(scaler) * (int64_t)(steps_nb) + 1) + +#define TIMER_BASE_PHYS 0x1e000600 + +#define TIMER_LOAD 0x00 +#define TIMER_COUNTER 0x04 +#define TIMER_CONTROL 0x08 +#define TIMER_INTSTAT 0x0C + +#define TIMER_CONTROL_ENABLE (1 << 0) +#define TIMER_CONTROL_PERIODIC (1 << 1) +#define TIMER_CONTROL_IT_ENABLE (1 << 2) +#define TIMER_CONTROL_PRESCALER(p) (((p) & 0xff) << 8) + +#define PERIODIC 1 +#define ONESHOT 0 +#define NOSCALE 0 + +static int nonscaled = NOSCALE; +static int scaled = 122; + +static void timer_load(uint32_t load) +{ + writel(TIMER_BASE_PHYS + TIMER_LOAD, load); +} + +static void timer_start(int periodic, uint32_t scale) +{ + uint32_t ctl = TIMER_CONTROL_ENABLE | TIMER_CONTROL_PRESCALER(scale); + + if (periodic) { + ctl |= TIMER_CONTROL_PERIODIC; + } + + writel(TIMER_BASE_PHYS + TIMER_CONTROL, ctl); +} + +static void timer_stop(void) +{ + writel(TIMER_BASE_PHYS + TIMER_CONTROL, 0); +} + +static void timer_int_clr(void) +{ + writel(TIMER_BASE_PHYS + TIMER_INTSTAT, 1); +} + +static void timer_reset(void) +{ + timer_stop(); + timer_load(0); + timer_int_clr(); +} + +static uint32_t timer_get_and_clr_int_sts(void) +{ + uint32_t int_sts = readl(TIMER_BASE_PHYS + TIMER_INTSTAT); + + if (int_sts) { + timer_int_clr(); + } + + return int_sts; +} + +static uint32_t timer_counter(void) +{ + return readl(TIMER_BASE_PHYS + TIMER_COUNTER); +} + +static void timer_set_counter(uint32_t value) +{ + writel(TIMER_BASE_PHYS + TIMER_COUNTER, value); +} + +static void test_timer_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 9999); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + g_assert_cmpuint(timer_counter(), ==, 9990000); + + TIMER_BLOCK_STEP(scaler, 9990000); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + TIMER_BLOCK_STEP(scaler, 9990000); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_pause(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(999999999); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 999); + + g_assert_cmpuint(timer_counter(), ==, 999999000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 9000); + + g_assert_cmpuint(timer_counter(), ==, 999990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_stop(); + + g_assert_cmpuint(timer_counter(), ==, 999990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 90000); + + g_assert_cmpuint(timer_counter(), ==, 999990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 999990000); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_counter(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 999990000); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + g_assert_cmpuint(timer_counter(), ==, 0); +} + +static void test_timer_reload(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 90000); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(UINT32_MAX); + + TIMER_BLOCK_STEP(scaler, 90000); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + int repeat = 10; + + timer_reset(); + timer_load(100); + timer_start(PERIODIC, scaler); + + while (repeat--) { + clock_step(TIMER_BLOCK_SCALE(scaler) * (101 + repeat) + 1); + + g_assert_cmpuint(timer_counter(), ==, 100 - repeat); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + clock_step(TIMER_BLOCK_SCALE(scaler) * (101 - repeat) - 1); + } +} + +static void test_timer_oneshot_to_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(10000); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1000); + + g_assert_cmpuint(timer_counter(), ==, 9000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 14001); + + g_assert_cmpuint(timer_counter(), ==, 5000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_periodic_to_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(99999999); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 999); + + g_assert_cmpuint(timer_counter(), ==, 99999000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 99999009); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_prescaler(void) +{ + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 9999998); + + g_assert_cmpuint(timer_counter(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, 0xAB); + + TIMER_BLOCK_STEP(0xAB, 9999998); + + g_assert_cmpuint(timer_counter(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(0xAB, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_prescaler_on_the_fly(void) +{ + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 999); + + g_assert_cmpuint(timer_counter(), ==, 9999000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, 0xAB); + + TIMER_BLOCK_STEP(0xAB, 9000); + + g_assert_cmpuint(timer_counter(), ==, 9990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_oneshot_counter_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + TIMER_BLOCK_STEP(scaler, 10); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_set_periodic_counter_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - (scaler ? 0 : 1)); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_reset(); + timer_set_counter(UINT32_MAX); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_noload_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_noload_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_zero_load_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_zero_load_oneshot_to_nonzero(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(999); + + TIMER_BLOCK_STEP(scaler, 1001); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_zero_load_periodic_to_nonzero(gconstpointer arg) +{ + int scaler = *((int *) arg); + int i; + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_load(1999999); + + for (i = 1; i < 10; i++) { + TIMER_BLOCK_STEP(scaler, 2000001); + + g_assert_cmpuint(timer_counter(), ==, 1999999 - i); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + } +} + +static void test_timer_nonzero_load_oneshot_to_zero(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(UINT32_MAX); + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_nonzero_load_periodic_to_zero(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_load(UINT32_MAX); + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_set_periodic_counter_on_the_fly(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX / 2); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX / 2 - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(UINT32_MAX); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_enable_and_set_counter(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_set_counter(UINT32_MAX); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_counter_and_enable(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_set_counter(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_counter_disabled(void) +{ + timer_reset(); + timer_set_counter(999999999); + + TIMER_BLOCK_STEP(NOSCALE, 100); + + g_assert_cmpuint(timer_counter(), ==, 999999999); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_load_disabled(void) +{ + timer_reset(); + timer_load(999999999); + + TIMER_BLOCK_STEP(NOSCALE, 100); + + g_assert_cmpuint(timer_counter(), ==, 999999999); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_oneshot_with_counter_0_on_start(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(999); + timer_set_counter(0); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_periodic_with_counter_0_on_start(gconstpointer arg) +{ + int scaler = *((int *) arg); + int i; + + timer_reset(); + timer_load(UINT32_MAX); + timer_set_counter(0); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 100); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 200); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_reset(); + timer_load(1999999); + timer_set_counter(0); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + for (i = 2 - (!!scaler ? 1 : 0); i < 10; i++) { + TIMER_BLOCK_STEP(scaler, 2000001); + + g_assert_cmpuint(timer_counter(), ==, 1999999 - i); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + } +} + +static void test_periodic_counter(gconstpointer arg) +{ + const int test_load = 10; + int scaler = *((int *) arg); + int test_val; + + timer_reset(); + timer_load(test_load); + timer_start(PERIODIC, scaler); + + clock_step(1); + + for (test_val = 0; test_val <= test_load; test_val++) { + clock_step(TIMER_BLOCK_SCALE(scaler) * test_load); + g_assert_cmpint(timer_counter(), ==, test_val); + } +} + +static void test_timer_set_counter_periodic_with_zero_load(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_set_counter(999); + + TIMER_BLOCK_STEP(scaler, 999); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_set_oneshot_load_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_periodic_load_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); +} + +static void test_deferred_trigger(void) +{ + int mode = ONESHOT; + +again: + timer_reset(); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_counter(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(2); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(0); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + if (mode == ONESHOT) { + mode = PERIODIC; + goto again; + } +} + +static void test_timer_zero_load_mode_switch(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(0); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 1); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot(void) +{ + timer_reset(); + timer_load(0); + timer_start(PERIODIC, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic(void) +{ + timer_reset(); + timer_load(0); + timer_start(ONESHOT, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic(void) +{ + timer_reset(); + timer_load(0); + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot(void) +{ + timer_reset(); + timer_load(0); + timer_start(PERIODIC, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +int main(int argc, char **argv) +{ + int *scaler = &nonscaled; + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("mptimer/deferred_trigger", test_deferred_trigger); + qtest_add_func("mptimer/load_disabled", test_timer_load_disabled); + qtest_add_func("mptimer/set_counter_disabled", test_timer_set_counter_disabled); + qtest_add_func("mptimer/zero_load_prescaled_periodic_to_nonscaled_oneshot", + test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot); + qtest_add_func("mptimer/zero_load_prescaled_oneshot_to_nonscaled_periodic", + test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic); + qtest_add_func("mptimer/zero_load_nonscaled_oneshot_to_prescaled_periodic", + test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic); + qtest_add_func("mptimer/zero_load_nonscaled_periodic_to_prescaled_oneshot", + test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot); + qtest_add_func("mptimer/prescaler", test_timer_prescaler); + qtest_add_func("mptimer/prescaler_on_the_fly", test_timer_prescaler_on_the_fly); + +tests_with_prescaler_arg: + qtest_add_data_func( + g_strdup_printf("mptimer/oneshot scaler=%d", *scaler), + scaler, test_timer_oneshot); + qtest_add_data_func( + g_strdup_printf("mptimer/pause scaler=%d", *scaler), + scaler, test_timer_pause); + qtest_add_data_func( + g_strdup_printf("mptimer/reload scaler=%d", *scaler), + scaler, test_timer_reload); + qtest_add_data_func( + g_strdup_printf("mptimer/periodic scaler=%d", *scaler), + scaler, test_timer_periodic); + qtest_add_data_func( + g_strdup_printf("mptimer/oneshot_to_periodic scaler=%d", *scaler), + scaler, test_timer_oneshot_to_periodic); + qtest_add_data_func( + g_strdup_printf("mptimer/periodic_to_oneshot scaler=%d", *scaler), + scaler, test_timer_periodic_to_oneshot); + qtest_add_data_func( + g_strdup_printf("mptimer/set_oneshot_counter_to_0 scaler=%d", *scaler), + scaler, test_timer_set_oneshot_counter_to_0); + qtest_add_data_func( + g_strdup_printf("mptimer/set_periodic_counter_to_0 scaler=%d", *scaler), + scaler, test_timer_set_periodic_counter_to_0); + qtest_add_data_func( + g_strdup_printf("mptimer/noload_oneshot scaler=%d", *scaler), + scaler, test_timer_noload_oneshot); + qtest_add_data_func( + g_strdup_printf("mptimer/noload_periodic scaler=%d", *scaler), + scaler, test_timer_noload_periodic); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_oneshot scaler=%d", *scaler), + scaler, test_timer_zero_load_oneshot); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_periodic scaler=%d", *scaler), + scaler, test_timer_zero_load_periodic); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_oneshot_to_nonzero scaler=%d", *scaler), + scaler, test_timer_zero_load_oneshot_to_nonzero); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_periodic_to_nonzero scaler=%d", *scaler), + scaler, test_timer_zero_load_periodic_to_nonzero); + qtest_add_data_func( + g_strdup_printf("mptimer/nonzero_load_oneshot_to_zero scaler=%d", *scaler), + scaler, test_timer_nonzero_load_oneshot_to_zero); + qtest_add_data_func( + g_strdup_printf("mptimer/nonzero_load_periodic_to_zero scaler=%d", *scaler), + scaler, test_timer_nonzero_load_periodic_to_zero); + qtest_add_data_func( + g_strdup_printf("mptimer/set_periodic_counter_on_the_fly scaler=%d", *scaler), + scaler, test_timer_set_periodic_counter_on_the_fly); + qtest_add_data_func( + g_strdup_printf("mptimer/enable_and_set_counter scaler=%d", *scaler), + scaler, test_timer_enable_and_set_counter); + qtest_add_data_func( + g_strdup_printf("mptimer/set_counter_and_enable scaler=%d", *scaler), + scaler, test_timer_set_counter_and_enable); + qtest_add_data_func( + g_strdup_printf("mptimer/oneshot_with_counter_0_on_start scaler=%d", *scaler), + scaler, test_timer_oneshot_with_counter_0_on_start); + qtest_add_data_func( + g_strdup_printf("mptimer/periodic_with_counter_0_on_start scaler=%d", *scaler), + scaler, test_timer_periodic_with_counter_0_on_start); + qtest_add_data_func( + g_strdup_printf("mptimer/periodic_counter scaler=%d", *scaler), + scaler, test_periodic_counter); + qtest_add_data_func( + g_strdup_printf("mptimer/set_counter_periodic_with_zero_load scaler=%d", *scaler), + scaler, test_timer_set_counter_periodic_with_zero_load); + qtest_add_data_func( + g_strdup_printf("mptimer/set_oneshot_load_to_0 scaler=%d", *scaler), + scaler, test_timer_set_oneshot_load_to_0); + qtest_add_data_func( + g_strdup_printf("mptimer/set_periodic_load_to_0 scaler=%d", *scaler), + scaler, test_timer_set_periodic_load_to_0); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_mode_switch scaler=%d", *scaler), + scaler, test_timer_zero_load_mode_switch); + + if (scaler == &nonscaled) { + scaler = &scaled; + goto tests_with_prescaler_arg; + } + + qtest_start("-machine vexpress-a9"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index c0e9895fb9..9b7495cc32 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -11,6 +11,8 @@ #include "qemu/osdep.h" #include "qemu/hbitmap.h" +#include "qemu/bitmap.h" +#include "block/block.h" #define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6) @@ -20,6 +22,7 @@ typedef struct TestHBitmapData { HBitmap *hb; + HBitmap *meta; unsigned long *bits; size_t size; size_t old_size; @@ -91,6 +94,14 @@ static void hbitmap_test_init(TestHBitmapData *data, } } +static void hbitmap_test_init_meta(TestHBitmapData *data, + uint64_t size, int granularity, + int meta_chunk) +{ + hbitmap_test_init(data, size, granularity); + data->meta = hbitmap_create_meta(data->hb, meta_chunk); +} + static inline size_t hbitmap_test_array_size(size_t bits) { size_t n = DIV_ROUND_UP(bits, BITS_PER_LONG); @@ -133,6 +144,9 @@ static void hbitmap_test_teardown(TestHBitmapData *data, const void *unused) { if (data->hb) { + if (data->meta) { + hbitmap_free_meta(data->hb); + } hbitmap_free(data->hb); data->hb = NULL; } @@ -634,6 +648,249 @@ static void test_hbitmap_truncate_shrink_large(TestHBitmapData *data, hbitmap_test_truncate(data, size, -diff, 0); } +static void hbitmap_check_meta(TestHBitmapData *data, + int64_t start, int count) +{ + int64_t i; + + for (i = 0; i < data->size; i++) { + if (i >= start && i < start + count) { + g_assert(hbitmap_get(data->meta, i)); + } else { + g_assert(!hbitmap_get(data->meta, i)); + } + } +} + +static void hbitmap_test_meta(TestHBitmapData *data, + int64_t start, int count, + int64_t check_start, int check_count) +{ + hbitmap_reset_all(data->hb); + hbitmap_reset_all(data->meta); + + /* Test "unset" -> "unset" will not update meta. */ + hbitmap_reset(data->hb, start, count); + hbitmap_check_meta(data, 0, 0); + + /* Test "unset" -> "set" will update meta */ + hbitmap_set(data->hb, start, count); + hbitmap_check_meta(data, check_start, check_count); + + /* Test "set" -> "set" will not update meta */ + hbitmap_reset_all(data->meta); + hbitmap_set(data->hb, start, count); + hbitmap_check_meta(data, 0, 0); + + /* Test "set" -> "unset" will update meta */ + hbitmap_reset_all(data->meta); + hbitmap_reset(data->hb, start, count); + hbitmap_check_meta(data, check_start, check_count); +} + +static void hbitmap_test_meta_do(TestHBitmapData *data, int chunk_size) +{ + uint64_t size = chunk_size * 100; + hbitmap_test_init_meta(data, size, 0, chunk_size); + + hbitmap_test_meta(data, 0, 1, 0, chunk_size); + hbitmap_test_meta(data, 0, chunk_size, 0, chunk_size); + hbitmap_test_meta(data, chunk_size - 1, 1, 0, chunk_size); + hbitmap_test_meta(data, chunk_size - 1, 2, 0, chunk_size * 2); + hbitmap_test_meta(data, chunk_size - 1, chunk_size + 1, 0, chunk_size * 2); + hbitmap_test_meta(data, chunk_size - 1, chunk_size + 2, 0, chunk_size * 3); + hbitmap_test_meta(data, 7 * chunk_size - 1, chunk_size + 2, + 6 * chunk_size, chunk_size * 3); + hbitmap_test_meta(data, size - 1, 1, size - chunk_size, chunk_size); + hbitmap_test_meta(data, 0, size, 0, size); +} + +static void test_hbitmap_meta_byte(TestHBitmapData *data, const void *unused) +{ + hbitmap_test_meta_do(data, BITS_PER_BYTE); +} + +static void test_hbitmap_meta_word(TestHBitmapData *data, const void *unused) +{ + hbitmap_test_meta_do(data, BITS_PER_LONG); +} + +static void test_hbitmap_meta_sector(TestHBitmapData *data, const void *unused) +{ + hbitmap_test_meta_do(data, BDRV_SECTOR_SIZE * BITS_PER_BYTE); +} + +/** + * Create an HBitmap and test set/unset. + */ +static void test_hbitmap_meta_one(TestHBitmapData *data, const void *unused) +{ + int i; + int64_t offsets[] = { + 0, 1, L1 - 1, L1, L1 + 1, L2 - 1, L2, L2 + 1, L3 - 1, L3, L3 + 1 + }; + + hbitmap_test_init_meta(data, L3 * 2, 0, 1); + for (i = 0; i < ARRAY_SIZE(offsets); i++) { + hbitmap_test_meta(data, offsets[i], 1, offsets[i], 1); + hbitmap_test_meta(data, offsets[i], L1, offsets[i], L1); + hbitmap_test_meta(data, offsets[i], L2, offsets[i], L2); + } +} + +static void test_hbitmap_serialize_granularity(TestHBitmapData *data, + const void *unused) +{ + int r; + + hbitmap_test_init(data, L3 * 2, 3); + r = hbitmap_serialization_granularity(data->hb); + g_assert_cmpint(r, ==, 64 << 3); +} + +static void test_hbitmap_meta_zero(TestHBitmapData *data, const void *unused) +{ + hbitmap_test_init_meta(data, 0, 0, 1); + + hbitmap_check_meta(data, 0, 0); +} + +static void hbitmap_test_serialize_range(TestHBitmapData *data, + uint8_t *buf, size_t buf_size, + uint64_t pos, uint64_t count) +{ + size_t i; + unsigned long *el = (unsigned long *)buf; + + assert(hbitmap_granularity(data->hb) == 0); + hbitmap_reset_all(data->hb); + memset(buf, 0, buf_size); + if (count) { + hbitmap_set(data->hb, pos, count); + } + hbitmap_serialize_part(data->hb, buf, 0, data->size); + + /* Serialized buffer is inherently LE, convert it back manually to test */ + for (i = 0; i < buf_size / sizeof(unsigned long); i++) { + el[i] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[i]) : le64_to_cpu(el[i])); + } + + for (i = 0; i < data->size; i++) { + int is_set = test_bit(i, (unsigned long *)buf); + if (i >= pos && i < pos + count) { + g_assert(is_set); + } else { + g_assert(!is_set); + } + } + + /* Re-serialize for deserialization testing */ + memset(buf, 0, buf_size); + hbitmap_serialize_part(data->hb, buf, 0, data->size); + hbitmap_reset_all(data->hb); + hbitmap_deserialize_part(data->hb, buf, 0, data->size, true); + + for (i = 0; i < data->size; i++) { + int is_set = hbitmap_get(data->hb, i); + if (i >= pos && i < pos + count) { + g_assert(is_set); + } else { + g_assert(!is_set); + } + } +} + +static void test_hbitmap_serialize_basic(TestHBitmapData *data, + const void *unused) +{ + int i, j; + size_t buf_size; + uint8_t *buf; + uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 }; + int num_positions = sizeof(positions) / sizeof(positions[0]); + + hbitmap_test_init(data, L3, 0); + buf_size = hbitmap_serialization_size(data->hb, 0, data->size); + buf = g_malloc0(buf_size); + + for (i = 0; i < num_positions; i++) { + for (j = 0; j < num_positions; j++) { + hbitmap_test_serialize_range(data, buf, buf_size, + positions[i], + MIN(positions[j], L3 - positions[i])); + } + } + + g_free(buf); +} + +static void test_hbitmap_serialize_part(TestHBitmapData *data, + const void *unused) +{ + int i, j, k; + size_t buf_size; + uint8_t *buf; + uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 }; + int num_positions = sizeof(positions) / sizeof(positions[0]); + + hbitmap_test_init(data, L3, 0); + buf_size = L2; + buf = g_malloc0(buf_size); + + for (i = 0; i < num_positions; i++) { + hbitmap_set(data->hb, positions[i], 1); + } + + for (i = 0; i < data->size; i += buf_size) { + unsigned long *el = (unsigned long *)buf; + hbitmap_serialize_part(data->hb, buf, i, buf_size); + for (j = 0; j < buf_size / sizeof(unsigned long); j++) { + el[j] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[j]) : le64_to_cpu(el[j])); + } + + for (j = 0; j < buf_size; j++) { + bool should_set = false; + for (k = 0; k < num_positions; k++) { + if (positions[k] == j + i) { + should_set = true; + break; + } + } + g_assert_cmpint(should_set, ==, test_bit(j, (unsigned long *)buf)); + } + } + + g_free(buf); +} + +static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, + const void *unused) +{ + int i; + HBitmapIter iter; + int64_t next; + uint64_t min_l1 = MAX(L1, 64); + uint64_t positions[] = { 0, min_l1, L2, L3 - min_l1}; + int num_positions = sizeof(positions) / sizeof(positions[0]); + + hbitmap_test_init(data, L3, 0); + + for (i = 0; i < num_positions; i++) { + hbitmap_set(data->hb, positions[i], L1); + } + + for (i = 0; i < num_positions; i++) { + hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); + hbitmap_iter_init(&iter, data->hb, 0); + next = hbitmap_iter_next(&iter); + if (i == num_positions - 1) { + g_assert_cmpint(next, ==, -1); + } else { + g_assert_cmpint(next, ==, positions[i + 1]); + } + } +} + static void hbitmap_test_add(const char *testpath, void (*test_func)(TestHBitmapData *data, const void *user_data)) { @@ -683,6 +940,21 @@ int main(int argc, char **argv) test_hbitmap_truncate_grow_large); hbitmap_test_add("/hbitmap/truncate/shrink/large", test_hbitmap_truncate_shrink_large); + + hbitmap_test_add("/hbitmap/meta/zero", test_hbitmap_meta_zero); + hbitmap_test_add("/hbitmap/meta/one", test_hbitmap_meta_one); + hbitmap_test_add("/hbitmap/meta/byte", test_hbitmap_meta_byte); + hbitmap_test_add("/hbitmap/meta/word", test_hbitmap_meta_word); + hbitmap_test_add("/hbitmap/meta/sector", test_hbitmap_meta_sector); + + hbitmap_test_add("/hbitmap/serialize/granularity", + test_hbitmap_serialize_granularity); + hbitmap_test_add("/hbitmap/serialize/basic", + test_hbitmap_serialize_basic); + hbitmap_test_add("/hbitmap/serialize/part", + test_hbitmap_serialize_part); + hbitmap_test_add("/hbitmap/serialize/zeroes", + test_hbitmap_serialize_zeroes); g_test_run(); return 0; diff --git a/translate-all.c b/translate-all.c index 8ca393c9d0..86b45a19c5 100644 --- a/translate-all.c +++ b/translate-all.c @@ -97,25 +97,24 @@ typedef struct PageDesc { #define V_L2_BITS 10 #define V_L2_SIZE (1 << V_L2_BITS) -/* The bits remaining after N lower levels of page tables. */ -#define V_L1_BITS_REM \ - ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS) - -#if V_L1_BITS_REM < 4 -#define V_L1_BITS (V_L1_BITS_REM + V_L2_BITS) -#else -#define V_L1_BITS V_L1_BITS_REM -#endif - -#define V_L1_SIZE ((target_ulong)1 << V_L1_BITS) - -#define V_L1_SHIFT (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - V_L1_BITS) - uintptr_t qemu_host_page_size; intptr_t qemu_host_page_mask; -/* The bottom level has pointers to PageDesc */ -static void *l1_map[V_L1_SIZE]; +/* + * L1 Mapping properties + */ +static int v_l1_size; +static int v_l1_shift; +static int v_l2_levels; + +/* The bottom level has pointers to PageDesc, and is indexed by + * anything from 4 to (V_L2_BITS + 3) bits, depending on target page size. + */ +#define V_L1_MIN_BITS 4 +#define V_L1_MAX_BITS (V_L2_BITS + 3) +#define V_L1_MAX_SIZE (1 << V_L1_MAX_BITS) + +static void *l1_map[V_L1_MAX_SIZE]; /* code generation context */ TCGContext tcg_ctx; @@ -125,6 +124,26 @@ TCGContext tcg_ctx; __thread int have_tb_lock; #endif +static void page_table_config_init(void) +{ + uint32_t v_l1_bits; + + assert(TARGET_PAGE_BITS); + /* The bits remaining after N lower levels of page tables. */ + v_l1_bits = (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS; + if (v_l1_bits < V_L1_MIN_BITS) { + v_l1_bits += V_L2_BITS; + } + + v_l1_size = 1 << v_l1_bits; + v_l1_shift = L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - v_l1_bits; + v_l2_levels = v_l1_shift / V_L2_BITS - 1; + + assert(v_l1_bits <= V_L1_MAX_BITS); + assert(v_l1_shift % V_L2_BITS == 0); + assert(v_l2_levels >= 0); +} + void tb_lock(void) { #ifdef CONFIG_USER_ONLY @@ -332,6 +351,8 @@ void page_size_init(void) static void page_init(void) { page_size_init(); + page_table_config_init(); + #if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY) { #ifdef HAVE_KINFO_GETVMMAP @@ -408,10 +429,10 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) int i; /* Level 1. Always allocated. */ - lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1)); + lp = l1_map + ((index >> v_l1_shift) & (v_l1_size - 1)); /* Level 2..N-1. */ - for (i = V_L1_SHIFT / V_L2_BITS - 1; i > 0; i--) { + for (i = v_l2_levels; i > 0; i--) { void **p = atomic_rcu_read(lp); if (p == NULL) { @@ -826,10 +847,10 @@ static void page_flush_tb_1(int level, void **lp) static void page_flush_tb(void) { - int i; + int i, l1_sz = v_l1_size; - for (i = 0; i < V_L1_SIZE; i++) { - page_flush_tb_1(V_L1_SHIFT / V_L2_BITS - 1, l1_map + i); + for (i = 0; i < l1_sz; i++) { + page_flush_tb_1(v_l2_levels, l1_map + i); } } @@ -1883,16 +1904,16 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data, int walk_memory_regions(void *priv, walk_memory_regions_fn fn) { struct walk_memory_regions_data data; - uintptr_t i; + uintptr_t i, l1_sz = v_l1_size; data.fn = fn; data.priv = priv; data.start = -1u; data.prot = 0; - for (i = 0; i < V_L1_SIZE; i++) { - int rc = walk_memory_regions_1(&data, (target_ulong)i << (V_L1_SHIFT + TARGET_PAGE_BITS), - V_L1_SHIFT / V_L2_BITS - 1, l1_map + i); + for (i = 0; i < l1_sz; i++) { + target_ulong base = i << (v_l1_shift + TARGET_PAGE_BITS); + int rc = walk_memory_regions_1(&data, base, v_l2_levels, l1_map + i); if (rc != 0) { return rc; } diff --git a/util/hbitmap.c b/util/hbitmap.c index 99fd2ba37b..5d1a21ce91 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -78,6 +78,9 @@ struct HBitmap { */ int granularity; + /* A meta dirty bitmap to track the dirtiness of bits in this HBitmap. */ + HBitmap *meta; + /* A number of progressively less coarse bitmaps (i.e. level 0 is the * coarsest). Each bit in level N represents a word in level N+1 that * has a set bit, except the last level where each bit represents the @@ -209,25 +212,27 @@ static uint64_t hb_count_between(HBitmap *hb, uint64_t start, uint64_t last) } /* Setting starts at the last layer and propagates up if an element - * changes from zero to non-zero. + * changes. */ static inline bool hb_set_elem(unsigned long *elem, uint64_t start, uint64_t last) { unsigned long mask; - bool changed; + unsigned long old; assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); assert(start <= last); mask = 2UL << (last & (BITS_PER_LONG - 1)); mask -= 1UL << (start & (BITS_PER_LONG - 1)); - changed = (*elem == 0); + old = *elem; *elem |= mask; - return changed; + return old != *elem; } -/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */ -static void hb_set_between(HBitmap *hb, int level, uint64_t start, uint64_t last) +/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... + * Returns true if at least one bit is changed. */ +static bool hb_set_between(HBitmap *hb, int level, uint64_t start, + uint64_t last) { size_t pos = start >> BITS_PER_LEVEL; size_t lastpos = last >> BITS_PER_LEVEL; @@ -256,23 +261,28 @@ static void hb_set_between(HBitmap *hb, int level, uint64_t start, uint64_t last if (level > 0 && changed) { hb_set_between(hb, level - 1, pos, lastpos); } + return changed; } void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count) { /* Compute range in the last layer. */ + uint64_t first, n; uint64_t last = start + count - 1; trace_hbitmap_set(hb, start, count, start >> hb->granularity, last >> hb->granularity); - start >>= hb->granularity; + first = start >> hb->granularity; last >>= hb->granularity; - count = last - start + 1; assert(last < hb->size); + n = last - first + 1; - hb->count += count - hb_count_between(hb, start, last); - hb_set_between(hb, HBITMAP_LEVELS - 1, start, last); + hb->count += n - hb_count_between(hb, first, last); + if (hb_set_between(hb, HBITMAP_LEVELS - 1, first, last) && + hb->meta) { + hbitmap_set(hb->meta, start, count); + } } /* Resetting works the other way round: propagate up if the new @@ -293,8 +303,10 @@ static inline bool hb_reset_elem(unsigned long *elem, uint64_t start, uint64_t l return blanked; } -/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */ -static void hb_reset_between(HBitmap *hb, int level, uint64_t start, uint64_t last) +/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... + * Returns true if at least one bit is changed. */ +static bool hb_reset_between(HBitmap *hb, int level, uint64_t start, + uint64_t last) { size_t pos = start >> BITS_PER_LEVEL; size_t lastpos = last >> BITS_PER_LEVEL; @@ -337,22 +349,29 @@ static void hb_reset_between(HBitmap *hb, int level, uint64_t start, uint64_t la if (level > 0 && changed) { hb_reset_between(hb, level - 1, pos, lastpos); } + + return changed; + } void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count) { /* Compute range in the last layer. */ + uint64_t first; uint64_t last = start + count - 1; trace_hbitmap_reset(hb, start, count, start >> hb->granularity, last >> hb->granularity); - start >>= hb->granularity; + first = start >> hb->granularity; last >>= hb->granularity; assert(last < hb->size); - hb->count -= hb_count_between(hb, start, last); - hb_reset_between(hb, HBITMAP_LEVELS - 1, start, last); + hb->count -= hb_count_between(hb, first, last); + if (hb_reset_between(hb, HBITMAP_LEVELS - 1, first, last) && + hb->meta) { + hbitmap_set(hb->meta, start, count); + } } void hbitmap_reset_all(HBitmap *hb) @@ -378,9 +397,147 @@ bool hbitmap_get(const HBitmap *hb, uint64_t item) return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0; } +uint64_t hbitmap_serialization_granularity(const HBitmap *hb) +{ + /* Require at least 64 bit granularity to be safe on both 64 bit and 32 bit + * hosts. */ + return 64 << hb->granularity; +} + +/* Start should be aligned to serialization granularity, chunk size should be + * aligned to serialization granularity too, except for last chunk. + */ +static void serialization_chunk(const HBitmap *hb, + uint64_t start, uint64_t count, + unsigned long **first_el, uint64_t *el_count) +{ + uint64_t last = start + count - 1; + uint64_t gran = hbitmap_serialization_granularity(hb); + + assert((start & (gran - 1)) == 0); + assert((last >> hb->granularity) < hb->size); + if ((last >> hb->granularity) != hb->size - 1) { + assert((count & (gran - 1)) == 0); + } + + start = (start >> hb->granularity) >> BITS_PER_LEVEL; + last = (last >> hb->granularity) >> BITS_PER_LEVEL; + + *first_el = &hb->levels[HBITMAP_LEVELS - 1][start]; + *el_count = last - start + 1; +} + +uint64_t hbitmap_serialization_size(const HBitmap *hb, + uint64_t start, uint64_t count) +{ + uint64_t el_count; + unsigned long *cur; + + if (!count) { + return 0; + } + serialization_chunk(hb, start, count, &cur, &el_count); + + return el_count * sizeof(unsigned long); +} + +void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf, + uint64_t start, uint64_t count) +{ + uint64_t el_count; + unsigned long *cur, *end; + + if (!count) { + return; + } + serialization_chunk(hb, start, count, &cur, &el_count); + end = cur + el_count; + + while (cur != end) { + unsigned long el = + (BITS_PER_LONG == 32 ? cpu_to_le32(*cur) : cpu_to_le64(*cur)); + + memcpy(buf, &el, sizeof(el)); + buf += sizeof(el); + cur++; + } +} + +void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf, + uint64_t start, uint64_t count, + bool finish) +{ + uint64_t el_count; + unsigned long *cur, *end; + + if (!count) { + return; + } + serialization_chunk(hb, start, count, &cur, &el_count); + end = cur + el_count; + + while (cur != end) { + memcpy(cur, buf, sizeof(*cur)); + + if (BITS_PER_LONG == 32) { + le32_to_cpus((uint32_t *)cur); + } else { + le64_to_cpus((uint64_t *)cur); + } + + buf += sizeof(unsigned long); + cur++; + } + if (finish) { + hbitmap_deserialize_finish(hb); + } +} + +void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count, + bool finish) +{ + uint64_t el_count; + unsigned long *first; + + if (!count) { + return; + } + serialization_chunk(hb, start, count, &first, &el_count); + + memset(first, 0, el_count * sizeof(unsigned long)); + if (finish) { + hbitmap_deserialize_finish(hb); + } +} + +void hbitmap_deserialize_finish(HBitmap *bitmap) +{ + int64_t i, size, prev_size; + int lev; + + /* restore levels starting from penultimate to zero level, assuming + * that the last level is ok */ + size = MAX((bitmap->size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); + for (lev = HBITMAP_LEVELS - 1; lev-- > 0; ) { + prev_size = size; + size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); + memset(bitmap->levels[lev], 0, size * sizeof(unsigned long)); + + for (i = 0; i < prev_size; ++i) { + if (bitmap->levels[lev + 1][i]) { + bitmap->levels[lev][i >> BITS_PER_LEVEL] |= + 1UL << (i & (BITS_PER_LONG - 1)); + } + } + } + + bitmap->levels[0][0] |= 1UL << (BITS_PER_LONG - 1); +} + void hbitmap_free(HBitmap *hb) { unsigned i; + assert(!hb->meta); for (i = HBITMAP_LEVELS; i-- > 0; ) { g_free(hb->levels[i]); } @@ -458,6 +615,9 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) (size - old) * sizeof(*hb->levels[i])); } } + if (hb->meta) { + hbitmap_truncate(hb->meta, hb->size << hb->granularity); + } } @@ -493,3 +653,19 @@ bool hbitmap_merge(HBitmap *a, const HBitmap *b) return true; } + +HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size) +{ + assert(!(chunk_size & (chunk_size - 1))); + assert(!hb->meta); + hb->meta = hbitmap_alloc(hb->size << hb->granularity, + hb->granularity + ctz32(chunk_size)); + return hb->meta; +} + +void hbitmap_free_meta(HBitmap *hb) +{ + assert(hb->meta); + hbitmap_free(hb->meta); + hb->meta = NULL; +} @@ -4088,6 +4088,16 @@ int main(int argc, char **argv, char **envp) } object_property_add_child(object_get_root(), "machine", OBJECT(current_machine), &error_abort); + + if (machine_class->minimum_page_bits) { + if (!set_preferred_target_page_bits(machine_class->minimum_page_bits)) { + /* This would be a board error: specifying a minimum smaller than + * a target's compile-time fixed setting. + */ + g_assert_not_reached(); + } + } + cpu_exec_init_all(); if (machine_class->hw_version) { |