diff options
275 files changed, 9867 insertions, 2983 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 3ca8148..fe4dc51 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1517,7 +1517,7 @@ F: tests/virtio-balloon-test.c virtio-9p M: Greg Kurz <groug@kaod.org> -S: Supported +S: Odd Fixes F: hw/9pfs/ X: hw/9pfs/xen-9p* F: fsdev/ @@ -983,7 +983,10 @@ sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index # Canned command to build a single manual # Arguments: $1 = manual name, $2 = Sphinx builder ('html' or 'man') -build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" sphinx-build $(if $(V),,-q) -W -n -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") +# Note the use of different doctree for each (manual, builder) tuple; +# this works around Sphinx not handling parallel invocation on +# a single doctree: https://github.com/sphinx-doc/sphinx/issues/2946 +build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" sphinx-build $(if $(V),,-q) -W -n -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") # We assume all RST files in the manual's directory are used in it manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py @@ -5155,6 +5155,15 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, return NULL; } +BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (!drv || !drv->bdrv_get_specific_stats) { + return NULL; + } + return drv->bdrv_get_specific_stats(bs); +} + void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) { if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) { @@ -5164,14 +5173,35 @@ void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) bs->drv->bdrv_debug_event(bs, event); } -int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, - const char *tag) +static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs) { while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) { - bs = bs->file ? bs->file->bs : NULL; + if (bs->file) { + bs = bs->file->bs; + continue; + } + + if (bs->drv->is_filter && bs->backing) { + bs = bs->backing->bs; + continue; + } + + break; } if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) { + assert(bs->drv->bdrv_debug_remove_breakpoint); + return bs; + } + + return NULL; +} + +int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, + const char *tag) +{ + bs = bdrv_find_debug_node(bs); + if (bs) { return bs->drv->bdrv_debug_breakpoint(bs, event, tag); } @@ -5180,11 +5210,8 @@ int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) { - while (bs && bs->drv && !bs->drv->bdrv_debug_remove_breakpoint) { - bs = bs->file ? bs->file->bs : NULL; - } - - if (bs && bs->drv && bs->drv->bdrv_debug_remove_breakpoint) { + bs = bdrv_find_debug_node(bs); + if (bs) { return bs->drv->bdrv_debug_remove_breakpoint(bs, tag); } diff --git a/block/Makefile.objs b/block/Makefile.objs index 35f3bca..e394fe0 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -37,9 +37,13 @@ block-obj-y += write-threshold.o block-obj-y += backup.o block-obj-$(CONFIG_REPLICATION) += replication.o block-obj-y += throttle.o copy-on-read.o +block-obj-y += block-copy.o block-obj-y += crypto.o +block-obj-y += aio_task.o +block-obj-y += backup-top.o + common-obj-y += stream.o nfs.o-libs := $(LIBNFS_LIBS) diff --git a/block/accounting.c b/block/accounting.c index 70a3d9a..8d41c8a 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -195,6 +195,10 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, assert(cookie->type < BLOCK_MAX_IOTYPE); + if (cookie->type == BLOCK_ACCT_NONE) { + return; + } + qemu_mutex_lock(&stats->lock); if (failed) { @@ -217,6 +221,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, } qemu_mutex_unlock(&stats->lock); + + cookie->type = BLOCK_ACCT_NONE; } void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie) diff --git a/block/aio_task.c b/block/aio_task.c new file mode 100644 index 0000000..88989fa --- /dev/null +++ b/block/aio_task.c @@ -0,0 +1,124 @@ +/* + * Aio tasks loops + * + * Copyright (c) 2019 Virtuozzo International GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "block/aio.h" +#include "block/aio_task.h" + +struct AioTaskPool { + Coroutine *main_co; + int status; + int max_busy_tasks; + int busy_tasks; + bool waiting; +}; + +static void coroutine_fn aio_task_co(void *opaque) +{ + AioTask *task = opaque; + AioTaskPool *pool = task->pool; + + assert(pool->busy_tasks < pool->max_busy_tasks); + pool->busy_tasks++; + + task->ret = task->func(task); + + pool->busy_tasks--; + + if (task->ret < 0 && pool->status == 0) { + pool->status = task->ret; + } + + g_free(task); + + if (pool->waiting) { + pool->waiting = false; + aio_co_wake(pool->main_co); + } +} + +void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool) +{ + assert(pool->busy_tasks > 0); + assert(qemu_coroutine_self() == pool->main_co); + + pool->waiting = true; + qemu_coroutine_yield(); + + assert(!pool->waiting); + assert(pool->busy_tasks < pool->max_busy_tasks); +} + +void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool) +{ + if (pool->busy_tasks < pool->max_busy_tasks) { + return; + } + + aio_task_pool_wait_one(pool); +} + +void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool) +{ + while (pool->busy_tasks > 0) { + aio_task_pool_wait_one(pool); + } +} + +void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task) +{ + aio_task_pool_wait_slot(pool); + + task->pool = pool; + qemu_coroutine_enter(qemu_coroutine_create(aio_task_co, task)); +} + +AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks) +{ + AioTaskPool *pool = g_new0(AioTaskPool, 1); + + pool->main_co = qemu_coroutine_self(); + pool->max_busy_tasks = max_busy_tasks; + + return pool; +} + +void aio_task_pool_free(AioTaskPool *pool) +{ + g_free(pool); +} + +int aio_task_pool_status(AioTaskPool *pool) +{ + if (!pool) { + return 0; /* Sugar for lazy allocation of aio pool */ + } + + return pool->status; +} + +bool aio_task_pool_empty(AioTaskPool *pool) +{ + return pool->busy_tasks == 0; +} diff --git a/block/backup-top.c b/block/backup-top.c new file mode 100644 index 0000000..7cdb1f8 --- /dev/null +++ b/block/backup-top.c @@ -0,0 +1,276 @@ +/* + * backup-top filter driver + * + * The driver performs Copy-Before-Write (CBW) operation: it is injected above + * some node, and before each write it copies _old_ data to the target node. + * + * Copyright (c) 2018-2019 Virtuozzo International GmbH. + * + * Author: + * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" + +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "block/block_int.h" +#include "block/qdict.h" +#include "block/block-copy.h" + +#include "block/backup-top.h" + +typedef struct BDRVBackupTopState { + BlockCopyState *bcs; + BdrvChild *target; + bool active; +} BDRVBackupTopState; + +static coroutine_fn int backup_top_co_preadv( + BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); +} + +static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, + uint64_t bytes) +{ + BDRVBackupTopState *s = bs->opaque; + uint64_t end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size); + uint64_t off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size); + + return block_copy(s->bcs, off, end - off, NULL); +} + +static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs, + int64_t offset, int bytes) +{ + int ret = backup_top_cbw(bs, offset, bytes); + if (ret < 0) { + return ret; + } + + return bdrv_co_pdiscard(bs->backing, offset, bytes); +} + +static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, BdrvRequestFlags flags) +{ + int ret = backup_top_cbw(bs, offset, bytes); + if (ret < 0) { + return ret; + } + + return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); +} + +static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + if (!(flags & BDRV_REQ_WRITE_UNCHANGED)) { + int ret = backup_top_cbw(bs, offset, bytes); + if (ret < 0) { + return ret; + } + } + + return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); +} + +static int coroutine_fn backup_top_co_flush(BlockDriverState *bs) +{ + if (!bs->backing) { + return 0; + } + + return bdrv_co_flush(bs->backing->bs); +} + +static void backup_top_refresh_filename(BlockDriverState *bs) +{ + if (bs->backing == NULL) { + /* + * we can be here after failed bdrv_attach_child in + * bdrv_set_backing_hd + */ + return; + } + pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), + bs->backing->bs->filename); +} + +static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + BDRVBackupTopState *s = bs->opaque; + + if (!s->active) { + /* + * The filter node may be in process of bdrv_append(), which firstly do + * bdrv_set_backing_hd() and then bdrv_replace_node(). This means that + * we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So, + * let's require nothing during bdrv_append() and refresh permissions + * after it (see bdrv_backup_top_append()). + */ + *nperm = 0; + *nshared = BLK_PERM_ALL; + return; + } + + if (role == &child_file) { + /* + * Target child + * + * Share write to target (child_file), to not interfere + * with guest writes to its disk which may be in target backing chain. + */ + *nshared = BLK_PERM_ALL; + *nperm = BLK_PERM_WRITE; + } else { + /* Source child */ + bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared, + nperm, nshared); + + if (perm & BLK_PERM_WRITE) { + *nperm = *nperm | BLK_PERM_CONSISTENT_READ; + } + *nshared &= ~BLK_PERM_WRITE; + } +} + +BlockDriver bdrv_backup_top_filter = { + .format_name = "backup-top", + .instance_size = sizeof(BDRVBackupTopState), + + .bdrv_co_preadv = backup_top_co_preadv, + .bdrv_co_pwritev = backup_top_co_pwritev, + .bdrv_co_pwrite_zeroes = backup_top_co_pwrite_zeroes, + .bdrv_co_pdiscard = backup_top_co_pdiscard, + .bdrv_co_flush = backup_top_co_flush, + + .bdrv_co_block_status = bdrv_co_block_status_from_backing, + + .bdrv_refresh_filename = backup_top_refresh_filename, + + .bdrv_child_perm = backup_top_child_perm, + + .is_filter = true, +}; + +BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, + BlockDriverState *target, + const char *filter_node_name, + uint64_t cluster_size, + BdrvRequestFlags write_flags, + BlockCopyState **bcs, + Error **errp) +{ + Error *local_err = NULL; + BDRVBackupTopState *state; + BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter, + filter_node_name, + BDRV_O_RDWR, errp); + + if (!top) { + return NULL; + } + + top->total_sectors = source->total_sectors; + top->opaque = state = g_new0(BDRVBackupTopState, 1); + + bdrv_ref(target); + state->target = bdrv_attach_child(top, target, "target", &child_file, errp); + if (!state->target) { + bdrv_unref(target); + bdrv_unref(top); + return NULL; + } + + bdrv_drained_begin(source); + + bdrv_ref(top); + bdrv_append(top, source, &local_err); + if (local_err) { + error_prepend(&local_err, "Cannot append backup-top filter: "); + goto append_failed; + } + + /* + * bdrv_append() finished successfully, now we can require permissions + * we want. + */ + state->active = true; + bdrv_child_refresh_perms(top, top->backing, &local_err); + if (local_err) { + error_prepend(&local_err, + "Cannot set permissions for backup-top filter: "); + goto failed_after_append; + } + + state->bcs = block_copy_state_new(top->backing, state->target, + cluster_size, write_flags, &local_err); + if (local_err) { + error_prepend(&local_err, "Cannot create block-copy-state: "); + goto failed_after_append; + } + *bcs = state->bcs; + + bdrv_drained_end(source); + + return top; + +failed_after_append: + state->active = false; + bdrv_backup_top_drop(top); + +append_failed: + bdrv_drained_end(source); + bdrv_unref_child(top, state->target); + bdrv_unref(top); + error_propagate(errp, local_err); + + return NULL; +} + +void bdrv_backup_top_drop(BlockDriverState *bs) +{ + BDRVBackupTopState *s = bs->opaque; + AioContext *aio_context = bdrv_get_aio_context(bs); + + block_copy_state_free(s->bcs); + + aio_context_acquire(aio_context); + + bdrv_drained_begin(bs); + + s->active = false; + bdrv_child_refresh_perms(bs, bs->backing, &error_abort); + bdrv_replace_node(bs, backing_bs(bs), &error_abort); + bdrv_set_backing_hd(bs, NULL, &error_abort); + + bdrv_drained_end(bs); + + bdrv_unref(bs); + + aio_context_release(aio_context); +} diff --git a/block/backup-top.h b/block/backup-top.h new file mode 100644 index 0000000..e5cabfa --- /dev/null +++ b/block/backup-top.h @@ -0,0 +1,41 @@ +/* + * backup-top filter driver + * + * The driver performs Copy-Before-Write (CBW) operation: it is injected above + * some node, and before each write it copies _old_ data to the target node. + * + * Copyright (c) 2018-2019 Virtuozzo International GmbH. + * + * Author: + * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef BACKUP_TOP_H +#define BACKUP_TOP_H + +#include "block/block_int.h" +#include "block/block-copy.h" + +BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, + BlockDriverState *target, + const char *filter_node_name, + uint64_t cluster_size, + BdrvRequestFlags write_flags, + BlockCopyState **bcs, + Error **errp); +void bdrv_backup_top_drop(BlockDriverState *bs); + +#endif /* BACKUP_TOP_H */ diff --git a/block/backup.c b/block/backup.c index 763f0d7..46978c1 100644 --- a/block/backup.c +++ b/block/backup.c @@ -2,6 +2,7 @@ * QEMU backup * * Copyright (C) 2013 Proxmox Server Solutions + * Copyright (c) 2019 Virtuozzo International GmbH. * * Authors: * Dietmar Maurer (dietmar@proxmox.com) @@ -18,6 +19,7 @@ #include "block/block_int.h" #include "block/blockjob_int.h" #include "block/block_backup.h" +#include "block/block-copy.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" @@ -26,333 +28,68 @@ #include "qemu/bitmap.h" #include "qemu/error-report.h" -#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) +#include "block/backup-top.h" -typedef struct CowRequest { - int64_t start_byte; - int64_t end_byte; - QLIST_ENTRY(CowRequest) list; - CoQueue wait_queue; /* coroutines blocked on this request */ -} CowRequest; +#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) typedef struct BackupBlockJob { BlockJob common; - BlockBackend *target; + BlockDriverState *backup_top; + BlockDriverState *source_bs; BdrvDirtyBitmap *sync_bitmap; - BdrvDirtyBitmap *copy_bitmap; MirrorSyncMode sync_mode; BitmapSyncMode bitmap_mode; BlockdevOnError on_source_error; BlockdevOnError on_target_error; - CoRwlock flush_rwlock; uint64_t len; uint64_t bytes_read; int64_t cluster_size; - NotifierWithReturn before_write; - QLIST_HEAD(, CowRequest) inflight_reqs; - bool use_copy_range; - int64_t copy_range_size; - - BdrvRequestFlags write_flags; - bool initializing_bitmap; + BlockCopyState *bcs; } BackupBlockJob; static const BlockJobDriver backup_job_driver; -/* See if in-flight requests overlap and wait for them to complete */ -static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, - int64_t start, - int64_t end) +static void backup_progress_bytes_callback(int64_t bytes, void *opaque) { - CowRequest *req; - bool retry; - - do { - retry = false; - QLIST_FOREACH(req, &job->inflight_reqs, list) { - if (end > req->start_byte && start < req->end_byte) { - qemu_co_queue_wait(&req->wait_queue, NULL); - retry = true; - break; - } - } - } while (retry); -} + BackupBlockJob *s = opaque; -/* Keep track of an in-flight request */ -static void cow_request_begin(CowRequest *req, BackupBlockJob *job, - int64_t start, int64_t end) -{ - req->start_byte = start; - req->end_byte = end; - qemu_co_queue_init(&req->wait_queue); - QLIST_INSERT_HEAD(&job->inflight_reqs, req, list); + s->bytes_read += bytes; + job_progress_update(&s->common.job, bytes); } -/* Forget about a completed request */ -static void cow_request_end(CowRequest *req) +static void backup_progress_reset_callback(void *opaque) { - QLIST_REMOVE(req, list); - qemu_co_queue_restart_all(&req->wait_queue); -} - -/* Copy range to target with a bounce buffer and return the bytes copied. If - * error occurred, return a negative error number */ -static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, - int64_t start, - int64_t end, - bool is_write_notifier, - bool *error_is_read, - void **bounce_buffer) -{ - int ret; - BlockBackend *blk = job->common.blk; - int nbytes; - int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; - - assert(QEMU_IS_ALIGNED(start, job->cluster_size)); - bdrv_reset_dirty_bitmap(job->copy_bitmap, start, job->cluster_size); - nbytes = MIN(job->cluster_size, job->len - start); - if (!*bounce_buffer) { - *bounce_buffer = blk_blockalign(blk, job->cluster_size); - } - - ret = blk_co_pread(blk, start, nbytes, *bounce_buffer, read_flags); - if (ret < 0) { - trace_backup_do_cow_read_fail(job, start, ret); - if (error_is_read) { - *error_is_read = true; - } - goto fail; - } - - ret = blk_co_pwrite(job->target, start, nbytes, *bounce_buffer, - job->write_flags); - if (ret < 0) { - trace_backup_do_cow_write_fail(job, start, ret); - if (error_is_read) { - *error_is_read = false; - } - goto fail; - } - - return nbytes; -fail: - bdrv_set_dirty_bitmap(job->copy_bitmap, start, job->cluster_size); - return ret; - -} - -/* Copy range to target and return the bytes copied. If error occurred, return a - * negative error number. */ -static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, - int64_t start, - int64_t end, - bool is_write_notifier) -{ - int ret; - int nr_clusters; - BlockBackend *blk = job->common.blk; - int nbytes; - int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; - - assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); - assert(QEMU_IS_ALIGNED(start, job->cluster_size)); - nbytes = MIN(job->copy_range_size, end - start); - nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size); - bdrv_reset_dirty_bitmap(job->copy_bitmap, start, - job->cluster_size * nr_clusters); - ret = blk_co_copy_range(blk, start, job->target, start, nbytes, - read_flags, job->write_flags); - if (ret < 0) { - trace_backup_do_cow_copy_range_fail(job, start, ret); - bdrv_set_dirty_bitmap(job->copy_bitmap, start, - job->cluster_size * nr_clusters); - return ret; - } - - return nbytes; -} - -/* - * Check if the cluster starting at offset is allocated or not. - * return via pnum the number of contiguous clusters sharing this allocation. - */ -static int backup_is_cluster_allocated(BackupBlockJob *s, int64_t offset, - int64_t *pnum) -{ - BlockDriverState *bs = blk_bs(s->common.blk); - int64_t count, total_count = 0; - int64_t bytes = s->len - offset; - int ret; - - assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); - - while (true) { - ret = bdrv_is_allocated(bs, offset, bytes, &count); - if (ret < 0) { - return ret; - } - - total_count += count; - - if (ret || count == 0) { - /* - * ret: partial segment(s) are considered allocated. - * otherwise: unallocated tail is treated as an entire segment. - */ - *pnum = DIV_ROUND_UP(total_count, s->cluster_size); - return ret; - } - - /* Unallocated segment(s) with uncertain following segment(s) */ - if (total_count >= s->cluster_size) { - *pnum = total_count / s->cluster_size; - return 0; - } - - offset += count; - bytes -= count; - } -} - -/** - * Reset bits in copy_bitmap starting at offset if they represent unallocated - * data in the image. May reset subsequent contiguous bits. - * @return 0 when the cluster at @offset was unallocated, - * 1 otherwise, and -ret on error. - */ -static int64_t backup_bitmap_reset_unallocated(BackupBlockJob *s, - int64_t offset, int64_t *count) -{ - int ret; - int64_t clusters, bytes, estimate; - - ret = backup_is_cluster_allocated(s, offset, &clusters); - if (ret < 0) { - return ret; - } - - bytes = clusters * s->cluster_size; - - if (!ret) { - bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); - estimate = bdrv_get_dirty_count(s->copy_bitmap); - job_progress_set_remaining(&s->common.job, estimate); - } + BackupBlockJob *s = opaque; + uint64_t estimate = bdrv_get_dirty_count(s->bcs->copy_bitmap); - *count = bytes; - return ret; + job_progress_set_remaining(&s->common.job, estimate); } static int coroutine_fn backup_do_cow(BackupBlockJob *job, int64_t offset, uint64_t bytes, - bool *error_is_read, - bool is_write_notifier) + bool *error_is_read) { - CowRequest cow_request; int ret = 0; int64_t start, end; /* bytes */ - void *bounce_buffer = NULL; - int64_t status_bytes; - - qemu_co_rwlock_rdlock(&job->flush_rwlock); start = QEMU_ALIGN_DOWN(offset, job->cluster_size); end = QEMU_ALIGN_UP(bytes + offset, job->cluster_size); trace_backup_do_cow_enter(job, start, offset, bytes); - wait_for_overlapping_requests(job, start, end); - cow_request_begin(&cow_request, job, start, end); - - while (start < end) { - int64_t dirty_end; - - if (!bdrv_dirty_bitmap_get(job->copy_bitmap, start)) { - trace_backup_do_cow_skip(job, start); - start += job->cluster_size; - continue; /* already copied */ - } - - dirty_end = bdrv_dirty_bitmap_next_zero(job->copy_bitmap, start, - (end - start)); - if (dirty_end < 0) { - dirty_end = end; - } - - if (job->initializing_bitmap) { - ret = backup_bitmap_reset_unallocated(job, start, &status_bytes); - if (ret == 0) { - trace_backup_do_cow_skip_range(job, start, status_bytes); - start += status_bytes; - continue; - } - /* Clamp to known allocated region */ - dirty_end = MIN(dirty_end, start + status_bytes); - } - - trace_backup_do_cow_process(job, start); - - if (job->use_copy_range) { - ret = backup_cow_with_offload(job, start, dirty_end, - is_write_notifier); - if (ret < 0) { - job->use_copy_range = false; - } - } - if (!job->use_copy_range) { - ret = backup_cow_with_bounce_buffer(job, start, dirty_end, - is_write_notifier, - error_is_read, &bounce_buffer); - } - if (ret < 0) { - break; - } - - /* Publish progress, guest I/O counts as progress too. Note that the - * offset field is an opaque progress value, it is not a disk offset. - */ - start += ret; - job->bytes_read += ret; - job_progress_update(&job->common.job, ret); - ret = 0; - } - - if (bounce_buffer) { - qemu_vfree(bounce_buffer); - } - - cow_request_end(&cow_request); + ret = block_copy(job->bcs, start, end - start, error_is_read); trace_backup_do_cow_return(job, offset, bytes, ret); - qemu_co_rwlock_unlock(&job->flush_rwlock); - return ret; } -static int coroutine_fn backup_before_write_notify( - NotifierWithReturn *notifier, - void *opaque) -{ - BackupBlockJob *job = container_of(notifier, BackupBlockJob, before_write); - BdrvTrackedRequest *req = opaque; - - assert(req->bs == blk_bs(job->common.blk)); - assert(QEMU_IS_ALIGNED(req->offset, BDRV_SECTOR_SIZE)); - assert(QEMU_IS_ALIGNED(req->bytes, BDRV_SECTOR_SIZE)); - - return backup_do_cow(job, req->offset, req->bytes, NULL, true); -} - static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) { BdrvDirtyBitmap *bm; - BlockDriverState *bs = blk_bs(job->common.blk); bool sync = (((ret == 0) || (job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS)) \ && (job->bitmap_mode != BITMAP_SYNC_MODE_NEVER)); @@ -361,20 +98,20 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) * We succeeded, or we always intended to sync the bitmap. * Delete this bitmap and install the child. */ - bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL); + bm = bdrv_dirty_bitmap_abdicate(job->source_bs, job->sync_bitmap, NULL); } else { /* * We failed, or we never intended to sync the bitmap anyway. * Merge the successor back into the parent, keeping all data. */ - bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); + bm = bdrv_reclaim_dirty_bitmap(job->source_bs, job->sync_bitmap, NULL); } assert(bm); if (ret < 0 && job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS) { /* If we failed and synced, merge in the bits we didn't copy: */ - bdrv_dirty_bitmap_merge_internal(bm, job->copy_bitmap, + bdrv_dirty_bitmap_merge_internal(bm, job->bcs->copy_bitmap, NULL, true); } } @@ -398,16 +135,8 @@ static void backup_abort(Job *job) static void backup_clean(Job *job) { BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); - BlockDriverState *bs = blk_bs(s->common.blk); - - if (s->copy_bitmap) { - bdrv_release_dirty_bitmap(bs, s->copy_bitmap); - s->copy_bitmap = NULL; - } - assert(s->target); - blk_unref(s->target); - s->target = NULL; + bdrv_backup_top_drop(s->backup_top); } void backup_do_checkpoint(BlockJob *job, Error **errp) @@ -422,7 +151,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) return; } - bdrv_set_dirty_bitmap(backup_job->copy_bitmap, 0, backup_job->len); + bdrv_set_dirty_bitmap(backup_job->bcs->copy_bitmap, 0, backup_job->len); } static BlockErrorAction backup_error_action(BackupBlockJob *job, @@ -445,8 +174,10 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) return true; } - /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can - * return. Without a yield, the VM would not reboot. */ + /* + * We need to yield even for delay_ns = 0 so that bdrv_drain_all() can + * return. Without a yield, the VM would not reboot. + */ delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); job->bytes_read = 0; job_sleep_ns(&job->common.job, delay_ns); @@ -465,14 +196,13 @@ static int coroutine_fn backup_loop(BackupBlockJob *job) BdrvDirtyBitmapIter *bdbi; int ret = 0; - bdbi = bdrv_dirty_iter_new(job->copy_bitmap); + bdbi = bdrv_dirty_iter_new(job->bcs->copy_bitmap); while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) { do { if (yield_and_check(job)) { goto out; } - ret = backup_do_cow(job, offset, - job->cluster_size, &error_is_read, false); + ret = backup_do_cow(job, offset, job->cluster_size, &error_is_read); if (ret < 0 && backup_error_action(job, error_is_read, -ret) == BLOCK_ERROR_ACTION_REPORT) { @@ -492,7 +222,7 @@ static void backup_init_copy_bitmap(BackupBlockJob *job) uint64_t estimate; if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) { - ret = bdrv_dirty_bitmap_merge_internal(job->copy_bitmap, + ret = bdrv_dirty_bitmap_merge_internal(job->bcs->copy_bitmap, job->sync_bitmap, NULL, true); assert(ret); @@ -502,29 +232,22 @@ static void backup_init_copy_bitmap(BackupBlockJob *job) * We can't hog the coroutine to initialize this thoroughly. * Set a flag and resume work when we are able to yield safely. */ - job->initializing_bitmap = true; + job->bcs->skip_unallocated = true; } - bdrv_set_dirty_bitmap(job->copy_bitmap, 0, job->len); + bdrv_set_dirty_bitmap(job->bcs->copy_bitmap, 0, job->len); } - estimate = bdrv_get_dirty_count(job->copy_bitmap); + estimate = bdrv_get_dirty_count(job->bcs->copy_bitmap); job_progress_set_remaining(&job->common.job, estimate); } static int coroutine_fn backup_run(Job *job, Error **errp) { BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); - BlockDriverState *bs = blk_bs(s->common.blk); int ret = 0; - QLIST_INIT(&s->inflight_reqs); - qemu_co_rwlock_init(&s->flush_rwlock); - backup_init_copy_bitmap(s); - s->before_write.notify = backup_before_write_notify; - bdrv_add_before_write_notifier(bs, &s->before_write); - if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { int64_t offset = 0; int64_t count; @@ -535,22 +258,26 @@ static int coroutine_fn backup_run(Job *job, Error **errp) goto out; } - ret = backup_bitmap_reset_unallocated(s, offset, &count); + ret = block_copy_reset_unallocated(s->bcs, offset, &count); if (ret < 0) { goto out; } offset += count; } - s->initializing_bitmap = false; + s->bcs->skip_unallocated = false; } if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { - /* All bits are set in copy_bitmap to allow any cluster to be copied. - * This does not actually require them to be copied. */ + /* + * All bits are set in copy_bitmap to allow any cluster to be copied. + * This does not actually require them to be copied. + */ while (!job_is_cancelled(job)) { - /* Yield until the job is cancelled. We just let our before_write - * notify callback service CoW requests. */ + /* + * Yield until the job is cancelled. We just let our before_write + * notify callback service CoW requests. + */ job_yield(job); } } else { @@ -558,12 +285,6 @@ static int coroutine_fn backup_run(Job *job, Error **errp) } out: - notifier_with_return_remove(&s->before_write); - - /* wait until pending backup_do_cow() calls have completed */ - qemu_co_rwlock_wrlock(&s->flush_rwlock); - qemu_co_rwlock_unlock(&s->flush_rwlock); - return ret; } @@ -621,6 +342,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap, BitmapSyncMode bitmap_mode, bool compress, + const char *filter_node_name, BlockdevOnError on_source_error, BlockdevOnError on_target_error, int creation_flags, @@ -629,9 +351,10 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, { int64_t len; BackupBlockJob *job = NULL; - int ret; int64_t cluster_size; - BdrvDirtyBitmap *copy_bitmap = NULL; + BdrvRequestFlags write_flags; + BlockDriverState *backup_top = NULL; + BlockCopyState *bcs = NULL; assert(bs); assert(target); @@ -696,76 +419,66 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, goto error; } - copy_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp); - if (!copy_bitmap) { + /* + * If source is in backing chain of target assume that target is going to be + * used for "image fleecing", i.e. it should represent a kind of snapshot of + * source at backup-start point in time. And target is going to be read by + * somebody (for example, used as NBD export) during backup job. + * + * In this case, we need to add BDRV_REQ_SERIALISING write flag to avoid + * intersection of backup writes and third party reads from target, + * otherwise reading from target we may occasionally read already updated by + * guest data. + * + * For more information see commit f8d59dfb40bb and test + * tests/qemu-iotests/222 + */ + write_flags = (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) | + (compress ? BDRV_REQ_WRITE_COMPRESSED : 0), + + backup_top = bdrv_backup_top_append(bs, target, filter_node_name, + cluster_size, write_flags, &bcs, errp); + if (!backup_top) { goto error; } - bdrv_disable_dirty_bitmap(copy_bitmap); /* job->len is fixed, so we can't allow resize */ - job = block_job_create(job_id, &backup_job_driver, txn, bs, - BLK_PERM_CONSISTENT_READ, - BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | - BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD, + job = block_job_create(job_id, &backup_job_driver, txn, backup_top, + 0, BLK_PERM_ALL, speed, creation_flags, cb, opaque, errp); if (!job) { goto error; } - /* The target must match the source in size, so no resize here either */ - job->target = blk_new(job->common.job.aio_context, - BLK_PERM_WRITE, - BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | - BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD); - ret = blk_insert_bs(job->target, target, errp); - if (ret < 0) { - goto error; - } - blk_set_disable_request_queuing(job->target, true); - + job->backup_top = backup_top; + job->source_bs = bs; job->on_source_error = on_source_error; job->on_target_error = on_target_error; job->sync_mode = sync_mode; job->sync_bitmap = sync_bitmap; job->bitmap_mode = bitmap_mode; + job->bcs = bcs; + job->cluster_size = cluster_size; + job->len = len; - /* - * Set write flags: - * 1. Detect image-fleecing (and similar) schemes - * 2. Handle compression - */ - job->write_flags = - (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) | - (compress ? BDRV_REQ_WRITE_COMPRESSED : 0); + block_copy_set_callbacks(bcs, backup_progress_bytes_callback, + backup_progress_reset_callback, job); - job->cluster_size = cluster_size; - job->copy_bitmap = copy_bitmap; - copy_bitmap = NULL; - job->use_copy_range = !compress; /* compression isn't supported for it */ - job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), - blk_get_max_transfer(job->target)); - job->copy_range_size = MAX(job->cluster_size, - QEMU_ALIGN_UP(job->copy_range_size, - job->cluster_size)); - - /* Required permissions are already taken with target's blk_new() */ + /* Required permissions are already taken by backup-top target */ block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); - job->len = len; return &job->common; error: - if (copy_bitmap) { - assert(!job || !job->copy_bitmap); - bdrv_release_dirty_bitmap(bs, copy_bitmap); - } if (sync_bitmap) { bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); } if (job) { backup_clean(&job->common.job); job_early_fail(&job->common.job); + } else if (backup_top) { + bdrv_backup_top_drop(backup_top); } return NULL; diff --git a/block/blkreplay.c b/block/blkreplay.c index 2b7931b..c96ac8f 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -126,6 +126,12 @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs) return ret; } +static int blkreplay_snapshot_goto(BlockDriverState *bs, + const char *snapshot_id) +{ + return bdrv_snapshot_goto(bs->file->bs, snapshot_id, NULL); +} + static BlockDriver bdrv_blkreplay = { .format_name = "blkreplay", .instance_size = 0, @@ -140,6 +146,8 @@ static BlockDriver bdrv_blkreplay = { .bdrv_co_pwrite_zeroes = blkreplay_co_pwrite_zeroes, .bdrv_co_pdiscard = blkreplay_co_pdiscard, .bdrv_co_flush = blkreplay_co_flush, + + .bdrv_snapshot_goto = blkreplay_snapshot_goto, }; static void bdrv_blkreplay_init(void) diff --git a/block/block-backend.c b/block/block-backend.c index 1c605d5..eb22ff3 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -18,6 +18,8 @@ #include "hw/qdev-core.h" #include "sysemu/blockdev.h" #include "sysemu/runstate.h" +#include "sysemu/sysemu.h" +#include "sysemu/replay.h" #include "qapi/error.h" #include "qapi/qapi-events-block.h" #include "qemu/id.h" @@ -1306,7 +1308,8 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, acb->blk = blk; acb->ret = ret; - aio_bh_schedule_oneshot(blk_get_aio_context(blk), error_callback_bh, acb); + replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), + error_callback_bh, acb); return &acb->common; } @@ -1362,8 +1365,8 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes, acb->has_returned = true; if (acb->rwco.ret != NOT_DONE) { - aio_bh_schedule_oneshot(blk_get_aio_context(blk), - blk_aio_complete_bh, acb); + replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), + blk_aio_complete_bh, acb); } return &acb->common; diff --git a/block/block-copy.c b/block/block-copy.c new file mode 100644 index 0000000..0f76ea1 --- /dev/null +++ b/block/block-copy.c @@ -0,0 +1,345 @@ +/* + * block_copy API + * + * Copyright (C) 2013 Proxmox Server Solutions + * Copyright (c) 2019 Virtuozzo International GmbH. + * + * Authors: + * Dietmar Maurer (dietmar@proxmox.com) + * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.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 "trace.h" +#include "qapi/error.h" +#include "block/block-copy.h" +#include "sysemu/block-backend.h" + +static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s, + int64_t start, + int64_t end) +{ + BlockCopyInFlightReq *req; + bool waited; + + do { + waited = false; + QLIST_FOREACH(req, &s->inflight_reqs, list) { + if (end > req->start_byte && start < req->end_byte) { + qemu_co_queue_wait(&req->wait_queue, NULL); + waited = true; + break; + } + } + } while (waited); +} + +static void block_copy_inflight_req_begin(BlockCopyState *s, + BlockCopyInFlightReq *req, + int64_t start, int64_t end) +{ + req->start_byte = start; + req->end_byte = end; + qemu_co_queue_init(&req->wait_queue); + QLIST_INSERT_HEAD(&s->inflight_reqs, req, list); +} + +static void coroutine_fn block_copy_inflight_req_end(BlockCopyInFlightReq *req) +{ + QLIST_REMOVE(req, list); + qemu_co_queue_restart_all(&req->wait_queue); +} + +void block_copy_state_free(BlockCopyState *s) +{ + if (!s) { + return; + } + + bdrv_release_dirty_bitmap(s->source->bs, s->copy_bitmap); + g_free(s); +} + +BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, + int64_t cluster_size, + BdrvRequestFlags write_flags, Error **errp) +{ + BlockCopyState *s; + BdrvDirtyBitmap *copy_bitmap; + uint32_t max_transfer = + MIN_NON_ZERO(INT_MAX, MIN_NON_ZERO(source->bs->bl.max_transfer, + target->bs->bl.max_transfer)); + + copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL, + errp); + if (!copy_bitmap) { + return NULL; + } + bdrv_disable_dirty_bitmap(copy_bitmap); + + s = g_new(BlockCopyState, 1); + *s = (BlockCopyState) { + .source = source, + .target = target, + .copy_bitmap = copy_bitmap, + .cluster_size = cluster_size, + .len = bdrv_dirty_bitmap_size(copy_bitmap), + .write_flags = write_flags, + }; + + s->copy_range_size = QEMU_ALIGN_DOWN(max_transfer, cluster_size), + /* + * Set use_copy_range, consider the following: + * 1. Compression is not supported for copy_range. + * 2. copy_range does not respect max_transfer (it's a TODO), so we factor + * that in here. If max_transfer is smaller than the job->cluster_size, + * we do not use copy_range (in that case it's zero after aligning down + * above). + */ + s->use_copy_range = + !(write_flags & BDRV_REQ_WRITE_COMPRESSED) && s->copy_range_size > 0; + + QLIST_INIT(&s->inflight_reqs); + + return s; +} + +void block_copy_set_callbacks( + BlockCopyState *s, + ProgressBytesCallbackFunc progress_bytes_callback, + ProgressResetCallbackFunc progress_reset_callback, + void *progress_opaque) +{ + s->progress_bytes_callback = progress_bytes_callback; + s->progress_reset_callback = progress_reset_callback; + s->progress_opaque = progress_opaque; +} + +/* + * Copy range to target with a bounce buffer and return the bytes copied. If + * error occurred, return a negative error number + */ +static int coroutine_fn block_copy_with_bounce_buffer(BlockCopyState *s, + int64_t start, + int64_t end, + bool *error_is_read, + void **bounce_buffer) +{ + int ret; + int nbytes; + + assert(QEMU_IS_ALIGNED(start, s->cluster_size)); + bdrv_reset_dirty_bitmap(s->copy_bitmap, start, s->cluster_size); + nbytes = MIN(s->cluster_size, s->len - start); + if (!*bounce_buffer) { + *bounce_buffer = qemu_blockalign(s->source->bs, s->cluster_size); + } + + ret = bdrv_co_pread(s->source, start, nbytes, *bounce_buffer, 0); + if (ret < 0) { + trace_block_copy_with_bounce_buffer_read_fail(s, start, ret); + if (error_is_read) { + *error_is_read = true; + } + goto fail; + } + + ret = bdrv_co_pwrite(s->target, start, nbytes, *bounce_buffer, + s->write_flags); + if (ret < 0) { + trace_block_copy_with_bounce_buffer_write_fail(s, start, ret); + if (error_is_read) { + *error_is_read = false; + } + goto fail; + } + + return nbytes; +fail: + bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size); + return ret; + +} + +/* + * Copy range to target and return the bytes copied. If error occurred, return a + * negative error number. + */ +static int coroutine_fn block_copy_with_offload(BlockCopyState *s, + int64_t start, + int64_t end) +{ + int ret; + int nr_clusters; + int nbytes; + + assert(QEMU_IS_ALIGNED(s->copy_range_size, s->cluster_size)); + assert(QEMU_IS_ALIGNED(start, s->cluster_size)); + nbytes = MIN(s->copy_range_size, MIN(end, s->len) - start); + nr_clusters = DIV_ROUND_UP(nbytes, s->cluster_size); + bdrv_reset_dirty_bitmap(s->copy_bitmap, start, + s->cluster_size * nr_clusters); + ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes, + 0, s->write_flags); + if (ret < 0) { + trace_block_copy_with_offload_fail(s, start, ret); + bdrv_set_dirty_bitmap(s->copy_bitmap, start, + s->cluster_size * nr_clusters); + return ret; + } + + return nbytes; +} + +/* + * Check if the cluster starting at offset is allocated or not. + * return via pnum the number of contiguous clusters sharing this allocation. + */ +static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset, + int64_t *pnum) +{ + BlockDriverState *bs = s->source->bs; + int64_t count, total_count = 0; + int64_t bytes = s->len - offset; + int ret; + + assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); + + while (true) { + ret = bdrv_is_allocated(bs, offset, bytes, &count); + if (ret < 0) { + return ret; + } + + total_count += count; + + if (ret || count == 0) { + /* + * ret: partial segment(s) are considered allocated. + * otherwise: unallocated tail is treated as an entire segment. + */ + *pnum = DIV_ROUND_UP(total_count, s->cluster_size); + return ret; + } + + /* Unallocated segment(s) with uncertain following segment(s) */ + if (total_count >= s->cluster_size) { + *pnum = total_count / s->cluster_size; + return 0; + } + + offset += count; + bytes -= count; + } +} + +/* + * Reset bits in copy_bitmap starting at offset if they represent unallocated + * data in the image. May reset subsequent contiguous bits. + * @return 0 when the cluster at @offset was unallocated, + * 1 otherwise, and -ret on error. + */ +int64_t block_copy_reset_unallocated(BlockCopyState *s, + int64_t offset, int64_t *count) +{ + int ret; + int64_t clusters, bytes; + + ret = block_copy_is_cluster_allocated(s, offset, &clusters); + if (ret < 0) { + return ret; + } + + bytes = clusters * s->cluster_size; + + if (!ret) { + bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); + s->progress_reset_callback(s->progress_opaque); + } + + *count = bytes; + return ret; +} + +int coroutine_fn block_copy(BlockCopyState *s, + int64_t start, uint64_t bytes, + bool *error_is_read) +{ + int ret = 0; + int64_t end = bytes + start; /* bytes */ + void *bounce_buffer = NULL; + int64_t status_bytes; + BlockCopyInFlightReq req; + + /* + * block_copy() user is responsible for keeping source and target in same + * aio context + */ + assert(bdrv_get_aio_context(s->source->bs) == + bdrv_get_aio_context(s->target->bs)); + + assert(QEMU_IS_ALIGNED(start, s->cluster_size)); + assert(QEMU_IS_ALIGNED(end, s->cluster_size)); + + block_copy_wait_inflight_reqs(s, start, bytes); + block_copy_inflight_req_begin(s, &req, start, end); + + while (start < end) { + int64_t dirty_end; + + if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) { + trace_block_copy_skip(s, start); + start += s->cluster_size; + continue; /* already copied */ + } + + dirty_end = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start, + (end - start)); + if (dirty_end < 0) { + dirty_end = end; + } + + if (s->skip_unallocated) { + ret = block_copy_reset_unallocated(s, start, &status_bytes); + if (ret == 0) { + trace_block_copy_skip_range(s, start, status_bytes); + start += status_bytes; + continue; + } + /* Clamp to known allocated region */ + dirty_end = MIN(dirty_end, start + status_bytes); + } + + trace_block_copy_process(s, start); + + if (s->use_copy_range) { + ret = block_copy_with_offload(s, start, dirty_end); + if (ret < 0) { + s->use_copy_range = false; + } + } + if (!s->use_copy_range) { + ret = block_copy_with_bounce_buffer(s, start, dirty_end, + error_is_read, &bounce_buffer); + } + if (ret < 0) { + break; + } + + start += ret; + s->progress_bytes_callback(ret, s->progress_opaque); + ret = 0; + } + + if (bounce_buffer) { + qemu_vfree(bounce_buffer); + } + + block_copy_inflight_req_end(&req); + + return ret; +} diff --git a/block/file-posix.c b/block/file-posix.c index f12c06d..695fcf7 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -161,6 +161,11 @@ typedef struct BDRVRawState { bool needs_alignment; bool drop_cache; bool check_cache_dropped; + struct { + uint64_t discard_nb_ok; + uint64_t discard_nb_failed; + uint64_t discard_bytes_ok; + } stats; PRManager *pr_mgr; } BDRVRawState; @@ -2660,11 +2665,22 @@ static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs, #endif /* !__linux__ */ } +static void raw_account_discard(BDRVRawState *s, uint64_t nbytes, int ret) +{ + if (ret) { + s->stats.discard_nb_failed++; + } else { + s->stats.discard_nb_ok++; + s->stats.discard_bytes_ok += nbytes; + } +} + static coroutine_fn int raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev) { BDRVRawState *s = bs->opaque; RawPosixAIOData acb; + int ret; acb = (RawPosixAIOData) { .bs = bs, @@ -2678,7 +2694,9 @@ raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev) acb.aio_type |= QEMU_AIO_BLKDEV; } - return raw_thread_pool_submit(bs, handle_aiocb_discard, &acb); + ret = raw_thread_pool_submit(bs, handle_aiocb_discard, &acb); + raw_account_discard(s, bytes, ret); + return ret; } static coroutine_fn int @@ -2735,6 +2753,36 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } +static BlockStatsSpecificFile get_blockstats_specific_file(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + return (BlockStatsSpecificFile) { + .discard_nb_ok = s->stats.discard_nb_ok, + .discard_nb_failed = s->stats.discard_nb_failed, + .discard_bytes_ok = s->stats.discard_bytes_ok, + }; +} + +static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs) +{ + BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1); + + stats->driver = BLOCKDEV_DRIVER_FILE; + stats->u.file = get_blockstats_specific_file(bs); + + return stats; +} + +static BlockStatsSpecific *hdev_get_specific_stats(BlockDriverState *bs) +{ + BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1); + + stats->driver = BLOCKDEV_DRIVER_HOST_DEVICE; + stats->u.host_device = get_blockstats_specific_file(bs); + + return stats; +} + static QemuOptsList raw_create_opts = { .name = "raw-create-opts", .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head), @@ -2942,6 +2990,7 @@ BlockDriver bdrv_file = { .bdrv_get_info = raw_get_info, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, + .bdrv_get_specific_stats = raw_get_specific_stats, .bdrv_check_perm = raw_check_perm, .bdrv_set_perm = raw_set_perm, .bdrv_abort_perm_update = raw_abort_perm_update, @@ -3301,10 +3350,12 @@ static int fd_open(BlockDriverState *bs) static coroutine_fn int hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { + BDRVRawState *s = bs->opaque; int ret; ret = fd_open(bs); if (ret < 0) { + raw_account_discard(s, bytes, ret); return ret; } return raw_do_pdiscard(bs, offset, bytes, true); @@ -3418,6 +3469,7 @@ static BlockDriver bdrv_host_device = { .bdrv_get_info = raw_get_info, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, + .bdrv_get_specific_stats = hdev_get_specific_stats, .bdrv_check_perm = raw_check_perm, .bdrv_set_perm = raw_set_perm, .bdrv_abort_perm_update = raw_abort_perm_update, @@ -33,6 +33,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "sysemu/replay.h" #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ @@ -368,8 +369,8 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, if (bs) { bdrv_inc_in_flight(bs); } - aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), - bdrv_co_drain_bh_cb, &data); + replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs), + bdrv_co_drain_bh_cb, &data); qemu_coroutine_yield(); /* If we are resumed from some other event (such as an aio completion or a @@ -600,6 +601,15 @@ void bdrv_drain_all_begin(void) return; } + /* + * bdrv queue is managed by record/replay, + * waiting for finishing the I/O requests may + * be infinite + */ + if (replay_events_enabled()) { + return; + } + /* AIO_WAIT_WHILE() with a NULL context can only be called from the main * loop AioContext, so make sure we're in the main context. */ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); @@ -629,6 +639,15 @@ void bdrv_drain_all_end(void) BlockDriverState *bs = NULL; int drained_end_counter = 0; + /* + * bdrv queue is managed by record/replay, + * waiting for finishing the I/O requests may + * be endless + */ + if (replay_events_enabled()) { + return; + } + while ((bs = bdrv_next_all_states(bs))) { AioContext *aio_context = bdrv_get_aio_context(bs); @@ -2071,6 +2090,13 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, return ret; } + /* If the request is misaligned then we can't make it efficient */ + if ((flags & BDRV_REQ_NO_FALLBACK) && + !QEMU_IS_ALIGNED(offset | bytes, align)) + { + return -ENOTSUP; + } + bdrv_inc_in_flight(bs); /* * Align write if necessary by performing a read-modify-write cycle. @@ -2124,6 +2150,15 @@ int bdrv_flush_all(void) BlockDriverState *bs = NULL; int result = 0; + /* + * bdrv queue is managed by record/replay, + * creating new flush request for stopping + * the VM may break the determinism + */ + if (replay_events_enabled()) { + return result; + } + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { AioContext *aio_context = bdrv_get_aio_context(bs); int ret; diff --git a/block/iscsi.c b/block/iscsi.c index 506bf5f..2ced150 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -40,6 +40,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/uuid.h" +#include "sysemu/replay.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qmp/qdict.h" @@ -280,8 +281,8 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, } if (iTask->co) { - aio_bh_schedule_oneshot(iTask->iscsilun->aio_context, - iscsi_co_generic_bh_cb, iTask); + replay_bh_schedule_oneshot_event(iTask->iscsilun->aio_context, + iscsi_co_generic_bh_cb, iTask); } else { iTask->complete = 1; } diff --git a/block/nbd.c b/block/nbd.c index 813c40d..fd78e5f 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1158,6 +1158,18 @@ static int coroutine_fn nbd_client_co_block_status( BDRV_BLOCK_OFFSET_VALID; } +static int nbd_client_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + BDRVNBDState *s = (BDRVNBDState *)state->bs->opaque; + + if ((state->flags & BDRV_O_RDWR) && (s->info.flags & NBD_FLAG_READ_ONLY)) { + error_setg(errp, "Can't reopen read-only NBD mount as read/write"); + return -EACCES; + } + return 0; +} + static void nbd_client_close(BlockDriverState *bs) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; @@ -1798,6 +1810,7 @@ static BlockDriver bdrv_nbd = { .instance_size = sizeof(BDRVNBDState), .bdrv_parse_filename = nbd_parse_filename, .bdrv_file_open = nbd_open, + .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, .bdrv_co_pwritev = nbd_client_co_pwritev, .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, @@ -1820,6 +1833,7 @@ static BlockDriver bdrv_nbd_tcp = { .instance_size = sizeof(BDRVNBDState), .bdrv_parse_filename = nbd_parse_filename, .bdrv_file_open = nbd_open, + .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, .bdrv_co_pwritev = nbd_client_co_pwritev, .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, @@ -1842,6 +1856,7 @@ static BlockDriver bdrv_nbd_unix = { .instance_size = sizeof(BDRVNBDState), .bdrv_parse_filename = nbd_parse_filename, .bdrv_file_open = nbd_open, + .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, .bdrv_co_pwritev = nbd_client_co_pwritev, .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, diff --git a/block/nfs.c b/block/nfs.c index f39acfd..40f2349 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -37,6 +37,8 @@ #include "qemu/option.h" #include "qemu/uri.h" #include "qemu/cutils.h" +#include "sysemu/sysemu.h" +#include "sysemu/replay.h" #include "qapi/qapi-visit-block-core.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -257,8 +259,8 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, if (task->ret < 0) { error_report("NFS Error: %s", nfs_get_error(nfs)); } - aio_bh_schedule_oneshot(task->client->aio_context, - nfs_co_generic_bh_cb, task); + replay_bh_schedule_oneshot_event(task->client->aio_context, + nfs_co_generic_bh_cb, task); } static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, uint64_t offset, diff --git a/block/null.c b/block/null.c index 699aa29..15e1d56 100644 --- a/block/null.c +++ b/block/null.c @@ -17,6 +17,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "block/block_int.h" +#include "sysemu/replay.h" #define NULL_OPT_LATENCY "latency-ns" #define NULL_OPT_ZEROES "read-zeroes" @@ -179,7 +180,8 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs, timer_mod_ns(&acb->timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns); } else { - aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), null_bh_cb, acb); + replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs), + null_bh_cb, acb); } return &acb->common; } diff --git a/block/nvme.c b/block/nvme.c index 5be3a39..910872e 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -23,6 +23,7 @@ #include "qemu/option.h" #include "qemu/vfio-helpers.h" #include "block/block_int.h" +#include "sysemu/replay.h" #include "trace.h" #include "block/nvme.h" @@ -351,7 +352,8 @@ static bool nvme_process_completion(BDRVNVMeState *s, NVMeQueuePair *q) smp_mb_release(); *q->cq.doorbell = cpu_to_le32(q->cq.head); if (!qemu_co_queue_empty(&q->free_req_queue)) { - aio_bh_schedule_oneshot(s->aio_context, nvme_free_req_queue_cb, q); + replay_bh_schedule_oneshot_event(s->aio_context, + nvme_free_req_queue_cb, q); } } q->busy = false; @@ -935,7 +937,7 @@ static void nvme_rw_cb(void *opaque, int ret) /* The rw coroutine hasn't yielded, don't try to enter. */ return; } - aio_bh_schedule_oneshot(data->ctx, nvme_rw_cb_bh, data); + replay_bh_schedule_oneshot_event(data->ctx, nvme_rw_cb_bh, data); } static coroutine_fn int nvme_co_prw_aligned(BlockDriverState *bs, diff --git a/block/qapi.c b/block/qapi.c index 7ee2ee0..9a5d0c9 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -440,24 +440,30 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ]; ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE]; + ds->unmap_bytes = stats->nr_bytes[BLOCK_ACCT_UNMAP]; ds->rd_operations = stats->nr_ops[BLOCK_ACCT_READ]; ds->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE]; + ds->unmap_operations = stats->nr_ops[BLOCK_ACCT_UNMAP]; ds->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ]; ds->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE]; ds->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH]; + ds->failed_unmap_operations = stats->failed_ops[BLOCK_ACCT_UNMAP]; ds->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ]; ds->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE]; ds->invalid_flush_operations = stats->invalid_ops[BLOCK_ACCT_FLUSH]; + ds->invalid_unmap_operations = stats->invalid_ops[BLOCK_ACCT_UNMAP]; ds->rd_merged = stats->merged[BLOCK_ACCT_READ]; ds->wr_merged = stats->merged[BLOCK_ACCT_WRITE]; + ds->unmap_merged = stats->merged[BLOCK_ACCT_UNMAP]; ds->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH]; ds->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE]; ds->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ]; ds->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH]; + ds->unmap_total_time_ns = stats->total_time_ns[BLOCK_ACCT_UNMAP]; ds->has_idle_time_ns = stats->last_access_time_ns > 0; if (ds->has_idle_time_ns) { @@ -537,6 +543,11 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset); + s->driver_specific = bdrv_get_specific_stats(bs); + if (s->driver_specific) { + s->has_driver_specific = true; + } + if (bs->file) { s->has_parent = true; s->parent = bdrv_query_bds_stats(bs->file->bs, blk_level); diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 8d5fa15..8982b7b 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1330,6 +1330,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); assert(nb_clusters <= INT_MAX); + /* Limit total allocation byte count to INT_MAX */ + nb_clusters = MIN(nb_clusters, INT_MAX >> s->cluster_bits); + /* Find L2 entry for the first involved cluster */ ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); if (ret < 0) { @@ -1412,7 +1415,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, * request actually writes to (excluding COW at the end) */ uint64_t requested_bytes = *bytes + offset_into_cluster(s, guest_offset); - int avail_bytes = MIN(INT_MAX, nb_clusters << s->cluster_bits); + int avail_bytes = nb_clusters << s->cluster_bits; int nb_bytes = MIN(requested_bytes, avail_bytes); QCowL2Meta *old_m = *m; diff --git a/block/qcow2.c b/block/qcow2.c index 4d16393..7961c05 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -41,6 +41,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "crypto.h" +#include "block/aio_task.h" /* Differences with QCOW: @@ -1972,20 +1973,184 @@ out: return ret; } +static coroutine_fn int +qcow2_co_preadv_encrypted(BlockDriverState *bs, + uint64_t file_cluster_offset, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, + uint64_t qiov_offset) +{ + int ret; + BDRVQcow2State *s = bs->opaque; + uint8_t *buf; + + assert(bs->encrypted && s->crypto); + assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); + + /* + * For encrypted images, read everything into a temporary + * contiguous buffer on which the AES functions can work. + * Also, decryption in a separate buffer is better as it + * prevents the guest from learning information about the + * encrypted nature of the virtual disk. + */ + + buf = qemu_try_blockalign(s->data_file->bs, bytes); + if (buf == NULL) { + return -ENOMEM; + } + + BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + ret = bdrv_co_pread(s->data_file, + file_cluster_offset + offset_into_cluster(s, offset), + bytes, buf, 0); + if (ret < 0) { + goto fail; + } + + assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)); + assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE)); + if (qcow2_co_decrypt(bs, + file_cluster_offset + offset_into_cluster(s, offset), + offset, buf, bytes) < 0) + { + ret = -EIO; + goto fail; + } + qemu_iovec_from_buf(qiov, qiov_offset, buf, bytes); + +fail: + qemu_vfree(buf); + + return ret; +} + +typedef struct Qcow2AioTask { + AioTask task; + + BlockDriverState *bs; + QCow2ClusterType cluster_type; /* only for read */ + uint64_t file_cluster_offset; + uint64_t offset; + uint64_t bytes; + QEMUIOVector *qiov; + uint64_t qiov_offset; + QCowL2Meta *l2meta; /* only for write */ +} Qcow2AioTask; + +static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task); +static coroutine_fn int qcow2_add_task(BlockDriverState *bs, + AioTaskPool *pool, + AioTaskFunc func, + QCow2ClusterType cluster_type, + uint64_t file_cluster_offset, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, + size_t qiov_offset, + QCowL2Meta *l2meta) +{ + Qcow2AioTask local_task; + Qcow2AioTask *task = pool ? g_new(Qcow2AioTask, 1) : &local_task; + + *task = (Qcow2AioTask) { + .task.func = func, + .bs = bs, + .cluster_type = cluster_type, + .qiov = qiov, + .file_cluster_offset = file_cluster_offset, + .offset = offset, + .bytes = bytes, + .qiov_offset = qiov_offset, + .l2meta = l2meta, + }; + + trace_qcow2_add_task(qemu_coroutine_self(), bs, pool, + func == qcow2_co_preadv_task_entry ? "read" : "write", + cluster_type, file_cluster_offset, offset, bytes, + qiov, qiov_offset); + + if (!pool) { + return func(&task->task); + } + + aio_task_pool_start_task(pool, &task->task); + + return 0; +} + +static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs, + QCow2ClusterType cluster_type, + uint64_t file_cluster_offset, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, + size_t qiov_offset) +{ + BDRVQcow2State *s = bs->opaque; + int offset_in_cluster = offset_into_cluster(s, offset); + + switch (cluster_type) { + case QCOW2_CLUSTER_ZERO_PLAIN: + case QCOW2_CLUSTER_ZERO_ALLOC: + /* Both zero types are handled in qcow2_co_preadv_part */ + g_assert_not_reached(); + + case QCOW2_CLUSTER_UNALLOCATED: + assert(bs->backing); /* otherwise handled in qcow2_co_preadv_part */ + + BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); + return bdrv_co_preadv_part(bs->backing, offset, bytes, + qiov, qiov_offset, 0); + + case QCOW2_CLUSTER_COMPRESSED: + return qcow2_co_preadv_compressed(bs, file_cluster_offset, + offset, bytes, qiov, qiov_offset); + + case QCOW2_CLUSTER_NORMAL: + if ((file_cluster_offset & 511) != 0) { + return -EIO; + } + + if (bs->encrypted) { + return qcow2_co_preadv_encrypted(bs, file_cluster_offset, + offset, bytes, qiov, qiov_offset); + } + + BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + return bdrv_co_preadv_part(s->data_file, + file_cluster_offset + offset_in_cluster, + bytes, qiov, qiov_offset, 0); + + default: + g_assert_not_reached(); + } + + g_assert_not_reached(); +} + +static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task) +{ + Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); + + assert(!t->l2meta); + + return qcow2_co_preadv_task(t->bs, t->cluster_type, t->file_cluster_offset, + t->offset, t->bytes, t->qiov, t->qiov_offset); +} + static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, int flags) { BDRVQcow2State *s = bs->opaque; - int offset_in_cluster; - int ret; + int ret = 0; unsigned int cur_bytes; /* number of bytes in current iteration */ uint64_t cluster_offset = 0; - uint8_t *cluster_data = NULL; - - while (bytes != 0) { + AioTaskPool *aio = NULL; + while (bytes != 0 && aio_task_pool_status(aio) == 0) { /* prepare next request */ cur_bytes = MIN(bytes, INT_MAX); if (s->crypto) { @@ -1997,110 +2162,39 @@ static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs, ret = qcow2_get_cluster_offset(bs, offset, &cur_bytes, &cluster_offset); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { - goto fail; + goto out; } - offset_in_cluster = offset_into_cluster(s, offset); - - switch (ret) { - case QCOW2_CLUSTER_UNALLOCATED: - - if (bs->backing) { - BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); - ret = bdrv_co_preadv_part(bs->backing, offset, cur_bytes, - qiov, qiov_offset, 0); - if (ret < 0) { - goto fail; - } - } else { - /* Note: in this case, no need to wait */ - qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes); - } - break; - - case QCOW2_CLUSTER_ZERO_PLAIN: - case QCOW2_CLUSTER_ZERO_ALLOC: + if (ret == QCOW2_CLUSTER_ZERO_PLAIN || + ret == QCOW2_CLUSTER_ZERO_ALLOC || + (ret == QCOW2_CLUSTER_UNALLOCATED && !bs->backing)) + { qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes); - break; - - case QCOW2_CLUSTER_COMPRESSED: - ret = qcow2_co_preadv_compressed(bs, cluster_offset, - offset, cur_bytes, - qiov, qiov_offset); - if (ret < 0) { - goto fail; - } - - break; - - case QCOW2_CLUSTER_NORMAL: - if ((cluster_offset & 511) != 0) { - ret = -EIO; - goto fail; + } else { + if (!aio && cur_bytes != bytes) { + aio = aio_task_pool_new(QCOW2_MAX_WORKERS); } - - if (bs->encrypted) { - assert(s->crypto); - - /* - * For encrypted images, read everything into a temporary - * contiguous buffer on which the AES functions can work. - */ - if (!cluster_data) { - cluster_data = - qemu_try_blockalign(s->data_file->bs, - QCOW_MAX_CRYPT_CLUSTERS - * s->cluster_size); - if (cluster_data == NULL) { - ret = -ENOMEM; - goto fail; - } - } - - assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); - - BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - ret = bdrv_co_pread(s->data_file, - cluster_offset + offset_in_cluster, - cur_bytes, cluster_data, 0); - if (ret < 0) { - goto fail; - } - - assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)); - assert(QEMU_IS_ALIGNED(cur_bytes, BDRV_SECTOR_SIZE)); - if (qcow2_co_decrypt(bs, cluster_offset + offset_in_cluster, - offset, - cluster_data, cur_bytes) < 0) { - ret = -EIO; - goto fail; - } - qemu_iovec_from_buf(qiov, qiov_offset, cluster_data, cur_bytes); - } else { - BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - ret = bdrv_co_preadv_part(s->data_file, - cluster_offset + offset_in_cluster, - cur_bytes, qiov, qiov_offset, 0); - if (ret < 0) { - goto fail; - } + ret = qcow2_add_task(bs, aio, qcow2_co_preadv_task_entry, ret, + cluster_offset, offset, cur_bytes, + qiov, qiov_offset, NULL); + if (ret < 0) { + goto out; } - break; - - default: - g_assert_not_reached(); - ret = -EIO; - goto fail; } bytes -= cur_bytes; offset += cur_bytes; qiov_offset += cur_bytes; } - ret = 0; -fail: - qemu_vfree(cluster_data); +out: + if (aio) { + aio_task_pool_wait_all(aio); + if (ret == 0) { + ret = aio_task_pool_status(aio); + } + g_free(aio); + } return ret; } @@ -2225,6 +2319,99 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) return 0; } +/* + * qcow2_co_pwritev_task + * Called with s->lock unlocked + * l2meta - if not NULL, qcow2_co_pwritev_task() will consume it. Caller must + * not use it somehow after qcow2_co_pwritev_task() call + */ +static coroutine_fn int qcow2_co_pwritev_task(BlockDriverState *bs, + uint64_t file_cluster_offset, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, + uint64_t qiov_offset, + QCowL2Meta *l2meta) +{ + int ret; + BDRVQcow2State *s = bs->opaque; + void *crypt_buf = NULL; + int offset_in_cluster = offset_into_cluster(s, offset); + QEMUIOVector encrypted_qiov; + + if (bs->encrypted) { + assert(s->crypto); + assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); + crypt_buf = qemu_try_blockalign(bs->file->bs, bytes); + if (crypt_buf == NULL) { + ret = -ENOMEM; + goto out_unlocked; + } + qemu_iovec_to_buf(qiov, qiov_offset, crypt_buf, bytes); + + if (qcow2_co_encrypt(bs, file_cluster_offset + offset_in_cluster, + offset, crypt_buf, bytes) < 0) + { + ret = -EIO; + goto out_unlocked; + } + + qemu_iovec_init_buf(&encrypted_qiov, crypt_buf, bytes); + qiov = &encrypted_qiov; + qiov_offset = 0; + } + + /* Try to efficiently initialize the physical space with zeroes */ + ret = handle_alloc_space(bs, l2meta); + if (ret < 0) { + goto out_unlocked; + } + + /* + * If we need to do COW, check if it's possible to merge the + * writing of the guest data together with that of the COW regions. + * If it's not possible (or not necessary) then write the + * guest data now. + */ + if (!merge_cow(offset, bytes, qiov, qiov_offset, l2meta)) { + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + trace_qcow2_writev_data(qemu_coroutine_self(), + file_cluster_offset + offset_in_cluster); + ret = bdrv_co_pwritev_part(s->data_file, + file_cluster_offset + offset_in_cluster, + bytes, qiov, qiov_offset, 0); + if (ret < 0) { + goto out_unlocked; + } + } + + qemu_co_mutex_lock(&s->lock); + + ret = qcow2_handle_l2meta(bs, &l2meta, true); + goto out_locked; + +out_unlocked: + qemu_co_mutex_lock(&s->lock); + +out_locked: + qcow2_handle_l2meta(bs, &l2meta, false); + qemu_co_mutex_unlock(&s->lock); + + qemu_vfree(crypt_buf); + + return ret; +} + +static coroutine_fn int qcow2_co_pwritev_task_entry(AioTask *task) +{ + Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); + + assert(!t->cluster_type); + + return qcow2_co_pwritev_task(t->bs, t->file_cluster_offset, + t->offset, t->bytes, t->qiov, t->qiov_offset, + t->l2meta); +} + static coroutine_fn int qcow2_co_pwritev_part( BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, int flags) @@ -2234,16 +2421,12 @@ static coroutine_fn int qcow2_co_pwritev_part( int ret; unsigned int cur_bytes; /* number of sectors in current iteration */ uint64_t cluster_offset; - QEMUIOVector encrypted_qiov; - uint64_t bytes_done = 0; - uint8_t *cluster_data = NULL; QCowL2Meta *l2meta = NULL; + AioTaskPool *aio = NULL; trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes); - qemu_co_mutex_lock(&s->lock); - - while (bytes != 0) { + while (bytes != 0 && aio_task_pool_status(aio) == 0) { l2meta = NULL; @@ -2256,6 +2439,8 @@ static coroutine_fn int qcow2_co_pwritev_part( - offset_in_cluster); } + qemu_co_mutex_lock(&s->lock); + ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes, &cluster_offset, &l2meta); if (ret < 0) { @@ -2273,73 +2458,24 @@ static coroutine_fn int qcow2_co_pwritev_part( qemu_co_mutex_unlock(&s->lock); - if (bs->encrypted) { - assert(s->crypto); - if (!cluster_data) { - cluster_data = qemu_try_blockalign(bs->file->bs, - QCOW_MAX_CRYPT_CLUSTERS - * s->cluster_size); - if (cluster_data == NULL) { - ret = -ENOMEM; - goto out_unlocked; - } - } - - assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); - qemu_iovec_to_buf(qiov, qiov_offset + bytes_done, - cluster_data, cur_bytes); - - if (qcow2_co_encrypt(bs, cluster_offset + offset_in_cluster, offset, - cluster_data, cur_bytes) < 0) { - ret = -EIO; - goto out_unlocked; - } - - qemu_iovec_init_buf(&encrypted_qiov, cluster_data, cur_bytes); + if (!aio && cur_bytes != bytes) { + aio = aio_task_pool_new(QCOW2_MAX_WORKERS); } - - /* Try to efficiently initialize the physical space with zeroes */ - ret = handle_alloc_space(bs, l2meta); + ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_task_entry, 0, + cluster_offset, offset, cur_bytes, + qiov, qiov_offset, l2meta); + l2meta = NULL; /* l2meta is consumed by qcow2_co_pwritev_task() */ if (ret < 0) { - goto out_unlocked; - } - - /* If we need to do COW, check if it's possible to merge the - * writing of the guest data together with that of the COW regions. - * If it's not possible (or not necessary) then write the - * guest data now. */ - if (!merge_cow(offset, cur_bytes, - bs->encrypted ? &encrypted_qiov : qiov, - bs->encrypted ? 0 : qiov_offset + bytes_done, l2meta)) - { - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - trace_qcow2_writev_data(qemu_coroutine_self(), - cluster_offset + offset_in_cluster); - ret = bdrv_co_pwritev_part( - s->data_file, cluster_offset + offset_in_cluster, cur_bytes, - bs->encrypted ? &encrypted_qiov : qiov, - bs->encrypted ? 0 : qiov_offset + bytes_done, 0); - if (ret < 0) { - goto out_unlocked; - } - } - - qemu_co_mutex_lock(&s->lock); - - ret = qcow2_handle_l2meta(bs, &l2meta, true); - if (ret) { - goto out_locked; + goto fail_nometa; } bytes -= cur_bytes; offset += cur_bytes; - bytes_done += cur_bytes; + qiov_offset += cur_bytes; trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes); } ret = 0; - goto out_locked; -out_unlocked: qemu_co_mutex_lock(&s->lock); out_locked: @@ -2347,7 +2483,15 @@ out_locked: qemu_co_mutex_unlock(&s->lock); - qemu_vfree(cluster_data); +fail_nometa: + if (aio) { + aio_task_pool_wait_all(aio); + if (ret == 0) { + ret = aio_task_pool_status(aio); + } + g_free(aio); + } + trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); return ret; diff --git a/block/qcow2.h b/block/qcow2.h index a488d76..f51f478 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -65,6 +65,9 @@ #define QCOW2_MAX_BITMAPS 65535 #define QCOW2_MAX_BITMAP_DIRECTORY_SIZE (1024 * QCOW2_MAX_BITMAPS) +/* Maximum of parallel sub-request per guest request */ +#define QCOW2_MAX_WORKERS 8 + /* indicate that the refcount of the referenced cluster is exactly one. */ #define QCOW_OFLAG_COPIED (1ULL << 63) /* indicate that the cluster is compressed (they never have the copied flag) */ diff --git a/block/rbd.c b/block/rbd.c index 057af43..c71e45d 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -22,6 +22,7 @@ #include "block/qdict.h" #include "crypto/secret.h" #include "qemu/cutils.h" +#include "sysemu/replay.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" @@ -884,8 +885,8 @@ static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb) rcb->ret = rbd_aio_get_return_value(c); rbd_aio_release(c); - aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs), - rbd_finish_bh, rcb); + replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs), + rbd_finish_bh, rcb); } static int rbd_aio_discard_wrapper(rbd_image_t image, diff --git a/block/replication.c b/block/replication.c index 936b2f8..99532ce 100644 --- a/block/replication.c +++ b/block/replication.c @@ -543,7 +543,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, s->backup_job = backup_job_create( NULL, s->secondary_disk->bs, s->hidden_disk->bs, - 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, + 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, backup_job_completed, bs, NULL, &local_err); diff --git a/block/trace-events b/block/trace-events index 04209f0..b8d70f5 100644 --- a/block/trace-events +++ b/block/trace-events @@ -40,12 +40,14 @@ mirror_yield_in_flight(void *s, int64_t offset, int in_flight) "s %p offset %" P # backup.c backup_do_cow_enter(void *job, int64_t start, int64_t offset, uint64_t bytes) "job %p start %" PRId64 " offset %" PRId64 " bytes %" PRIu64 backup_do_cow_return(void *job, int64_t offset, uint64_t bytes, int ret) "job %p offset %" PRId64 " bytes %" PRIu64 " ret %d" -backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64 -backup_do_cow_skip_range(void *job, int64_t start, uint64_t bytes) "job %p start %"PRId64" bytes %"PRId64 -backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64 -backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" -backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" -backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" + +# block-copy.c +block_copy_skip(void *bcs, int64_t start) "bcs %p start %"PRId64 +block_copy_skip_range(void *bcs, int64_t start, uint64_t bytes) "bcs %p start %"PRId64" bytes %"PRId64 +block_copy_process(void *bcs, int64_t start) "bcs %p start %"PRId64 +block_copy_with_bounce_buffer_read_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" +block_copy_with_bounce_buffer_write_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" +block_copy_with_offload_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" # ../blockdev.c qmp_block_job_cancel(void *job) "job %p" @@ -62,6 +64,7 @@ file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) " file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64 # qcow2.c +qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t file_cluster_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu" qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" qcow2_writev_done_req(void *co, int ret) "co %p ret %d" qcow2_writev_start_part(void *co) "co %p" diff --git a/block/vhdx.c b/block/vhdx.c index 6a09d0a..371f226 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -24,6 +24,7 @@ #include "qemu/option.h" #include "qemu/crc32c.h" #include "qemu/bswap.h" +#include "qemu/error-report.h" #include "vhdx.h" #include "migration/blocker.h" #include "qemu/uuid.h" @@ -235,6 +236,9 @@ static int vhdx_region_check(BDRVVHDXState *s, uint64_t start, uint64_t length) end = start + length; QLIST_FOREACH(r, &s->regions, entries) { if (!((start >= r->end) || (end <= r->start))) { + error_report("VHDX region %" PRIu64 "-%" PRIu64 " overlaps with " + "region %" PRIu64 "-%." PRIu64, start, end, r->start, + r->end); ret = -EINVAL; goto exit; } @@ -877,6 +881,95 @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s) } +static int vhdx_check_bat_entries(BlockDriverState *bs, int *errcnt) +{ + BDRVVHDXState *s = bs->opaque; + int64_t image_file_size = bdrv_getlength(bs->file->bs); + uint64_t payblocks = s->chunk_ratio; + uint64_t i; + int ret = 0; + + if (image_file_size < 0) { + error_report("Could not determinate VHDX image file size."); + return image_file_size; + } + + for (i = 0; i < s->bat_entries; i++) { + if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) == + PAYLOAD_BLOCK_FULLY_PRESENT) { + uint64_t offset = s->bat[i] & VHDX_BAT_FILE_OFF_MASK; + /* + * Allow that the last block exists only partially. The VHDX spec + * states that the image file can only grow in blocksize increments, + * but QEMU created images with partial last blocks in the past. + */ + uint32_t block_length = MIN(s->block_size, + bs->total_sectors * BDRV_SECTOR_SIZE - i * s->block_size); + /* + * Check for BAT entry overflow. + */ + if (offset > INT64_MAX - s->block_size) { + error_report("VHDX BAT entry %" PRIu64 " offset overflow.", i); + ret = -EINVAL; + if (!errcnt) { + break; + } + (*errcnt)++; + } + /* + * Check if fully allocated BAT entries do not reside after + * end of the image file. + */ + if (offset >= image_file_size) { + error_report("VHDX BAT entry %" PRIu64 " start offset %" PRIu64 + " points after end of file (%" PRIi64 "). Image" + " has probably been truncated.", + i, offset, image_file_size); + ret = -EINVAL; + if (!errcnt) { + break; + } + (*errcnt)++; + } else if (offset + block_length > image_file_size) { + error_report("VHDX BAT entry %" PRIu64 " end offset %" PRIu64 + " points after end of file (%" PRIi64 "). Image" + " has probably been truncated.", + i, offset + block_length - 1, image_file_size); + ret = -EINVAL; + if (!errcnt) { + break; + } + (*errcnt)++; + } + + /* + * verify populated BAT field file offsets against + * region table and log entries + */ + if (payblocks--) { + /* payload bat entries */ + int ret2; + ret2 = vhdx_region_check(s, offset, s->block_size); + if (ret2 < 0) { + ret = -EINVAL; + if (!errcnt) { + break; + } + (*errcnt)++; + } + } else { + payblocks = s->chunk_ratio; + /* + * Once differencing files are supported, verify sector bitmap + * blocks here + */ + } + } + } + + return ret; +} + static void vhdx_close(BlockDriverState *bs) { BDRVVHDXState *s = bs->opaque; @@ -981,25 +1074,15 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - uint64_t payblocks = s->chunk_ratio; - /* endian convert, and verify populated BAT field file offsets against - * region table and log entries */ + /* endian convert populated BAT field entires */ for (i = 0; i < s->bat_entries; i++) { s->bat[i] = le64_to_cpu(s->bat[i]); - if (payblocks--) { - /* payload bat entries */ - if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) == - PAYLOAD_BLOCK_FULLY_PRESENT) { - ret = vhdx_region_check(s, s->bat[i] & VHDX_BAT_FILE_OFF_MASK, - s->block_size); - if (ret < 0) { - goto fail; - } - } - } else { - payblocks = s->chunk_ratio; - /* Once differencing files are supported, verify sector bitmap - * blocks here */ + } + + if (!(flags & BDRV_O_CHECK)) { + ret = vhdx_check_bat_entries(bs, NULL); + if (ret < 0) { + goto fail; } } @@ -2072,6 +2155,9 @@ static int coroutine_fn vhdx_co_check(BlockDriverState *bs, if (s->log_replayed_on_open) { result->corruptions_fixed++; } + + vhdx_check_bat_entries(bs, &result->corruptions); + return 0; } diff --git a/block/vxhs.c b/block/vxhs.c index 77fd5eb..d79fc97 100644 --- a/block/vxhs.c +++ b/block/vxhs.c @@ -22,6 +22,7 @@ #include "qapi/error.h" #include "qemu/uuid.h" #include "crypto/tlscredsx509.h" +#include "sysemu/replay.h" #define VXHS_OPT_FILENAME "filename" #define VXHS_OPT_VDISK_ID "vdisk-id" @@ -105,8 +106,8 @@ static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error) trace_vxhs_iio_callback(error); } - aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs), - vxhs_complete_aio_bh, acb); + replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs), + vxhs_complete_aio_bh, acb); break; default: @@ -3601,6 +3601,7 @@ static BlockJob *do_backup_common(BackupCommon *backup, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, bmap, backup->bitmap_mode, backup->compress, + backup->filter_node_name, backup->on_source_error, backup->on_target_error, job_flags, NULL, NULL, txn, errp); @@ -381,6 +381,7 @@ vhost_crypto="" vhost_scsi="" vhost_vsock="" vhost_user="" +vhost_user_fs="" kvm="no" hax="no" hvf="no" @@ -1293,6 +1294,10 @@ for opt do ;; --enable-vhost-vsock) vhost_vsock="yes" ;; + --disable-vhost-user-fs) vhost_user_fs="no" + ;; + --enable-vhost-user-fs) vhost_user_fs="yes" + ;; --disable-opengl) opengl="no" ;; --enable-opengl) opengl="yes" @@ -2236,6 +2241,10 @@ test "$vhost_crypto" = "" && vhost_crypto=$vhost_user if test "$vhost_crypto" = "yes" && test "$vhost_user" = "no"; then error_exit "--enable-vhost-crypto requires --enable-vhost-user" fi +test "$vhost_user_fs" = "" && vhost_user_fs=$vhost_user +if test "$vhost_user_fs" = "yes" && test "$vhost_user" = "no"; then + error_exit "--enable-vhost-user-fs requires --enable-vhost-user" +fi # OR the vhost-kernel and vhost-user values for simplicity if test "$vhost_net" = ""; then @@ -6374,6 +6383,7 @@ echo "vhost-crypto support $vhost_crypto" echo "vhost-scsi support $vhost_scsi" echo "vhost-vsock support $vhost_vsock" echo "vhost-user support $vhost_user" +echo "vhost-user-fs support $vhost_user_fs" echo "Trace backends $trace_backends" if have_backend "simple"; then echo "Trace output file $trace_file-<pid>" @@ -6870,6 +6880,9 @@ fi if test "$vhost_user" = "yes" ; then echo "CONFIG_VHOST_USER=y" >> $config_host_mak fi +if test "$vhost_user_fs" = "yes" ; then + echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak +fi if test "$blobs" = "yes" ; then echo "INSTALL_BLOBS=yes" >> $config_host_mak fi @@ -1107,7 +1107,6 @@ static int do_vm_stop(RunState state, bool send_stop) } bdrv_drain_all(); - replay_disable_events(); ret = bdrv_flush_all(); return ret; @@ -2193,7 +2192,6 @@ int vm_prepare_start(void) /* We are sending this now, but the CPUs will be resumed shortly later */ qapi_event_send_resume(); - replay_enable_events(); cpu_enable_ticks(); runstate_set(RUN_STATE_RUNNING); vm_state_notify(1, RUN_STATE_RUNNING); diff --git a/docs/devel/rcu.txt b/docs/devel/rcu.txt index c84e7f4..d83fed2 100644 --- a/docs/devel/rcu.txt +++ b/docs/devel/rcu.txt @@ -187,6 +187,22 @@ The following APIs must be used before RCU is used in a thread: Note that these APIs are relatively heavyweight, and should _not_ be nested. +Convenience macros +================== + +Two macros are provided that automatically release the read lock at the +end of the scope. + + RCU_READ_LOCK_GUARD() + + Takes the lock and will release it at the end of the block it's + used in. + + WITH_RCU_READ_LOCK_GUARD() { code } + + Is used at the head of a block to protect the code within the block. + +Note that 'goto'ing out of the guarded block will also drop the lock. DIFFERENCES WITH LINUX ====================== diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt index 8231bbf..8c0376f 100644 --- a/docs/devel/tracing.txt +++ b/docs/devel/tracing.txt @@ -317,7 +317,8 @@ probes: --binary path/to/qemu-binary \ --target-type system \ --target-name x86_64 \ - <trace-events-all >qemu.stp + --group=all \ + trace-events-all >qemu.stp To facilitate simple usage of systemtap where there merely needs to be printf logging of certain probes, a helper script "qemu-trace-stap" is provided. diff --git a/docs/replay.txt b/docs/replay.txt index ee6aee9..ce97c3f 100644 --- a/docs/replay.txt +++ b/docs/replay.txt @@ -27,7 +27,7 @@ Usage of the record/replay: * First, record the execution with the following command line: qemu-system-i386 \ -icount shift=7,rr=record,rrfile=replay.bin \ - -drive file=disk.qcow2,if=none,id=img-direct \ + -drive file=disk.qcow2,if=none,snapshot,id=img-direct \ -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \ -device ide-hd,drive=img-blkreplay \ -netdev user,id=net1 -device rtl8139,netdev=net1 \ @@ -35,7 +35,7 @@ Usage of the record/replay: * After recording, you can replay it by using another command line: qemu-system-i386 \ -icount shift=7,rr=replay,rrfile=replay.bin \ - -drive file=disk.qcow2,if=none,id=img-direct \ + -drive file=disk.qcow2,if=none,snapshot,id=img-direct \ -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \ -device ide-hd,drive=img-blkreplay \ -netdev user,id=net1 -device rtl8139,netdev=net1 \ @@ -223,7 +223,7 @@ Block devices record/replay module intercepts calls of bdrv coroutine functions at the top of block drivers stack. To record and replay block operations the drive must be configured as following: - -drive file=disk.qcow2,if=none,id=img-direct + -drive file=disk.qcow2,if=none,snapshot,id=img-direct -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay -device ide-hd,drive=img-blkreplay @@ -252,6 +252,12 @@ This snapshot is created at start of recording and restored at start of replaying. It also can be loaded while replaying to roll back the execution. +'snapshot' flag of the disk image must be removed to save the snapshots +in the overlay (or original image) instead of using the temporary overlay. + -drive file=disk.ovl,if=none,id=img-direct + -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay + -device ide-hd,drive=img-blkreplay + Use QEMU monitor to create additional snapshots. 'savevm <name>' command created the snapshot and 'loadvm <name>' restores it. To prevent corruption of the original disk image, use overlay files linked to the original images. diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst new file mode 100644 index 0000000..911a982 --- /dev/null +++ b/docs/specs/acpi_hw_reduced_hotplug.rst @@ -0,0 +1,70 @@ +================================================== +QEMU and ACPI BIOS Generic Event Device interface +================================================== + +The ACPI *Generic Event Device* (GED) is a HW reduced platform +specific device introduced in ACPI v6.1 that handles all platform +events, including the hotplug ones. GED is modelled as a device +in the namespace with a _HID defined to be ACPI0013. This document +describes the interface between QEMU and the ACPI BIOS. + +GED allows HW reduced platforms to handle interrupts in ACPI ASL +statements. It follows a very similar approach to the _EVT method +from GPIO events. All interrupts are listed in _CRS and the handler +is written in _EVT method. However, the QEMU implementation uses a +single interrupt for the GED device, relying on an IO memory region +to communicate the type of device affected by the interrupt. This way, +we can support up to 32 events with a unique interrupt. + +**Here is an example,** + +:: + + Device (\_SB.GED) + { + Name (_HID, "ACPI0013") + Name (_UID, Zero) + Name (_CRS, ResourceTemplate () + { + Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, ) + { + 0x00000029, + } + }) + OperationRegion (EREG, SystemMemory, 0x09080000, 0x04) + Field (EREG, DWordAcc, NoLock, WriteAsZeros) + { + ESEL, 32 + } + Method (_EVT, 1, Serialized) + { + Local0 = ESEL // ESEL = IO memory region which specifies the + // device type. + If (((Local0 & One) == One)) + { + MethodEvent1() + } + If ((Local0 & 0x2) == 0x2) + { + MethodEvent2() + } + ... + } + } + +GED IO interface (4 byte access) +-------------------------------- +**read access:** + +:: + + [0x0-0x3] Event selector bit field (32 bit) set by QEMU. + + bits: + 0: Memory hotplug event + 1: System power down event + 2-31: Reserved + +**write_access:** + +Nothing is expected to be written into GED IO memory diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 40adb97..984ba44 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -12,3 +12,4 @@ Contents: ppc-xive ppc-spapr-xive + acpi_hw_reduced_hotplug @@ -1037,16 +1037,14 @@ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) return; } - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); mr = address_space_translate(as, addr, &addr, &l, false, attrs); if (!(memory_region_is_ram(mr) || memory_region_is_romd(mr))) { - rcu_read_unlock(); return; } ram_addr = memory_region_get_ram_addr(mr) + addr; tb_invalidate_phys_page_range(ram_addr, ram_addr + 1); - rcu_read_unlock(); } static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) @@ -1332,14 +1330,13 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); block = qemu_get_ram_block(start); assert(block == qemu_get_ram_block(end - 1)); start1 = (uintptr_t)ramblock_ptr(block, start - block->offset); CPU_FOREACH(cpu) { tlb_reset_dirty(cpu, start1, length); } - rcu_read_unlock(); } /* Note: start and end must be within the same ram block. */ @@ -1360,30 +1357,29 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - rcu_read_lock(); + WITH_RCU_READ_LOCK_GUARD() { + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + ramblock = qemu_get_ram_block(start); + /* Range sanity check on the ramblock */ + assert(start >= ramblock->offset && + start + length <= ramblock->offset + ramblock->used_length); - blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); - ramblock = qemu_get_ram_block(start); - /* Range sanity check on the ramblock */ - assert(start >= ramblock->offset && - start + length <= ramblock->offset + ramblock->used_length); + while (page < end) { + unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; + unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; + unsigned long num = MIN(end - page, + DIRTY_MEMORY_BLOCK_SIZE - offset); - while (page < end) { - unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; - unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; - unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset); + dirty |= bitmap_test_and_clear_atomic(blocks->blocks[idx], + offset, num); + page += num; + } - dirty |= bitmap_test_and_clear_atomic(blocks->blocks[idx], - offset, num); - page += num; + mr_offset = (ram_addr_t)(page << TARGET_PAGE_BITS) - ramblock->offset; + mr_size = (end - page) << TARGET_PAGE_BITS; + memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size); } - mr_offset = (ram_addr_t)(page << TARGET_PAGE_BITS) - ramblock->offset; - mr_size = (end - page) << TARGET_PAGE_BITS; - memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size); - - rcu_read_unlock(); - if (dirty && tcg_enabled()) { tlb_reset_dirty_range_all(start, length); } @@ -1411,28 +1407,27 @@ DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty end = last >> TARGET_PAGE_BITS; dest = 0; - rcu_read_lock(); + WITH_RCU_READ_LOCK_GUARD() { + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); - blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + while (page < end) { + unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; + unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; + unsigned long num = MIN(end - page, + DIRTY_MEMORY_BLOCK_SIZE - offset); - while (page < end) { - unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; - unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; - unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset); - - assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL))); - assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL))); - offset >>= BITS_PER_LEVEL; + assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL))); + assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL))); + offset >>= BITS_PER_LEVEL; - bitmap_copy_and_clear_atomic(snap->dirty + dest, - blocks->blocks[idx] + offset, - num); - page += num; - dest += num >> BITS_PER_LEVEL; + bitmap_copy_and_clear_atomic(snap->dirty + dest, + blocks->blocks[idx] + offset, + num); + page += num; + dest += num >> BITS_PER_LEVEL; + } } - rcu_read_unlock(); - if (tcg_enabled()) { tlb_reset_dirty_range_all(start, length); } @@ -1643,7 +1638,7 @@ void ram_block_dump(Monitor *mon) RAMBlock *block; char *psize; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); monitor_printf(mon, "%24s %8s %18s %18s %18s\n", "Block Name", "PSize", "Offset", "Used", "Total"); RAMBLOCK_FOREACH(block) { @@ -1655,7 +1650,6 @@ void ram_block_dump(Monitor *mon) (uint64_t)block->max_length); g_free(psize); } - rcu_read_unlock(); } #ifdef __linux__ @@ -2009,11 +2003,10 @@ static unsigned long last_ram_page(void) RAMBlock *block; ram_addr_t last = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); RAMBLOCK_FOREACH(block) { last = MAX(last, block->offset + block->max_length); } - rcu_read_unlock(); return last >> TARGET_PAGE_BITS; } @@ -2100,7 +2093,7 @@ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) } pstrcat(new_block->idstr, sizeof(new_block->idstr), name); - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); RAMBLOCK_FOREACH(block) { if (block != new_block && !strcmp(block->idstr, new_block->idstr)) { @@ -2109,7 +2102,6 @@ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) abort(); } } - rcu_read_unlock(); } /* Called with iothread lock held. */ @@ -2651,17 +2643,16 @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, if (xen_enabled()) { ram_addr_t ram_addr; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); ram_addr = xen_ram_addr_from_mapcache(ptr); block = qemu_get_ram_block(ram_addr); if (block) { *offset = ram_addr - block->offset; } - rcu_read_unlock(); return block; } - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); block = atomic_rcu_read(&ram_list.mru_block); if (block && block->host && host - block->host < block->max_length) { goto found; @@ -2677,7 +2668,6 @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, } } - rcu_read_unlock(); return NULL; found: @@ -2685,7 +2675,6 @@ found: if (round_offset) { *offset &= TARGET_PAGE_MASK; } - rcu_read_unlock(); return block; } @@ -3281,10 +3270,9 @@ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, FlatView *fv; if (len > 0) { - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); fv = address_space_to_flatview(as); result = flatview_read(fv, addr, attrs, buf, len); - rcu_read_unlock(); } return result; @@ -3298,10 +3286,9 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, FlatView *fv; if (len > 0) { - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); fv = address_space_to_flatview(as); result = flatview_write(fv, addr, attrs, buf, len); - rcu_read_unlock(); } return result; @@ -3341,7 +3328,7 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, hwaddr addr1; MemoryRegion *mr; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); while (len > 0) { l = len; mr = address_space_translate(as, addr, &addr1, &l, true, attrs); @@ -3366,7 +3353,6 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, buf += l; addr += l; } - rcu_read_unlock(); return MEMTX_OK; } @@ -3511,10 +3497,9 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, FlatView *fv; bool result; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); fv = address_space_to_flatview(as); result = flatview_access_valid(fv, addr, len, is_write, attrs); - rcu_read_unlock(); return result; } @@ -3569,13 +3554,12 @@ void *address_space_map(AddressSpace *as, } l = len; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); fv = address_space_to_flatview(as); mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { if (atomic_xchg(&bounce.in_use, true)) { - rcu_read_unlock(); return NULL; } /* Avoid unbounded allocations */ @@ -3591,7 +3575,6 @@ void *address_space_map(AddressSpace *as, bounce.buffer, l); } - rcu_read_unlock(); *plen = l; return bounce.buffer; } @@ -3601,7 +3584,6 @@ void *address_space_map(AddressSpace *as, *plen = flatview_extend_translation(fv, addr, len, mr, xlat, l, is_write, attrs); ptr = qemu_ram_ptr_length(mr->ram_block, xlat, plen, true); - rcu_read_unlock(); return ptr; } @@ -3869,13 +3851,12 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr) hwaddr l = 1; bool res; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); mr = address_space_translate(&address_space_memory, phys_addr, &phys_addr, &l, false, MEMTXATTRS_UNSPECIFIED); res = !(memory_region_is_ram(mr) || memory_region_is_romd(mr)); - rcu_read_unlock(); return res; } @@ -3884,14 +3865,13 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) RAMBlock *block; int ret = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); RAMBLOCK_FOREACH(block) { ret = func(block, opaque); if (ret) { break; } } - rcu_read_unlock(); return ret; } diff --git a/fsdev/9p-marshal.h b/fsdev/9p-marshal.h index c8823d8..8f3babb6 100644 --- a/fsdev/9p-marshal.h +++ b/fsdev/9p-marshal.h @@ -9,9 +9,9 @@ typedef struct V9fsString typedef struct V9fsQID { - int8_t type; - int32_t version; - int64_t path; + uint8_t type; + uint32_t version; + uint64_t path; } V9fsQID; typedef struct V9fsStat diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index c757c80..f2f7772 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -59,6 +59,11 @@ typedef struct ExtendedOps { #define V9FS_RDONLY 0x00000040 #define V9FS_PROXY_SOCK_FD 0x00000080 #define V9FS_PROXY_SOCK_NAME 0x00000100 +/* + * multidevs option (either one of the two applies exclusively) + */ +#define V9FS_REMAP_INODES 0x00000200 +#define V9FS_FORBID_MULTIDEVS 0x00000400 #define V9FS_SEC_MASK 0x0000003C diff --git a/fsdev/qemu-fsdev-opts.c b/fsdev/qemu-fsdev-opts.c index 7c31fff..07a18c6 100644 --- a/fsdev/qemu-fsdev-opts.c +++ b/fsdev/qemu-fsdev-opts.c @@ -31,7 +31,9 @@ static QemuOptsList qemu_fsdev_opts = { }, { .name = "readonly", .type = QEMU_OPT_BOOL, - + }, { + .name = "multidevs", + .type = QEMU_OPT_STRING, }, { .name = "socket", .type = QEMU_OPT_STRING, @@ -76,6 +78,9 @@ static QemuOptsList qemu_virtfs_opts = { .name = "readonly", .type = QEMU_OPT_BOOL, }, { + .name = "multidevs", + .type = QEMU_OPT_STRING, + }, { .name = "socket", .type = QEMU_OPT_STRING, }, { diff --git a/fsdev/qemu-fsdev-throttle.c b/fsdev/qemu-fsdev-throttle.c index fe1d76d..5c83a1c 100644 --- a/fsdev/qemu-fsdev-throttle.c +++ b/fsdev/qemu-fsdev-throttle.c @@ -31,7 +31,7 @@ static void fsdev_throttle_write_timer_cb(void *opaque) qemu_co_enter_next(&fst->throttled_reqs[true], NULL); } -void fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp) +int fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp) { throttle_config_init(&fst->cfg); fst->cfg.buckets[THROTTLE_BPS_TOTAL].avg = @@ -75,7 +75,7 @@ void fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp) fst->cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0); - throttle_is_valid(&fst->cfg, errp); + return throttle_is_valid(&fst->cfg, errp) ? 0 : -1; } void fsdev_throttle_init(FsThrottle *fst) diff --git a/fsdev/qemu-fsdev-throttle.h b/fsdev/qemu-fsdev-throttle.h index c98e2fe..a21aecd 100644 --- a/fsdev/qemu-fsdev-throttle.h +++ b/fsdev/qemu-fsdev-throttle.h @@ -26,7 +26,7 @@ typedef struct FsThrottle { CoQueue throttled_reqs[2]; } FsThrottle; -void fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **); +int fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **); void fsdev_throttle_init(FsThrottle *); diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c index 077a8c4..a9e069c 100644 --- a/fsdev/qemu-fsdev.c +++ b/fsdev/qemu-fsdev.c @@ -58,6 +58,7 @@ static FsDriverTable FsDrivers[] = { "writeout", "fmode", "dmode", + "multidevs", "throttling.bps-total", "throttling.bps-read", "throttling.bps-write", diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 08e673a..4708c0b 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -1465,6 +1465,10 @@ static void local_cleanup(FsContext *ctx) { LocalData *data = ctx->private; + if (!data) { + return; + } + close(data->mountfd); g_free(data); } @@ -1479,6 +1483,7 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) { const char *sec_model = qemu_opt_get(opts, "security_model"); const char *path = qemu_opt_get(opts, "path"); + const char *multidevs = qemu_opt_get(opts, "multidevs"); Error *local_err = NULL; if (!sec_model) { @@ -1502,13 +1507,32 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) return -1; } + if (multidevs) { + if (!strcmp(multidevs, "remap")) { + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; + fse->export_flags |= V9FS_REMAP_INODES; + } else if (!strcmp(multidevs, "forbid")) { + fse->export_flags &= ~V9FS_REMAP_INODES; + fse->export_flags |= V9FS_FORBID_MULTIDEVS; + } else if (!strcmp(multidevs, "warn")) { + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; + fse->export_flags &= ~V9FS_REMAP_INODES; + } else { + error_setg(&local_err, "invalid multidevs property '%s'", + multidevs); + error_append_hint(&local_err, "Valid options are: multidevs=" + "[remap|forbid|warn]\n"); + error_propagate(errp, local_err); + return -1; + } + } + if (!path) { error_setg(errp, "path property not set"); return -1; } - fsdev_throttle_parse_opts(opts, &fse->fst, &local_err); - if (local_err) { + if (fsdev_throttle_parse_opts(opts, &fse->fst, &local_err)) { error_propagate_prepend(errp, local_err, "invalid throttle configuration: "); return -1; diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c index 57a8c1c..97ab9c5 100644 --- a/hw/9pfs/9p-proxy.c +++ b/hw/9pfs/9p-proxy.c @@ -1185,6 +1185,10 @@ static void proxy_cleanup(FsContext *ctx) { V9fsProxy *proxy = ctx->private; + if (!proxy) { + return; + } + g_free(proxy->out_iovec.iov_base); g_free(proxy->in_iovec.iov_base); if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index cce2366..37abcdb 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -26,6 +26,8 @@ #include "trace.h" #include "migration/blocker.h" #include "sysemu/qtest.h" +#include "qemu/xxhash.h" +#include <math.h> int open_fd_hw; int total_open_fd; @@ -572,14 +574,374 @@ static void coroutine_fn virtfs_reset(V9fsPDU *pdu) P9_STAT_MODE_NAMED_PIPE | \ P9_STAT_MODE_SOCKET) -/* This is the algorithm from ufs in spfs */ -static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp) +/* Mirrors all bits of a byte. So e.g. binary 10100000 would become 00000101. */ +static inline uint8_t mirror8bit(uint8_t byte) { + return (byte * 0x0202020202ULL & 0x010884422010ULL) % 1023; +} + +/* Same as mirror8bit() just for a 64 bit data type instead for a byte. */ +static inline uint64_t mirror64bit(uint64_t value) +{ + return ((uint64_t)mirror8bit(value & 0xff) << 56) | + ((uint64_t)mirror8bit((value >> 8) & 0xff) << 48) | + ((uint64_t)mirror8bit((value >> 16) & 0xff) << 40) | + ((uint64_t)mirror8bit((value >> 24) & 0xff) << 32) | + ((uint64_t)mirror8bit((value >> 32) & 0xff) << 24) | + ((uint64_t)mirror8bit((value >> 40) & 0xff) << 16) | + ((uint64_t)mirror8bit((value >> 48) & 0xff) << 8) | + ((uint64_t)mirror8bit((value >> 56) & 0xff)); +} + +/** + * @brief Parameter k for the Exponential Golomb algorihm to be used. + * + * The smaller this value, the smaller the minimum bit count for the Exp. + * Golomb generated affixes will be (at lowest index) however for the + * price of having higher maximum bit count of generated affixes (at highest + * index). Likewise increasing this parameter yields in smaller maximum bit + * count for the price of having higher minimum bit count. + * + * In practice that means: a good value for k depends on the expected amount + * of devices to be exposed by one export. For a small amount of devices k + * should be small, for a large amount of devices k might be increased + * instead. The default of k=0 should be fine for most users though. + * + * @b IMPORTANT: In case this ever becomes a runtime parameter; the value of + * k should not change as long as guest is still running! Because that would + * cause completely different inode numbers to be generated on guest. + */ +#define EXP_GOLOMB_K 0 + +/** + * @brief Exponential Golomb algorithm for arbitrary k (including k=0). + * + * The Exponential Golomb algorithm generates @b prefixes (@b not suffixes!) + * with growing length and with the mathematical property of being + * "prefix-free". The latter means the generated prefixes can be prepended + * in front of arbitrary numbers and the resulting concatenated numbers are + * guaranteed to be always unique. + * + * This is a minor adjustment to the original Exp. Golomb algorithm in the + * sense that lowest allowed index (@param n) starts with 1, not with zero. + * + * @param n - natural number (or index) of the prefix to be generated + * (1, 2, 3, ...) + * @param k - parameter k of Exp. Golomb algorithm to be used + * (see comment on EXP_GOLOMB_K macro for details about k) + */ +static VariLenAffix expGolombEncode(uint64_t n, int k) +{ + const uint64_t value = n + (1 << k) - 1; + const int bits = (int) log2(value) + 1; + return (VariLenAffix) { + .type = AffixType_Prefix, + .value = value, + .bits = bits + MAX((bits - 1 - k), 0) + }; +} + +/** + * @brief Converts a suffix into a prefix, or a prefix into a suffix. + * + * Simply mirror all bits of the affix value, for the purpose to preserve + * respectively the mathematical "prefix-free" or "suffix-free" property + * after the conversion. + * + * If a passed prefix is suitable to create unique numbers, then the + * returned suffix is suitable to create unique numbers as well (and vice + * versa). + */ +static VariLenAffix invertAffix(const VariLenAffix *affix) +{ + return (VariLenAffix) { + .type = + (affix->type == AffixType_Suffix) ? + AffixType_Prefix : AffixType_Suffix, + .value = + mirror64bit(affix->value) >> + ((sizeof(affix->value) * 8) - affix->bits), + .bits = affix->bits + }; +} + +/** + * @brief Generates suffix numbers with "suffix-free" property. + * + * This is just a wrapper function on top of the Exp. Golomb algorithm. + * + * Since the Exp. Golomb algorithm generates prefixes, but we need suffixes, + * this function converts the Exp. Golomb prefixes into appropriate suffixes + * which are still suitable for generating unique numbers. + * + * @param n - natural number (or index) of the suffix to be generated + * (1, 2, 3, ...) + */ +static VariLenAffix affixForIndex(uint64_t index) +{ + VariLenAffix prefix; + prefix = expGolombEncode(index, EXP_GOLOMB_K); + return invertAffix(&prefix); /* convert prefix to suffix */ +} + +/* creative abuse of tb_hash_func7, which is based on xxhash */ +static uint32_t qpp_hash(QppEntry e) +{ + return qemu_xxhash7(e.ino_prefix, e.dev, 0, 0, 0); +} + +static uint32_t qpf_hash(QpfEntry e) +{ + return qemu_xxhash7(e.ino, e.dev, 0, 0, 0); +} + +static bool qpd_cmp_func(const void *obj, const void *userp) +{ + const QpdEntry *e1 = obj, *e2 = userp; + return e1->dev == e2->dev; +} + +static bool qpp_cmp_func(const void *obj, const void *userp) +{ + const QppEntry *e1 = obj, *e2 = userp; + return e1->dev == e2->dev && e1->ino_prefix == e2->ino_prefix; +} + +static bool qpf_cmp_func(const void *obj, const void *userp) +{ + const QpfEntry *e1 = obj, *e2 = userp; + return e1->dev == e2->dev && e1->ino == e2->ino; +} + +static void qp_table_remove(void *p, uint32_t h, void *up) +{ + g_free(p); +} + +static void qp_table_destroy(struct qht *ht) +{ + if (!ht || !ht->map) { + return; + } + qht_iter(ht, qp_table_remove, NULL); + qht_destroy(ht); +} + +static void qpd_table_init(struct qht *ht) +{ + qht_init(ht, qpd_cmp_func, 1, QHT_MODE_AUTO_RESIZE); +} + +static void qpp_table_init(struct qht *ht) +{ + qht_init(ht, qpp_cmp_func, 1, QHT_MODE_AUTO_RESIZE); +} + +static void qpf_table_init(struct qht *ht) +{ + qht_init(ht, qpf_cmp_func, 1 << 16, QHT_MODE_AUTO_RESIZE); +} + +/* + * Returns how many (high end) bits of inode numbers of the passed fs + * device shall be used (in combination with the device number) to + * generate hash values for qpp_table entries. + * + * This function is required if variable length suffixes are used for inode + * number mapping on guest level. Since a device may end up having multiple + * entries in qpp_table, each entry most probably with a different suffix + * length, we thus need this function in conjunction with qpd_table to + * "agree" about a fix amount of bits (per device) to be always used for + * generating hash values for the purpose of accessing qpp_table in order + * get consistent behaviour when accessing qpp_table. + */ +static int qid_inode_prefix_hash_bits(V9fsPDU *pdu, dev_t dev) +{ + QpdEntry lookup = { + .dev = dev + }, *val; + uint32_t hash = dev; + VariLenAffix affix; + + val = qht_lookup(&pdu->s->qpd_table, &lookup, hash); + if (!val) { + val = g_malloc0(sizeof(QpdEntry)); + *val = lookup; + affix = affixForIndex(pdu->s->qp_affix_next); + val->prefix_bits = affix.bits; + qht_insert(&pdu->s->qpd_table, val, hash, NULL); + pdu->s->qp_ndevices++; + } + return val->prefix_bits; +} + +/** + * @brief Slow / full mapping host inode nr -> guest inode nr. + * + * This function performs a slower and much more costly remapping of an + * original file inode number on host to an appropriate different inode + * number on guest. For every (dev, inode) combination on host a new + * sequential number is generated, cached and exposed as inode number on + * guest. + * + * This is just a "last resort" fallback solution if the much faster/cheaper + * qid_path_suffixmap() failed. In practice this slow / full mapping is not + * expected ever to be used at all though. + * + * @see qid_path_suffixmap() for details + * + */ +static int qid_path_fullmap(V9fsPDU *pdu, const struct stat *stbuf, + uint64_t *path) +{ + QpfEntry lookup = { + .dev = stbuf->st_dev, + .ino = stbuf->st_ino + }, *val; + uint32_t hash = qpf_hash(lookup); + VariLenAffix affix; + + val = qht_lookup(&pdu->s->qpf_table, &lookup, hash); + + if (!val) { + if (pdu->s->qp_fullpath_next == 0) { + /* no more files can be mapped :'( */ + error_report_once( + "9p: No more prefixes available for remapping inodes from " + "host to guest." + ); + return -ENFILE; + } + + val = g_malloc0(sizeof(QppEntry)); + *val = lookup; + + /* new unique inode and device combo */ + affix = affixForIndex( + 1ULL << (sizeof(pdu->s->qp_affix_next) * 8) + ); + val->path = (pdu->s->qp_fullpath_next++ << affix.bits) | affix.value; + pdu->s->qp_fullpath_next &= ((1ULL << (64 - affix.bits)) - 1); + qht_insert(&pdu->s->qpf_table, val, hash, NULL); + } + + *path = val->path; + return 0; +} + +/** + * @brief Quick mapping host inode nr -> guest inode nr. + * + * This function performs quick remapping of an original file inode number + * on host to an appropriate different inode number on guest. This remapping + * of inodes is required to avoid inode nr collisions on guest which would + * happen if the 9p export contains more than 1 exported file system (or + * more than 1 file system data set), because unlike on host level where the + * files would have different device nrs, all files exported by 9p would + * share the same device nr on guest (the device nr of the virtual 9p device + * that is). + * + * Inode remapping is performed by chopping off high end bits of the original + * inode number from host, shifting the result upwards and then assigning a + * generated suffix number for the low end bits, where the same suffix number + * will be shared by all inodes with the same device id AND the same high end + * bits that have been chopped off. That approach utilizes the fact that inode + * numbers very likely share the same high end bits (i.e. due to their common + * sequential generation by file systems) and hence we only have to generate + * and track a very limited amount of suffixes in practice due to that. + * + * We generate variable size suffixes for that purpose. The 1st generated + * suffix will only have 1 bit and hence we only need to chop off 1 bit from + * the original inode number. The subsequent suffixes being generated will + * grow in (bit) size subsequently, i.e. the 2nd and 3rd suffix being + * generated will have 3 bits and hence we have to chop off 3 bits from their + * original inodes, and so on. That approach of using variable length suffixes + * (i.e. over fixed size ones) utilizes the fact that in practice only a very + * limited amount of devices are shared by the same export (e.g. typically + * less than 2 dozen devices per 9p export), so in practice we need to chop + * off less bits than with fixed size prefixes and yet are flexible to add + * new devices at runtime below host's export directory at any time without + * having to reboot guest nor requiring to reconfigure guest for that. And due + * to the very limited amount of original high end bits that we chop off that + * way, the total amount of suffixes we need to generate is less than by using + * fixed size prefixes and hence it also improves performance of the inode + * remapping algorithm, and finally has the nice side effect that the inode + * numbers on guest will be much smaller & human friendly. ;-) + */ +static int qid_path_suffixmap(V9fsPDU *pdu, const struct stat *stbuf, + uint64_t *path) +{ + const int ino_hash_bits = qid_inode_prefix_hash_bits(pdu, stbuf->st_dev); + QppEntry lookup = { + .dev = stbuf->st_dev, + .ino_prefix = (uint16_t) (stbuf->st_ino >> (64 - ino_hash_bits)) + }, *val; + uint32_t hash = qpp_hash(lookup); + + val = qht_lookup(&pdu->s->qpp_table, &lookup, hash); + + if (!val) { + if (pdu->s->qp_affix_next == 0) { + /* we ran out of affixes */ + warn_report_once( + "9p: Potential degraded performance of inode remapping" + ); + return -ENFILE; + } + + val = g_malloc0(sizeof(QppEntry)); + *val = lookup; + + /* new unique inode affix and device combo */ + val->qp_affix_index = pdu->s->qp_affix_next++; + val->qp_affix = affixForIndex(val->qp_affix_index); + qht_insert(&pdu->s->qpp_table, val, hash, NULL); + } + /* assuming generated affix to be suffix type, not prefix */ + *path = (stbuf->st_ino << val->qp_affix.bits) | val->qp_affix.value; + return 0; +} + +static int stat_to_qid(V9fsPDU *pdu, const struct stat *stbuf, V9fsQID *qidp) +{ + int err; size_t size; - memset(&qidp->path, 0, sizeof(qidp->path)); - size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path)); - memcpy(&qidp->path, &stbuf->st_ino, size); + if (pdu->s->ctx.export_flags & V9FS_REMAP_INODES) { + /* map inode+device to qid path (fast path) */ + err = qid_path_suffixmap(pdu, stbuf, &qidp->path); + if (err == -ENFILE) { + /* fast path didn't work, fall back to full map */ + err = qid_path_fullmap(pdu, stbuf, &qidp->path); + } + if (err) { + return err; + } + } else { + if (pdu->s->dev_id != stbuf->st_dev) { + if (pdu->s->ctx.export_flags & V9FS_FORBID_MULTIDEVS) { + error_report_once( + "9p: Multiple devices detected in same VirtFS export. " + "Access of guest to additional devices is (partly) " + "denied due to virtfs option 'multidevs=forbid' being " + "effective." + ); + return -ENODEV; + } else { + warn_report_once( + "9p: Multiple devices detected in same VirtFS export, " + "which might lead to file ID collisions and severe " + "misbehaviours on guest! You should either use a " + "separate export for each device shared from host or " + "use virtfs option 'multidevs=remap'!" + ); + } + } + memset(&qidp->path, 0, sizeof(qidp->path)); + size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path)); + memcpy(&qidp->path, &stbuf->st_ino, size); + } + qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8); qidp->type = 0; if (S_ISDIR(stbuf->st_mode)) { @@ -588,6 +950,8 @@ static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp) if (S_ISLNK(stbuf->st_mode)) { qidp->type |= P9_QID_TYPE_SYMLINK; } + + return 0; } static int coroutine_fn fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, @@ -600,10 +964,37 @@ static int coroutine_fn fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, if (err < 0) { return err; } - stat_to_qid(&stbuf, qidp); + err = stat_to_qid(pdu, &stbuf, qidp); + if (err < 0) { + return err; + } return 0; } +static int coroutine_fn dirent_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, + struct dirent *dent, V9fsQID *qidp) +{ + struct stat stbuf; + V9fsPath path; + int err; + + v9fs_path_init(&path); + + err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path); + if (err < 0) { + goto out; + } + err = v9fs_co_lstat(pdu, &path, &stbuf); + if (err < 0) { + goto out; + } + err = stat_to_qid(pdu, &stbuf, qidp); + +out: + v9fs_path_free(&path); + return err; +} + V9fsPDU *pdu_alloc(V9fsState *s) { V9fsPDU *pdu = NULL; @@ -744,9 +1135,9 @@ static int donttouch_stat(V9fsStat *stat) { if (stat->type == -1 && stat->dev == -1 && - stat->qid.type == -1 && - stat->qid.version == -1 && - stat->qid.path == -1 && + stat->qid.type == 0xff && + stat->qid.version == (uint32_t) -1 && + stat->qid.path == (uint64_t) -1 && stat->mode == -1 && stat->atime == -1 && stat->mtime == -1 && @@ -831,7 +1222,10 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path, memset(v9stat, 0, sizeof(*v9stat)); - stat_to_qid(stbuf, &v9stat->qid); + err = stat_to_qid(pdu, stbuf, &v9stat->qid); + if (err < 0) { + return err; + } v9stat->mode = stat_to_v9mode(stbuf); v9stat->atime = stbuf->st_atime; v9stat->mtime = stbuf->st_mtime; @@ -892,7 +1286,7 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path, #define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ -static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf, +static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, V9fsStatDotl *v9lstat) { memset(v9lstat, 0, sizeof(*v9lstat)); @@ -914,7 +1308,7 @@ static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf, /* Currently we only support BASIC fields in stat */ v9lstat->st_result_mask = P9_STATS_BASIC; - stat_to_qid(stbuf, &v9lstat->qid); + return stat_to_qid(pdu, stbuf, &v9lstat->qid); } static void print_sg(struct iovec *sg, int cnt) @@ -1116,7 +1510,6 @@ static void coroutine_fn v9fs_getattr(void *opaque) uint64_t request_mask; V9fsStatDotl v9stat_dotl; V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; retval = pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask); if (retval < 0) { @@ -1137,7 +1530,10 @@ static void coroutine_fn v9fs_getattr(void *opaque) if (retval < 0) { goto out; } - stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl); + retval = stat_to_v9stat_dotl(pdu, &stbuf, &v9stat_dotl); + if (retval < 0) { + goto out; + } /* fill st_gen if requested and supported by underlying fs */ if (request_mask & P9_STATS_GEN) { @@ -1382,7 +1778,10 @@ static void coroutine_fn v9fs_walk(void *opaque) if (err < 0) { goto out; } - stat_to_qid(&stbuf, &qid); + err = stat_to_qid(pdu, &stbuf, &qid); + if (err < 0) { + goto out; + } v9fs_path_copy(&dpath, &path); } memcpy(&qids[name_idx], &qid, sizeof(qid)); @@ -1484,7 +1883,10 @@ static void coroutine_fn v9fs_open(void *opaque) if (err < 0) { goto out; } - stat_to_qid(&stbuf, &qid); + err = stat_to_qid(pdu, &stbuf, &qid); + if (err < 0) { + goto out; + } if (S_ISDIR(stbuf.st_mode)) { err = v9fs_co_opendir(pdu, fidp); if (err < 0) { @@ -1594,7 +1996,10 @@ static void coroutine_fn v9fs_lcreate(void *opaque) fidp->flags |= FID_NON_RECLAIMABLE; } iounit = get_iounit(pdu, &fidp->path); - stat_to_qid(&stbuf, &qid); + err = stat_to_qid(pdu, &stbuf, &qid); + if (err < 0) { + goto out; + } err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); if (err < 0) { goto out; @@ -1938,16 +2343,39 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp, v9fs_string_free(&name); return count; } - /* - * Fill up just the path field of qid because the client uses - * only that. To fill the entire qid structure we will have - * to stat each dirent found, which is expensive - */ - size = MIN(sizeof(dent->d_ino), sizeof(qid.path)); - memcpy(&qid.path, &dent->d_ino, size); - /* Fill the other fields with dummy values */ - qid.type = 0; - qid.version = 0; + + if (pdu->s->ctx.export_flags & V9FS_REMAP_INODES) { + /* + * dirent_to_qid() implies expensive stat call for each entry, + * we must do that here though since inode remapping requires + * the device id, which in turn might be different for + * different entries; we cannot make any assumption to avoid + * that here. + */ + err = dirent_to_qid(pdu, fidp, dent, &qid); + if (err < 0) { + v9fs_readdir_unlock(&fidp->fs.dir); + v9fs_co_seekdir(pdu, fidp, saved_dir_pos); + v9fs_string_free(&name); + return err; + } + } else { + /* + * Fill up just the path field of qid because the client uses + * only that. To fill the entire qid structure we will have + * to stat each dirent found, which is expensive. For the + * latter reason we don't call dirent_to_qid() here. Only drawback + * is that no multi-device export detection of stat_to_qid() + * would be done and provided as error to the user here. But + * user would get that error anyway when accessing those + * files/dirs through other ways. + */ + size = MIN(sizeof(dent->d_ino), sizeof(qid.path)); + memcpy(&qid.path, &dent->d_ino, size); + /* Fill the other fields with dummy values */ + qid.type = 0; + qid.version = 0; + } /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ len = pdu_marshal(pdu, 11 + count, "Qqbs", @@ -2328,7 +2756,10 @@ static void coroutine_fn v9fs_create(void *opaque) } } iounit = get_iounit(pdu, &fidp->path); - stat_to_qid(&stbuf, &qid); + err = stat_to_qid(pdu, &stbuf, &qid); + if (err < 0) { + goto out; + } err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); if (err < 0) { goto out; @@ -2385,7 +2816,10 @@ static void coroutine_fn v9fs_symlink(void *opaque) if (err < 0) { goto out; } - stat_to_qid(&stbuf, &qid); + err = stat_to_qid(pdu, &stbuf, &qid); + if (err < 0) { + goto out; + } err = pdu_marshal(pdu, offset, "Q", &qid); if (err < 0) { goto out; @@ -3065,7 +3499,10 @@ static void coroutine_fn v9fs_mknod(void *opaque) if (err < 0) { goto out; } - stat_to_qid(&stbuf, &qid); + err = stat_to_qid(pdu, &stbuf, &qid); + if (err < 0) { + goto out; + } err = pdu_marshal(pdu, offset, "Q", &qid); if (err < 0) { goto out; @@ -3223,7 +3660,10 @@ static void coroutine_fn v9fs_mkdir(void *opaque) if (err < 0) { goto out; } - stat_to_qid(&stbuf, &qid); + err = stat_to_qid(pdu, &stbuf, &qid); + if (err < 0) { + goto out; + } err = pdu_marshal(pdu, offset, "Q", &qid); if (err < 0) { goto out; @@ -3634,31 +4074,43 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t, goto out; } + s->dev_id = stat.st_dev; + + /* init inode remapping : */ + /* hash table for variable length inode suffixes */ + qpd_table_init(&s->qpd_table); + /* hash table for slow/full inode remapping (most users won't need it) */ + qpf_table_init(&s->qpf_table); + /* hash table for quick inode remapping */ + qpp_table_init(&s->qpp_table); + s->qp_ndevices = 0; + s->qp_affix_next = 1; /* reserve 0 to detect overflow */ + s->qp_fullpath_next = 1; + s->ctx.fst = &fse->fst; fsdev_throttle_init(s->ctx.fst); - v9fs_path_free(&path); - rc = 0; out: if (rc) { - if (s->ops && s->ops->cleanup && s->ctx.private) { - s->ops->cleanup(&s->ctx); - } - g_free(s->tag); - g_free(s->ctx.fs_root); - v9fs_path_free(&path); + v9fs_device_unrealize_common(s, NULL); } + v9fs_path_free(&path); return rc; } void v9fs_device_unrealize_common(V9fsState *s, Error **errp) { - if (s->ops->cleanup) { + if (s->ops && s->ops->cleanup) { s->ops->cleanup(&s->ctx); } - fsdev_throttle_cleanup(s->ctx.fst); + if (s->ctx.fst) { + fsdev_throttle_cleanup(s->ctx.fst); + } g_free(s->tag); + qp_table_destroy(&s->qpd_table); + qp_table_destroy(&s->qpp_table); + qp_table_destroy(&s->qpf_table); g_free(s->ctx.fs_root); } diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 8883761..3904f82 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -8,6 +8,7 @@ #include "fsdev/9p-iov-marshal.h" #include "qemu/thread.h" #include "qemu/coroutine.h" +#include "qemu/qht.h" enum { P9_TLERROR = 6, @@ -235,6 +236,58 @@ struct V9fsFidState V9fsFidState *rclm_lst; }; +typedef enum AffixType_t { + AffixType_Prefix, + AffixType_Suffix, /* A.k.a. postfix. */ +} AffixType_t; + +/** + * @brief Unique affix of variable length. + * + * An affix is (currently) either a suffix or a prefix, which is either + * going to be prepended (prefix) or appended (suffix) with some other + * number for the goal to generate unique numbers. Accordingly the + * suffixes (or prefixes) we generate @b must all have the mathematical + * property of being suffix-free (or prefix-free in case of prefixes) + * so that no matter what number we concatenate the affix with, that we + * always reliably get unique numbers as result after concatenation. + */ +typedef struct VariLenAffix { + AffixType_t type; /* Whether this affix is a suffix or a prefix. */ + uint64_t value; /* Actual numerical value of this affix. */ + /* + * Lenght of the affix, that is how many (of the lowest) bits of @c value + * must be used for appending/prepending this affix to its final resulting, + * unique number. + */ + int bits; +} VariLenAffix; + +/* See qid_inode_prefix_hash_bits(). */ +typedef struct { + dev_t dev; /* FS device on host. */ + /* + * How many (high) bits of the original inode number shall be used for + * hashing. + */ + int prefix_bits; +} QpdEntry; + +/* QID path prefix entry, see stat_to_qid */ +typedef struct { + dev_t dev; + uint16_t ino_prefix; + uint32_t qp_affix_index; + VariLenAffix qp_affix; +} QppEntry; + +/* QID path full entry, as above */ +typedef struct { + dev_t dev; + ino_t ino; + uint64_t path; +} QpfEntry; + struct V9fsState { QLIST_HEAD(, V9fsPDU) free_list; @@ -256,6 +309,13 @@ struct V9fsState Error *migration_blocker; V9fsConf fsconf; V9fsQID root_qid; + dev_t dev_id; + struct qht qpd_table; + struct qht qpp_table; + struct qht qpf_table; + uint64_t qp_ndevices; /* Amount of entries in qpd_table. */ + uint16_t qp_affix_next; + uint64_t qp_fullpath_next; }; /* 9p2000.L open flags */ diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events index c0a0a4a..10188da 100644 --- a/hw/9pfs/trace-events +++ b/hw/9pfs/trace-events @@ -6,7 +6,7 @@ v9fs_rerror(uint16_t tag, uint8_t id, int err) "tag %d id %d err %d" v9fs_version(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s" v9fs_version_return(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s" v9fs_attach(uint16_t tag, uint8_t id, int32_t fid, int32_t afid, char* uname, char* aname) "tag %u id %u fid %d afid %d uname %s aname %s" -v9fs_attach_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d type %d version %d path %"PRId64 +v9fs_attach_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path) "tag %u id %u type %u version %u path %"PRIu64 v9fs_stat(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_stat_return(uint16_t tag, uint8_t id, int32_t mode, int32_t atime, int32_t mtime, int64_t length) "tag %d id %d stat={mode %d atime %d mtime %d length %"PRId64"}" v9fs_getattr(uint16_t tag, uint8_t id, int32_t fid, uint64_t request_mask) "tag %d id %d fid %d request_mask %"PRIu64 @@ -14,9 +14,9 @@ v9fs_getattr_return(uint16_t tag, uint8_t id, uint64_t result_mask, uint32_t mod v9fs_walk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, uint16_t nwnames) "tag %d id %d fid %d newfid %d nwnames %d" v9fs_walk_return(uint16_t tag, uint8_t id, uint16_t nwnames, void* qids) "tag %d id %d nwnames %d qids %p" v9fs_open(uint16_t tag, uint8_t id, int32_t fid, int32_t mode) "tag %d id %d fid %d mode %d" -v9fs_open_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d" +v9fs_open_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d" v9fs_lcreate(uint16_t tag, uint8_t id, int32_t dfid, int32_t flags, int32_t mode, uint32_t gid) "tag %d id %d dfid %d flags %d mode %d gid %u" -v9fs_lcreate_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int32_t iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d" +v9fs_lcreate_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int32_t iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d" v9fs_fsync(uint16_t tag, uint8_t id, int32_t fid, int datasync) "tag %d id %d fid %d datasync %d" v9fs_clunk(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_read(uint16_t tag, uint8_t id, int32_t fid, uint64_t off, uint32_t max_count) "tag %d id %d fid %d off %"PRIu64" max_count %u" @@ -26,21 +26,21 @@ v9fs_readdir_return(uint16_t tag, uint8_t id, uint32_t count, ssize_t retval) "t v9fs_write(uint16_t tag, uint8_t id, int32_t fid, uint64_t off, uint32_t count, int cnt) "tag %d id %d fid %d off %"PRIu64" count %u cnt %d" v9fs_write_return(uint16_t tag, uint8_t id, int32_t total, ssize_t err) "tag %d id %d total %d err %zd" v9fs_create(uint16_t tag, uint8_t id, int32_t fid, char* name, int32_t perm, int8_t mode) "tag %d id %d fid %d name %s perm %d mode %d" -v9fs_create_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d" +v9fs_create_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d" v9fs_symlink(uint16_t tag, uint8_t id, int32_t fid, char* name, char* symname, uint32_t gid) "tag %d id %d fid %d name %s symname %s gid %u" -v9fs_symlink_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d qid={type %d version %d path %"PRId64"}" +v9fs_symlink_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path) "tag %u id %u qid={type %u version %u path %"PRIu64"}" v9fs_flush(uint16_t tag, uint8_t id, int16_t flush_tag) "tag %d id %d flush_tag %d" v9fs_link(uint16_t tag, uint8_t id, int32_t dfid, int32_t oldfid, char* name) "tag %d id %d dfid %d oldfid %d name %s" v9fs_remove(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_wstat(uint16_t tag, uint8_t id, int32_t fid, int32_t mode, int32_t atime, int32_t mtime) "tag %u id %u fid %d stat={mode %d atime %d mtime %d}" v9fs_mknod(uint16_t tag, uint8_t id, int32_t fid, int mode, int major, int minor) "tag %d id %d fid %d mode %d major %d minor %d" -v9fs_mknod_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d qid={type %d version %d path %"PRId64"}" +v9fs_mknod_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path) "tag %u id %u qid={type %u version %u path %"PRIu64"}" v9fs_lock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length) "tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64 v9fs_lock_return(uint16_t tag, uint8_t id, int8_t status) "tag %d id %d status %d" v9fs_getlock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length)"tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64 v9fs_getlock_return(uint16_t tag, uint8_t id, uint8_t type, uint64_t start, uint64_t length, uint32_t proc_id) "tag %d id %d type %d start %"PRIu64" length %"PRIu64" proc_id %u" v9fs_mkdir(uint16_t tag, uint8_t id, int32_t fid, char* name, int mode, uint32_t gid) "tag %u id %u fid %d name %s mode %d gid %u" -v9fs_mkdir_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int err) "tag %u id %u qid={type %d version %d path %"PRId64"} err %d" +v9fs_mkdir_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int err) "tag %u id %u qid={type %u version %u path %"PRIu64"} err %d" v9fs_xattrwalk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, char* name) "tag %d id %d fid %d newfid %d name %s" v9fs_xattrwalk_return(uint16_t tag, uint8_t id, int64_t size) "tag %d id %d size %"PRId64 v9fs_xattrcreate(uint16_t tag, uint8_t id, int32_t fid, char* name, uint64_t size, int flags) "tag %d id %d fid %d name %s size %"PRIu64" flags %d" diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 7c59cf9..12e3f1e 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -31,3 +31,7 @@ config ACPI_VMGENID bool default y depends on PC + +config ACPI_HW_REDUCED + bool + depends on ACPI diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 9bb2101..655a9c1 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o common-obj-y += acpi_interface.o diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c new file mode 100644 index 0000000..9cee90c --- /dev/null +++ b/hw/acpi/generic_event_device.c @@ -0,0 +1,311 @@ +/* + * + * Copyright (c) 2018 Intel Corporation + * Copyright (c) 2019 Huawei Technologies R & D (UK) Ltd + * Written by Samuel Ortiz, Shameer Kolothum + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "exec/address-spaces.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/generic_event_device.h" +#include "hw/irq.h" +#include "hw/mem/pc-dimm.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qemu/error-report.h" + +static const uint32_t ged_supported_events[] = { + ACPI_GED_MEM_HOTPLUG_EVT, + ACPI_GED_PWR_DOWN_EVT, +}; + +/* + * The ACPI Generic Event Device (GED) is a hardware-reduced specific + * device[ACPI v6.1 Section 5.6.9] that handles all platform events, + * including the hotplug ones. Platforms need to specify their own + * GED Event bitmap to describe what kind of events they want to support + * through GED. This routine uses a single interrupt for the GED device, + * relying on IO memory region to communicate the type of device + * affected by the interrupt. This way, we can support up to 32 events + * with a unique interrupt. + */ +void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, + uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base) +{ + AcpiGedState *s = ACPI_GED(hotplug_dev); + Aml *crs = aml_resource_template(); + Aml *evt, *field; + Aml *dev = aml_device("%s", name); + Aml *evt_sel = aml_local(0); + Aml *esel = aml_name(AML_GED_EVT_SEL); + + /* _CRS interrupt */ + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH, + AML_EXCLUSIVE, &ged_irq, 1)); + + aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013"))); + aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE))); + aml_append(dev, aml_name_decl("_CRS", crs)); + + /* Append IO region */ + aml_append(dev, aml_operation_region(AML_GED_EVT_REG, rs, + aml_int(ged_base + ACPI_GED_EVT_SEL_OFFSET), + ACPI_GED_EVT_SEL_LEN)); + field = aml_field(AML_GED_EVT_REG, AML_DWORD_ACC, AML_NOLOCK, + AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field(AML_GED_EVT_SEL, + ACPI_GED_EVT_SEL_LEN * BITS_PER_BYTE)); + aml_append(dev, field); + + /* + * For each GED event we: + * - Add a conditional block for each event, inside a loop. + * - Call a method for each supported GED event type. + * + * The resulting ASL code looks like: + * + * Local0 = ESEL + * If ((Local0 & One) == One) + * { + * MethodEvent0() + * } + * + * If ((Local0 & 0x2) == 0x2) + * { + * MethodEvent1() + * } + * ... + */ + evt = aml_method("_EVT", 1, AML_SERIALIZED); + { + Aml *if_ctx; + uint32_t i; + uint32_t ged_events = ctpop32(s->ged_event_bitmap); + + /* Local0 = ESEL */ + aml_append(evt, aml_store(esel, evt_sel)); + + for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) { + uint32_t event = s->ged_event_bitmap & ged_supported_events[i]; + + if (!event) { + continue; + } + + if_ctx = aml_if(aml_equal(aml_and(evt_sel, aml_int(event), NULL), + aml_int(event))); + switch (event) { + case ACPI_GED_MEM_HOTPLUG_EVT: + aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "." + MEMORY_SLOT_SCAN_METHOD)); + break; + case ACPI_GED_PWR_DOWN_EVT: + aml_append(if_ctx, + aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE), + aml_int(0x80))); + break; + default: + /* + * Please make sure all the events in ged_supported_events[] + * are handled above. + */ + g_assert_not_reached(); + } + + aml_append(evt, if_ctx); + ged_events--; + } + + if (ged_events) { + error_report("Unsupported events specified"); + abort(); + } + } + + /* Append _EVT method */ + aml_append(dev, evt); + + aml_append(table, dev); +} + +/* Memory read by the GED _EVT AML dynamic method */ +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val = 0; + GEDState *ged_st = opaque; + + switch (addr) { + case ACPI_GED_EVT_SEL_OFFSET: + /* Read the selector value and reset it */ + val = ged_st->sel; + ged_st->sel = 0; + break; + default: + break; + } + + return val; +} + +/* Nothing is expected to be written to the GED memory region */ +static void ged_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ +} + +static const MemoryRegionOps ged_ops = { + .read = ged_read, + .write = ged_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + AcpiGedState *s = ACPI_GED(hotplug_dev); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp); + } else { + error_setg(errp, "virt: device plug request for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); + } +} + +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) +{ + AcpiGedState *s = ACPI_GED(adev); + GEDState *ged_st = &s->ged_state; + uint32_t sel; + + if (ev & ACPI_MEMORY_HOTPLUG_STATUS) { + sel = ACPI_GED_MEM_HOTPLUG_EVT; + } else if (ev & ACPI_POWER_DOWN_STATUS) { + sel = ACPI_GED_PWR_DOWN_EVT; + } else { + /* Unknown event. Return without generating interrupt. */ + warn_report("GED: Unsupported event %d. No irq injected", ev); + return; + } + + /* + * Set the GED selector field to communicate the event type. + * This will be read by GED aml code to select the appropriate + * event method. + */ + ged_st->sel |= sel; + + /* Trigger the event by sending an interrupt to the guest. */ + qemu_irq_pulse(s->irq); +} + +static Property acpi_ged_properties[] = { + DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_memhp_state = { + .name = "acpi-ged/memhp", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_MEMORY_HOTPLUG(memhp_state, AcpiGedState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ged_state = { + .name = "acpi-ged-state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(sel, GEDState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_acpi_ged = { + .name = "acpi-ged", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(ged_state, AcpiGedState, 1, vmstate_ged_state, GEDState), + VMSTATE_END_OF_LIST(), + }, + .subsections = (const VMStateDescription * []) { + &vmstate_memhp_state, + NULL + } +}; + +static void acpi_ged_initfn(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + AcpiGedState *s = ACPI_GED(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + GEDState *ged_st = &s->ged_state; + + memory_region_init_io(&ged_st->io, obj, &ged_ops, ged_st, + TYPE_ACPI_GED, ACPI_GED_EVT_SEL_LEN); + sysbus_init_mmio(sbd, &ged_st->io); + + sysbus_init_irq(sbd, &s->irq); + + s->memhp_state.is_enabled = true; + /* + * GED handles memory hotplug event and acpi-mem-hotplug + * memory region gets initialized here. Create an exclusive + * container for memory hotplug IO and expose it as GED sysbus + * MMIO so that boards can map it separately. + */ + memory_region_init(&s->container_memhp, OBJECT(dev), "memhp container", + MEMORY_HOTPLUG_IO_LEN); + sysbus_init_mmio(sbd, &s->container_memhp); + acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev), + &s->memhp_state, 0); +} + +static void acpi_ged_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class); + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class); + + dc->desc = "ACPI Generic Event Device"; + dc->props = acpi_ged_properties; + dc->vmsd = &vmstate_acpi_ged; + + hc->plug = acpi_ged_device_plug_cb; + + adevc->send_event = acpi_ged_send_event; +} + +static const TypeInfo acpi_ged_info = { + .name = TYPE_ACPI_GED, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AcpiGedState), + .instance_init = acpi_ged_initfn, + .class_init = acpi_ged_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { TYPE_ACPI_DEVICE_IF }, + { } + } +}; + +static void acpi_ged_register_types(void) +{ + type_register_static(&acpi_ged_info); +} + +type_init(acpi_ged_register_types) diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 9483d66..8d2e822 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -30,12 +30,7 @@ #define MEMORY_SLOT_PROXIMITY_METHOD "MPXM" #define MEMORY_SLOT_EJECT_METHOD "MEJ0" #define MEMORY_SLOT_NOTIFY_METHOD "MTFY" -#define MEMORY_SLOT_SCAN_METHOD "MSCN" #define MEMORY_HOTPLUG_DEVICE "MHPD" -#define MEMORY_HOTPLUG_IO_LEN 24 -#define MEMORY_DEVICES_CONTAINER "\\_SB.MHPC" - -static uint16_t memhp_io_base; static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev) { @@ -210,7 +205,7 @@ static const MemoryRegionOps acpi_memory_hotplug_ops = { }; void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, - MemHotplugState *state, uint16_t io_base) + MemHotplugState *state, hwaddr io_base) { MachineState *machine = MACHINE(qdev_get_machine()); @@ -219,12 +214,10 @@ void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, return; } - assert(!memhp_io_base); - memhp_io_base = io_base; state->devs = g_malloc0(sizeof(*state->devs) * state->dev_count); memory_region_init_io(&state->io, owner, &acpi_memory_hotplug_ops, state, "acpi-mem-hotplug", MEMORY_HOTPLUG_IO_LEN); - memory_region_add_subregion(as, memhp_io_base, &state->io); + memory_region_add_subregion(as, io_base, &state->io); } /** @@ -343,7 +336,8 @@ const VMStateDescription vmstate_memory_hotplug = { void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem, const char *res_root, - const char *event_handler_method) + const char *event_handler_method, + AmlRegionSpace rs, hwaddr memhp_io_base) { int i; Aml *ifctx; @@ -352,10 +346,6 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem, Aml *mem_ctrl_dev; char *mhp_res_path; - if (!memhp_io_base) { - return; - } - mhp_res_path = g_strdup_printf("%s." MEMORY_HOTPLUG_DEVICE, res_root); mem_ctrl_dev = aml_device("%s", mhp_res_path); { @@ -366,14 +356,19 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem, aml_name_decl("_UID", aml_string("Memory hotplug resources"))); crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0, - MEMORY_HOTPLUG_IO_LEN) - ); + if (rs == AML_SYSTEM_IO) { + aml_append(crs, + aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0, + MEMORY_HOTPLUG_IO_LEN) + ); + } else { + aml_append(crs, aml_memory32_fixed(memhp_io_base, + MEMORY_HOTPLUG_IO_LEN, AML_READ_WRITE)); + } aml_append(mem_ctrl_dev, aml_name_decl("_CRS", crs)); aml_append(mem_ctrl_dev, aml_operation_region( - MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO, + MEMORY_HOTPLUG_IO_REGION, rs, aml_int(memhp_io_base), MEMORY_HOTPLUG_IO_LEN) ); @@ -717,10 +712,12 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem, } aml_append(table, dev_container); - method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED); - aml_append(method, - aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD)); - aml_append(table, method); + if (event_handler_method) { + method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED); + aml_append(method, aml_call0(MEMORY_DEVICES_CONTAINER "." + MEMORY_SLOT_SCAN_METHOD)); + aml_append(table, method); + } g_free(mhp_res_path); } diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 76a2a6b..c6e7782 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -20,6 +20,10 @@ config ARM_VIRT select SMBIOS select VIRTIO_MMIO select ACPI_PCI + select MEM_DEVICE + select DIMM + select ACPI_MEMORY_HOTPLUG + select ACPI_HW_REDUCED config CHEETAH bool diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 43ce8d5..fe749f6 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -41,7 +41,7 @@ obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o -obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o +obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o aspeed_ast2600.o obj-$(CONFIG_MPS2) += mps2.o obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index aa72be3..52993f8 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -170,6 +170,7 @@ static void aspeed_board_init(MachineState *machine, AspeedSoCClass *sc; DriveInfo *drive0 = drive_get(IF_MTD, 0, 0); ram_addr_t max_ram_size; + int i; bmc = g_new0(AspeedBoardState, 1); @@ -214,7 +215,7 @@ static void aspeed_board_init(MachineState *machine, memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size); memory_region_add_subregion(&bmc->ram_container, 0, &bmc->ram); memory_region_add_subregion(get_system_memory(), - sc->info->memmap[ASPEED_SDRAM], + sc->memmap[ASPEED_SDRAM], &bmc->ram_container); max_ram_size = object_property_get_uint(OBJECT(&bmc->soc), "max-ram-size", @@ -245,13 +246,26 @@ static void aspeed_board_init(MachineState *machine, } aspeed_board_binfo.ram_size = ram_size; - aspeed_board_binfo.loader_start = sc->info->memmap[ASPEED_SDRAM]; + aspeed_board_binfo.loader_start = sc->memmap[ASPEED_SDRAM]; aspeed_board_binfo.nb_cpus = bmc->soc.num_cpus; if (cfg->i2c_init) { cfg->i2c_init(bmc); } + for (i = 0; i < ARRAY_SIZE(bmc->soc.sdhci.slots); i++) { + SDHCIState *sdhci = &bmc->soc.sdhci.slots[i]; + DriveInfo *dinfo = drive_get_next(IF_SD); + BlockBackend *blk; + DeviceState *card; + + blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; + card = qdev_create(qdev_get_child_bus(DEVICE(sdhci), "sd-bus"), + TYPE_SD_CARD); + qdev_prop_set_drive(card, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(card), true, "realized", &error_fatal); + } + arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo); } @@ -373,7 +387,6 @@ static void aspeed_machine_class_init(ObjectClass *oc, void *data) mc->desc = board->desc; mc->init = aspeed_machine_init; mc->max_cpus = ASPEED_CPUS_NUM; - mc->no_sdcard = 1; mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c new file mode 100644 index 0000000..931887a --- /dev/null +++ b/hw/arm/aspeed_ast2600.c @@ -0,0 +1,523 @@ +/* + * ASPEED SoC 2600 family + * + * Copyright (c) 2016-2019, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "exec/address-spaces.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/char/serial.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "hw/i2c/aspeed_i2c.h" +#include "net/net.h" +#include "sysemu/sysemu.h" + +#define ASPEED_SOC_IOMEM_SIZE 0x00200000 + +static const hwaddr aspeed_soc_ast2600_memmap[] = { + [ASPEED_SRAM] = 0x10000000, + /* 0x16000000 0x17FFFFFF : AHB BUS do LPC Bus bridge */ + [ASPEED_IOMEM] = 0x1E600000, + [ASPEED_PWM] = 0x1E610000, + [ASPEED_FMC] = 0x1E620000, + [ASPEED_SPI1] = 0x1E630000, + [ASPEED_SPI2] = 0x1E641000, + [ASPEED_MII1] = 0x1E650000, + [ASPEED_MII2] = 0x1E650008, + [ASPEED_MII3] = 0x1E650010, + [ASPEED_MII4] = 0x1E650018, + [ASPEED_ETH1] = 0x1E660000, + [ASPEED_ETH3] = 0x1E670000, + [ASPEED_ETH2] = 0x1E680000, + [ASPEED_ETH4] = 0x1E690000, + [ASPEED_VIC] = 0x1E6C0000, + [ASPEED_SDMC] = 0x1E6E0000, + [ASPEED_SCU] = 0x1E6E2000, + [ASPEED_XDMA] = 0x1E6E7000, + [ASPEED_ADC] = 0x1E6E9000, + [ASPEED_VIDEO] = 0x1E700000, + [ASPEED_SDHCI] = 0x1E740000, + [ASPEED_GPIO] = 0x1E780000, + [ASPEED_GPIO_1_8V] = 0x1E780800, + [ASPEED_RTC] = 0x1E781000, + [ASPEED_TIMER1] = 0x1E782000, + [ASPEED_WDT] = 0x1E785000, + [ASPEED_LPC] = 0x1E789000, + [ASPEED_IBT] = 0x1E789140, + [ASPEED_I2C] = 0x1E78A000, + [ASPEED_UART1] = 0x1E783000, + [ASPEED_UART5] = 0x1E784000, + [ASPEED_VUART] = 0x1E787000, + [ASPEED_SDRAM] = 0x80000000, +}; + +#define ASPEED_A7MPCORE_ADDR 0x40460000 + +#define ASPEED_SOC_AST2600_MAX_IRQ 128 + +static const int aspeed_soc_ast2600_irqmap[] = { + [ASPEED_UART1] = 47, + [ASPEED_UART2] = 48, + [ASPEED_UART3] = 49, + [ASPEED_UART4] = 50, + [ASPEED_UART5] = 8, + [ASPEED_VUART] = 8, + [ASPEED_FMC] = 39, + [ASPEED_SDMC] = 0, + [ASPEED_SCU] = 12, + [ASPEED_ADC] = 78, + [ASPEED_XDMA] = 6, + [ASPEED_SDHCI] = 43, + [ASPEED_GPIO] = 40, + [ASPEED_GPIO_1_8V] = 11, + [ASPEED_RTC] = 13, + [ASPEED_TIMER1] = 16, + [ASPEED_TIMER2] = 17, + [ASPEED_TIMER3] = 18, + [ASPEED_TIMER4] = 19, + [ASPEED_TIMER5] = 20, + [ASPEED_TIMER6] = 21, + [ASPEED_TIMER7] = 22, + [ASPEED_TIMER8] = 23, + [ASPEED_WDT] = 24, + [ASPEED_PWM] = 44, + [ASPEED_LPC] = 35, + [ASPEED_IBT] = 35, /* LPC */ + [ASPEED_I2C] = 110, /* 110 -> 125 */ + [ASPEED_ETH1] = 2, + [ASPEED_ETH2] = 3, + [ASPEED_ETH3] = 32, + [ASPEED_ETH4] = 33, + +}; + +static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + + return qdev_get_gpio_in(DEVICE(&s->a7mpcore), sc->irqmap[ctrl]); +} + +static void aspeed_soc_ast2600_init(Object *obj) +{ + AspeedSoCState *s = ASPEED_SOC(obj); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + char socname[8]; + char typename[64]; + + if (sscanf(sc->name, "%7s", socname) != 1) { + g_assert_not_reached(); + } + + for (i = 0; i < sc->num_cpus; i++) { + object_initialize_child(obj, "cpu[*]", OBJECT(&s->cpu[i]), + sizeof(s->cpu[i]), sc->cpu_type, + &error_abort, NULL); + } + + snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); + sysbus_init_child_obj(obj, "scu", OBJECT(&s->scu), sizeof(s->scu), + typename); + qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", + sc->silicon_rev); + object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), + "hw-strap1", &error_abort); + object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), + "hw-strap2", &error_abort); + object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), + "hw-prot-key", &error_abort); + + sysbus_init_child_obj(obj, "a7mpcore", &s->a7mpcore, + sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV); + + sysbus_init_child_obj(obj, "rtc", OBJECT(&s->rtc), sizeof(s->rtc), + TYPE_ASPEED_RTC); + + snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); + sysbus_init_child_obj(obj, "timerctrl", OBJECT(&s->timerctrl), + sizeof(s->timerctrl), typename); + object_property_add_const_link(OBJECT(&s->timerctrl), "scu", + OBJECT(&s->scu), &error_abort); + + snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); + sysbus_init_child_obj(obj, "i2c", OBJECT(&s->i2c), sizeof(s->i2c), + typename); + + snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); + sysbus_init_child_obj(obj, "fmc", OBJECT(&s->fmc), sizeof(s->fmc), + typename); + object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs", + &error_abort); + object_property_add_alias(obj, "dram", OBJECT(&s->fmc), "dram", + &error_abort); + + for (i = 0; i < sc->spis_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname); + sysbus_init_child_obj(obj, "spi[*]", OBJECT(&s->spi[i]), + sizeof(s->spi[i]), typename); + } + + snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); + sysbus_init_child_obj(obj, "sdmc", OBJECT(&s->sdmc), sizeof(s->sdmc), + typename); + object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), + "ram-size", &error_abort); + object_property_add_alias(obj, "max-ram-size", OBJECT(&s->sdmc), + "max-ram-size", &error_abort); + + for (i = 0; i < sc->wdts_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); + sysbus_init_child_obj(obj, "wdt[*]", OBJECT(&s->wdt[i]), + sizeof(s->wdt[i]), typename); + object_property_add_const_link(OBJECT(&s->wdt[i]), "scu", + OBJECT(&s->scu), &error_abort); + } + + for (i = 0; i < sc->macs_num; i++) { + sysbus_init_child_obj(obj, "ftgmac100[*]", OBJECT(&s->ftgmac100[i]), + sizeof(s->ftgmac100[i]), TYPE_FTGMAC100); + + sysbus_init_child_obj(obj, "mii[*]", &s->mii[i], sizeof(s->mii[i]), + TYPE_ASPEED_MII); + object_property_add_const_link(OBJECT(&s->mii[i]), "nic", + OBJECT(&s->ftgmac100[i]), + &error_abort); + } + + sysbus_init_child_obj(obj, "xdma", OBJECT(&s->xdma), sizeof(s->xdma), + TYPE_ASPEED_XDMA); + + snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname); + sysbus_init_child_obj(obj, "gpio", OBJECT(&s->gpio), sizeof(s->gpio), + typename); + + snprintf(typename, sizeof(typename), "aspeed.gpio-%s-1_8v", socname); + sysbus_init_child_obj(obj, "gpio_1_8v", OBJECT(&s->gpio_1_8v), + sizeof(s->gpio_1_8v), typename); + + sysbus_init_child_obj(obj, "sdc", OBJECT(&s->sdhci), sizeof(s->sdhci), + TYPE_ASPEED_SDHCI); + + /* Init sd card slot class here so that they're under the correct parent */ + for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) { + sysbus_init_child_obj(obj, "sdhci[*]", OBJECT(&s->sdhci.slots[i]), + sizeof(s->sdhci.slots[i]), TYPE_SYSBUS_SDHCI); + } +} + +/* + * ASPEED ast2600 has 0xf as cluster ID + * + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0388e/CIHEBGFG.html + */ +static uint64_t aspeed_calc_affinity(int cpu) +{ + return (0xf << ARM_AFF1_SHIFT) | cpu; +} + +static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) +{ + int i; + AspeedSoCState *s = ASPEED_SOC(dev); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + Error *err = NULL, *local_err = NULL; + qemu_irq irq; + + /* IO space */ + create_unimplemented_device("aspeed_soc.io", sc->memmap[ASPEED_IOMEM], + ASPEED_SOC_IOMEM_SIZE); + + /* Video engine stub */ + create_unimplemented_device("aspeed.video", sc->memmap[ASPEED_VIDEO], + 0x1000); + + if (s->num_cpus > sc->num_cpus) { + warn_report("%s: invalid number of CPUs %d, using default %d", + sc->name, s->num_cpus, sc->num_cpus); + s->num_cpus = sc->num_cpus; + } + + /* CPU */ + for (i = 0; i < s->num_cpus; i++) { + object_property_set_int(OBJECT(&s->cpu[i]), QEMU_PSCI_CONDUIT_SMC, + "psci-conduit", &error_abort); + if (s->num_cpus > 1) { + object_property_set_int(OBJECT(&s->cpu[i]), + ASPEED_A7MPCORE_ADDR, + "reset-cbar", &error_abort); + } + object_property_set_int(OBJECT(&s->cpu[i]), aspeed_calc_affinity(i), + "mp-affinity", &error_abort); + + /* + * TODO: the secondary CPUs are started and a boot helper + * is needed when using -kernel + */ + + object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + /* A7MPCORE */ + object_property_set_int(OBJECT(&s->a7mpcore), s->num_cpus, "num-cpu", + &error_abort); + object_property_set_int(OBJECT(&s->a7mpcore), + ASPEED_SOC_AST2600_MAX_IRQ + GIC_INTERNAL, + "num-irq", &error_abort); + + object_property_set_bool(OBJECT(&s->a7mpcore), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, ASPEED_A7MPCORE_ADDR); + + for (i = 0; i < s->num_cpus; i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore); + DeviceState *d = DEVICE(qemu_get_cpu(i)); + + irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); + sysbus_connect_irq(sbd, i, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_FIQ); + sysbus_connect_irq(sbd, i + s->num_cpus, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_VIRQ); + sysbus_connect_irq(sbd, i + 2 * s->num_cpus, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_VFIQ); + sysbus_connect_irq(sbd, i + 3 * s->num_cpus, irq); + } + + /* SRAM */ + memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram", + sc->sram_size, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), + sc->memmap[ASPEED_SRAM], &s->sram); + + /* SCU */ + object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_SCU]); + + /* RTC */ + object_property_set_bool(OBJECT(&s->rtc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_RTC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, + aspeed_soc_get_irq(s, ASPEED_RTC)); + + /* Timer */ + object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, + sc->memmap[ASPEED_TIMER1]); + for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { + qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_TIMER1 + i); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); + } + + /* UART - attach an 8250 to the IO space as our UART5 */ + if (serial_hd(0)) { + qemu_irq uart5 = aspeed_soc_get_irq(s, ASPEED_UART5); + serial_mm_init(get_system_memory(), sc->memmap[ASPEED_UART5], 2, + uart5, 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN); + } + + /* I2C */ + object_property_set_bool(OBJECT(&s->i2c), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_I2C]); + for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { + qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), + sc->irqmap[ASPEED_I2C] + i); + /* + * The AST2600 SoC has one IRQ per I2C bus. Skip the common + * IRQ (AST2400 and AST2500) and connect all bussses. + */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), i + 1, irq); + } + + /* FMC, The number of CS is set at the board level */ + object_property_set_int(OBJECT(&s->fmc), sc->memmap[ASPEED_SDRAM], + "sdram-base", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->fmc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_FMC]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, + s->fmc.ctrl->flash_window_base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, + aspeed_soc_get_irq(s, ASPEED_FMC)); + + /* SPI */ + for (i = 0; i < sc->spis_num; i++) { + object_property_set_int(OBJECT(&s->spi[i]), 1, "num-cs", &err); + object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", + &local_err); + error_propagate(&err, local_err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + sc->memmap[ASPEED_SPI1 + i]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, + s->spi[i].ctrl->flash_window_base); + } + + /* SDMC - SDRAM Memory Controller */ + object_property_set_bool(OBJECT(&s->sdmc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_SDMC]); + + /* Watch dog */ + for (i = 0; i < sc->wdts_num; i++) { + AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); + + object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, + sc->memmap[ASPEED_WDT] + i * awc->offset); + } + + /* Net */ + for (i = 0; i < nb_nics && i < sc->macs_num; i++) { + qdev_set_nic_properties(DEVICE(&s->ftgmac100[i]), &nd_table[i]); + object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "aspeed", + &err); + object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "realized", + &local_err); + error_propagate(&err, local_err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + sc->memmap[ASPEED_ETH1 + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + aspeed_soc_get_irq(s, ASPEED_ETH1 + i)); + + object_property_set_bool(OBJECT(&s->mii[i]), true, "realized", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mii[i]), 0, + sc->memmap[ASPEED_MII1 + i]); + } + + /* XDMA */ + object_property_set_bool(OBJECT(&s->xdma), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0, + sc->memmap[ASPEED_XDMA]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, + aspeed_soc_get_irq(s, ASPEED_XDMA)); + + /* GPIO */ + object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_GPIO]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, + aspeed_soc_get_irq(s, ASPEED_GPIO)); + + object_property_set_bool(OBJECT(&s->gpio_1_8v), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio_1_8v), 0, + sc->memmap[ASPEED_GPIO_1_8V]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio_1_8v), 0, + aspeed_soc_get_irq(s, ASPEED_GPIO_1_8V)); + + /* SDHCI */ + object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0, + sc->memmap[ASPEED_SDHCI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_soc_get_irq(s, ASPEED_SDHCI)); +} + +static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + + dc->realize = aspeed_soc_ast2600_realize; + + sc->name = "ast2600-a0"; + sc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); + sc->silicon_rev = AST2600_A0_SILICON_REV; + sc->sram_size = 0x10000; + sc->spis_num = 2; + sc->wdts_num = 4; + sc->macs_num = 4; + sc->irqmap = aspeed_soc_ast2600_irqmap; + sc->memmap = aspeed_soc_ast2600_memmap; + sc->num_cpus = 2; +} + +static const TypeInfo aspeed_soc_ast2600_type_info = { + .name = "ast2600-a0", + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(AspeedSoCState), + .instance_init = aspeed_soc_ast2600_init, + .class_init = aspeed_soc_ast2600_class_init, + .class_size = sizeof(AspeedSoCClass), +}; + +static void aspeed_soc_register_types(void) +{ + type_register_static(&aspeed_soc_ast2600_type_info); +}; + +type_init(aspeed_soc_register_types) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index cf1d0cf..f4fe243 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -34,8 +34,10 @@ static const hwaddr aspeed_soc_ast2400_memmap[] = { [ASPEED_SDMC] = 0x1E6E0000, [ASPEED_SCU] = 0x1E6E2000, [ASPEED_XDMA] = 0x1E6E7000, + [ASPEED_VIDEO] = 0x1E700000, [ASPEED_ADC] = 0x1E6E9000, [ASPEED_SRAM] = 0x1E720000, + [ASPEED_SDHCI] = 0x1E740000, [ASPEED_GPIO] = 0x1E780000, [ASPEED_RTC] = 0x1E781000, [ASPEED_TIMER1] = 0x1E782000, @@ -62,7 +64,9 @@ static const hwaddr aspeed_soc_ast2500_memmap[] = { [ASPEED_SCU] = 0x1E6E2000, [ASPEED_XDMA] = 0x1E6E7000, [ASPEED_ADC] = 0x1E6E9000, + [ASPEED_VIDEO] = 0x1E700000, [ASPEED_SRAM] = 0x1E720000, + [ASPEED_SDHCI] = 0x1E740000, [ASPEED_GPIO] = 0x1E780000, [ASPEED_RTC] = 0x1E781000, [ASPEED_TIMER1] = 0x1E782000, @@ -108,39 +112,16 @@ static const int aspeed_soc_ast2400_irqmap[] = { [ASPEED_ETH1] = 2, [ASPEED_ETH2] = 3, [ASPEED_XDMA] = 6, + [ASPEED_SDHCI] = 26, }; #define aspeed_soc_ast2500_irqmap aspeed_soc_ast2400_irqmap -static const AspeedSoCInfo aspeed_socs[] = { - { - .name = "ast2400-a1", - .cpu_type = ARM_CPU_TYPE_NAME("arm926"), - .silicon_rev = AST2400_A1_SILICON_REV, - .sram_size = 0x8000, - .spis_num = 1, - .wdts_num = 2, - .irqmap = aspeed_soc_ast2400_irqmap, - .memmap = aspeed_soc_ast2400_memmap, - .num_cpus = 1, - }, { - .name = "ast2500-a1", - .cpu_type = ARM_CPU_TYPE_NAME("arm1176"), - .silicon_rev = AST2500_A1_SILICON_REV, - .sram_size = 0x9000, - .spis_num = 2, - .wdts_num = 3, - .irqmap = aspeed_soc_ast2500_irqmap, - .memmap = aspeed_soc_ast2500_memmap, - .num_cpus = 1, - }, -}; - static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl) { AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->vic), sc->info->irqmap[ctrl]); + return qdev_get_gpio_in(DEVICE(&s->vic), sc->irqmap[ctrl]); } static void aspeed_soc_init(Object *obj) @@ -151,13 +132,13 @@ static void aspeed_soc_init(Object *obj) char socname[8]; char typename[64]; - if (sscanf(sc->info->name, "%7s", socname) != 1) { + if (sscanf(sc->name, "%7s", socname) != 1) { g_assert_not_reached(); } - for (i = 0; i < sc->info->num_cpus; i++) { + for (i = 0; i < sc->num_cpus; i++) { object_initialize_child(obj, "cpu[*]", OBJECT(&s->cpu[i]), - sizeof(s->cpu[i]), sc->info->cpu_type, + sizeof(s->cpu[i]), sc->cpu_type, &error_abort, NULL); } @@ -165,7 +146,7 @@ static void aspeed_soc_init(Object *obj) sysbus_init_child_obj(obj, "scu", OBJECT(&s->scu), sizeof(s->scu), typename); qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", - sc->info->silicon_rev); + sc->silicon_rev); object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), "hw-strap1", &error_abort); object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), @@ -179,13 +160,15 @@ static void aspeed_soc_init(Object *obj) sysbus_init_child_obj(obj, "rtc", OBJECT(&s->rtc), sizeof(s->rtc), TYPE_ASPEED_RTC); + snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); sysbus_init_child_obj(obj, "timerctrl", OBJECT(&s->timerctrl), - sizeof(s->timerctrl), TYPE_ASPEED_TIMER); + sizeof(s->timerctrl), typename); object_property_add_const_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), &error_abort); + snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); sysbus_init_child_obj(obj, "i2c", OBJECT(&s->i2c), sizeof(s->i2c), - TYPE_ASPEED_I2C); + typename); snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); sysbus_init_child_obj(obj, "fmc", OBJECT(&s->fmc), sizeof(s->fmc), @@ -195,31 +178,29 @@ static void aspeed_soc_init(Object *obj) object_property_add_alias(obj, "dram", OBJECT(&s->fmc), "dram", &error_abort); - for (i = 0; i < sc->info->spis_num; i++) { + for (i = 0; i < sc->spis_num; i++) { snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname); sysbus_init_child_obj(obj, "spi[*]", OBJECT(&s->spi[i]), sizeof(s->spi[i]), typename); } + snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); sysbus_init_child_obj(obj, "sdmc", OBJECT(&s->sdmc), sizeof(s->sdmc), - TYPE_ASPEED_SDMC); - qdev_prop_set_uint32(DEVICE(&s->sdmc), "silicon-rev", - sc->info->silicon_rev); + typename); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), "ram-size", &error_abort); object_property_add_alias(obj, "max-ram-size", OBJECT(&s->sdmc), "max-ram-size", &error_abort); - for (i = 0; i < sc->info->wdts_num; i++) { + for (i = 0; i < sc->wdts_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); sysbus_init_child_obj(obj, "wdt[*]", OBJECT(&s->wdt[i]), - sizeof(s->wdt[i]), TYPE_ASPEED_WDT); - qdev_prop_set_uint32(DEVICE(&s->wdt[i]), "silicon-rev", - sc->info->silicon_rev); + sizeof(s->wdt[i]), typename); object_property_add_const_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&s->scu), &error_abort); } - for (i = 0; i < ASPEED_MACS_NUM; i++) { + for (i = 0; i < sc->macs_num; i++) { sysbus_init_child_obj(obj, "ftgmac100[*]", OBJECT(&s->ftgmac100[i]), sizeof(s->ftgmac100[i]), TYPE_FTGMAC100); } @@ -230,6 +211,15 @@ static void aspeed_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname); sysbus_init_child_obj(obj, "gpio", OBJECT(&s->gpio), sizeof(s->gpio), typename); + + sysbus_init_child_obj(obj, "sdc", OBJECT(&s->sdhci), sizeof(s->sdhci), + TYPE_ASPEED_SDHCI); + + /* Init sd card slot class here so that they're under the correct parent */ + for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) { + sysbus_init_child_obj(obj, "sdhci[*]", OBJECT(&s->sdhci.slots[i]), + sizeof(s->sdhci.slots[i]), TYPE_SYSBUS_SDHCI); + } } static void aspeed_soc_realize(DeviceState *dev, Error **errp) @@ -240,13 +230,17 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) Error *err = NULL, *local_err = NULL; /* IO space */ - create_unimplemented_device("aspeed_soc.io", sc->info->memmap[ASPEED_IOMEM], + create_unimplemented_device("aspeed_soc.io", sc->memmap[ASPEED_IOMEM], ASPEED_SOC_IOMEM_SIZE); - if (s->num_cpus > sc->info->num_cpus) { + /* Video engine stub */ + create_unimplemented_device("aspeed.video", sc->memmap[ASPEED_VIDEO], + 0x1000); + + if (s->num_cpus > sc->num_cpus) { warn_report("%s: invalid number of CPUs %d, using default %d", - sc->info->name, s->num_cpus, sc->info->num_cpus); - s->num_cpus = sc->info->num_cpus; + sc->name, s->num_cpus, sc->num_cpus); + s->num_cpus = sc->num_cpus; } /* CPU */ @@ -260,13 +254,13 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) /* SRAM */ memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram", - sc->info->sram_size, &err); + sc->sram_size, &err); if (err) { error_propagate(errp, err); return; } memory_region_add_subregion(get_system_memory(), - sc->info->memmap[ASPEED_SRAM], &s->sram); + sc->memmap[ASPEED_SRAM], &s->sram); /* SCU */ object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); @@ -274,7 +268,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->info->memmap[ASPEED_SCU]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_SCU]); /* VIC */ object_property_set_bool(OBJECT(&s->vic), true, "realized", &err); @@ -282,7 +276,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, sc->info->memmap[ASPEED_VIC]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, sc->memmap[ASPEED_VIC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0, qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1, @@ -294,7 +288,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->info->memmap[ASPEED_RTC]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, aspeed_soc_get_irq(s, ASPEED_RTC)); @@ -305,7 +299,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, - sc->info->memmap[ASPEED_TIMER1]); + sc->memmap[ASPEED_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_TIMER1 + i); sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); @@ -314,7 +308,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) /* UART - attach an 8250 to the IO space as our UART5 */ if (serial_hd(0)) { qemu_irq uart5 = aspeed_soc_get_irq(s, ASPEED_UART5); - serial_mm_init(get_system_memory(), sc->info->memmap[ASPEED_UART5], 2, + serial_mm_init(get_system_memory(), sc->memmap[ASPEED_UART5], 2, uart5, 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN); } @@ -324,12 +318,12 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->info->memmap[ASPEED_I2C]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_I2C]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0, aspeed_soc_get_irq(s, ASPEED_I2C)); /* FMC, The number of CS is set at the board level */ - object_property_set_int(OBJECT(&s->fmc), sc->info->memmap[ASPEED_SDRAM], + object_property_set_int(OBJECT(&s->fmc), sc->memmap[ASPEED_SDRAM], "sdram-base", &err); if (err) { error_propagate(errp, err); @@ -340,14 +334,14 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->info->memmap[ASPEED_FMC]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_FMC]); sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, s->fmc.ctrl->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_FMC)); /* SPI */ - for (i = 0; i < sc->info->spis_num; i++) { + for (i = 0; i < sc->spis_num; i++) { object_property_set_int(OBJECT(&s->spi[i]), 1, "num-cs", &err); object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &local_err); @@ -357,7 +351,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, - sc->info->memmap[ASPEED_SPI1 + i]); + sc->memmap[ASPEED_SPI1 + i]); sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, s->spi[i].ctrl->flash_window_base); } @@ -368,21 +362,23 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->info->memmap[ASPEED_SDMC]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_SDMC]); /* Watch dog */ - for (i = 0; i < sc->info->wdts_num; i++) { + for (i = 0; i < sc->wdts_num; i++) { + AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); + object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", &err); if (err) { error_propagate(errp, err); return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, - sc->info->memmap[ASPEED_WDT] + i * 0x20); + sc->memmap[ASPEED_WDT] + i * awc->offset); } /* Net */ - for (i = 0; i < nb_nics; i++) { + for (i = 0; i < nb_nics && i < sc->macs_num; i++) { qdev_set_nic_properties(DEVICE(&s->ftgmac100[i]), &nd_table[i]); object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "aspeed", &err); @@ -394,7 +390,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, - sc->info->memmap[ASPEED_ETH1 + i]); + sc->memmap[ASPEED_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, aspeed_soc_get_irq(s, ASPEED_ETH1 + i)); } @@ -406,7 +402,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0, - sc->info->memmap[ASPEED_XDMA]); + sc->memmap[ASPEED_XDMA]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, aspeed_soc_get_irq(s, ASPEED_XDMA)); @@ -416,9 +412,20 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->info->memmap[ASPEED_GPIO]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_GPIO)); + + /* SDHCI */ + object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0, + sc->memmap[ASPEED_SDHCI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_soc_get_irq(s, ASPEED_SDHCI)); } static Property aspeed_soc_properties[] = { DEFINE_PROP_UINT32("num-cpus", AspeedSoCState, num_cpus, 0), @@ -428,9 +435,7 @@ static Property aspeed_soc_properties[] = { static void aspeed_soc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); - sc->info = (AspeedSoCInfo *) data; dc->realize = aspeed_soc_realize; /* Reason: Uses serial_hds and nd_table in realize() directly */ dc->user_creatable = false; @@ -440,26 +445,64 @@ static void aspeed_soc_class_init(ObjectClass *oc, void *data) static const TypeInfo aspeed_soc_type_info = { .name = TYPE_ASPEED_SOC, .parent = TYPE_DEVICE, - .instance_init = aspeed_soc_init, .instance_size = sizeof(AspeedSoCState), .class_size = sizeof(AspeedSoCClass), + .class_init = aspeed_soc_class_init, .abstract = true, }; -static void aspeed_soc_register_types(void) +static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) { - int i; + AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); - type_register_static(&aspeed_soc_type_info); - for (i = 0; i < ARRAY_SIZE(aspeed_socs); ++i) { - TypeInfo ti = { - .name = aspeed_socs[i].name, - .parent = TYPE_ASPEED_SOC, - .class_init = aspeed_soc_class_init, - .class_data = (void *) &aspeed_socs[i], - }; - type_register(&ti); - } + sc->name = "ast2400-a1"; + sc->cpu_type = ARM_CPU_TYPE_NAME("arm926"); + sc->silicon_rev = AST2400_A1_SILICON_REV; + sc->sram_size = 0x8000; + sc->spis_num = 1; + sc->wdts_num = 2; + sc->macs_num = 2; + sc->irqmap = aspeed_soc_ast2400_irqmap; + sc->memmap = aspeed_soc_ast2400_memmap; + sc->num_cpus = 1; } +static const TypeInfo aspeed_soc_ast2400_type_info = { + .name = "ast2400-a1", + .parent = TYPE_ASPEED_SOC, + .instance_init = aspeed_soc_init, + .instance_size = sizeof(AspeedSoCState), + .class_init = aspeed_soc_ast2400_class_init, +}; + +static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) +{ + AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + + sc->name = "ast2500-a1"; + sc->cpu_type = ARM_CPU_TYPE_NAME("arm1176"); + sc->silicon_rev = AST2500_A1_SILICON_REV; + sc->sram_size = 0x9000; + sc->spis_num = 2; + sc->wdts_num = 3; + sc->macs_num = 2; + sc->irqmap = aspeed_soc_ast2500_irqmap; + sc->memmap = aspeed_soc_ast2500_memmap; + sc->num_cpus = 1; +} + +static const TypeInfo aspeed_soc_ast2500_type_info = { + .name = "ast2500-a1", + .parent = TYPE_ASPEED_SOC, + .instance_init = aspeed_soc_init, + .instance_size = sizeof(AspeedSoCState), + .class_init = aspeed_soc_ast2500_class_init, +}; +static void aspeed_soc_register_types(void) +{ + type_register_static(&aspeed_soc_type_info); + type_register_static(&aspeed_soc_ast2400_type_info); + type_register_static(&aspeed_soc_ast2500_type_info); +}; + type_init(aspeed_soc_register_types) diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 8984e2e..fdcf616 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -22,6 +22,20 @@ /* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */ #define BCM2835_SDHC_CAPAREG 0x52134b4 +static void create_unimp(BCM2835PeripheralState *ps, + UnimplementedDeviceState *uds, + const char *name, hwaddr ofs, hwaddr size) +{ + sysbus_init_child_obj(OBJECT(ps), name, uds, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(uds), "name", name); + qdev_prop_set_uint64(DEVICE(uds), "size", size); + object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal); + memory_region_add_subregion_overlap(&ps->peri_mr, ofs, + sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0), -1000); +} + static void bcm2835_peripherals_init(Object *obj) { BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj); @@ -165,7 +179,8 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart0), 0)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart0), 0, qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, - INTERRUPT_UART)); + INTERRUPT_UART0)); + /* AUX / UART1 */ qdev_prop_set_chr(DEVICE(&s->aux), "chardev", serial_hd(1)); @@ -175,7 +190,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) return; } - memory_region_add_subregion(&s->peri_mr, UART1_OFFSET, + memory_region_add_subregion(&s->peri_mr, AUX_OFFSET, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0, qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, @@ -268,7 +283,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) return; } - memory_region_add_subregion(&s->peri_mr, EMMC_OFFSET, + memory_region_add_subregion(&s->peri_mr, EMMC1_OFFSET, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhci), 0)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, @@ -322,6 +337,23 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } + + create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40); + create_unimp(s, &s->systmr, "bcm2835-systimer", ST_OFFSET, 0x20); + create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x1000); + create_unimp(s, &s->a2w, "bcm2835-a2w", A2W_OFFSET, 0x1000); + create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100); + create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100); + create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20); + create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100); + create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20); + create_unimp(s, &s->i2c[1], "bcm2835-i2c1", BSC1_OFFSET, 0x20); + create_unimp(s, &s->i2c[2], "bcm2835-i2c2", BSC2_OFFSET, 0x20); + create_unimp(s, &s->otp, "bcm2835-otp", OTP_OFFSET, 0x80); + create_unimp(s, &s->dbus, "bcm2835-dbus", DBUS_OFFSET, 0x8000); + create_unimp(s, &s->ave0, "bcm2835-ave0", AVE0_OFFSET, 0x8000); + create_unimp(s, &s->dwc2, "dwc-usb2", USB_OTG_OFFSET, 0x1000); + create_unimp(s, &s->sdramc, "bcm2835-sdramc", SDRAMC_OFFSET, 0x100); } static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 493a913..723aef6 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -126,7 +126,7 @@ static void bcm2836_realize(DeviceState *dev, Error **errp) /* set periphbase/CBAR value for CPU-local registers */ object_property_set_int(OBJECT(&s->cpus[n]), - BCM2836_PERI_BASE + MCORE_OFFSET, + BCM2836_PERI_BASE + MSYNC_OFFSET, "reset-cbar", &err); if (err) { error_propagate(errp, err); diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 246cbb1..f68a399 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -843,13 +843,10 @@ static void mv88w8618_timer_tick(void *opaque) static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s, uint32_t freq) { - QEMUBH *bh; - sysbus_init_irq(dev, &s->irq); s->freq = freq; - bh = qemu_bh_new(mv88w8618_timer_tick, s); - s->ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->ptimer = ptimer_init(mv88w8618_timer_tick, s, PTIMER_POLICY_DEFAULT); } static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset, @@ -879,16 +876,19 @@ static void mv88w8618_pit_write(void *opaque, hwaddr offset, case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH: t = &s->timer[offset >> 2]; t->limit = value; + ptimer_transaction_begin(t->ptimer); if (t->limit > 0) { ptimer_set_limit(t->ptimer, t->limit, 1); } else { ptimer_stop(t->ptimer); } + ptimer_transaction_commit(t->ptimer); break; case MP_PIT_CONTROL: for (i = 0; i < 4; i++) { t = &s->timer[i]; + ptimer_transaction_begin(t->ptimer); if (value & 0xf && t->limit > 0) { ptimer_set_limit(t->ptimer, t->limit, 0); ptimer_set_freq(t->ptimer, t->freq); @@ -896,6 +896,7 @@ static void mv88w8618_pit_write(void *opaque, hwaddr offset, } else { ptimer_stop(t->ptimer); } + ptimer_transaction_commit(t->ptimer); value >>= 4; } break; @@ -914,8 +915,11 @@ static void mv88w8618_pit_reset(DeviceState *d) int i; for (i = 0; i < 4; i++) { - ptimer_stop(s->timer[i].ptimer); - s->timer[i].limit = 0; + mv88w8618_timer_state *t = &s->timer[i]; + ptimer_transaction_begin(t->ptimer); + ptimer_stop(t->ptimer); + ptimer_transaction_commit(t->ptimer); + t->limit = 0; } } diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 74c062d..615d755 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -230,7 +230,7 @@ static void raspi2_machine_init(MachineClass *mc) mc->max_cpus = BCM283X_NCPUS; mc->min_cpus = BCM283X_NCPUS; mc->default_cpus = BCM283X_NCPUS; - mc->default_ram_size = 1024 * 1024 * 1024; + mc->default_ram_size = 1 * GiB; mc->ignore_memory_transaction_failures = true; }; DEFINE_MACHINE("raspi2", raspi2_machine_init) @@ -252,7 +252,7 @@ static void raspi3_machine_init(MachineClass *mc) mc->max_cpus = BCM283X_NCPUS; mc->min_cpus = BCM283X_NCPUS; mc->default_cpus = BCM283X_NCPUS; - mc->default_ram_size = 1024 * 1024 * 1024; + mc->default_ram_size = 1 * GiB; } DEFINE_MACHINE("raspi3", raspi3_machine_init) #endif diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 6cdf156..4cd5017 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -39,6 +39,8 @@ #include "hw/acpi/aml-build.h" #include "hw/acpi/utils.h" #include "hw/acpi/pci.h" +#include "hw/acpi/memory_hotplug.h" +#include "hw/acpi/generic_event_device.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci.h" #include "hw/arm/virt.h" @@ -48,7 +50,6 @@ #include "migration/vmstate.h" #define ARM_SPI_BASE 32 -#define ACPI_POWER_BUTTON_DEVICE "PWRB" static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus) { @@ -544,6 +545,14 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } } + if (ms->device_memory) { + numamem = acpi_data_push(table_data, sizeof *numamem); + build_srat_memory(numamem, ms->device_memory->base, + memory_region_size(&ms->device_memory->mr), + ms->numa_state->num_nodes - 1, + MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); + } + build_header(linker, table_data, (void *)(table_data->data + srat_start), "SRAT", table_data->len - srat_start, 3, NULL, NULL); } @@ -708,6 +717,7 @@ static void build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { Aml *scope, *dsdt; + MachineState *ms = MACHINE(vms); const MemMapEntry *memmap = vms->memmap; const int *irqmap = vms->irqmap; @@ -730,8 +740,27 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS); acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE), vms->highmem, vms->highmem_ecam); - acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO], - (irqmap[VIRT_GPIO] + ARM_SPI_BASE)); + if (vms->acpi_dev) { + build_ged_aml(scope, "\\_SB."GED_DEVICE, + HOTPLUG_HANDLER(vms->acpi_dev), + irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY, + memmap[VIRT_ACPI_GED].base); + } else { + acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO], + (irqmap[VIRT_GPIO] + ARM_SPI_BASE)); + } + + if (vms->acpi_dev) { + uint32_t event = object_property_get_uint(OBJECT(vms->acpi_dev), + "ged-event", &error_abort); + + if (event & ACPI_GED_MEM_HOTPLUG_EVT) { + build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL, + AML_SYSTEM_MEMORY, + memmap[VIRT_PCDIMM_ACPI].base); + } + } + acpi_dsdt_add_power_button(scope); aml_append(dsdt, scope); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index d74538b..d4bedc2 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -68,6 +68,9 @@ #include "hw/arm/smmuv3.h" #include "hw/acpi/acpi.h" #include "target/arm/internals.h" +#include "hw/mem/pc-dimm.h" +#include "hw/mem/nvdimm.h" +#include "hw/acpi/generic_event_device.h" #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ @@ -138,6 +141,8 @@ static const MemMapEntry base_memmap[] = { [VIRT_GPIO] = { 0x09030000, 0x00001000 }, [VIRT_SECURE_UART] = { 0x09040000, 0x00001000 }, [VIRT_SMMU] = { 0x09050000, 0x00020000 }, + [VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN }, + [VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, @@ -173,6 +178,7 @@ static const int a15irqmap[] = { [VIRT_PCIE] = 3, /* ... to 6 */ [VIRT_GPIO] = 7, [VIRT_SECURE_UART] = 8, + [VIRT_ACPI_GED] = 9, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ [VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */ @@ -525,6 +531,29 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) } } +static inline DeviceState *create_acpi_ged(VirtMachineState *vms, qemu_irq *pic) +{ + DeviceState *dev; + MachineState *ms = MACHINE(vms); + int irq = vms->irqmap[VIRT_ACPI_GED]; + uint32_t event = ACPI_GED_PWR_DOWN_EVT; + + if (ms->ram_slots) { + event |= ACPI_GED_MEM_HOTPLUG_EVT; + } + + dev = qdev_create(NULL, TYPE_ACPI_GED); + qdev_prop_set_uint32(dev, "ged-event", event); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irq]); + + qdev_init_nofail(dev); + + return dev; +} + static void create_its(VirtMachineState *vms, DeviceState *gicdev) { const char *itsclass = its_class_name(); @@ -764,13 +793,15 @@ static void create_rtc(const VirtMachineState *vms, qemu_irq *pic) static DeviceState *gpio_key_dev; static void virt_powerdown_req(Notifier *n, void *opaque) { - /* use gpio Pin 3 for power button event */ - qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1); -} + VirtMachineState *s = container_of(n, VirtMachineState, powerdown_notifier); -static Notifier virt_system_powerdown_notifier = { - .notify = virt_powerdown_req -}; + if (s->acpi_dev) { + acpi_send_event(s->acpi_dev, ACPI_POWER_DOWN_STATUS); + } else { + /* use gpio Pin 3 for power button event */ + qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1); + } +} static void create_gpio(const VirtMachineState *vms, qemu_irq *pic) { @@ -812,10 +843,6 @@ static void create_gpio(const VirtMachineState *vms, qemu_irq *pic) KEY_POWER); qemu_fdt_setprop_cells(vms->fdt, "/gpio-keys/poweroff", "gpios", phandle, 3, 0); - - /* connect powerdown request */ - qemu_register_powerdown_notifier(&virt_system_powerdown_notifier); - g_free(nodename); } @@ -1489,6 +1516,7 @@ static void machvirt_init(MachineState *machine) MemoryRegion *ram = g_new(MemoryRegion, 1); bool firmware_loaded; bool aarch64 = true; + bool has_ged = !vmc->no_ged; unsigned int smp_cpus = machine->smp.cpus; unsigned int max_cpus = machine->smp.max_cpus; @@ -1701,7 +1729,15 @@ static void machvirt_init(MachineState *machine) create_pcie(vms, pic); - create_gpio(vms, pic); + if (has_ged && aarch64 && firmware_loaded && acpi_enabled) { + vms->acpi_dev = create_acpi_ged(vms, pic); + } else { + create_gpio(vms, pic); + } + + /* connect powerdown request */ + vms->powerdown_notifier.notify = virt_powerdown_req; + qemu_register_powerdown_notifier(&vms->powerdown_notifier); /* Create mmio transports, so the user can create virtio backends * (which will be automatically plugged in to the transports). If @@ -1876,6 +1912,52 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) return ms->possible_cpus; } +static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); + + if (is_nvdimm) { + error_setg(errp, "nvdimm is not yet supported"); + return; + } + + if (!vms->acpi_dev) { + error_setg(errp, + "memory hotplug is not enabled: missing acpi-ged device"); + return; + } + + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp); +} + +static void virt_memory_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + HotplugHandlerClass *hhc; + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + Error *local_err = NULL; + + pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), &local_err); + if (local_err) { + goto out; + } + + hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev); + hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &error_abort); +out: + error_propagate(errp, local_err); +} + +static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + virt_memory_pre_plug(hotplug_dev, dev, errp); + } +} + static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -1887,12 +1969,23 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, SYS_BUS_DEVICE(dev)); } } + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + virt_memory_plug(hotplug_dev, dev, errp); + } +} + +static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + error_setg(errp, "device unplug request for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); } static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, DeviceState *dev) { - if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE) || + (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) { return HOTPLUG_HANDLER(machine); } @@ -1956,8 +2049,11 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->kvm_type = virt_kvm_type; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = virt_machine_get_hotplug_handler; + hc->pre_plug = virt_machine_device_pre_plug_cb; hc->plug = virt_machine_device_plug_cb; + hc->unplug_request = virt_machine_device_unplug_request_cb; mc->numa_mem_supported = true; + mc->auto_enable_numa_with_memhp = true; } static void virt_instance_init(Object *obj) @@ -2058,8 +2154,12 @@ DEFINE_VIRT_MACHINE_AS_LATEST(4, 2) static void virt_machine_4_1_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + virt_machine_4_2_options(mc); compat_props_add(mc->compat_props, hw_compat_4_1, hw_compat_4_1_len); + vmc->no_ged = true; + mc->auto_enable_numa_with_memhp = false; } DEFINE_VIRT_MACHINE(4, 1) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 47159de..11ff5b9 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -327,6 +327,7 @@ static const FlashPartInfo known_devices[] = { { INFO("w25q80", 0xef5014, 0, 64 << 10, 16, ER_4K) }, { INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) }, { INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) }, + { INFO("w25q512jv", 0xef4020, 0, 64 << 10, 1024, ER_4K) }, }; typedef enum { diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 3f85519..a6fc1bf 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -162,8 +162,9 @@ static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value, switch (offset) { case AUX_ENABLES: if (value != 1) { - qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI " - "or disable UART\n", __func__); + qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI" + " or disable UART: 0x%"PRIx64"\n", + __func__, value); } break; diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index d58e2df..7239b82 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -31,6 +31,16 @@ struct ptimer_state uint8_t policy_mask; QEMUBH *bh; QEMUTimer *timer; + ptimer_cb callback; + void *callback_opaque; + /* + * These track whether we're in a transaction block, and if we + * need to do a timer reload when the block finishes. They don't + * need to be migrated because migration can never happen in the + * middle of a transaction block. + */ + bool in_transaction; + bool need_reload; }; /* Use a bottom-half routine to avoid reentrancy issues. */ @@ -39,13 +49,16 @@ static void ptimer_trigger(ptimer_state *s) if (s->bh) { replay_bh_schedule_event(s->bh); } + if (s->callback) { + s->callback(s->callback_opaque); + } } 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; + uint32_t period_frac; + uint64_t period; + uint64_t delta; bool suppress_trigger = false; /* @@ -58,11 +71,20 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust) (s->policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT)) { suppress_trigger = true; } - if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) + if (s->delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) && !suppress_trigger) { ptimer_trigger(s); } + /* + * Note that ptimer_trigger() might call the device callback function, + * which can then modify timer state, so we must not cache any fields + * from ptimer_state until after we have called it. + */ + delta = s->delta; + period = s->period; + period_frac = s->period_frac; + if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) { delta = s->delta = s->limit; } @@ -136,6 +158,15 @@ static void ptimer_tick(void *opaque) ptimer_state *s = (ptimer_state *)opaque; bool trigger = true; + /* + * We perform all the tick actions within a begin/commit block + * because the callback function that ptimer_trigger() calls + * might make calls into the ptimer APIs that provoke another + * trigger, and we want that to cause the callback function + * to be called iteratively, not recursively. + */ + ptimer_transaction_begin(s); + if (s->enabled == 2) { s->delta = 0; s->enabled = 0; @@ -164,6 +195,8 @@ static void ptimer_tick(void *opaque) if (trigger) { ptimer_trigger(s); } + + ptimer_transaction_commit(s); } uint64_t ptimer_get_count(ptimer_state *s) @@ -263,10 +296,15 @@ uint64_t ptimer_get_count(ptimer_state *s) void ptimer_set_count(ptimer_state *s, uint64_t count) { + assert(s->in_transaction || !s->callback); s->delta = count; if (s->enabled) { - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s, 0); + if (!s->callback) { + s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_reload(s, 0); + } else { + s->need_reload = true; + } } } @@ -274,6 +312,8 @@ void ptimer_run(ptimer_state *s, int oneshot) { bool was_disabled = !s->enabled; + assert(s->in_transaction || !s->callback); + if (was_disabled && s->period == 0) { if (!qtest_enabled()) { fprintf(stderr, "Timer with period zero, disabling\n"); @@ -282,8 +322,12 @@ 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, 0); + if (!s->callback) { + s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_reload(s, 0); + } else { + s->need_reload = true; + } } } @@ -291,35 +335,50 @@ void ptimer_run(ptimer_state *s, int oneshot) is immediately restarted. */ void ptimer_stop(ptimer_state *s) { + assert(s->in_transaction || !s->callback); + if (!s->enabled) return; s->delta = ptimer_get_count(s); timer_del(s->timer); s->enabled = 0; + if (s->callback) { + s->need_reload = false; + } } /* Set counter increment interval in nanoseconds. */ void ptimer_set_period(ptimer_state *s, int64_t period) { + assert(s->in_transaction || !s->callback); s->delta = ptimer_get_count(s); s->period = period; s->period_frac = 0; if (s->enabled) { - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s, 0); + if (!s->callback) { + s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_reload(s, 0); + } else { + s->need_reload = true; + } } } /* Set counter frequency in Hz. */ void ptimer_set_freq(ptimer_state *s, uint32_t freq) { + assert(s->in_transaction || !s->callback); s->delta = ptimer_get_count(s); s->period = 1000000000ll / freq; s->period_frac = (1000000000ll << 32) / freq; if (s->enabled) { - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s, 0); + if (!s->callback) { + s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_reload(s, 0); + } else { + s->need_reload = true; + } } } @@ -327,12 +386,17 @@ void ptimer_set_freq(ptimer_state *s, uint32_t freq) count = limit. */ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload) { + assert(s->in_transaction || !s->callback); s->limit = limit; if (reload) s->delta = limit; if (s->enabled && reload) { - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s, 0); + if (!s->callback) { + s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_reload(s, 0); + } else { + s->need_reload = true; + } } } @@ -341,6 +405,32 @@ uint64_t ptimer_get_limit(ptimer_state *s) return s->limit; } +void ptimer_transaction_begin(ptimer_state *s) +{ + assert(!s->in_transaction || !s->callback); + s->in_transaction = true; + s->need_reload = false; +} + +void ptimer_transaction_commit(ptimer_state *s) +{ + assert(s->in_transaction); + /* + * We must loop here because ptimer_reload() can call the callback + * function, which might then update ptimer state in a way that + * means we need to do another reload and possibly another callback. + * A disabled timer never needs reloading (and if we don't check + * this then we loop forever if ptimer_reload() disables the timer). + */ + while (s->need_reload && s->enabled) { + s->need_reload = false; + s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_reload(s, 0); + } + /* Now we've finished reload we can leave the transaction block. */ + s->in_transaction = false; +} + const VMStateDescription vmstate_ptimer = { .name = "ptimer", .version_id = 1, @@ -358,7 +448,7 @@ const VMStateDescription vmstate_ptimer = { } }; -ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask) +ptimer_state *ptimer_init_with_bh(QEMUBH *bh, uint8_t policy_mask) { ptimer_state *s; @@ -377,9 +467,41 @@ ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask) return s; } +ptimer_state *ptimer_init(ptimer_cb callback, void *callback_opaque, + uint8_t policy_mask) +{ + ptimer_state *s; + + /* + * The callback function is mandatory; so we use it to distinguish + * old-style QEMUBH ptimers from new transaction API ptimers. + * (ptimer_init_with_bh() allows a NULL bh pointer and at least + * one device (digic-timer) passes NULL, so it's not the case + * that either s->bh != NULL or s->callback != NULL.) + */ + assert(callback); + + s = g_new0(ptimer_state, 1); + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s); + s->policy_mask = policy_mask; + s->callback = callback; + s->callback_opaque = callback_opaque; + + /* + * These two policies are incompatible -- trigger-on-decrement implies + * a timer trigger when the count becomes 0, but no-immediate-trigger + * implies a trigger when the count stops being 0. + */ + assert(!((policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) && + (policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER))); + return s; +} + void ptimer_free(ptimer_state *s) { - qemu_bh_delete(s->bh); + if (s->bh) { + qemu_bh_delete(s->bh); + } timer_free(s->timer); g_free(s); } diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 8f85687..85aaa54 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -425,7 +425,7 @@ static void bcm2835_fb_realize(DeviceState *dev, Error **errp) s->initial_config.base = s->vcram_base + BCM2835_FB_OFFSET; s->dma_mr = MEMORY_REGION(obj); - address_space_init(&s->dma_as, s->dma_mr, NULL); + address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_FB "-memory"); bcm2835_fb_reset(dev); diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c index 192bd37..1e458d7 100644 --- a/hw/dma/bcm2835_dma.c +++ b/hw/dma/bcm2835_dma.c @@ -180,7 +180,7 @@ static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset, res = ch->debug; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset); break; } @@ -228,7 +228,7 @@ static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset, ch->debug = value; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset); break; } @@ -247,7 +247,7 @@ static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size) case BCM2708_DMA_ENABLE: return s->enable; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset); return 0; } @@ -274,7 +274,7 @@ static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value, s->enable = (value & 0xffff); break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset); } } @@ -383,7 +383,7 @@ static void bcm2835_dma_realize(DeviceState *dev, Error **errp) } s->dma_mr = MEMORY_REGION(obj); - address_space_init(&s->dma_as, s->dma_mr, NULL); + address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_DMA "-memory"); bcm2835_dma_reset(dev); } diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index a254275..e035d1f 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -552,7 +552,7 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp) st->nr = i; st->bh = qemu_bh_new(timer_hit, st); - st->ptimer = ptimer_init(st->bh, PTIMER_POLICY_DEFAULT); + st->ptimer = ptimer_init_with_bh(st->bh, PTIMER_POLICY_DEFAULT); ptimer_set_freq(st->ptimer, s->freqhz); } return; diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index 25fbfec..196e47c 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -169,6 +169,48 @@ #define GPIO_3_6V_MEM_SIZE 0x1F0 #define GPIO_3_6V_REG_ARRAY_SIZE (GPIO_3_6V_MEM_SIZE >> 2) +/* AST2600 only - 1.8V gpios */ +/* + * The AST2600 has same 3.6V gpios as the AST2400 (memory offsets 0x0-0x198) + * and addtional 1.8V gpios (memory offsets 0x800-0x9D4). + */ +#define GPIO_1_8V_REG_OFFSET 0x800 +#define GPIO_1_8V_ABCD_DATA_VALUE ((0x800 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_DIRECTION ((0x804 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_INT_ENABLE ((0x808 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_INT_SENS_0 ((0x80C - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_INT_SENS_1 ((0x810 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_INT_SENS_2 ((0x814 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_INT_STATUS ((0x818 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_RESET_TOLERANT ((0x81C - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_DATA_VALUE ((0x820 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_DIRECTION ((0x824 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_INT_ENABLE ((0x828 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_INT_SENS_0 ((0x82C - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_INT_SENS_1 ((0x830 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_INT_SENS_2 ((0x834 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_INT_STATUS ((0x838 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_RESET_TOLERANT ((0x83C - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_DEBOUNCE_1 ((0x840 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_DEBOUNCE_2 ((0x844 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_DEBOUNCE_1 ((0x848 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_DEBOUNCE_2 ((0x84C - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_DEBOUNCE_TIME_1 ((0x850 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_DEBOUNCE_TIME_2 ((0x854 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_DEBOUNCE_TIME_3 ((0x858 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_COMMAND_SRC_0 ((0x860 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_COMMAND_SRC_1 ((0x864 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_COMMAND_SRC_0 ((0x868 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_COMMAND_SRC_1 ((0x86C - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_DATA_READ ((0x8C0 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_DATA_READ ((0x8C4 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_ABCD_INPUT_MASK ((0x9D0 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_E_INPUT_MASK ((0x9D4 - GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_1_8V_MEM_SIZE 0x9D8 +#define GPIO_1_8V_REG_ARRAY_SIZE ((GPIO_1_8V_MEM_SIZE - \ + GPIO_1_8V_REG_OFFSET) >> 2) +#define GPIO_MAX_MEM_SIZE MAX(GPIO_3_6V_MEM_SIZE, GPIO_1_8V_MEM_SIZE) + static int aspeed_evaluate_irq(GPIOSets *regs, int gpio_prev_high, int gpio) { uint32_t falling_edge = 0, rising_edge = 0; @@ -465,6 +507,39 @@ static const AspeedGPIOReg aspeed_3_6v_gpios[GPIO_3_6V_REG_ARRAY_SIZE] = { [GPIO_AC_INPUT_MASK] = { 7, gpio_reg_input_mask }, }; +static const AspeedGPIOReg aspeed_1_8v_gpios[GPIO_1_8V_REG_ARRAY_SIZE] = { + /* 1.8V Set ABCD */ + [GPIO_1_8V_ABCD_DATA_VALUE] = {0, gpio_reg_data_value}, + [GPIO_1_8V_ABCD_DIRECTION] = {0, gpio_reg_direction}, + [GPIO_1_8V_ABCD_INT_ENABLE] = {0, gpio_reg_int_enable}, + [GPIO_1_8V_ABCD_INT_SENS_0] = {0, gpio_reg_int_sens_0}, + [GPIO_1_8V_ABCD_INT_SENS_1] = {0, gpio_reg_int_sens_1}, + [GPIO_1_8V_ABCD_INT_SENS_2] = {0, gpio_reg_int_sens_2}, + [GPIO_1_8V_ABCD_INT_STATUS] = {0, gpio_reg_int_status}, + [GPIO_1_8V_ABCD_RESET_TOLERANT] = {0, gpio_reg_reset_tolerant}, + [GPIO_1_8V_ABCD_DEBOUNCE_1] = {0, gpio_reg_debounce_1}, + [GPIO_1_8V_ABCD_DEBOUNCE_2] = {0, gpio_reg_debounce_2}, + [GPIO_1_8V_ABCD_COMMAND_SRC_0] = {0, gpio_reg_cmd_source_0}, + [GPIO_1_8V_ABCD_COMMAND_SRC_1] = {0, gpio_reg_cmd_source_1}, + [GPIO_1_8V_ABCD_DATA_READ] = {0, gpio_reg_data_read}, + [GPIO_1_8V_ABCD_INPUT_MASK] = {0, gpio_reg_input_mask}, + /* 1.8V Set E */ + [GPIO_1_8V_E_DATA_VALUE] = {1, gpio_reg_data_value}, + [GPIO_1_8V_E_DIRECTION] = {1, gpio_reg_direction}, + [GPIO_1_8V_E_INT_ENABLE] = {1, gpio_reg_int_enable}, + [GPIO_1_8V_E_INT_SENS_0] = {1, gpio_reg_int_sens_0}, + [GPIO_1_8V_E_INT_SENS_1] = {1, gpio_reg_int_sens_1}, + [GPIO_1_8V_E_INT_SENS_2] = {1, gpio_reg_int_sens_2}, + [GPIO_1_8V_E_INT_STATUS] = {1, gpio_reg_int_status}, + [GPIO_1_8V_E_RESET_TOLERANT] = {1, gpio_reg_reset_tolerant}, + [GPIO_1_8V_E_DEBOUNCE_1] = {1, gpio_reg_debounce_1}, + [GPIO_1_8V_E_DEBOUNCE_2] = {1, gpio_reg_debounce_2}, + [GPIO_1_8V_E_COMMAND_SRC_0] = {1, gpio_reg_cmd_source_0}, + [GPIO_1_8V_E_COMMAND_SRC_1] = {1, gpio_reg_cmd_source_1}, + [GPIO_1_8V_E_DATA_READ] = {1, gpio_reg_data_read}, + [GPIO_1_8V_E_INPUT_MASK] = {1, gpio_reg_input_mask}, +}; + static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size) { AspeedGPIOState *s = ASPEED_GPIO(opaque); @@ -663,8 +738,11 @@ static void aspeed_gpio_get_pin(Object *obj, Visitor *v, const char *name, int set_idx, group_idx = 0; if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) { - error_setg(errp, "%s: error reading %s", __func__, name); - return; + /* 1.8V gpio */ + if (sscanf(name, "gpio%3s%1d", group, &pin) != 2) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } } set_idx = get_set_idx(s, group, &group_idx); if (set_idx == -1) { @@ -692,8 +770,11 @@ static void aspeed_gpio_set_pin(Object *obj, Visitor *v, const char *name, return; } if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) { - error_setg(errp, "%s: error reading %s", __func__, name); - return; + /* 1.8V gpio */ + if (sscanf(name, "gpio%3s%1d", group, &pin) != 2) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } } set_idx = get_set_idx(s, group, &group_idx); if (set_idx == -1) { @@ -726,6 +807,21 @@ static const GPIOSetProperties ast2500_set_props[] = { [7] = {0x000000ff, 0x000000ff, {"AC"} }, }; +static GPIOSetProperties ast2600_3_6v_set_props[] = { + [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} }, + [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} }, + [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} }, + [3] = {0xffffffff, 0xffffffff, {"M", "N", "O", "P"} }, + [4] = {0xffffffff, 0xffffffff, {"Q", "R", "S", "T"} }, + [5] = {0xffffffff, 0x0000ffff, {"U", "V", "W", "X"} }, + [6] = {0xffff0000, 0x0fff0000, {"Y", "Z", "", ""} }, +}; + +static GPIOSetProperties ast2600_1_8v_set_props[] = { + [0] = {0xffffffff, 0xffffffff, {"18A", "18B", "18C", "18D"} }, + [1] = {0x0000000f, 0x0000000f, {"18E"} }, +}; + static const MemoryRegionOps aspeed_gpio_ops = { .read = aspeed_gpio_read, .write = aspeed_gpio_write, @@ -758,7 +854,7 @@ static void aspeed_gpio_realize(DeviceState *dev, Error **errp) } memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_gpio_ops, s, - TYPE_ASPEED_GPIO, GPIO_3_6V_MEM_SIZE); + TYPE_ASPEED_GPIO, GPIO_MAX_MEM_SIZE); sysbus_init_mmio(sbd, &s->iomem); } @@ -851,6 +947,26 @@ static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data) agc->reg_table = aspeed_3_6v_gpios; } +static void aspeed_gpio_ast2600_3_6v_class_init(ObjectClass *klass, void *data) +{ + AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); + + agc->props = ast2600_3_6v_set_props; + agc->nr_gpio_pins = 208; + agc->nr_gpio_sets = 7; + agc->reg_table = aspeed_3_6v_gpios; +} + +static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data) +{ + AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); + + agc->props = ast2600_1_8v_set_props; + agc->nr_gpio_pins = 36; + agc->nr_gpio_sets = 2; + agc->reg_table = aspeed_1_8v_gpios; +} + static const TypeInfo aspeed_gpio_info = { .name = TYPE_ASPEED_GPIO, .parent = TYPE_SYS_BUS_DEVICE, @@ -874,11 +990,27 @@ static const TypeInfo aspeed_gpio_ast2500_info = { .instance_init = aspeed_gpio_init, }; +static const TypeInfo aspeed_gpio_ast2600_3_6v_info = { + .name = TYPE_ASPEED_GPIO "-ast2600", + .parent = TYPE_ASPEED_GPIO, + .class_init = aspeed_gpio_ast2600_3_6v_class_init, + .instance_init = aspeed_gpio_init, +}; + +static const TypeInfo aspeed_gpio_ast2600_1_8v_info = { + .name = TYPE_ASPEED_GPIO "-ast2600-1_8v", + .parent = TYPE_ASPEED_GPIO, + .class_init = aspeed_gpio_ast2600_1_8v_class_init, + .instance_init = aspeed_gpio_init, +}; + static void aspeed_gpio_register_types(void) { type_register_static(&aspeed_gpio_info); type_register_static(&aspeed_gpio_ast2400_info); type_register_static(&aspeed_gpio_ast2500_info); + type_register_static(&aspeed_gpio_ast2600_3_6v_info); + type_register_static(&aspeed_gpio_ast2600_1_8v_info); } type_init(aspeed_gpio_register_types); diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index a956eb3..06c119f 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -145,10 +145,12 @@ static inline bool aspeed_i2c_bus_is_enabled(AspeedI2CBus *bus) static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus) { + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + bus->intr_status &= bus->intr_ctrl; if (bus->intr_status) { bus->controller->intr_status |= 1 << bus->id; - qemu_irq_raise(bus->controller->irq); + qemu_irq_raise(aic->bus_get_irq(bus)); } } @@ -273,6 +275,7 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { AspeedI2CBus *bus = opaque; + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); bool handle_rx; switch (offset) { @@ -299,7 +302,7 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, bus->intr_status &= ~(value & 0x7FFF); if (!bus->intr_status) { bus->controller->intr_status &= ~(1 << bus->id); - qemu_irq_lower(bus->controller->irq); + qemu_irq_lower(aic->bus_get_irq(bus)); } if (handle_rx && (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST))) { aspeed_i2c_handle_rx_cmd(bus); @@ -408,10 +411,11 @@ static void aspeed_i2c_reset(DeviceState *dev) { int i; AspeedI2CState *s = ASPEED_I2C(dev); + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); s->intr_status = 0; - for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) { + for (i = 0; i < aic->num_busses; i++) { s->busses[i].intr_ctrl = 0; s->busses[i].intr_status = 0; s->busses[i].cmd = 0; @@ -421,7 +425,7 @@ static void aspeed_i2c_reset(DeviceState *dev) } /* - * Address Definitions + * Address Definitions (AST2400 and AST2500) * * 0x000 ... 0x03F: Global Register * 0x040 ... 0x07F: Device 1 @@ -446,22 +450,26 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) int i; SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedI2CState *s = ASPEED_I2C(dev); + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); sysbus_init_irq(sbd, &s->irq); memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i2c_ctrl_ops, s, "aspeed.i2c", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) { - char name[16]; - int offset = i < 7 ? 1 : 5; + for (i = 0; i < aic->num_busses; i++) { + char name[32]; + int offset = i < aic->gap ? 1 : 5; + + sysbus_init_irq(sbd, &s->busses[i].irq); snprintf(name, sizeof(name), "aspeed.i2c.%d", i); s->busses[i].controller = s; s->busses[i].id = i; s->busses[i].bus = i2c_init_bus(dev, name); memory_region_init_io(&s->busses[i].mr, OBJECT(dev), - &aspeed_i2c_bus_ops, &s->busses[i], name, 0x40); - memory_region_add_subregion(&s->iomem, 0x40 * (i + offset), + &aspeed_i2c_bus_ops, &s->busses[i], name, + aic->reg_size); + memory_region_add_subregion(&s->iomem, aic->reg_size * (i + offset), &s->busses[i].mr); } } @@ -481,11 +489,88 @@ static const TypeInfo aspeed_i2c_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedI2CState), .class_init = aspeed_i2c_class_init, + .class_size = sizeof(AspeedI2CClass), + .abstract = true, +}; + +static qemu_irq aspeed_2400_i2c_bus_get_irq(AspeedI2CBus *bus) +{ + return bus->controller->irq; +} + +static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); + + dc->desc = "ASPEED 2400 I2C Controller"; + + aic->num_busses = 14; + aic->reg_size = 0x40; + aic->gap = 7; + aic->bus_get_irq = aspeed_2400_i2c_bus_get_irq; +} + +static const TypeInfo aspeed_2400_i2c_info = { + .name = TYPE_ASPEED_2400_I2C, + .parent = TYPE_ASPEED_I2C, + .class_init = aspeed_2400_i2c_class_init, +}; + +static qemu_irq aspeed_2500_i2c_bus_get_irq(AspeedI2CBus *bus) +{ + return bus->controller->irq; +} + +static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); + + dc->desc = "ASPEED 2500 I2C Controller"; + + aic->num_busses = 14; + aic->reg_size = 0x40; + aic->gap = 7; + aic->bus_get_irq = aspeed_2500_i2c_bus_get_irq; +} + +static const TypeInfo aspeed_2500_i2c_info = { + .name = TYPE_ASPEED_2500_I2C, + .parent = TYPE_ASPEED_I2C, + .class_init = aspeed_2500_i2c_class_init, +}; + +static qemu_irq aspeed_2600_i2c_bus_get_irq(AspeedI2CBus *bus) +{ + return bus->irq; +} + +static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); + + dc->desc = "ASPEED 2600 I2C Controller"; + + aic->num_busses = 16; + aic->reg_size = 0x80; + aic->gap = -1; /* no gap */ + aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; +} + +static const TypeInfo aspeed_2600_i2c_info = { + .name = TYPE_ASPEED_2600_I2C, + .parent = TYPE_ASPEED_I2C, + .class_init = aspeed_2600_i2c_class_init, }; static void aspeed_i2c_register_types(void) { type_register_static(&aspeed_i2c_info); + type_register_static(&aspeed_2400_i2c_info); + type_register_static(&aspeed_2500_i2c_info); + type_register_static(&aspeed_2600_i2c_info); } type_init(aspeed_i2c_register_types) @@ -494,9 +579,10 @@ type_init(aspeed_i2c_register_types) I2CBus *aspeed_i2c_get_bus(DeviceState *dev, int busnr) { AspeedI2CState *s = ASPEED_I2C(dev); + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); I2CBus *bus = NULL; - if (busnr >= 0 && busnr < ASPEED_I2C_NR_BUSSES) { + if (busnr >= 0 && busnr < aic->num_busses) { bus = s->busses[busnr].bus; } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 4e0f9f4..1d077a7 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1888,7 +1888,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02"); } - build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0", "\\_GPE._E03"); + + if (pcms->memhp_io_base && nr_mem) { + build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0", + "\\_GPE._E03", AML_SYSTEM_IO, + pcms->memhp_io_base); + } scope = aml_scope("_GPE"); { diff --git a/hw/i386/pc.c b/hw/i386/pc.c index bcda50e..4b19042 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1766,6 +1766,9 @@ void pc_memory_init(PCMachineState *pcms, /* Init default IOAPIC address space */ pcms->ioapic_as = &address_space_memory; + + /* Init ACPI memory hotplug IO base address */ + pcms->memhp_io_base = ACPI_MEMORY_HOTPLUG_BASE; } /* diff --git a/hw/ide/core.c b/hw/ide/core.c index e6e54c6..754ff4d 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -442,6 +442,14 @@ static void ide_issue_trim_cb(void *opaque, int ret) TrimAIOCB *iocb = opaque; IDEState *s = iocb->s; + if (iocb->i >= 0) { + if (ret >= 0) { + block_acct_done(blk_get_stats(s->blk), &s->acct); + } else { + block_acct_failed(blk_get_stats(s->blk), &s->acct); + } + } + if (ret >= 0) { while (iocb->j < iocb->qiov->niov) { int j = iocb->j; @@ -459,10 +467,14 @@ static void ide_issue_trim_cb(void *opaque, int ret) } if (!ide_sect_range_ok(s, sector, count)) { + block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_UNMAP); iocb->ret = -EINVAL; goto done; } + block_acct_start(blk_get_stats(s->blk), &s->acct, + count << BDRV_SECTOR_BITS, BLOCK_ACCT_UNMAP); + /* Got an entry! Submit and exit. */ iocb->aiocb = blk_aio_pdiscard(s->blk, sector << BDRV_SECTOR_BITS, diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index b56fda1..9deb15e 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -55,7 +55,7 @@ void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level) * has separate fields in the irq number for type, * CPU number and interrupt number. */ - int kvm_irq, irqtype, cpu; + int irqtype, cpu; if (irq < (num_irq - GIC_INTERNAL)) { /* External interrupt. The kernel numbers these like the GIC @@ -72,10 +72,7 @@ void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level) cpu = irq / GIC_INTERNAL; irq %= GIC_INTERNAL; } - kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT) - | (cpu << KVM_ARM_IRQ_VCPU_SHIFT) | irq; - - kvm_set_irq(kvm_state, kvm_irq, !!level); + kvm_arm_set_irq(cpu, irqtype, irq, !!level); } static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level) diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c index 04229b8..61f884f 100644 --- a/hw/intc/bcm2836_control.c +++ b/hw/intc/bcm2836_control.c @@ -264,7 +264,7 @@ static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size) } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) { return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2]; } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx"\n", __func__, offset); return 0; } @@ -293,8 +293,9 @@ static void bcm2836_control_write(void *opaque, hwaddr offset, } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) { s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val; } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", - __func__, offset); + qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx + " value 0x%"PRIx64"\n", + __func__, offset, val); return; } diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index a9c2c95..a490963 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -141,7 +141,7 @@ static m5206_timer_state *m5206_timer_init(qemu_irq irq) s = g_new0(m5206_timer_state, 1); bh = qemu_bh_new(m5206_timer_trigger, s); - s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT); s->irq = irq; m5206_timer_reset(s); return s; diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 60c5802..34d34eb 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -192,7 +192,7 @@ static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic) for (i = 0; i < 2; i++) { s = g_new0(m5208_timer_state, 1); bh = qemu_bh_new(m5208_timer_trigger, s); - s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT); memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s, "m5208-timer", 0x00004000); memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i, diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 620b25c..717509b 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -88,6 +88,36 @@ #define BMC_REV TO_REG(0x19C) #define BMC_DEV_ID TO_REG(0x1A4) +#define AST2600_PROT_KEY TO_REG(0x00) +#define AST2600_SILICON_REV TO_REG(0x04) +#define AST2600_SILICON_REV2 TO_REG(0x14) +#define AST2600_SYS_RST_CTRL TO_REG(0x40) +#define AST2600_SYS_RST_CTRL_CLR TO_REG(0x44) +#define AST2600_SYS_RST_CTRL2 TO_REG(0x50) +#define AST2600_SYS_RST_CTRL2_CLR TO_REG(0x54) +#define AST2600_CLK_STOP_CTRL TO_REG(0x80) +#define AST2600_CLK_STOP_CTRL_CLR TO_REG(0x84) +#define AST2600_CLK_STOP_CTRL2 TO_REG(0x90) +#define AST2600_CLK_STOP_CTR2L_CLR TO_REG(0x94) +#define AST2600_SDRAM_HANDSHAKE TO_REG(0x100) +#define AST2600_HPLL_PARAM TO_REG(0x200) +#define AST2600_HPLL_EXT TO_REG(0x204) +#define AST2600_MPLL_EXT TO_REG(0x224) +#define AST2600_EPLL_EXT TO_REG(0x244) +#define AST2600_CLK_SEL TO_REG(0x300) +#define AST2600_CLK_SEL2 TO_REG(0x304) +#define AST2600_CLK_SEL3 TO_REG(0x310) +#define AST2600_HW_STRAP1 TO_REG(0x500) +#define AST2600_HW_STRAP1_CLR TO_REG(0x504) +#define AST2600_HW_STRAP1_PROT TO_REG(0x508) +#define AST2600_HW_STRAP2 TO_REG(0x510) +#define AST2600_HW_STRAP2_CLR TO_REG(0x514) +#define AST2600_HW_STRAP2_PROT TO_REG(0x518) +#define AST2600_RNG_CTRL TO_REG(0x524) +#define AST2600_RNG_DATA TO_REG(0x540) + +#define AST2600_CLK TO_REG(0x40) + #define SCU_IO_REGION_SIZE 0x1000 static const uint32_t ast2400_a0_resets[ASPEED_SCU_NR_REGS] = { @@ -178,7 +208,7 @@ static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) AspeedSCUState *s = ASPEED_SCU(opaque); int reg = TO_REG(offset); - if (reg >= ARRAY_SIZE(s->regs)) { + if (reg >= ASPEED_SCU_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -208,7 +238,7 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data, AspeedSCUState *s = ASPEED_SCU(opaque); int reg = TO_REG(offset); - if (reg >= ARRAY_SIZE(s->regs)) { + if (reg >= ASPEED_SCU_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -346,7 +376,7 @@ static void aspeed_scu_reset(DeviceState *dev) AspeedSCUState *s = ASPEED_SCU(dev); AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev); - memcpy(s->regs, asc->resets, sizeof(s->regs)); + memcpy(s->regs, asc->resets, asc->nr_regs * 4); s->regs[SILICON_REV] = s->silicon_rev; s->regs[HW_STRAP1] = s->hw_strap1; s->regs[HW_STRAP2] = s->hw_strap2; @@ -358,6 +388,7 @@ static uint32_t aspeed_silicon_revs[] = { AST2400_A1_SILICON_REV, AST2500_A0_SILICON_REV, AST2500_A1_SILICON_REV, + AST2600_A0_SILICON_REV, }; bool is_supported_silicon_rev(uint32_t silicon_rev) @@ -377,6 +408,7 @@ static void aspeed_scu_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedSCUState *s = ASPEED_SCU(dev); + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev); if (!is_supported_silicon_rev(s->silicon_rev)) { error_setg(errp, "Unknown silicon revision: 0x%" PRIx32, @@ -384,7 +416,7 @@ static void aspeed_scu_realize(DeviceState *dev, Error **errp) return; } - memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_scu_ops, s, + memory_region_init_io(&s->iomem, OBJECT(s), asc->ops, s, TYPE_ASPEED_SCU, SCU_IO_REGION_SIZE); sysbus_init_mmio(sbd, &s->iomem); @@ -392,10 +424,10 @@ static void aspeed_scu_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_aspeed_scu = { .name = "aspeed.scu", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, AspeedSCUState, ASPEED_SCU_NR_REGS), + VMSTATE_UINT32_ARRAY(regs, AspeedSCUState, ASPEED_AST2600_SCU_NR_REGS), VMSTATE_END_OF_LIST() } }; @@ -436,6 +468,8 @@ static void aspeed_2400_scu_class_init(ObjectClass *klass, void *data) asc->resets = ast2400_a0_resets; asc->calc_hpll = aspeed_2400_scu_calc_hpll; asc->apb_divider = 2; + asc->nr_regs = ASPEED_SCU_NR_REGS; + asc->ops = &aspeed_scu_ops; } static const TypeInfo aspeed_2400_scu_info = { @@ -454,6 +488,8 @@ static void aspeed_2500_scu_class_init(ObjectClass *klass, void *data) asc->resets = ast2500_a1_resets; asc->calc_hpll = aspeed_2500_scu_calc_hpll; asc->apb_divider = 4; + asc->nr_regs = ASPEED_SCU_NR_REGS; + asc->ops = &aspeed_scu_ops; } static const TypeInfo aspeed_2500_scu_info = { @@ -463,11 +499,155 @@ static const TypeInfo aspeed_2500_scu_info = { .class_init = aspeed_2500_scu_class_init, }; +static uint64_t aspeed_ast2600_scu_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + + if (reg >= ASPEED_AST2600_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + + switch (reg) { + case AST2600_HPLL_EXT: + case AST2600_EPLL_EXT: + case AST2600_MPLL_EXT: + /* PLLs are always "locked" */ + return s->regs[reg] | BIT(31); + case AST2600_RNG_DATA: + /* + * On hardware, RNG_DATA works regardless of the state of the + * enable bit in RNG_CTRL + * + * TODO: Check this is true for ast2600 + */ + s->regs[AST2600_RNG_DATA] = aspeed_scu_get_random(); + break; + } + + return s->regs[reg]; +} + +static void aspeed_ast2600_scu_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + + if (reg >= ASPEED_AST2600_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + if (reg > PROT_KEY && !s->regs[PROT_KEY]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: SCU is locked!\n", __func__); + } + + trace_aspeed_scu_write(offset, size, data); + + switch (reg) { + case AST2600_PROT_KEY: + s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0; + return; + case AST2600_HW_STRAP1: + case AST2600_HW_STRAP2: + if (s->regs[reg + 2]) { + return; + } + /* fall through */ + case AST2600_SYS_RST_CTRL: + case AST2600_SYS_RST_CTRL2: + /* W1S (Write 1 to set) registers */ + s->regs[reg] |= data; + return; + case AST2600_SYS_RST_CTRL_CLR: + case AST2600_SYS_RST_CTRL2_CLR: + case AST2600_HW_STRAP1_CLR: + case AST2600_HW_STRAP2_CLR: + /* W1C (Write 1 to clear) registers */ + s->regs[reg] &= ~data; + return; + + case AST2600_RNG_DATA: + case AST2600_SILICON_REV: + case AST2600_SILICON_REV2: + /* Add read only registers here */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write to read-only offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + s->regs[reg] = data; +} + +static const MemoryRegionOps aspeed_ast2600_scu_ops = { + .read = aspeed_ast2600_scu_read, + .write = aspeed_ast2600_scu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static const uint32_t ast2600_a0_resets[ASPEED_AST2600_SCU_NR_REGS] = { + [AST2600_SILICON_REV] = AST2600_SILICON_REV, + [AST2600_SILICON_REV2] = AST2600_SILICON_REV, + [AST2600_SYS_RST_CTRL] = 0xF7CFFEDC | 0x100, + [AST2600_SYS_RST_CTRL2] = 0xFFFFFFFC, + [AST2600_CLK_STOP_CTRL] = 0xEFF43E8B, + [AST2600_CLK_STOP_CTRL2] = 0xFFF0FFF0, + [AST2600_SDRAM_HANDSHAKE] = 0x00000040, /* SoC completed DRAM init */ + [AST2600_HPLL_PARAM] = 0x1000405F, +}; + +static void aspeed_ast2600_scu_reset(DeviceState *dev) +{ + AspeedSCUState *s = ASPEED_SCU(dev); + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev); + + memcpy(s->regs, asc->resets, asc->nr_regs * 4); + + s->regs[AST2600_SILICON_REV] = s->silicon_rev; + s->regs[AST2600_SILICON_REV2] = s->silicon_rev; + s->regs[AST2600_HW_STRAP1] = s->hw_strap1; + s->regs[AST2600_HW_STRAP2] = s->hw_strap2; + s->regs[PROT_KEY] = s->hw_prot_key; +} + +static void aspeed_2600_scu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); + + dc->desc = "ASPEED 2600 System Control Unit"; + dc->reset = aspeed_ast2600_scu_reset; + asc->resets = ast2600_a0_resets; + asc->calc_hpll = aspeed_2500_scu_calc_hpll; /* No change since AST2500 */ + asc->apb_divider = 4; + asc->nr_regs = ASPEED_AST2600_SCU_NR_REGS; + asc->ops = &aspeed_ast2600_scu_ops; +} + +static const TypeInfo aspeed_2600_scu_info = { + .name = TYPE_ASPEED_2600_SCU, + .parent = TYPE_ASPEED_SCU, + .instance_size = sizeof(AspeedSCUState), + .class_init = aspeed_2600_scu_class_init, +}; + static void aspeed_scu_register_types(void) { type_register_static(&aspeed_scu_info); type_register_static(&aspeed_2400_scu_info); type_register_static(&aspeed_2500_scu_info); + type_register_static(&aspeed_2600_scu_info); } type_init(aspeed_scu_register_types); diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index cb13c63..f3a63a2 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -28,6 +28,7 @@ /* Control/Status Register #1 (ast2500) */ #define R_STATUS1 (0x60 / 4) #define PHY_BUSY_STATE BIT(0) +#define PHY_PLL_LOCK_STATUS BIT(4) #define R_ECC_TEST_CTRL (0x70 / 4) #define ECC_TEST_FINISHED BIT(12) @@ -85,6 +86,11 @@ #define ASPEED_SDMC_AST2500_512MB 0x2 #define ASPEED_SDMC_AST2500_1024MB 0x3 +#define ASPEED_SDMC_AST2600_256MB 0x0 +#define ASPEED_SDMC_AST2600_512MB 0x1 +#define ASPEED_SDMC_AST2600_1024MB 0x2 +#define ASPEED_SDMC_AST2600_2048MB 0x3 + #define ASPEED_SDMC_AST2500_READONLY_MASK \ (ASPEED_SDMC_HW_VERSION(0xf) | ASPEED_SDMC_CACHE_INITIAL_DONE | \ ASPEED_SDMC_AST2500_RESERVED | ASPEED_SDMC_VGA_COMPAT | \ @@ -110,6 +116,7 @@ static void aspeed_sdmc_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { AspeedSDMCState *s = ASPEED_SDMC(opaque); + AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s); addr >>= 2; @@ -130,41 +137,7 @@ static void aspeed_sdmc_write(void *opaque, hwaddr addr, uint64_t data, return; } - if (addr == R_CONF) { - /* Make sure readonly bits are kept */ - switch (s->silicon_rev) { - case AST2400_A0_SILICON_REV: - case AST2400_A1_SILICON_REV: - data &= ~ASPEED_SDMC_READONLY_MASK; - data |= s->fixed_conf; - break; - case AST2500_A0_SILICON_REV: - case AST2500_A1_SILICON_REV: - data &= ~ASPEED_SDMC_AST2500_READONLY_MASK; - data |= s->fixed_conf; - break; - default: - g_assert_not_reached(); - } - } - if (s->silicon_rev == AST2500_A0_SILICON_REV || - s->silicon_rev == AST2500_A1_SILICON_REV) { - switch (addr) { - case R_STATUS1: - /* Will never return 'busy' */ - data &= ~PHY_BUSY_STATE; - break; - case R_ECC_TEST_CTRL: - /* Always done, always happy */ - data |= ECC_TEST_FINISHED; - data &= ~ECC_TEST_FAIL; - break; - default: - break; - } - } - - s->regs[addr] = data; + asc->write(s, addr, data); } static const MemoryRegionOps aspeed_sdmc_ops = { @@ -219,47 +192,46 @@ static int ast2500_rambits(AspeedSDMCState *s) return ASPEED_SDMC_AST2500_512MB; } +static int ast2600_rambits(AspeedSDMCState *s) +{ + switch (s->ram_size >> 20) { + case 256: + return ASPEED_SDMC_AST2600_256MB; + case 512: + return ASPEED_SDMC_AST2600_512MB; + case 1024: + return ASPEED_SDMC_AST2600_1024MB; + case 2048: + return ASPEED_SDMC_AST2600_2048MB; + default: + break; + } + + /* use a common default */ + warn_report("Invalid RAM size 0x%" PRIx64 ". Using default 512M", + s->ram_size); + s->ram_size = 512 << 20; + return ASPEED_SDMC_AST2600_512MB; +} + static void aspeed_sdmc_reset(DeviceState *dev) { AspeedSDMCState *s = ASPEED_SDMC(dev); + AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s); memset(s->regs, 0, sizeof(s->regs)); /* Set ram size bit and defaults values */ - s->regs[R_CONF] = s->fixed_conf; + s->regs[R_CONF] = asc->compute_conf(s, 0); } static void aspeed_sdmc_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedSDMCState *s = ASPEED_SDMC(dev); + AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s); - if (!is_supported_silicon_rev(s->silicon_rev)) { - error_setg(errp, "Unknown silicon revision: 0x%" PRIx32, - s->silicon_rev); - return; - } - - switch (s->silicon_rev) { - case AST2400_A0_SILICON_REV: - case AST2400_A1_SILICON_REV: - s->ram_bits = ast2400_rambits(s); - s->max_ram_size = 512 << 20; - s->fixed_conf = ASPEED_SDMC_VGA_COMPAT | - ASPEED_SDMC_DRAM_SIZE(s->ram_bits); - break; - case AST2500_A0_SILICON_REV: - case AST2500_A1_SILICON_REV: - s->ram_bits = ast2500_rambits(s); - s->max_ram_size = 1024 << 20; - s->fixed_conf = ASPEED_SDMC_HW_VERSION(1) | - ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB) | - ASPEED_SDMC_CACHE_INITIAL_DONE | - ASPEED_SDMC_DRAM_SIZE(s->ram_bits); - break; - default: - g_assert_not_reached(); - } + s->max_ram_size = asc->max_ram_size; memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sdmc_ops, s, TYPE_ASPEED_SDMC, 0x1000); @@ -277,7 +249,6 @@ static const VMStateDescription vmstate_aspeed_sdmc = { }; static Property aspeed_sdmc_properties[] = { - DEFINE_PROP_UINT32("silicon-rev", AspeedSDMCState, silicon_rev, 0), DEFINE_PROP_UINT64("ram-size", AspeedSDMCState, ram_size, 0), DEFINE_PROP_UINT64("max-ram-size", AspeedSDMCState, max_ram_size, 0), DEFINE_PROP_END_OF_LIST(), @@ -298,11 +269,164 @@ static const TypeInfo aspeed_sdmc_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedSDMCState), .class_init = aspeed_sdmc_class_init, + .class_size = sizeof(AspeedSDMCClass), + .abstract = true, +}; + +static uint32_t aspeed_2400_sdmc_compute_conf(AspeedSDMCState *s, uint32_t data) +{ + uint32_t fixed_conf = ASPEED_SDMC_VGA_COMPAT | + ASPEED_SDMC_DRAM_SIZE(ast2400_rambits(s)); + + /* Make sure readonly bits are kept */ + data &= ~ASPEED_SDMC_READONLY_MASK; + + return data | fixed_conf; +} + +static void aspeed_2400_sdmc_write(AspeedSDMCState *s, uint32_t reg, + uint32_t data) +{ + switch (reg) { + case R_CONF: + data = aspeed_2400_sdmc_compute_conf(s, data); + break; + default: + break; + } + + s->regs[reg] = data; +} + +static void aspeed_2400_sdmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); + + dc->desc = "ASPEED 2400 SDRAM Memory Controller"; + asc->max_ram_size = 512 << 20; + asc->compute_conf = aspeed_2400_sdmc_compute_conf; + asc->write = aspeed_2400_sdmc_write; +} + +static const TypeInfo aspeed_2400_sdmc_info = { + .name = TYPE_ASPEED_2400_SDMC, + .parent = TYPE_ASPEED_SDMC, + .class_init = aspeed_2400_sdmc_class_init, +}; + +static uint32_t aspeed_2500_sdmc_compute_conf(AspeedSDMCState *s, uint32_t data) +{ + uint32_t fixed_conf = ASPEED_SDMC_HW_VERSION(1) | + ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB) | + ASPEED_SDMC_CACHE_INITIAL_DONE | + ASPEED_SDMC_DRAM_SIZE(ast2500_rambits(s)); + + /* Make sure readonly bits are kept */ + data &= ~ASPEED_SDMC_AST2500_READONLY_MASK; + + return data | fixed_conf; +} + +static void aspeed_2500_sdmc_write(AspeedSDMCState *s, uint32_t reg, + uint32_t data) +{ + switch (reg) { + case R_CONF: + data = aspeed_2500_sdmc_compute_conf(s, data); + break; + case R_STATUS1: + /* Will never return 'busy' */ + data &= ~PHY_BUSY_STATE; + break; + case R_ECC_TEST_CTRL: + /* Always done, always happy */ + data |= ECC_TEST_FINISHED; + data &= ~ECC_TEST_FAIL; + break; + default: + break; + } + + s->regs[reg] = data; +} + +static void aspeed_2500_sdmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); + + dc->desc = "ASPEED 2500 SDRAM Memory Controller"; + asc->max_ram_size = 1024 << 20; + asc->compute_conf = aspeed_2500_sdmc_compute_conf; + asc->write = aspeed_2500_sdmc_write; +} + +static const TypeInfo aspeed_2500_sdmc_info = { + .name = TYPE_ASPEED_2500_SDMC, + .parent = TYPE_ASPEED_SDMC, + .class_init = aspeed_2500_sdmc_class_init, +}; + +static uint32_t aspeed_2600_sdmc_compute_conf(AspeedSDMCState *s, uint32_t data) +{ + uint32_t fixed_conf = ASPEED_SDMC_HW_VERSION(3) | + ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB) | + ASPEED_SDMC_DRAM_SIZE(ast2600_rambits(s)); + + /* Make sure readonly bits are kept (use ast2500 mask) */ + data &= ~ASPEED_SDMC_AST2500_READONLY_MASK; + + return data | fixed_conf; +} + +static void aspeed_2600_sdmc_write(AspeedSDMCState *s, uint32_t reg, + uint32_t data) +{ + switch (reg) { + case R_CONF: + data = aspeed_2600_sdmc_compute_conf(s, data); + break; + case R_STATUS1: + /* Will never return 'busy'. 'lock status' is always set */ + data &= ~PHY_BUSY_STATE; + data |= PHY_PLL_LOCK_STATUS; + break; + case R_ECC_TEST_CTRL: + /* Always done, always happy */ + data |= ECC_TEST_FINISHED; + data &= ~ECC_TEST_FAIL; + break; + default: + break; + } + + s->regs[reg] = data; +} + +static void aspeed_2600_sdmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); + + dc->desc = "ASPEED 2600 SDRAM Memory Controller"; + asc->max_ram_size = 2048 << 20; + asc->compute_conf = aspeed_2600_sdmc_compute_conf; + asc->write = aspeed_2600_sdmc_write; +} + +static const TypeInfo aspeed_2600_sdmc_info = { + .name = TYPE_ASPEED_2600_SDMC, + .parent = TYPE_ASPEED_SDMC, + .class_init = aspeed_2600_sdmc_class_init, }; static void aspeed_sdmc_register_types(void) { type_register_static(&aspeed_sdmc_info); + type_register_static(&aspeed_2400_sdmc_info); + type_register_static(&aspeed_2500_sdmc_info); + type_register_static(&aspeed_2600_sdmc_info); } type_init(aspeed_sdmc_register_types); diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c index 79bad11..77d2d80 100644 --- a/hw/misc/bcm2835_mbox.c +++ b/hw/misc/bcm2835_mbox.c @@ -15,6 +15,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" +#include "trace.h" #define MAIL0_PEEK 0x90 #define MAIL0_SENDER 0x94 @@ -123,6 +124,7 @@ static void bcm2835_mbox_update(BCM2835MboxState *s) set = true; } } + trace_bcm2835_mbox_irq(set); qemu_set_irq(s->arm_irq, set); } @@ -176,10 +178,12 @@ static uint64_t bcm2835_mbox_read(void *opaque, hwaddr offset, unsigned size) break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx"\n", __func__, offset); + trace_bcm2835_mbox_read(size, offset, res); return 0; } + trace_bcm2835_mbox_read(size, offset, res); bcm2835_mbox_update(s); @@ -195,6 +199,7 @@ static void bcm2835_mbox_write(void *opaque, hwaddr offset, offset &= 0xff; + trace_bcm2835_mbox_write(size, offset, value); switch (offset) { case MAIL0_SENDER: break; @@ -228,8 +233,9 @@ static void bcm2835_mbox_write(void *opaque, hwaddr offset, break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", - __func__, offset); + qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx + " value 0x%"PRIx64"\n", + __func__, offset, value); return; } @@ -310,7 +316,7 @@ static void bcm2835_mbox_realize(DeviceState *dev, Error **errp) } s->mbox_mr = MEMORY_REGION(obj); - address_space_init(&s->mbox_as, s->mbox_mr, NULL); + address_space_init(&s->mbox_as, s->mbox_mr, TYPE_BCM2835_MBOX "-memory"); bcm2835_mbox_reset(dev); } diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index d86d510..0eea2e2 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -13,6 +13,7 @@ #include "sysemu/dma.h" #include "qemu/log.h" #include "qemu/module.h" +#include "trace.h" /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ @@ -56,7 +57,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; case 0x00010001: /* Get board model */ qemu_log_mask(LOG_UNIMP, - "bcm2835_property: %x get board model NYI\n", tag); + "bcm2835_property: 0x%08x get board model NYI\n", + tag); resplen = 4; break; case 0x00010002: /* Get board revision */ @@ -69,7 +71,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; case 0x00010004: /* Get board serial */ qemu_log_mask(LOG_UNIMP, - "bcm2835_property: %x get board serial NYI\n", tag); + "bcm2835_property: 0x%08x get board serial NYI\n", + tag); resplen = 8; break; case 0x00010005: /* Get ARM memory */ @@ -104,7 +107,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00038001: /* Set clock state */ qemu_log_mask(LOG_UNIMP, - "bcm2835_property: %x set clock state NYI\n", tag); + "bcm2835_property: 0x%08x set clock state NYI\n", + tag); resplen = 8; break; @@ -129,7 +133,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00038004: /* Set max clock rate */ case 0x00038007: /* Set min clock rate */ qemu_log_mask(LOG_UNIMP, - "bcm2835_property: %x set clock rates NYI\n", tag); + "bcm2835_property: 0x%08x set clock rate NYI\n", + tag); resplen = 8; break; @@ -274,11 +279,12 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; default: - qemu_log_mask(LOG_GUEST_ERROR, - "bcm2835_property: unhandled tag %08x\n", tag); + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: unhandled tag 0x%08x\n", tag); break; } + trace_bcm2835_mbox_property(tag, bufsize, resplen); if (tag == 0) { break; } @@ -403,7 +409,7 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) } s->dma_mr = MEMORY_REGION(obj); - address_space_init(&s->dma_as, s->dma_mr, NULL); + address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory"); /* TODO: connect to MAC address of USB NIC device, once we emulate it */ qemu_macaddr_default_if_unset(&s->macaddr); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 7427622..1deb1d0 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -143,3 +143,9 @@ armsse_mhu_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU wri # aspeed_xdma.c aspeed_xdma_write(uint64_t offset, uint64_t data) "XDMA write: offset 0x%" PRIx64 " data 0x%" PRIx64 + +# bcm2835_mbox.c +bcm2835_mbox_write(unsigned int size, uint64_t addr, uint64_t value) "mbox write sz:%u addr:0x%"PRIx64" data:0x%"PRIx64 +bcm2835_mbox_read(unsigned int size, uint64_t addr, uint64_t value) "mbox read sz:%u addr:0x%"PRIx64" data:0x%"PRIx64 +bcm2835_mbox_irq(unsigned level) "mbox irq:ARM level:%u" +bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu" diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 8451c17..d9b3e8c 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -393,7 +393,7 @@ static void etsec_realize(DeviceState *dev, Error **errp) etsec->bh = qemu_bh_new(etsec_timer_hit, etsec); - etsec->ptimer = ptimer_init(etsec->bh, PTIMER_POLICY_DEFAULT); + etsec->ptimer = ptimer_init_with_bh(etsec->bh, PTIMER_POLICY_DEFAULT); ptimer_set_freq(etsec->ptimer, 100); } diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 04c78e8..eb8b441 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -15,6 +15,7 @@ #include "hw/irq.h" #include "hw/net/ftgmac100.h" #include "sysemu/dma.h" +#include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" #include "net/checksum.h" @@ -1087,9 +1088,170 @@ static const TypeInfo ftgmac100_info = { .class_init = ftgmac100_class_init, }; +/* + * AST2600 MII controller + */ +#define ASPEED_MII_PHYCR_FIRE BIT(31) +#define ASPEED_MII_PHYCR_ST_22 BIT(28) +#define ASPEED_MII_PHYCR_OP(x) ((x) & (ASPEED_MII_PHYCR_OP_WRITE | \ + ASPEED_MII_PHYCR_OP_READ)) +#define ASPEED_MII_PHYCR_OP_WRITE BIT(26) +#define ASPEED_MII_PHYCR_OP_READ BIT(27) +#define ASPEED_MII_PHYCR_DATA(x) (x & 0xffff) +#define ASPEED_MII_PHYCR_PHY(x) (((x) >> 21) & 0x1f) +#define ASPEED_MII_PHYCR_REG(x) (((x) >> 16) & 0x1f) + +#define ASPEED_MII_PHYDATA_IDLE BIT(16) + +static void aspeed_mii_transition(AspeedMiiState *s, bool fire) +{ + if (fire) { + s->phycr |= ASPEED_MII_PHYCR_FIRE; + s->phydata &= ~ASPEED_MII_PHYDATA_IDLE; + } else { + s->phycr &= ~ASPEED_MII_PHYCR_FIRE; + s->phydata |= ASPEED_MII_PHYDATA_IDLE; + } +} + +static void aspeed_mii_do_phy_ctl(AspeedMiiState *s) +{ + uint8_t reg; + uint16_t data; + + if (!(s->phycr & ASPEED_MII_PHYCR_ST_22)) { + aspeed_mii_transition(s, !ASPEED_MII_PHYCR_FIRE); + qemu_log_mask(LOG_UNIMP, "%s: unsupported ST code\n", __func__); + return; + } + + /* Nothing to do */ + if (!(s->phycr & ASPEED_MII_PHYCR_FIRE)) { + return; + } + + reg = ASPEED_MII_PHYCR_REG(s->phycr); + data = ASPEED_MII_PHYCR_DATA(s->phycr); + + switch (ASPEED_MII_PHYCR_OP(s->phycr)) { + case ASPEED_MII_PHYCR_OP_WRITE: + do_phy_write(s->nic, reg, data); + break; + case ASPEED_MII_PHYCR_OP_READ: + s->phydata = (s->phydata & ~0xffff) | do_phy_read(s->nic, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid OP code %08x\n", + __func__, s->phycr); + } + + aspeed_mii_transition(s, !ASPEED_MII_PHYCR_FIRE); +} + +static uint64_t aspeed_mii_read(void *opaque, hwaddr addr, unsigned size) +{ + AspeedMiiState *s = ASPEED_MII(opaque); + + switch (addr) { + case 0x0: + return s->phycr; + case 0x4: + return s->phydata; + default: + g_assert_not_reached(); + } +} + +static void aspeed_mii_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + AspeedMiiState *s = ASPEED_MII(opaque); + + switch (addr) { + case 0x0: + s->phycr = value & ~(s->phycr & ASPEED_MII_PHYCR_FIRE); + break; + case 0x4: + s->phydata = value & ~(0xffff | ASPEED_MII_PHYDATA_IDLE); + break; + default: + g_assert_not_reached(); + } + + aspeed_mii_transition(s, !!(s->phycr & ASPEED_MII_PHYCR_FIRE)); + aspeed_mii_do_phy_ctl(s); +} + +static const MemoryRegionOps aspeed_mii_ops = { + .read = aspeed_mii_read, + .write = aspeed_mii_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void aspeed_mii_reset(DeviceState *dev) +{ + AspeedMiiState *s = ASPEED_MII(dev); + + s->phycr = 0; + s->phydata = 0; + + aspeed_mii_transition(s, !!(s->phycr & ASPEED_MII_PHYCR_FIRE)); +}; + +static void aspeed_mii_realize(DeviceState *dev, Error **errp) +{ + AspeedMiiState *s = ASPEED_MII(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj; + Error *local_err = NULL; + + obj = object_property_get_link(OBJECT(dev), "nic", &local_err); + if (!obj) { + error_propagate(errp, local_err); + error_prepend(errp, "required link 'nic' not found: "); + return; + } + + s->nic = FTGMAC100(obj); + + memory_region_init_io(&s->iomem, OBJECT(dev), &aspeed_mii_ops, s, + TYPE_ASPEED_MII, 0x8); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription vmstate_aspeed_mii = { + .name = TYPE_ASPEED_MII, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(phycr, FTGMAC100State), + VMSTATE_UINT32(phydata, FTGMAC100State), + VMSTATE_END_OF_LIST() + } +}; +static void aspeed_mii_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_aspeed_mii; + dc->reset = aspeed_mii_reset; + dc->realize = aspeed_mii_realize; + dc->desc = "Aspeed MII controller"; +} + +static const TypeInfo aspeed_mii_info = { + .name = TYPE_ASPEED_MII, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedMiiState), + .class_init = aspeed_mii_class_init, +}; + static void ftgmac100_register_types(void) { type_register_static(&ftgmac100_info); + type_register_static(&aspeed_mii_info); } type_init(ftgmac100_register_types) diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index 8bba2a8..ed551f2 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -21,7 +21,6 @@ #include "hw/ptimer.h" #include "hw/qdev-properties.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "qemu/module.h" /* For crc32 */ #include <zlib.h> @@ -450,8 +449,10 @@ static void lan9118_reset(DeviceState *d) s->e2p_data = 0; s->free_timer_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 40; + ptimer_transaction_begin(s->timer); ptimer_stop(s->timer); ptimer_set_count(s->timer, 0xffff); + ptimer_transaction_commit(s->timer); s->gpt_cfg = 0xffff; s->mac_cr = MAC_CR_PRMS; @@ -1100,6 +1101,7 @@ static void lan9118_writel(void *opaque, hwaddr offset, break; case CSR_GPT_CFG: if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) { + ptimer_transaction_begin(s->timer); if (val & GPT_TIMER_EN) { ptimer_set_count(s->timer, val & 0xffff); ptimer_run(s->timer, 0); @@ -1107,6 +1109,7 @@ static void lan9118_writel(void *opaque, hwaddr offset, ptimer_stop(s->timer); ptimer_set_count(s->timer, 0xffff); } + ptimer_transaction_commit(s->timer); } s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff); break; @@ -1328,7 +1331,6 @@ static void lan9118_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); lan9118_state *s = LAN9118(dev); - QEMUBH *bh; int i; const MemoryRegionOps *mem_ops = s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops; @@ -1349,10 +1351,11 @@ static void lan9118_realize(DeviceState *dev, Error **errp) s->pmt_ctrl = 1; s->txp = &s->tx_packet; - bh = qemu_bh_new(lan9118_tick, s); - s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->timer = ptimer_init(lan9118_tick, s, PTIMER_POLICY_DEFAULT); + ptimer_transaction_begin(s->timer); ptimer_set_freq(s->timer, 10000); ptimer_set_limit(s->timer, 0xffff, 1); + ptimer_transaction_commit(s->timer); } static Property lan9118_properties[] = { diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 4b3bd4a..92c7e45 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -157,7 +157,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) int i; if (env->psw.mask & PSW_MASK_PSTATE) { - s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); + s390_program_interrupt(env, PGM_PRIVILEGED, ra); return 0; } @@ -168,7 +168,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) reqh = (ClpReqHdr *)buffer; req_len = lduw_p(&reqh->len); if (req_len < 16 || req_len > 8184 || (req_len % 8 != 0)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } @@ -180,11 +180,11 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) resh = (ClpRspHdr *)(buffer + req_len); res_len = lduw_p(&resh->len); if (res_len < 8 || res_len > 8176 || (res_len % 8 != 0)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } if ((req_len + res_len) > 8192) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } @@ -390,12 +390,12 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) uint8_t pcias; if (env->psw.mask & PSW_MASK_PSTATE) { - s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); + s390_program_interrupt(env, PGM_PRIVILEGED, ra); return 0; } if (r2 & 0x1) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return 0; } @@ -429,25 +429,25 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) switch (pcias) { case ZPCI_IO_BAR_MIN...ZPCI_IO_BAR_MAX: if (!len || (len > (8 - (offset & 0x7)))) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } result = zpci_read_bar(pbdev, pcias, offset, &data, len); if (result != MEMTX_OK) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } break; case ZPCI_CONFIG_BAR: if (!len || (len > (4 - (offset & 0x3))) || len == 3) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } data = pci_host_config_read_common( pbdev->pdev, offset, pci_config_size(pbdev->pdev), len); if (zpci_endian_swap(&data, len)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } break; @@ -489,12 +489,12 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) uint8_t pcias; if (env->psw.mask & PSW_MASK_PSTATE) { - s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); + s390_program_interrupt(env, PGM_PRIVILEGED, ra); return 0; } if (r2 & 0x1) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return 0; } @@ -536,13 +536,13 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) * A length of 0 is invalid and length should not cross a double word */ if (!len || (len > (8 - (offset & 0x7)))) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } result = zpci_write_bar(pbdev, pcias, offset, data, len); if (result != MEMTX_OK) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } break; @@ -550,7 +550,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) /* ZPCI uses the pseudo BAR number 15 as configuration space */ /* possible access lengths are 1,2,4 and must not cross a word */ if (!len || (len > (4 - (offset & 0x3))) || len == 3) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } /* len = 1,2,4 so we do not need to test */ @@ -622,12 +622,12 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) hwaddr start, end; if (env->psw.mask & PSW_MASK_PSTATE) { - s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); + s390_program_interrupt(env, PGM_PRIVILEGED, ra); return 0; } if (r2 & 0x1) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return 0; } @@ -709,7 +709,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, uint8_t buffer[128]; if (env->psw.mask & PSW_MASK_PSTATE) { - s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra); + s390_program_interrupt(env, PGM_PRIVILEGED, ra); return 0; } @@ -772,7 +772,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, if (!memory_region_access_valid(mr, offset, len, true, MEMTXATTRS_UNSPECIFIED)) { - s390_program_interrupt(env, PGM_OPERAND, 6, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } @@ -786,7 +786,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, ldq_p(buffer + i * 8), MO_64, MEMTXATTRS_UNSPECIFIED); if (result != MEMTX_OK) { - s390_program_interrupt(env, PGM_OPERAND, 6, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } } @@ -797,7 +797,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, return 0; specification_error: - s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return 0; } @@ -871,14 +871,14 @@ static int reg_ioat(CPUS390XState *env, S390PCIIOMMU *iommu, ZpciFib fib, pba &= ~0xfff; pal |= 0xfff; if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) { - s390_program_interrupt(env, PGM_OPERAND, 6, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return -EINVAL; } /* currently we only support designation type 1 with translation */ if (!(dt == ZPCI_IOTA_RTTO && t)) { error_report("unsupported ioat dt %d t %d", dt, t); - s390_program_interrupt(env, PGM_OPERAND, 6, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return -EINVAL; } @@ -1003,7 +1003,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, uint64_t cc = ZPCI_PCI_LS_OK; if (env->psw.mask & PSW_MASK_PSTATE) { - s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra); + s390_program_interrupt(env, PGM_PRIVILEGED, ra); return 0; } @@ -1012,7 +1012,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, fh = env->regs[r1] >> 32; if (fiba & 0x7) { - s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return 0; } @@ -1040,7 +1040,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, } if (fib.fmt != 0) { - s390_program_interrupt(env, PGM_OPERAND, 6, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return 0; } @@ -1151,7 +1151,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, break; } default: - s390_program_interrupt(&cpu->env, PGM_OPERAND, 6, ra); + s390_program_interrupt(&cpu->env, PGM_OPERAND, ra); cc = ZPCI_PCI_LS_ERR; } @@ -1171,7 +1171,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, uint64_t cc = ZPCI_PCI_LS_OK; if (env->psw.mask & PSW_MASK_PSTATE) { - s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra); + s390_program_interrupt(env, PGM_PRIVILEGED, ra); return 0; } @@ -1185,7 +1185,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, } if (fiba & 0x7) { - s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return 0; } diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 18ad279..d3edeef 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -650,7 +650,9 @@ DEFINE_CCW_MACHINE(4_2, "4.2", true); static void ccw_machine_4_1_instance_options(MachineState *machine) { + static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V4_1 }; ccw_machine_4_2_instance_options(machine); + s390_set_qemu_cpu_model(0x2964, 13, 2, qemu_cpu_feat); } static void ccw_machine_4_1_class_options(MachineClass *mc) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 915641a..68b1675 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1608,25 +1608,28 @@ static void scsi_unmap_complete_noio(UnmapCBData *data, int ret) { SCSIDiskReq *r = data->r; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint64_t sector_num; - uint32_t nb_sectors; assert(r->req.aiocb == NULL); - if (scsi_disk_req_check_error(r, ret, false)) { - goto done; - } if (data->count > 0) { - sector_num = ldq_be_p(&data->inbuf[0]); - nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL; - if (!check_lba_range(s, sector_num, nb_sectors)) { + r->sector = ldq_be_p(&data->inbuf[0]) + * (s->qdev.blocksize / BDRV_SECTOR_SIZE); + r->sector_count = (ldl_be_p(&data->inbuf[8]) & 0xffffffffULL) + * (s->qdev.blocksize / BDRV_SECTOR_SIZE); + if (!check_lba_range(s, r->sector, r->sector_count)) { + block_acct_invalid(blk_get_stats(s->qdev.conf.blk), + BLOCK_ACCT_UNMAP); scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); goto done; } + block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, + r->sector_count * BDRV_SECTOR_SIZE, + BLOCK_ACCT_UNMAP); + r->req.aiocb = blk_aio_pdiscard(s->qdev.conf.blk, - sector_num * s->qdev.blocksize, - nb_sectors * s->qdev.blocksize, + r->sector * BDRV_SECTOR_SIZE, + r->sector_count * BDRV_SECTOR_SIZE, scsi_unmap_complete, data); data->count--; data->inbuf += 16; @@ -1650,7 +1653,13 @@ static void scsi_unmap_complete(void *opaque, int ret) r->req.aiocb = NULL; aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); - scsi_unmap_complete_noio(data, ret); + if (scsi_disk_req_check_error(r, ret, true)) { + scsi_req_unref(&r->req); + g_free(data); + } else { + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); + scsi_unmap_complete_noio(data, ret); + } aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } @@ -1680,6 +1689,7 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) } if (blk_is_read_only(s->qdev.conf.blk)) { + block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP); scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); return; } @@ -1695,10 +1705,12 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) return; invalid_param_len: + block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP); scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); return; invalid_field: + block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP); scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); } diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs index 0665727..a884c23 100644 --- a/hw/sd/Makefile.objs +++ b/hw/sd/Makefile.objs @@ -8,3 +8,4 @@ obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o obj-$(CONFIG_OMAP) += omap_mmc.o obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o obj-$(CONFIG_RASPI) += bcm2835_sdhost.o +obj-$(CONFIG_ASPEED_SOC) += aspeed_sdhci.o diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c new file mode 100644 index 0000000..cff3eb7 --- /dev/null +++ b/hw/sd/aspeed_sdhci.c @@ -0,0 +1,198 @@ +/* + * Aspeed SD Host Controller + * Eddie James <eajames@linux.ibm.com> + * + * Copyright (C) 2019 IBM Corp + * SPDX-License-Identifer: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/sd/aspeed_sdhci.h" +#include "qapi/error.h" +#include "hw/irq.h" +#include "migration/vmstate.h" + +#define ASPEED_SDHCI_INFO 0x00 +#define ASPEED_SDHCI_INFO_RESET 0x00030000 +#define ASPEED_SDHCI_DEBOUNCE 0x04 +#define ASPEED_SDHCI_DEBOUNCE_RESET 0x00000005 +#define ASPEED_SDHCI_BUS 0x08 +#define ASPEED_SDHCI_SDIO_140 0x10 +#define ASPEED_SDHCI_SDIO_148 0x18 +#define ASPEED_SDHCI_SDIO_240 0x20 +#define ASPEED_SDHCI_SDIO_248 0x28 +#define ASPEED_SDHCI_WP_POL 0xec +#define ASPEED_SDHCI_CARD_DET 0xf0 +#define ASPEED_SDHCI_IRQ_STAT 0xfc + +#define TO_REG(addr) ((addr) / sizeof(uint32_t)) + +static uint64_t aspeed_sdhci_read(void *opaque, hwaddr addr, unsigned int size) +{ + uint32_t val = 0; + AspeedSDHCIState *sdhci = opaque; + + switch (addr) { + case ASPEED_SDHCI_SDIO_140: + val = (uint32_t)sdhci->slots[0].capareg; + break; + case ASPEED_SDHCI_SDIO_148: + val = (uint32_t)sdhci->slots[0].maxcurr; + break; + case ASPEED_SDHCI_SDIO_240: + val = (uint32_t)sdhci->slots[1].capareg; + break; + case ASPEED_SDHCI_SDIO_248: + val = (uint32_t)sdhci->slots[1].maxcurr; + break; + default: + if (addr < ASPEED_SDHCI_REG_SIZE) { + val = sdhci->regs[TO_REG(addr)]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at 0x%" HWADDR_PRIx "\n", + __func__, addr); + } + } + + return (uint64_t)val; +} + +static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + AspeedSDHCIState *sdhci = opaque; + + switch (addr) { + case ASPEED_SDHCI_SDIO_140: + sdhci->slots[0].capareg = (uint64_t)(uint32_t)val; + break; + case ASPEED_SDHCI_SDIO_148: + sdhci->slots[0].maxcurr = (uint64_t)(uint32_t)val; + break; + case ASPEED_SDHCI_SDIO_240: + sdhci->slots[1].capareg = (uint64_t)(uint32_t)val; + break; + case ASPEED_SDHCI_SDIO_248: + sdhci->slots[1].maxcurr = (uint64_t)(uint32_t)val; + break; + default: + if (addr < ASPEED_SDHCI_REG_SIZE) { + sdhci->regs[TO_REG(addr)] = (uint32_t)val; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at 0x%" HWADDR_PRIx "\n", + __func__, addr); + } + } +} + +static const MemoryRegionOps aspeed_sdhci_ops = { + .read = aspeed_sdhci_read, + .write = aspeed_sdhci_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static void aspeed_sdhci_set_irq(void *opaque, int n, int level) +{ + AspeedSDHCIState *sdhci = opaque; + + if (level) { + sdhci->regs[TO_REG(ASPEED_SDHCI_IRQ_STAT)] |= BIT(n); + + qemu_irq_raise(sdhci->irq); + } else { + sdhci->regs[TO_REG(ASPEED_SDHCI_IRQ_STAT)] &= ~BIT(n); + + qemu_irq_lower(sdhci->irq); + } +} + +static void aspeed_sdhci_realize(DeviceState *dev, Error **errp) +{ + Error *err = NULL; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev); + + /* Create input irqs for the slots */ + qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_sdhci_set_irq, + sdhci, NULL, ASPEED_SDHCI_NUM_SLOTS); + + sysbus_init_irq(sbd, &sdhci->irq); + memory_region_init_io(&sdhci->iomem, OBJECT(sdhci), &aspeed_sdhci_ops, + sdhci, TYPE_ASPEED_SDHCI, 0x1000); + sysbus_init_mmio(sbd, &sdhci->iomem); + + for (int i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) { + Object *sdhci_slot = OBJECT(&sdhci->slots[i]); + SysBusDevice *sbd_slot = SYS_BUS_DEVICE(&sdhci->slots[i]); + + object_property_set_int(sdhci_slot, 2, "sd-spec-version", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_uint(sdhci_slot, ASPEED_SDHCI_CAPABILITIES, + "capareg", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(sdhci_slot, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_connect_irq(sbd_slot, 0, qdev_get_gpio_in(DEVICE(sbd), i)); + memory_region_add_subregion(&sdhci->iomem, (i + 1) * 0x100, + &sdhci->slots[i].iomem); + } +} + +static void aspeed_sdhci_reset(DeviceState *dev) +{ + AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev); + + memset(sdhci->regs, 0, ASPEED_SDHCI_REG_SIZE); + sdhci->regs[TO_REG(ASPEED_SDHCI_INFO)] = ASPEED_SDHCI_INFO_RESET; + sdhci->regs[TO_REG(ASPEED_SDHCI_DEBOUNCE)] = ASPEED_SDHCI_DEBOUNCE_RESET; +} + +static const VMStateDescription vmstate_aspeed_sdhci = { + .name = TYPE_ASPEED_SDHCI, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AspeedSDHCIState, ASPEED_SDHCI_NUM_REGS), + VMSTATE_END_OF_LIST(), + }, +}; + +static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(classp); + + dc->realize = aspeed_sdhci_realize; + dc->reset = aspeed_sdhci_reset; + dc->vmsd = &vmstate_aspeed_sdhci; +} + +static TypeInfo aspeed_sdhci_info = { + .name = TYPE_ASPEED_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedSDHCIState), + .class_init = aspeed_sdhci_class_init, +}; + +static void aspeed_sdhci_register_types(void) +{ + type_register_static(&aspeed_sdhci_info); +} + +type_init(aspeed_sdhci_register_types) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 9ffc7e0..f0c7bbb 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -30,6 +30,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "exec/address-spaces.h" +#include "qemu/units.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -50,7 +51,7 @@ #define CONF_FLASH_TYPE0 0 #define CONF_FLASH_TYPE_NOR 0x0 #define CONF_FLASH_TYPE_NAND 0x1 -#define CONF_FLASH_TYPE_SPI 0x2 +#define CONF_FLASH_TYPE_SPI 0x2 /* AST2600 is SPI only */ /* CE Control Register */ #define R_CE_CTRL (0x04 / 4) @@ -71,8 +72,11 @@ /* CEx Control Register */ #define R_CTRL0 (0x10 / 4) +#define CTRL_IO_QPI (1 << 31) +#define CTRL_IO_QUAD_DATA (1 << 30) #define CTRL_IO_DUAL_DATA (1 << 29) #define CTRL_IO_DUAL_ADDR_DATA (1 << 28) /* Includes dummies */ +#define CTRL_IO_QUAD_ADDR_DATA (1 << 28) /* Includes dummies */ #define CTRL_CMD_SHIFT 16 #define CTRL_CMD_MASK 0xff #define CTRL_DUMMY_HIGH_SHIFT 14 @@ -136,7 +140,7 @@ /* Misc Control Register #2 */ #define R_TIMINGS (0x94 / 4) -/* SPI controller registers and bits */ +/* SPI controller registers and bits (AST2400) */ #define R_SPI_CONF (0x00 / 4) #define SPI_CONF_ENABLE_W0 0 #define R_SPI_CTRL0 (0x4 / 4) @@ -211,6 +215,39 @@ static const AspeedSegments aspeed_segments_ast2500_spi2[] = { { 0x38000000, 32 * 1024 * 1024 }, /* start address is readonly */ { 0x3A000000, 96 * 1024 * 1024 }, /* end address is readonly */ }; +static uint32_t aspeed_smc_segment_to_reg(const AspeedSMCState *s, + const AspeedSegments *seg); +static void aspeed_smc_reg_to_segment(const AspeedSMCState *s, uint32_t reg, + AspeedSegments *seg); + +/* + * AST2600 definitions + */ +#define ASPEED26_SOC_FMC_FLASH_BASE 0x20000000 +#define ASPEED26_SOC_SPI_FLASH_BASE 0x30000000 +#define ASPEED26_SOC_SPI2_FLASH_BASE 0x50000000 + +static const AspeedSegments aspeed_segments_ast2600_fmc[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ + { 0x0, 0 }, /* disabled */ +}; + +static const AspeedSegments aspeed_segments_ast2600_spi1[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static const AspeedSegments aspeed_segments_ast2600_spi2[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ + { 0x0, 0 }, /* disabled */ +}; + +static uint32_t aspeed_2600_smc_segment_to_reg(const AspeedSMCState *s, + const AspeedSegments *seg); +static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s, + uint32_t reg, AspeedSegments *seg); static const AspeedSMCController controllers[] = { { @@ -226,6 +263,8 @@ static const AspeedSMCController controllers[] = { .flash_window_size = 0x6000000, .has_dma = false, .nregs = ASPEED_SMC_R_SMC_MAX, + .segment_to_reg = aspeed_smc_segment_to_reg, + .reg_to_segment = aspeed_smc_reg_to_segment, }, { .name = "aspeed.fmc-ast2400", .r_conf = R_CONF, @@ -241,6 +280,8 @@ static const AspeedSMCController controllers[] = { .dma_flash_mask = 0x0FFFFFFC, .dma_dram_mask = 0x1FFFFFFC, .nregs = ASPEED_SMC_R_MAX, + .segment_to_reg = aspeed_smc_segment_to_reg, + .reg_to_segment = aspeed_smc_reg_to_segment, }, { .name = "aspeed.spi1-ast2400", .r_conf = R_SPI_CONF, @@ -254,6 +295,8 @@ static const AspeedSMCController controllers[] = { .flash_window_size = 0x10000000, .has_dma = false, .nregs = ASPEED_SMC_R_SPI_MAX, + .segment_to_reg = aspeed_smc_segment_to_reg, + .reg_to_segment = aspeed_smc_reg_to_segment, }, { .name = "aspeed.fmc-ast2500", .r_conf = R_CONF, @@ -269,6 +312,8 @@ static const AspeedSMCController controllers[] = { .dma_flash_mask = 0x0FFFFFFC, .dma_dram_mask = 0x3FFFFFFC, .nregs = ASPEED_SMC_R_MAX, + .segment_to_reg = aspeed_smc_segment_to_reg, + .reg_to_segment = aspeed_smc_reg_to_segment, }, { .name = "aspeed.spi1-ast2500", .r_conf = R_CONF, @@ -282,6 +327,8 @@ static const AspeedSMCController controllers[] = { .flash_window_size = 0x8000000, .has_dma = false, .nregs = ASPEED_SMC_R_MAX, + .segment_to_reg = aspeed_smc_segment_to_reg, + .reg_to_segment = aspeed_smc_reg_to_segment, }, { .name = "aspeed.spi2-ast2500", .r_conf = R_CONF, @@ -295,19 +342,64 @@ static const AspeedSMCController controllers[] = { .flash_window_size = 0x8000000, .has_dma = false, .nregs = ASPEED_SMC_R_MAX, + .segment_to_reg = aspeed_smc_segment_to_reg, + .reg_to_segment = aspeed_smc_reg_to_segment, + }, { + .name = "aspeed.fmc-ast2600", + .r_conf = R_CONF, + .r_ce_ctrl = R_CE_CTRL, + .r_ctrl0 = R_CTRL0, + .r_timings = R_TIMINGS, + .conf_enable_w0 = CONF_ENABLE_W0, + .max_slaves = 3, + .segments = aspeed_segments_ast2600_fmc, + .flash_window_base = ASPEED26_SOC_FMC_FLASH_BASE, + .flash_window_size = 0x10000000, + .has_dma = true, + .nregs = ASPEED_SMC_R_MAX, + .segment_to_reg = aspeed_2600_smc_segment_to_reg, + .reg_to_segment = aspeed_2600_smc_reg_to_segment, + }, { + .name = "aspeed.spi1-ast2600", + .r_conf = R_CONF, + .r_ce_ctrl = R_CE_CTRL, + .r_ctrl0 = R_CTRL0, + .r_timings = R_TIMINGS, + .conf_enable_w0 = CONF_ENABLE_W0, + .max_slaves = 2, + .segments = aspeed_segments_ast2600_spi1, + .flash_window_base = ASPEED26_SOC_SPI_FLASH_BASE, + .flash_window_size = 0x10000000, + .has_dma = false, + .nregs = ASPEED_SMC_R_MAX, + .segment_to_reg = aspeed_2600_smc_segment_to_reg, + .reg_to_segment = aspeed_2600_smc_reg_to_segment, + }, { + .name = "aspeed.spi2-ast2600", + .r_conf = R_CONF, + .r_ce_ctrl = R_CE_CTRL, + .r_ctrl0 = R_CTRL0, + .r_timings = R_TIMINGS, + .conf_enable_w0 = CONF_ENABLE_W0, + .max_slaves = 3, + .segments = aspeed_segments_ast2600_spi2, + .flash_window_base = ASPEED26_SOC_SPI2_FLASH_BASE, + .flash_window_size = 0x10000000, + .has_dma = false, + .nregs = ASPEED_SMC_R_MAX, + .segment_to_reg = aspeed_2600_smc_segment_to_reg, + .reg_to_segment = aspeed_2600_smc_reg_to_segment, }, }; /* - * The Segment Register uses a 8MB unit to encode the start address - * and the end address of the mapping window of a flash SPI slave : - * - * | byte 1 | byte 2 | byte 3 | byte 4 | - * +--------+--------+--------+--------+ - * | end | start | 0 | 0 | - * + * The Segment Registers of the AST2400 and AST2500 have a 8MB + * unit. The address range of a flash SPI slave is encoded with + * absolute addresses which should be part of the overall controller + * window. */ -static inline uint32_t aspeed_smc_segment_to_reg(const AspeedSegments *seg) +static uint32_t aspeed_smc_segment_to_reg(const AspeedSMCState *s, + const AspeedSegments *seg) { uint32_t reg = 0; reg |= ((seg->addr >> 23) & SEG_START_MASK) << SEG_START_SHIFT; @@ -315,12 +407,47 @@ static inline uint32_t aspeed_smc_segment_to_reg(const AspeedSegments *seg) return reg; } -static inline void aspeed_smc_reg_to_segment(uint32_t reg, AspeedSegments *seg) +static void aspeed_smc_reg_to_segment(const AspeedSMCState *s, + uint32_t reg, AspeedSegments *seg) { seg->addr = ((reg >> SEG_START_SHIFT) & SEG_START_MASK) << 23; seg->size = (((reg >> SEG_END_SHIFT) & SEG_END_MASK) << 23) - seg->addr; } +/* + * The Segment Registers of the AST2600 have a 1MB unit. The address + * range of a flash SPI slave is encoded with offsets in the overall + * controller window. The previous SoC AST2400 and AST2500 used + * absolute addresses. Only bits [27:20] are relevant and the end + * address is an upper bound limit. + */ +#define AST2600_SEG_ADDR_MASK 0x0ff00000 + +static uint32_t aspeed_2600_smc_segment_to_reg(const AspeedSMCState *s, + const AspeedSegments *seg) +{ + uint32_t reg = 0; + + /* Disabled segments have a nil register */ + if (!seg->size) { + return 0; + } + + reg |= (seg->addr & AST2600_SEG_ADDR_MASK) >> 16; /* start offset */ + reg |= (seg->addr + seg->size - 1) & AST2600_SEG_ADDR_MASK; /* end offset */ + return reg; +} + +static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s, + uint32_t reg, AspeedSegments *seg) +{ + uint32_t start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK; + uint32_t end_offset = reg & AST2600_SEG_ADDR_MASK; + + seg->addr = s->ctrl->flash_window_base + start_offset; + seg->size = end_offset + MiB - start_offset; +} + static bool aspeed_smc_flash_overlap(const AspeedSMCState *s, const AspeedSegments *new, int cs) @@ -333,7 +460,7 @@ static bool aspeed_smc_flash_overlap(const AspeedSMCState *s, continue; } - aspeed_smc_reg_to_segment(s->regs[R_SEG_ADDR0 + i], &seg); + s->ctrl->reg_to_segment(s, s->regs[R_SEG_ADDR0 + i], &seg); if (new->addr + new->size > seg.addr && new->addr < seg.addr + seg.size) { @@ -354,7 +481,7 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, AspeedSMCFlash *fl = &s->flashes[cs]; AspeedSegments seg; - aspeed_smc_reg_to_segment(new, &seg); + s->ctrl->reg_to_segment(s, new, &seg); /* The start address of CS0 is read-only */ if (cs == 0 && seg.addr != s->ctrl->flash_window_base) { @@ -362,7 +489,7 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, "%s: Tried to change CS0 start address to 0x%" HWADDR_PRIx "\n", s->ctrl->name, seg.addr); seg.addr = s->ctrl->flash_window_base; - new = aspeed_smc_segment_to_reg(&seg); + new = s->ctrl->segment_to_reg(s, &seg); } /* @@ -379,7 +506,7 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, HWADDR_PRIx "\n", s->ctrl->name, cs, seg.addr + seg.size); seg.size = s->ctrl->segments[cs].addr + s->ctrl->segments[cs].size - seg.addr; - new = aspeed_smc_segment_to_reg(&seg); + new = s->ctrl->segment_to_reg(s, &seg); } /* Keep the segment in the overall flash window */ @@ -455,8 +582,12 @@ static inline int aspeed_smc_flash_cmd(const AspeedSMCFlash *fl) const AspeedSMCState *s = fl->controller; int cmd = (s->regs[s->r_ctrl0 + fl->id] >> CTRL_CMD_SHIFT) & CTRL_CMD_MASK; - /* In read mode, the default SPI command is READ (0x3). In other - * modes, the command should necessarily be defined */ + /* + * In read mode, the default SPI command is READ (0x3). In other + * modes, the command should necessarily be defined + * + * TODO: add support for READ4 (0x13) on AST2600 + */ if (aspeed_smc_flash_mode(fl) == CTRL_READMODE) { cmd = SPI_OP_READ; } @@ -509,7 +640,7 @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, const AspeedSMCState *s = fl->controller; AspeedSegments seg; - aspeed_smc_reg_to_segment(s->regs[R_SEG_ADDR0 + fl->id], &seg); + s->ctrl->reg_to_segment(s, s->regs[R_SEG_ADDR0 + fl->id], &seg); if ((addr % seg.size) != addr) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid address 0x%08x for CS%d segment : " @@ -769,7 +900,15 @@ static void aspeed_smc_reset(DeviceState *d) /* setup default segment register values for all */ for (i = 0; i < s->ctrl->max_slaves; ++i) { s->regs[R_SEG_ADDR0 + i] = - aspeed_smc_segment_to_reg(&s->ctrl->segments[i]); + s->ctrl->segment_to_reg(s, &s->ctrl->segments[i]); + } + + /* HW strapping flash type for the AST2600 controllers */ + if (s->ctrl->segments == aspeed_segments_ast2600_fmc) { + /* flash type is fixed to SPI for all */ + s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); + s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1); + s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE2); } /* HW strapping flash type for FMC controllers */ diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index ca5a905..aae880f 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -22,7 +22,6 @@ #include "hw/timer/allwinner-a10-pit.h" #include "migration/vmstate.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "qemu/module.h" static void a10_pit_update_irq(AwA10PITState *s) @@ -80,6 +79,7 @@ static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size) return 0; } +/* Must be called inside a ptimer transaction block for s->timer[index] */ static void a10_pit_set_freq(AwA10PITState *s, int index) { uint32_t prescaler, source, source_freq; @@ -118,6 +118,7 @@ static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, switch (offset & 0x0f) { case AW_A10_PIT_TIMER_CONTROL: s->control[index] = value; + ptimer_transaction_begin(s->timer[index]); a10_pit_set_freq(s, index); if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) { ptimer_set_count(s->timer[index], s->interval[index]); @@ -131,10 +132,13 @@ static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, } else { ptimer_stop(s->timer[index]); } + ptimer_transaction_commit(s->timer[index]); break; case AW_A10_PIT_TIMER_INTERVAL: s->interval[index] = value; + ptimer_transaction_begin(s->timer[index]); ptimer_set_limit(s->timer[index], s->interval[index], 1); + ptimer_transaction_commit(s->timer[index]); break; case AW_A10_PIT_TIMER_COUNT: s->count[index] = value; @@ -225,8 +229,10 @@ static void a10_pit_reset(DeviceState *dev) s->control[i] = AW_A10_PIT_DEFAULT_CLOCK; s->interval[i] = 0; s->count[i] = 0; + ptimer_transaction_begin(s->timer[i]); ptimer_stop(s->timer[i]); a10_pit_set_freq(s, i); + ptimer_transaction_commit(s->timer[i]); } s->watch_dog_mode = 0; s->watch_dog_control = 0; @@ -255,7 +261,6 @@ static void a10_pit_init(Object *obj) { AwA10PITState *s = AW_A10_PIT(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - QEMUBH * bh[AW_A10_PIT_TIMER_NR]; uint8_t i; for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { @@ -270,8 +275,7 @@ static void a10_pit_init(Object *obj) tc->container = s; tc->index = i; - bh[i] = qemu_bh_new(a10_pit_timer_cb, tc); - s->timer[i] = ptimer_init(bh[i], PTIMER_POLICY_DEFAULT); + s->timer[i] = ptimer_init(a10_pit_timer_cb, tc, PTIMER_POLICY_DEFAULT); } } diff --git a/hw/timer/altera_timer.c b/hw/timer/altera_timer.c index 936b313..ee32e0e 100644 --- a/hw/timer/altera_timer.c +++ b/hw/timer/altera_timer.c @@ -184,7 +184,7 @@ static void altera_timer_realize(DeviceState *dev, Error **errp) } t->bh = qemu_bh_new(timer_hit, t); - t->ptimer = ptimer_init(t->bh, PTIMER_POLICY_DEFAULT); + t->ptimer = ptimer_init_with_bh(t->bh, PTIMER_POLICY_DEFAULT); ptimer_set_freq(t->ptimer, t->freq_hz); memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 9f63abe..fdf97d1 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -27,7 +27,6 @@ #include "hw/timer/arm_mptimer.h" #include "migration/vmstate.h" #include "qapi/error.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "hw/core/cpu.h" @@ -65,6 +64,7 @@ static inline uint32_t timerblock_scale(uint32_t control) return (((control >> 8) & 0xff) + 1) * 10; } +/* Must be called within a ptimer transaction block */ static inline void timerblock_set_count(struct ptimer_state *timer, uint32_t control, uint64_t *count) { @@ -77,6 +77,7 @@ static inline void timerblock_set_count(struct ptimer_state *timer, ptimer_set_count(timer, *count); } +/* Must be called within a ptimer transaction block */ static inline void timerblock_run(struct ptimer_state *timer, uint32_t control, uint32_t load) { @@ -124,6 +125,7 @@ static void timerblock_write(void *opaque, hwaddr addr, uint32_t control = tb->control; switch (addr) { case 0: /* Load */ + ptimer_transaction_begin(tb->timer); /* Setting load to 0 stops the timer without doing the tick if * prescaler = 0. */ @@ -132,8 +134,10 @@ static void timerblock_write(void *opaque, hwaddr addr, } ptimer_set_limit(tb->timer, value, 1); timerblock_run(tb->timer, control, value); + ptimer_transaction_commit(tb->timer); break; case 4: /* Counter. */ + ptimer_transaction_begin(tb->timer); /* Setting counter to 0 stops the one-shot timer, or periodic with * load = 0, without doing the tick if prescaler = 0. */ @@ -143,8 +147,10 @@ static void timerblock_write(void *opaque, hwaddr addr, } timerblock_set_count(tb->timer, control, &value); timerblock_run(tb->timer, control, value); + ptimer_transaction_commit(tb->timer); break; case 8: /* Control. */ + ptimer_transaction_begin(tb->timer); if ((control & 3) != (value & 3)) { ptimer_stop(tb->timer); } @@ -160,6 +166,7 @@ static void timerblock_write(void *opaque, hwaddr addr, timerblock_run(tb->timer, value, count); } tb->control = value; + ptimer_transaction_commit(tb->timer); break; case 12: /* Interrupt status. */ tb->status &= ~value; @@ -212,9 +219,11 @@ static void timerblock_reset(TimerBlock *tb) tb->control = 0; tb->status = 0; if (tb->timer) { + ptimer_transaction_begin(tb->timer); ptimer_stop(tb->timer); ptimer_set_limit(tb->timer, 0, 1); ptimer_set_period(tb->timer, timerblock_scale(0)); + ptimer_transaction_commit(tb->timer); } } @@ -228,7 +237,7 @@ static void arm_mptimer_reset(DeviceState *dev) } } -static void arm_mptimer_init(Object *obj) +static void arm_mptimer_init_with_bh(Object *obj) { ARMMPTimerState *s = ARM_MPTIMER(obj); @@ -260,8 +269,7 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp) */ for (i = 0; i < s->num_cpu; i++) { TimerBlock *tb = &s->timerblock[i]; - QEMUBH *bh = qemu_bh_new(timerblock_tick, tb); - tb->timer = ptimer_init(bh, PTIMER_POLICY); + tb->timer = ptimer_init(timerblock_tick, tb, PTIMER_POLICY); sysbus_init_irq(sbd, &tb->irq); memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb, "arm_mptimer_timerblock", 0x20); @@ -311,7 +319,7 @@ static const TypeInfo arm_mptimer_info = { .name = TYPE_ARM_MPTIMER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ARMMPTimerState), - .instance_init = arm_mptimer_init, + .instance_init = arm_mptimer_init_with_bh, .class_init = arm_mptimer_class_init, }; diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index c2e6211..af524fa 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -14,7 +14,6 @@ #include "hw/irq.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/log.h" @@ -75,7 +74,10 @@ static uint32_t arm_timer_read(void *opaque, hwaddr offset) } } -/* Reset the timer limit after settings have changed. */ +/* + * Reset the timer limit after settings have changed. + * May only be called from inside a ptimer transaction block. + */ static void arm_timer_recalibrate(arm_timer_state *s, int reload) { uint32_t limit; @@ -102,13 +104,16 @@ static void arm_timer_write(void *opaque, hwaddr offset, switch (offset >> 2) { case 0: /* TimerLoad */ s->limit = value; + ptimer_transaction_begin(s->timer); arm_timer_recalibrate(s, 1); + ptimer_transaction_commit(s->timer); break; case 1: /* TimerValue */ /* ??? Linux seems to want to write to this readonly register. Ignore it. */ break; case 2: /* TimerControl */ + ptimer_transaction_begin(s->timer); if (s->control & TIMER_CTRL_ENABLE) { /* Pause the timer if it is running. This may cause some inaccuracy dure to rounding, but avoids a whole lot of other @@ -128,13 +133,16 @@ static void arm_timer_write(void *opaque, hwaddr offset, /* Restart the timer if still enabled. */ ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0); } + ptimer_transaction_commit(s->timer); break; case 3: /* TimerIntClr */ s->int_level = 0; break; case 6: /* TimerBGLoad */ s->limit = value; + ptimer_transaction_begin(s->timer); arm_timer_recalibrate(s, 0); + ptimer_transaction_commit(s->timer); break; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -166,14 +174,12 @@ static const VMStateDescription vmstate_arm_timer = { static arm_timer_state *arm_timer_init(uint32_t freq) { arm_timer_state *s; - QEMUBH *bh; s = (arm_timer_state *)g_malloc0(sizeof(arm_timer_state)); s->freq = freq; s->control = TIMER_CTRL_IE; - bh = qemu_bh_new(arm_timer_tick, s); - s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->timer = ptimer_init(arm_timer_tick, s, PTIMER_POLICY_DEFAULT); vmstate_register(NULL, -1, &vmstate_arm_timer, s); return s; } diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 2bda826..bcce219 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -160,7 +160,9 @@ static uint64_t calculate_next(struct AspeedTimer *t) timer_del(&t->timer); if (timer_overflow_interrupt(t)) { + AspeedTimerCtrlState *s = timer_to_ctrl(t); t->level = !t->level; + s->irq_sts |= BIT(t->id); qemu_set_irq(t->irq, t->level); } @@ -199,7 +201,9 @@ static void aspeed_timer_expire(void *opaque) } if (interrupt) { + AspeedTimerCtrlState *s = timer_to_ctrl(t); t->level = !t->level; + s->irq_sts |= BIT(t->id); qemu_set_irq(t->irq, t->level); } @@ -244,22 +248,14 @@ static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size) case 0x30: /* Control Register */ value = s->ctrl; break; - case 0x34: /* Control Register 2 */ - value = s->ctrl2; - break; case 0x00 ... 0x2c: /* Timers 1 - 4 */ value = aspeed_timer_get_value(&s->timers[(offset >> 4)], reg); break; case 0x40 ... 0x8c: /* Timers 5 - 8 */ value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg); break; - /* Illegal */ - case 0x38: - case 0x3C: default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, offset); - value = 0; + value = ASPEED_TIMER_GET_CLASS(s)->read(s, offset); break; } trace_aspeed_timer_read(offset, size, value); @@ -443,9 +439,6 @@ static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, case 0x30: aspeed_timer_set_ctrl(s, tv); break; - case 0x34: - aspeed_timer_set_ctrl2(s, tv); - break; /* Timer Registers */ case 0x00 ... 0x2c: aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS), reg, tv); @@ -453,12 +446,8 @@ static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, case 0x40 ... 0x8c: aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv); break; - /* Illegal */ - case 0x38: - case 0x3C: default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, offset); + ASPEED_TIMER_GET_CLASS(s)->write(s, offset, value); break; } } @@ -472,6 +461,135 @@ static const MemoryRegionOps aspeed_timer_ops = { .valid.unaligned = false, }; +static uint64_t aspeed_2400_timer_read(AspeedTimerCtrlState *s, hwaddr offset) +{ + uint64_t value; + + switch (offset) { + case 0x34: + value = s->ctrl2; + break; + case 0x38: + case 0x3C: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + value = 0; + break; + } + return value; +} + +static void aspeed_2400_timer_write(AspeedTimerCtrlState *s, hwaddr offset, + uint64_t value) +{ + const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF); + + switch (offset) { + case 0x34: + aspeed_timer_set_ctrl2(s, tv); + break; + case 0x38: + case 0x3C: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } +} + +static uint64_t aspeed_2500_timer_read(AspeedTimerCtrlState *s, hwaddr offset) +{ + uint64_t value; + + switch (offset) { + case 0x34: + value = s->ctrl2; + break; + case 0x38: + value = s->ctrl3 & BIT(0); + break; + case 0x3C: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + value = 0; + break; + } + return value; +} + +static void aspeed_2500_timer_write(AspeedTimerCtrlState *s, hwaddr offset, + uint64_t value) +{ + const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF); + uint8_t command; + + switch (offset) { + case 0x34: + aspeed_timer_set_ctrl2(s, tv); + break; + case 0x38: + command = (value >> 1) & 0xFF; + if (command == 0xAE) { + s->ctrl3 = 0x1; + } else if (command == 0xEA) { + s->ctrl3 = 0x0; + } + break; + case 0x3C: + if (s->ctrl3 & BIT(0)) { + aspeed_timer_set_ctrl(s, s->ctrl & ~tv); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } +} + +static uint64_t aspeed_2600_timer_read(AspeedTimerCtrlState *s, hwaddr offset) +{ + uint64_t value; + + switch (offset) { + case 0x34: + value = s->irq_sts; + break; + case 0x38: + case 0x3C: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + value = 0; + break; + } + return value; +} + +static void aspeed_2600_timer_write(AspeedTimerCtrlState *s, hwaddr offset, + uint64_t value) +{ + const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF); + + switch (offset) { + case 0x34: + s->irq_sts &= tv; + break; + case 0x3C: + aspeed_timer_set_ctrl(s, s->ctrl & ~tv); + break; + + case 0x38: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } +} + static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id) { AspeedTimer *t = &s->timers[id]; @@ -525,6 +643,8 @@ static void aspeed_timer_reset(DeviceState *dev) } s->ctrl = 0; s->ctrl2 = 0; + s->ctrl3 = 0; + s->irq_sts = 0; } static const VMStateDescription vmstate_aspeed_timer = { @@ -543,11 +663,13 @@ static const VMStateDescription vmstate_aspeed_timer = { static const VMStateDescription vmstate_aspeed_timer_state = { .name = "aspeed.timerctrl", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT32(ctrl, AspeedTimerCtrlState), VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState), + VMSTATE_UINT32(ctrl3, AspeedTimerCtrlState), + VMSTATE_UINT32(irq_sts, AspeedTimerCtrlState), VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState, ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer, AspeedTimer), @@ -570,11 +692,64 @@ static const TypeInfo aspeed_timer_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedTimerCtrlState), .class_init = timer_class_init, + .class_size = sizeof(AspeedTimerClass), + .abstract = true, +}; + +static void aspeed_2400_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); + + dc->desc = "ASPEED 2400 Timer"; + awc->read = aspeed_2400_timer_read; + awc->write = aspeed_2400_timer_write; +} + +static const TypeInfo aspeed_2400_timer_info = { + .name = TYPE_ASPEED_2400_TIMER, + .parent = TYPE_ASPEED_TIMER, + .class_init = aspeed_2400_timer_class_init, +}; + +static void aspeed_2500_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); + + dc->desc = "ASPEED 2500 Timer"; + awc->read = aspeed_2500_timer_read; + awc->write = aspeed_2500_timer_write; +} + +static const TypeInfo aspeed_2500_timer_info = { + .name = TYPE_ASPEED_2500_TIMER, + .parent = TYPE_ASPEED_TIMER, + .class_init = aspeed_2500_timer_class_init, +}; + +static void aspeed_2600_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); + + dc->desc = "ASPEED 2600 Timer"; + awc->read = aspeed_2600_timer_read; + awc->write = aspeed_2600_timer_write; +} + +static const TypeInfo aspeed_2600_timer_info = { + .name = TYPE_ASPEED_2600_TIMER, + .parent = TYPE_ASPEED_TIMER, + .class_init = aspeed_2600_timer_class_init, }; static void aspeed_timer_register_types(void) { type_register_static(&aspeed_timer_info); + type_register_static(&aspeed_2400_timer_info); + type_register_static(&aspeed_2500_timer_info); + type_register_static(&aspeed_2600_timer_info); } type_init(aspeed_timer_register_types) diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c index 5e2352d..e28ba9c 100644 --- a/hw/timer/cmsdk-apb-dualtimer.c +++ b/hw/timer/cmsdk-apb-dualtimer.c @@ -20,7 +20,6 @@ #include "qemu/log.h" #include "trace.h" #include "qapi/error.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "hw/sysbus.h" #include "hw/irq.h" @@ -112,6 +111,8 @@ static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m, /* Handle a write to the CONTROL register */ uint32_t changed; + ptimer_transaction_begin(m->timer); + newctrl &= R_CONTROL_VALID_MASK; changed = m->control ^ newctrl; @@ -213,6 +214,8 @@ static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m, } m->control = newctrl; + + ptimer_transaction_commit(m->timer); } static uint64_t cmsdk_apb_dualtimer_read(void *opaque, hwaddr offset, @@ -330,6 +333,7 @@ static void cmsdk_apb_dualtimer_write(void *opaque, hwaddr offset, if (!(m->control & R_CONTROL_SIZE_MASK)) { value &= 0xffff; } + ptimer_transaction_begin(m->timer); if (!(m->control & R_CONTROL_MODE_MASK)) { /* * In free-running mode this won't set the limit but will @@ -346,6 +350,7 @@ static void cmsdk_apb_dualtimer_write(void *opaque, hwaddr offset, ptimer_run(m->timer, 1); } } + ptimer_transaction_commit(m->timer); break; case A_TIMER1BGLOAD: /* Set the limit, but not the current count */ @@ -357,7 +362,9 @@ static void cmsdk_apb_dualtimer_write(void *opaque, hwaddr offset, if (!(m->control & R_CONTROL_SIZE_MASK)) { value &= 0xffff; } + ptimer_transaction_begin(m->timer); ptimer_set_limit(m->timer, value, 0); + ptimer_transaction_commit(m->timer); break; case A_TIMER1CONTROL: cmsdk_dualtimermod_write_control(m, value); @@ -398,6 +405,7 @@ static void cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule *m) m->intstatus = 0; m->load = 0; m->value = 0xffffffff; + ptimer_transaction_begin(m->timer); ptimer_stop(m->timer); /* * We start in free-running mode, with VALUE at 0xffffffff, and @@ -406,6 +414,7 @@ static void cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule *m) */ ptimer_set_limit(m->timer, 0xffff, 1); ptimer_set_freq(m->timer, m->parent->pclk_frq); + ptimer_transaction_commit(m->timer); } static void cmsdk_apb_dualtimer_reset(DeviceState *dev) @@ -450,10 +459,9 @@ static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp) for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { CMSDKAPBDualTimerModule *m = &s->timermod[i]; - QEMUBH *bh = qemu_bh_new(cmsdk_dualtimermod_tick, m); m->parent = s; - m->timer = ptimer_init(bh, + m->timer = ptimer_init(cmsdk_dualtimermod_tick, m, PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | PTIMER_POLICY_NO_IMMEDIATE_RELOAD | diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c index c83e265..40728e8 100644 --- a/hw/timer/cmsdk-apb-timer.c +++ b/hw/timer/cmsdk-apb-timer.c @@ -29,7 +29,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "qapi/error.h" #include "trace.h" @@ -121,14 +120,17 @@ static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value, "CMSDK APB timer: EXTIN input not supported\n"); } s->ctrl = value & 0xf; + ptimer_transaction_begin(s->timer); if (s->ctrl & R_CTRL_EN_MASK) { ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0); } else { ptimer_stop(s->timer); } + ptimer_transaction_commit(s->timer); break; case A_RELOAD: /* Writing to reload also sets the current timer value */ + ptimer_transaction_begin(s->timer); if (!value) { ptimer_stop(s->timer); } @@ -140,8 +142,10 @@ static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value, */ ptimer_run(s->timer, 0); } + ptimer_transaction_commit(s->timer); break; case A_VALUE: + ptimer_transaction_begin(s->timer); if (!value && !ptimer_get_limit(s->timer)) { ptimer_stop(s->timer); } @@ -149,6 +153,7 @@ static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value, if (value && (s->ctrl & R_CTRL_EN_MASK)) { ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0); } + ptimer_transaction_commit(s->timer); break; case A_INTSTATUS: /* Just one bit, which is W1C. */ @@ -191,9 +196,11 @@ static void cmsdk_apb_timer_reset(DeviceState *dev) trace_cmsdk_apb_timer_reset(); s->ctrl = 0; s->intstatus = 0; + ptimer_transaction_begin(s->timer); ptimer_stop(s->timer); /* Set the limit and the count */ ptimer_set_limit(s->timer, 0, 1); + ptimer_transaction_commit(s->timer); } static void cmsdk_apb_timer_init(Object *obj) @@ -210,21 +217,21 @@ static void cmsdk_apb_timer_init(Object *obj) static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp) { CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev); - QEMUBH *bh; if (s->pclk_frq == 0) { error_setg(errp, "CMSDK APB timer: pclk-frq property must be set"); return; } - bh = qemu_bh_new(cmsdk_apb_timer_tick, s); - s->timer = ptimer_init(bh, + s->timer = ptimer_init(cmsdk_apb_timer_tick, s, PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | PTIMER_POLICY_NO_IMMEDIATE_RELOAD | PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + ptimer_transaction_begin(s->timer); ptimer_set_freq(s->timer, s->pclk_frq); + ptimer_transaction_commit(s->timer); } static const VMStateDescription cmsdk_apb_timer_vmstate = { diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c index 021c4ef..3261222 100644 --- a/hw/timer/digic-timer.c +++ b/hw/timer/digic-timer.c @@ -29,7 +29,6 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/ptimer.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/log.h" @@ -52,7 +51,9 @@ static void digic_timer_reset(DeviceState *dev) { DigicTimerState *s = DIGIC_TIMER(dev); + ptimer_transaction_begin(s->ptimer); ptimer_stop(s->ptimer); + ptimer_transaction_commit(s->ptimer); s->control = 0; s->relvalue = 0; } @@ -93,16 +94,20 @@ static void digic_timer_write(void *opaque, hwaddr offset, break; } + ptimer_transaction_begin(s->ptimer); if (value & DIGIC_TIMER_CONTROL_EN) { ptimer_run(s->ptimer, 0); } s->control = (uint32_t)value; + ptimer_transaction_commit(s->ptimer); break; case DIGIC_TIMER_RELVALUE: s->relvalue = extract32(value, 0, 16); + ptimer_transaction_begin(s->ptimer); ptimer_set_limit(s->ptimer, s->relvalue, 1); + ptimer_transaction_commit(s->ptimer); break; case DIGIC_TIMER_VALUE: @@ -125,17 +130,24 @@ static const MemoryRegionOps digic_timer_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static void digic_timer_tick(void *opaque) +{ + /* Nothing to do on timer rollover */ +} + static void digic_timer_init(Object *obj) { DigicTimerState *s = DIGIC_TIMER(obj); - s->ptimer = ptimer_init(NULL, PTIMER_POLICY_DEFAULT); + s->ptimer = ptimer_init(digic_timer_tick, NULL, PTIMER_POLICY_DEFAULT); /* * FIXME: there is no documentation on Digic timer * frequency setup so let it always run at 1 MHz */ + ptimer_transaction_begin(s->ptimer); ptimer_set_freq(s->ptimer, 1 * 1000 * 1000); + ptimer_transaction_commit(s->ptimer); memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s, TYPE_DIGIC_TIMER, 0x100); diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c index d62025b..ab27fe1 100644 --- a/hw/timer/etraxfs_timer.c +++ b/hw/timer/etraxfs_timer.c @@ -328,9 +328,9 @@ static void etraxfs_timer_realize(DeviceState *dev, Error **errp) t->bh_t0 = qemu_bh_new(timer0_hit, t); t->bh_t1 = qemu_bh_new(timer1_hit, t); t->bh_wd = qemu_bh_new(watchdog_hit, t); - t->ptimer_t0 = ptimer_init(t->bh_t0, PTIMER_POLICY_DEFAULT); - t->ptimer_t1 = ptimer_init(t->bh_t1, PTIMER_POLICY_DEFAULT); - t->ptimer_wd = ptimer_init(t->bh_wd, PTIMER_POLICY_DEFAULT); + t->ptimer_t0 = ptimer_init_with_bh(t->bh_t0, PTIMER_POLICY_DEFAULT); + t->ptimer_t1 = ptimer_init_with_bh(t->bh_t1, PTIMER_POLICY_DEFAULT); + t->ptimer_wd = ptimer_init_with_bh(t->bh_wd, PTIMER_POLICY_DEFAULT); sysbus_init_irq(sbd, &t->irq); sysbus_init_irq(sbd, &t->nmi); diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c index 77b9af0..7225758 100644 --- a/hw/timer/exynos4210_mct.c +++ b/hw/timer/exynos4210_mct.c @@ -58,7 +58,6 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/timer.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "hw/ptimer.h" @@ -364,6 +363,7 @@ static void exynos4210_mct_update_freq(Exynos4210MCTState *s); /* * Set counter of FRC global timer. + * Must be called within exynos4210_gfrc_tx_begin/commit block. */ static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count) { @@ -385,6 +385,7 @@ static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s) /* * Stop global FRC timer + * Must be called within exynos4210_gfrc_tx_begin/commit block. */ static void exynos4210_gfrc_stop(Exynos4210MCTGT *s) { @@ -395,6 +396,7 @@ static void exynos4210_gfrc_stop(Exynos4210MCTGT *s) /* * Start global FRC timer + * Must be called within exynos4210_gfrc_tx_begin/commit block. */ static void exynos4210_gfrc_start(Exynos4210MCTGT *s) { @@ -404,6 +406,21 @@ static void exynos4210_gfrc_start(Exynos4210MCTGT *s) } /* + * Start ptimer transaction for global FRC timer; this is just for + * consistency with the way we wrap operations like stop and run. + */ +static void exynos4210_gfrc_tx_begin(Exynos4210MCTGT *s) +{ + ptimer_transaction_begin(s->ptimer_frc); +} + +/* Commit ptimer transaction for global FRC timer. */ +static void exynos4210_gfrc_tx_commit(Exynos4210MCTGT *s) +{ + ptimer_transaction_commit(s->ptimer_frc); +} + +/* * Find next nearest Comparator. If current Comparator value equals to other * Comparator value, skip them both */ @@ -492,6 +509,7 @@ static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id) /* * Restart global FRC timer + * Must be called within exynos4210_gfrc_tx_begin/commit block. */ static void exynos4210_gfrc_restart(Exynos4210MCTState *s) { @@ -589,6 +607,7 @@ static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s) /* * Set counter of FRC local timer. + * Must be called from within exynos4210_lfrc_tx_begin/commit block. */ static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s) { @@ -601,6 +620,7 @@ static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s) /* * Start local FRC timer + * Must be called from within exynos4210_lfrc_tx_begin/commit block. */ static void exynos4210_lfrc_start(Exynos4210MCTLT *s) { @@ -609,12 +629,25 @@ static void exynos4210_lfrc_start(Exynos4210MCTLT *s) /* * Stop local FRC timer + * Must be called from within exynos4210_lfrc_tx_begin/commit block. */ static void exynos4210_lfrc_stop(Exynos4210MCTLT *s) { ptimer_stop(s->ptimer_frc); } +/* Start ptimer transaction for local FRC timer */ +static void exynos4210_lfrc_tx_begin(Exynos4210MCTLT *s) +{ + ptimer_transaction_begin(s->ptimer_frc); +} + +/* Commit ptimer transaction for local FRC timer */ +static void exynos4210_lfrc_tx_commit(Exynos4210MCTLT *s) +{ + ptimer_transaction_commit(s->ptimer_frc); +} + /* * Local timer free running counter tick handler */ @@ -701,6 +734,7 @@ static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s) /* * Start local tick cnt timer. + * Must be called within exynos4210_ltick_tx_begin/commit block. */ static void exynos4210_ltick_cnt_start(struct tick_timer *s) { @@ -716,6 +750,7 @@ static void exynos4210_ltick_cnt_start(struct tick_timer *s) /* * Stop local tick cnt timer. + * Must be called within exynos4210_ltick_tx_begin/commit block. */ static void exynos4210_ltick_cnt_stop(struct tick_timer *s) { @@ -733,6 +768,18 @@ static void exynos4210_ltick_cnt_stop(struct tick_timer *s) } } +/* Start ptimer transaction for local tick timer */ +static void exynos4210_ltick_tx_begin(struct tick_timer *s) +{ + ptimer_transaction_begin(s->ptimer_tick); +} + +/* Commit ptimer transaction for local tick timer */ +static void exynos4210_ltick_tx_commit(struct tick_timer *s) +{ + ptimer_transaction_commit(s->ptimer_tick); +} + /* * Get counter for CNT timer */ @@ -778,6 +825,7 @@ static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s) /* * Set new values of counters for CNT and INT timers + * Must be called within exynos4210_ltick_tx_begin/commit block. */ static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt, uint32_t new_int) @@ -851,7 +899,9 @@ static void exynos4210_ltick_recalc_count(struct tick_timer *s) static void exynos4210_ltick_timer_init(struct tick_timer *s) { exynos4210_ltick_int_stop(s); + exynos4210_ltick_tx_begin(s); exynos4210_ltick_cnt_stop(s); + exynos4210_ltick_tx_commit(s); s->count = 0; s->distance = 0; @@ -933,6 +983,19 @@ static void exynos4210_ltick_event(void *opaque) exynos4210_ltick_int_start(&s->tick_timer); } +static void tx_ptimer_set_freq(ptimer_state *s, uint32_t freq) +{ + /* + * callers of exynos4210_mct_update_freq() never do anything + * else that needs to be in the same ptimer transaction, so + * to avoid a lot of repetition we have a convenience function + * for begin/set_freq/commit. + */ + ptimer_transaction_begin(s); + ptimer_set_freq(s, freq); + ptimer_transaction_commit(s); +} + /* update timer frequency */ static void exynos4210_mct_update_freq(Exynos4210MCTState *s) { @@ -945,13 +1008,13 @@ static void exynos4210_mct_update_freq(Exynos4210MCTState *s) DPRINTF("freq=%dHz\n", s->freq); /* global timer */ - ptimer_set_freq(s->g_timer.ptimer_frc, s->freq); + tx_ptimer_set_freq(s->g_timer.ptimer_frc, s->freq); /* local timer */ - ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq); - ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq); - ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq); - ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq); + tx_ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq); + tx_ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq); + tx_ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq); + tx_ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq); } } @@ -965,7 +1028,9 @@ static void exynos4210_mct_reset(DeviceState *d) /* global timer */ memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg)); + exynos4210_gfrc_tx_begin(&s->g_timer); exynos4210_gfrc_stop(&s->g_timer); + exynos4210_gfrc_tx_commit(&s->g_timer); /* local timer */ memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt)); @@ -978,7 +1043,9 @@ static void exynos4210_mct_reset(DeviceState *d) s->l_timer[i].tick_timer.count = 0; s->l_timer[i].tick_timer.distance = 0; s->l_timer[i].tick_timer.progress = 0; + exynos4210_lfrc_tx_begin(&s->l_timer[i]); ptimer_stop(s->l_timer[i].ptimer_frc); + exynos4210_lfrc_tx_commit(&s->l_timer[i]); exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer); } @@ -1144,7 +1211,9 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, } s->g_timer.reg.cnt = new_frc; + exynos4210_gfrc_tx_begin(&s->g_timer); exynos4210_gfrc_restart(s); + exynos4210_gfrc_tx_commit(&s->g_timer); break; case G_CNT_WSTAT: @@ -1168,7 +1237,9 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index); } + exynos4210_gfrc_tx_begin(&s->g_timer); exynos4210_gfrc_restart(s); + exynos4210_gfrc_tx_commit(&s->g_timer); break; case G_TCON: @@ -1178,6 +1249,8 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, DPRINTF("global timer write to reg.g_tcon %llx\n", value); + exynos4210_gfrc_tx_begin(&s->g_timer); + /* Start FRC if transition from disabled to enabled */ if ((value & G_TCON_TIMER_ENABLE) > (old_val & G_TCON_TIMER_ENABLE)) { @@ -1195,6 +1268,8 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, exynos4210_gfrc_restart(s); } } + + exynos4210_gfrc_tx_commit(&s->g_timer); break; case G_INT_CSTAT: @@ -1245,6 +1320,7 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE; s->l_timer[lt_i].reg.tcon = value; + exynos4210_ltick_tx_begin(&s->l_timer[lt_i].tick_timer); /* Stop local CNT */ if ((value & L_TCON_TICK_START) < (old_val & L_TCON_TICK_START)) { @@ -1272,8 +1348,10 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, DPRINTF("local timer[%d] start int\n", lt_i); exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer); } + exynos4210_ltick_tx_commit(&s->l_timer[lt_i].tick_timer); /* Start or Stop local FRC if TCON changed */ + exynos4210_lfrc_tx_begin(&s->l_timer[lt_i]); if ((value & L_TCON_FRC_START) > (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { DPRINTF("local timer[%d] start frc\n", lt_i); @@ -1284,6 +1362,7 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, DPRINTF("local timer[%d] stop frc\n", lt_i); exynos4210_lfrc_stop(&s->l_timer[lt_i]); } + exynos4210_lfrc_tx_commit(&s->l_timer[lt_i]); break; case L0_TCNTB: case L1_TCNTB: @@ -1295,8 +1374,10 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, * Due to this we should reload timer to nearest moment when CNT is * expired and then in event handler update tcntb to new TCNTB value. */ + exynos4210_ltick_tx_begin(&s->l_timer[lt_i].tick_timer); exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value, s->l_timer[lt_i].tick_timer.icntb); + exynos4210_ltick_tx_commit(&s->l_timer[lt_i].tick_timer); s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE; s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value; @@ -1425,20 +1506,20 @@ static void exynos4210_mct_init(Object *obj) int i; Exynos4210MCTState *s = EXYNOS4210_MCT(obj); SysBusDevice *dev = SYS_BUS_DEVICE(obj); - QEMUBH *bh[2]; /* Global timer */ - bh[0] = qemu_bh_new(exynos4210_gfrc_event, s); - s->g_timer.ptimer_frc = ptimer_init(bh[0], PTIMER_POLICY_DEFAULT); + s->g_timer.ptimer_frc = ptimer_init(exynos4210_gfrc_event, s, + PTIMER_POLICY_DEFAULT); memset(&s->g_timer.reg, 0, sizeof(struct gregs)); /* Local timers */ for (i = 0; i < 2; i++) { - bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]); - bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]); s->l_timer[i].tick_timer.ptimer_tick = - ptimer_init(bh[0], PTIMER_POLICY_DEFAULT); - s->l_timer[i].ptimer_frc = ptimer_init(bh[1], PTIMER_POLICY_DEFAULT); + ptimer_init(exynos4210_ltick_event, &s->l_timer[i], + PTIMER_POLICY_DEFAULT); + s->l_timer[i].ptimer_frc = + ptimer_init(exynos4210_lfrc_event, &s->l_timer[i], + PTIMER_POLICY_DEFAULT); s->l_timer[i].id = i; } diff --git a/hw/timer/exynos4210_pwm.c b/hw/timer/exynos4210_pwm.c index b7fad2a..59a8c08 100644 --- a/hw/timer/exynos4210_pwm.c +++ b/hw/timer/exynos4210_pwm.c @@ -25,7 +25,6 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/timer.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "hw/ptimer.h" @@ -150,7 +149,9 @@ static const VMStateDescription vmstate_exynos4210_pwm_state = { }; /* - * PWM update frequency + * PWM update frequency. + * Must be called within a ptimer_transaction_begin/commit block + * for s->timer[id].ptimer. */ static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id) { @@ -281,12 +282,15 @@ static void exynos4210_pwm_write(void *opaque, hwaddr offset, /* update timers frequencies */ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + ptimer_transaction_begin(s->timer[i].ptimer); exynos4210_pwm_update_freq(s, s->timer[i].id); + ptimer_transaction_commit(s->timer[i].ptimer); } break; case TCON: for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + ptimer_transaction_begin(s->timer[i].ptimer); if ((value & TCON_TIMER_MANUAL_UPD(i)) > (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) { /* @@ -315,6 +319,7 @@ static void exynos4210_pwm_write(void *opaque, hwaddr offset, ptimer_stop(s->timer[i].ptimer); DPRINTF("stop timer %d\n", i); } + ptimer_transaction_commit(s->timer[i].ptimer); } s->reg_tcon = value; break; @@ -369,8 +374,10 @@ static void exynos4210_pwm_reset(DeviceState *d) s->timer[i].reg_tcmpb = 0; s->timer[i].reg_tcntb = 0; + ptimer_transaction_begin(s->timer[i].ptimer); exynos4210_pwm_update_freq(s, s->timer[i].id); ptimer_stop(s->timer[i].ptimer); + ptimer_transaction_commit(s->timer[i].ptimer); } } @@ -388,12 +395,12 @@ static void exynos4210_pwm_init(Object *obj) Exynos4210PWMState *s = EXYNOS4210_PWM(obj); SysBusDevice *dev = SYS_BUS_DEVICE(obj); int i; - QEMUBH *bh; for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]); sysbus_init_irq(dev, &s->timer[i].irq); - s->timer[i].ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->timer[i].ptimer = ptimer_init(exynos4210_pwm_tick, + &s->timer[i], + PTIMER_POLICY_DEFAULT); s->timer[i].id = i; s->timer[i].parent = s; } diff --git a/hw/timer/exynos4210_rtc.c b/hw/timer/exynos4210_rtc.c index ea68904..f85483a 100644 --- a/hw/timer/exynos4210_rtc.c +++ b/hw/timer/exynos4210_rtc.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "hw/sysbus.h" #include "migration/vmstate.h" @@ -195,6 +194,7 @@ static void check_alarm_raise(Exynos4210RTCState *s) * RTC update frequency * Parameters: * reg_value - current RTCCON register or his new value + * Must be called within a ptimer_transaction_begin/commit block for s->ptimer. */ static void exynos4210_rtc_update_freq(Exynos4210RTCState *s, uint32_t reg_value) @@ -401,6 +401,8 @@ static void exynos4210_rtc_write(void *opaque, hwaddr offset, } break; case RTCCON: + ptimer_transaction_begin(s->ptimer_1Hz); + ptimer_transaction_begin(s->ptimer); if (value & RTC_ENABLE) { exynos4210_rtc_update_freq(s, value); } @@ -430,6 +432,8 @@ static void exynos4210_rtc_write(void *opaque, hwaddr offset, ptimer_stop(s->ptimer); } } + ptimer_transaction_commit(s->ptimer_1Hz); + ptimer_transaction_commit(s->ptimer); s->reg_rtccon = value; break; case TICCNT: @@ -537,9 +541,13 @@ static void exynos4210_rtc_reset(DeviceState *d) s->reg_curticcnt = 0; + ptimer_transaction_begin(s->ptimer); exynos4210_rtc_update_freq(s, s->reg_rtccon); ptimer_stop(s->ptimer); + ptimer_transaction_commit(s->ptimer); + ptimer_transaction_begin(s->ptimer_1Hz); ptimer_stop(s->ptimer_1Hz); + ptimer_transaction_commit(s->ptimer_1Hz); } static const MemoryRegionOps exynos4210_rtc_ops = { @@ -555,16 +563,18 @@ static void exynos4210_rtc_init(Object *obj) { Exynos4210RTCState *s = EXYNOS4210_RTC(obj); SysBusDevice *dev = SYS_BUS_DEVICE(obj); - QEMUBH *bh; - bh = qemu_bh_new(exynos4210_rtc_tick, s); - s->ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->ptimer = ptimer_init(exynos4210_rtc_tick, s, PTIMER_POLICY_DEFAULT); + ptimer_transaction_begin(s->ptimer); ptimer_set_freq(s->ptimer, RTC_BASE_FREQ); exynos4210_rtc_update_freq(s, 0); + ptimer_transaction_commit(s->ptimer); - bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s); - s->ptimer_1Hz = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->ptimer_1Hz = ptimer_init(exynos4210_rtc_1Hz_tick, + s, PTIMER_POLICY_DEFAULT); + ptimer_transaction_begin(s->ptimer_1Hz); ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ); + ptimer_transaction_commit(s->ptimer_1Hz); sysbus_init_irq(dev, &s->alm_irq); sysbus_init_irq(dev, &s->tick_irq); diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c index 32dbf87..bb09268 100644 --- a/hw/timer/grlib_gptimer.c +++ b/hw/timer/grlib_gptimer.c @@ -366,7 +366,7 @@ static void grlib_gptimer_realize(DeviceState *dev, Error **errp) timer->unit = unit; timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); - timer->ptimer = ptimer_init(timer->bh, PTIMER_POLICY_DEFAULT); + timer->ptimer = ptimer_init_with_bh(timer->bh, PTIMER_POLICY_DEFAULT); timer->id = i; /* One IRQ line for each timer */ diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c index f54e059..baf6338 100644 --- a/hw/timer/imx_epit.c +++ b/hw/timer/imx_epit.c @@ -17,7 +17,6 @@ #include "migration/vmstate.h" #include "hw/irq.h" #include "hw/misc/imx_ccm.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/log.h" @@ -74,6 +73,10 @@ static void imx_epit_update_int(IMXEPITState *s) } } +/* + * Must be called from within a ptimer_transaction_begin/commit block + * for both s->timer_cmp and s->timer_reload. + */ static void imx_epit_set_freq(IMXEPITState *s) { uint32_t clksrc; @@ -105,6 +108,8 @@ static void imx_epit_reset(DeviceState *dev) s->lr = EPIT_TIMER_MAX; s->cmp = 0; s->cnt = 0; + ptimer_transaction_begin(s->timer_cmp); + ptimer_transaction_begin(s->timer_reload); /* stop both timers */ ptimer_stop(s->timer_cmp); ptimer_stop(s->timer_reload); @@ -117,6 +122,8 @@ static void imx_epit_reset(DeviceState *dev) /* if the timer is still enabled, restart it */ ptimer_run(s->timer_reload, 0); } + ptimer_transaction_commit(s->timer_cmp); + ptimer_transaction_commit(s->timer_reload); } static uint32_t imx_epit_update_count(IMXEPITState *s) @@ -164,6 +171,7 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) return reg_value; } +/* Must be called from ptimer_transaction_begin/commit block for s->timer_cmp */ static void imx_epit_reload_compare_timer(IMXEPITState *s) { if ((s->cr & (CR_EN | CR_OCIEN)) == (CR_EN | CR_OCIEN)) { @@ -191,6 +199,8 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, switch (offset >> 2) { case 0: /* CR */ + ptimer_transaction_begin(s->timer_cmp); + ptimer_transaction_begin(s->timer_reload); oldcr = s->cr; s->cr = value & 0x03ffffff; @@ -231,6 +241,9 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, } else { ptimer_stop(s->timer_cmp); } + + ptimer_transaction_commit(s->timer_cmp); + ptimer_transaction_commit(s->timer_reload); break; case 1: /* SR - ACK*/ @@ -244,6 +257,8 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, case 2: /* LR - set ticks */ s->lr = value; + ptimer_transaction_begin(s->timer_cmp); + ptimer_transaction_begin(s->timer_reload); if (s->cr & CR_RLD) { /* Also set the limit if the LRD bit is set */ /* If IOVW bit is set then set the timer value */ @@ -255,12 +270,16 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, } imx_epit_reload_compare_timer(s); + ptimer_transaction_commit(s->timer_cmp); + ptimer_transaction_commit(s->timer_reload); break; case 3: /* CMP */ s->cmp = value; + ptimer_transaction_begin(s->timer_cmp); imx_epit_reload_compare_timer(s); + ptimer_transaction_commit(s->timer_cmp); break; @@ -281,6 +300,11 @@ static void imx_epit_cmp(void *opaque) imx_epit_update_int(s); } +static void imx_epit_reload(void *opaque) +{ + /* No action required on rollover of timer_reload */ +} + static const MemoryRegionOps imx_epit_ops = { .read = imx_epit_read, .write = imx_epit_write, @@ -308,7 +332,6 @@ static void imx_epit_realize(DeviceState *dev, Error **errp) { IMXEPITState *s = IMX_EPIT(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - QEMUBH *bh; DPRINTF("\n"); @@ -317,10 +340,9 @@ static void imx_epit_realize(DeviceState *dev, Error **errp) 0x00001000); sysbus_init_mmio(sbd, &s->iomem); - s->timer_reload = ptimer_init(NULL, PTIMER_POLICY_DEFAULT); + s->timer_reload = ptimer_init(imx_epit_reload, s, PTIMER_POLICY_DEFAULT); - bh = qemu_bh_new(imx_epit_cmp, s); - s->timer_cmp = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->timer_cmp = ptimer_init(imx_epit_cmp, s, PTIMER_POLICY_DEFAULT); } static void imx_epit_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 49a441f..5c0d9a2 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -16,7 +16,6 @@ #include "hw/irq.h" #include "hw/timer/imx_gpt.h" #include "migration/vmstate.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/log.h" @@ -127,6 +126,7 @@ static const IMXClk imx7_gpt_clocks[] = { CLK_NONE, /* 111 not defined */ }; +/* Must be called from within ptimer_transaction_begin/commit block */ static void imx_gpt_set_freq(IMXGPTState *s) { uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3); @@ -167,6 +167,7 @@ static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg, return timeout; } +/* Must be called from within ptimer_transaction_begin/commit block */ static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event) { uint32_t timeout = GPT_TIMER_MAX; @@ -313,6 +314,7 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) static void imx_gpt_reset_common(IMXGPTState *s, bool is_soft_reset) { + ptimer_transaction_begin(s->timer); /* stop timer */ ptimer_stop(s->timer); @@ -350,6 +352,7 @@ static void imx_gpt_reset_common(IMXGPTState *s, bool is_soft_reset) if (s->freq && (s->cr & GPT_CR_EN)) { ptimer_run(s->timer, 1); } + ptimer_transaction_commit(s->timer); } static void imx_gpt_soft_reset(DeviceState *dev) @@ -382,6 +385,7 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, imx_gpt_soft_reset(DEVICE(s)); } else { /* set our freq, as the source might have changed */ + ptimer_transaction_begin(s->timer); imx_gpt_set_freq(s); if ((oldreg ^ s->cr) & GPT_CR_EN) { @@ -397,12 +401,15 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, ptimer_stop(s->timer); } } + ptimer_transaction_commit(s->timer); } break; case 1: /* Prescaler */ s->pr = value & 0xfff; + ptimer_transaction_begin(s->timer); imx_gpt_set_freq(s); + ptimer_transaction_commit(s->timer); break; case 2: /* SR */ @@ -414,13 +421,16 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, s->ir = value & 0x3f; imx_gpt_update_int(s); + ptimer_transaction_begin(s->timer); imx_gpt_compute_next_timeout(s, false); + ptimer_transaction_commit(s->timer); break; case 4: /* OCR1 -- output compare register */ s->ocr1 = value; + ptimer_transaction_begin(s->timer); /* In non-freerun mode, reset count when this register is written */ if (!(s->cr & GPT_CR_FRR)) { s->next_timeout = GPT_TIMER_MAX; @@ -429,6 +439,7 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, /* compute the new timeout */ imx_gpt_compute_next_timeout(s, false); + ptimer_transaction_commit(s->timer); break; @@ -436,7 +447,9 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, s->ocr2 = value; /* compute the new timeout */ + ptimer_transaction_begin(s->timer); imx_gpt_compute_next_timeout(s, false); + ptimer_transaction_commit(s->timer); break; @@ -444,7 +457,9 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, s->ocr3 = value; /* compute the new timeout */ + ptimer_transaction_begin(s->timer); imx_gpt_compute_next_timeout(s, false); + ptimer_transaction_commit(s->timer); break; @@ -484,15 +499,13 @@ static void imx_gpt_realize(DeviceState *dev, Error **errp) { IMXGPTState *s = IMX_GPT(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - QEMUBH *bh; sysbus_init_irq(sbd, &s->irq); memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpt_ops, s, TYPE_IMX_GPT, 0x00001000); sysbus_init_mmio(sbd, &s->iomem); - bh = qemu_bh_new(imx_gpt_timeout, s); - s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->timer = ptimer_init(imx_gpt_timeout, s, PTIMER_POLICY_DEFAULT); } static void imx_gpt_class_init(ObjectClass *klass, void *data) diff --git a/hw/timer/lm32_timer.c b/hw/timer/lm32_timer.c index cf316ed..fabde76 100644 --- a/hw/timer/lm32_timer.c +++ b/hw/timer/lm32_timer.c @@ -196,7 +196,7 @@ static void lm32_timer_realize(DeviceState *dev, Error **errp) LM32TimerState *s = LM32_TIMER(dev); s->bh = qemu_bh_new(timer_hit, s); - s->ptimer = ptimer_init(s->bh, PTIMER_POLICY_DEFAULT); + s->ptimer = ptimer_init_with_bh(s->bh, PTIMER_POLICY_DEFAULT); ptimer_set_freq(s->ptimer, s->freq_hz); } diff --git a/hw/timer/milkymist-sysctl.c b/hw/timer/milkymist-sysctl.c index 6aedc11..5193c03 100644 --- a/hw/timer/milkymist-sysctl.c +++ b/hw/timer/milkymist-sysctl.c @@ -294,8 +294,8 @@ static void milkymist_sysctl_realize(DeviceState *dev, Error **errp) s->bh0 = qemu_bh_new(timer0_hit, s); s->bh1 = qemu_bh_new(timer1_hit, s); - s->ptimer0 = ptimer_init(s->bh0, PTIMER_POLICY_DEFAULT); - s->ptimer1 = ptimer_init(s->bh1, PTIMER_POLICY_DEFAULT); + s->ptimer0 = ptimer_init_with_bh(s->bh0, PTIMER_POLICY_DEFAULT); + s->ptimer1 = ptimer_init_with_bh(s->bh1, PTIMER_POLICY_DEFAULT); ptimer_set_freq(s->ptimer0, s->freq_hz); ptimer_set_freq(s->ptimer1, s->freq_hz); diff --git a/hw/timer/mss-timer.c b/hw/timer/mss-timer.c index 45f1cf4..b1c9a80 100644 --- a/hw/timer/mss-timer.c +++ b/hw/timer/mss-timer.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/log.h" #include "hw/irq.h" @@ -67,6 +66,7 @@ static void timer_update_irq(struct Msf2Timer *st) qemu_set_irq(st->irq, (ier && isr)); } +/* Must be called from within a ptimer_transaction_begin/commit block */ static void timer_update(struct Msf2Timer *st) { uint64_t count; @@ -159,7 +159,9 @@ timer_write(void *opaque, hwaddr offset, switch (addr) { case R_TIM_CTRL: st->regs[R_TIM_CTRL] = value; + ptimer_transaction_begin(st->ptimer); timer_update(st); + ptimer_transaction_commit(st->ptimer); break; case R_TIM_RIS: @@ -171,7 +173,9 @@ timer_write(void *opaque, hwaddr offset, case R_TIM_LOADVAL: st->regs[R_TIM_LOADVAL] = value; if (st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL) { + ptimer_transaction_begin(st->ptimer); timer_update(st); + ptimer_transaction_commit(st->ptimer); } break; @@ -228,9 +232,10 @@ static void mss_timer_init(Object *obj) for (i = 0; i < NUM_TIMERS; i++) { struct Msf2Timer *st = &t->timers[i]; - st->bh = qemu_bh_new(timer_hit, st); - st->ptimer = ptimer_init(st->bh, PTIMER_POLICY_DEFAULT); + st->ptimer = ptimer_init(timer_hit, st, PTIMER_POLICY_DEFAULT); + ptimer_transaction_begin(st->ptimer); ptimer_set_freq(st->ptimer, t->freq_hz); + ptimer_transaction_commit(st->ptimer); sysbus_init_irq(SYS_BUS_DEVICE(obj), &st->irq); } diff --git a/hw/timer/puv3_ost.c b/hw/timer/puv3_ost.c index 6fe3700..0898da5 100644 --- a/hw/timer/puv3_ost.c +++ b/hw/timer/puv3_ost.c @@ -129,7 +129,7 @@ static void puv3_ost_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq); s->bh = qemu_bh_new(puv3_ost_tick, s); - s->ptimer = ptimer_init(s->bh, PTIMER_POLICY_DEFAULT); + s->ptimer = ptimer_init_with_bh(s->bh, PTIMER_POLICY_DEFAULT); ptimer_set_freq(s->ptimer, 50 * 1000 * 1000); memory_region_init_io(&s->iomem, OBJECT(s), &puv3_ost_ops, s, "puv3_ost", diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index adcc0c1..48a81b4 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -204,7 +204,7 @@ static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq) s->irq = irq; bh = qemu_bh_new(sh_timer_tick, s); - s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT); sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor); sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt); diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index 38fd32b..692d213 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -393,7 +393,7 @@ static void slavio_timer_init(Object *obj) tc->timer_index = i; bh = qemu_bh_new(slavio_timer_irq, tc); - s->cputimer[i].timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->cputimer[i].timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT); ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD); size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE; diff --git a/hw/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c index 3555182..92dbff3 100644 --- a/hw/timer/xilinx_timer.c +++ b/hw/timer/xilinx_timer.c @@ -221,7 +221,7 @@ static void xilinx_timer_realize(DeviceState *dev, Error **errp) xt->parent = t; xt->nr = i; xt->bh = qemu_bh_new(timer_hit, xt); - xt->ptimer = ptimer_init(xt->bh, PTIMER_POLICY_DEFAULT); + xt->ptimer = ptimer_init_with_bh(xt->bh, PTIMER_POLICY_DEFAULT); ptimer_set_freq(xt->ptimer, t->freq_hz); } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c5e6fe6..12fac39 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -651,6 +651,7 @@ retry: } g_free(vdev->msi_vectors); + vdev->msi_vectors = NULL; if (ret > 0 && ret != vdev->nr_vectors) { vdev->nr_vectors = ret; diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index 964ce78..e2f70fb 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -11,9 +11,11 @@ common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-$(CONFIG_VIRTIO_MMIO) += virtio-mmio.o obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon.o obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-crypto.o +obj-$(CONFIG_VHOST_USER_FS) += vhost-user-fs.o obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-pci.o obj-$(CONFIG_VIRTIO_PMEM) += virtio-pmem.o common-obj-$(call land,$(CONFIG_VIRTIO_PMEM),$(CONFIG_VIRTIO_PCI)) += virtio-pmem-pci.o +obj-$(call land,$(CONFIG_VHOST_USER_FS),$(CONFIG_VIRTIO_PCI)) += vhost-user-fs-pci.o obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o ifeq ($(CONFIG_VIRTIO_PCI),y) diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c new file mode 100644 index 0000000..933a3f2 --- /dev/null +++ b/hw/virtio/vhost-user-fs-pci.c @@ -0,0 +1,85 @@ +/* + * Vhost-user filesystem virtio device PCI glue + * + * Copyright 2018-2019 Red Hat, Inc. + * + * Authors: + * Dr. David Alan Gilbert <dgilbert@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/vhost-user-fs.h" +#include "virtio-pci.h" + +struct VHostUserFSPCI { + VirtIOPCIProxy parent_obj; + VHostUserFS vdev; +}; + +typedef struct VHostUserFSPCI VHostUserFSPCI; + +#define TYPE_VHOST_USER_FS_PCI "vhost-user-fs-pci-base" + +#define VHOST_USER_FS_PCI(obj) \ + OBJECT_CHECK(VHostUserFSPCI, (obj), TYPE_VHOST_USER_FS_PCI) + +static Property vhost_user_fs_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserFSPCI *dev = VHOST_USER_FS_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = dev->vdev.conf.num_request_queues + 1; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_user_fs_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vhost_user_fs_pci_realize; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->props = vhost_user_fs_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */ + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_STORAGE_OTHER; +} + +static void vhost_user_fs_pci_instance_init(Object *obj) +{ + VHostUserFSPCI *dev = VHOST_USER_FS_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_FS); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_fs_pci_info = { + .base_name = TYPE_VHOST_USER_FS_PCI, + .non_transitional_name = "vhost-user-fs-pci", + .instance_size = sizeof(VHostUserFSPCI), + .instance_init = vhost_user_fs_pci_instance_init, + .class_init = vhost_user_fs_pci_class_init, +}; + +static void vhost_user_fs_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_fs_pci_info); +} + +type_init(vhost_user_fs_pci_register); diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c new file mode 100644 index 0000000..f0df7f4 --- /dev/null +++ b/hw/virtio/vhost-user-fs.c @@ -0,0 +1,299 @@ +/* + * Vhost-user filesystem virtio device + * + * Copyright 2018-2019 Red Hat, Inc. + * + * Authors: + * Stefan Hajnoczi <stefanha@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include <sys/ioctl.h> +#include "standard-headers/linux/virtio_fs.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/virtio-access.h" +#include "qemu/error-report.h" +#include "hw/virtio/vhost-user-fs.h" +#include "monitor/monitor.h" + +static void vuf_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VHostUserFS *fs = VHOST_USER_FS(vdev); + struct virtio_fs_config fscfg = {}; + + memcpy((char *)fscfg.tag, fs->conf.tag, + MIN(strlen(fs->conf.tag) + 1, sizeof(fscfg.tag))); + + virtio_stl_p(vdev, &fscfg.num_request_queues, fs->conf.num_request_queues); + + memcpy(config, &fscfg, sizeof(fscfg)); +} + +static void vuf_start(VirtIODevice *vdev) +{ + VHostUserFS *fs = VHOST_USER_FS(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + int i; + + if (!k->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + return; + } + + ret = vhost_dev_enable_notifiers(&fs->vhost_dev, vdev); + if (ret < 0) { + error_report("Error enabling host notifiers: %d", -ret); + return; + } + + ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, true); + if (ret < 0) { + error_report("Error binding guest notifier: %d", -ret); + goto err_host_notifiers; + } + + fs->vhost_dev.acked_features = vdev->guest_features; + ret = vhost_dev_start(&fs->vhost_dev, vdev); + if (ret < 0) { + error_report("Error starting vhost: %d", -ret); + goto err_guest_notifiers; + } + + /* + * guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i = 0; i < fs->vhost_dev.nvqs; i++) { + vhost_virtqueue_mask(&fs->vhost_dev, vdev, i, false); + } + + return; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(&fs->vhost_dev, vdev); +} + +static void vuf_stop(VirtIODevice *vdev) +{ + VHostUserFS *fs = VHOST_USER_FS(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + + if (!k->set_guest_notifiers) { + return; + } + + vhost_dev_stop(&fs->vhost_dev, vdev); + + ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return; + } + + vhost_dev_disable_notifiers(&fs->vhost_dev, vdev); +} + +static void vuf_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserFS *fs = VHOST_USER_FS(vdev); + bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; + + if (!vdev->vm_running) { + should_start = false; + } + + if (fs->vhost_dev.started == should_start) { + return; + } + + if (should_start) { + vuf_start(vdev); + } else { + vuf_stop(vdev); + } +} + +static uint64_t vuf_get_features(VirtIODevice *vdev, + uint64_t requested_features, + Error **errp) +{ + /* No feature bits used yet */ + return requested_features; +} + +static void vuf_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + /* + * Not normally called; it's the daemon that handles the queue; + * however virtio's cleanup path can call this. + */ +} + +static void vuf_guest_notifier_mask(VirtIODevice *vdev, int idx, + bool mask) +{ + VHostUserFS *fs = VHOST_USER_FS(vdev); + + vhost_virtqueue_mask(&fs->vhost_dev, vdev, idx, mask); +} + +static bool vuf_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VHostUserFS *fs = VHOST_USER_FS(vdev); + + return vhost_virtqueue_pending(&fs->vhost_dev, idx); +} + +static void vuf_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserFS *fs = VHOST_USER_FS(dev); + unsigned int i; + size_t len; + int ret; + + if (!fs->conf.chardev.chr) { + error_setg(errp, "missing chardev"); + return; + } + + if (!fs->conf.tag) { + error_setg(errp, "missing tag property"); + return; + } + len = strlen(fs->conf.tag); + if (len == 0) { + error_setg(errp, "tag property cannot be empty"); + return; + } + if (len > sizeof_field(struct virtio_fs_config, tag)) { + error_setg(errp, "tag property must be %zu bytes or less", + sizeof_field(struct virtio_fs_config, tag)); + return; + } + + if (fs->conf.num_request_queues == 0) { + error_setg(errp, "num-request-queues property must be larger than 0"); + return; + } + + if (!is_power_of_2(fs->conf.queue_size)) { + error_setg(errp, "queue-size property must be a power of 2"); + return; + } + + if (fs->conf.queue_size > VIRTQUEUE_MAX_SIZE) { + error_setg(errp, "queue-size property must be %u or smaller", + VIRTQUEUE_MAX_SIZE); + return; + } + + if (!vhost_user_init(&fs->vhost_user, &fs->conf.chardev, errp)) { + return; + } + + virtio_init(vdev, "vhost-user-fs", VIRTIO_ID_FS, + sizeof(struct virtio_fs_config)); + + /* Hiprio queue */ + virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output); + + /* Request queues */ + for (i = 0; i < fs->conf.num_request_queues; i++) { + virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output); + } + + /* 1 high prio queue, plus the number configured */ + fs->vhost_dev.nvqs = 1 + fs->conf.num_request_queues; + fs->vhost_dev.vqs = g_new0(struct vhost_virtqueue, fs->vhost_dev.nvqs); + ret = vhost_dev_init(&fs->vhost_dev, &fs->vhost_user, + VHOST_BACKEND_TYPE_USER, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, "vhost_dev_init failed"); + goto err_virtio; + } + + return; + +err_virtio: + vhost_user_cleanup(&fs->vhost_user); + virtio_cleanup(vdev); + g_free(fs->vhost_dev.vqs); + return; +} + +static void vuf_device_unrealize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserFS *fs = VHOST_USER_FS(dev); + + /* This will stop vhost backend if appropriate. */ + vuf_set_status(vdev, 0); + + vhost_dev_cleanup(&fs->vhost_dev); + + vhost_user_cleanup(&fs->vhost_user); + + virtio_cleanup(vdev); + g_free(fs->vhost_dev.vqs); + fs->vhost_dev.vqs = NULL; +} + +static const VMStateDescription vuf_vmstate = { + .name = "vhost-user-fs", + .unmigratable = 1, +}; + +static Property vuf_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev), + DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag), + DEFINE_PROP_UINT16("num-request-queues", VHostUserFS, + conf.num_request_queues, 1), + DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128), + DEFINE_PROP_STRING("vhostfd", VHostUserFS, conf.vhostfd), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vuf_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + dc->props = vuf_properties; + dc->vmsd = &vuf_vmstate; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + vdc->realize = vuf_device_realize; + vdc->unrealize = vuf_device_unrealize; + vdc->get_features = vuf_get_features; + vdc->get_config = vuf_get_config; + vdc->set_status = vuf_set_status; + vdc->guest_notifier_mask = vuf_guest_notifier_mask; + vdc->guest_notifier_pending = vuf_guest_notifier_pending; +} + +static const TypeInfo vuf_info = { + .name = TYPE_VHOST_USER_FS, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VHostUserFS), + .class_init = vuf_class_init, +}; + +static void vuf_register_types(void) +{ + type_register_static(&vuf_info); +} + +type_init(vuf_register_types) diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c index 6bf43f9..e6f3b93 100644 --- a/hw/watchdog/cmsdk-apb-watchdog.c +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -24,7 +24,6 @@ #include "qemu/log.h" #include "trace.h" #include "qapi/error.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "sysemu/watchdog.h" #include "hw/sysbus.h" @@ -200,8 +199,10 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset, * Reset the load value and the current count, and make sure * we're counting. */ + ptimer_transaction_begin(s->timer); ptimer_set_limit(s->timer, value, 1); ptimer_run(s->timer, 0); + ptimer_transaction_commit(s->timer); break; case A_WDOGCONTROL: if (s->is_luminary && 0 != (R_WDOGCONTROL_INTEN_MASK & s->control)) { @@ -217,7 +218,9 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset, break; case A_WDOGINTCLR: s->intstatus = 0; + ptimer_transaction_begin(s->timer); ptimer_set_count(s->timer, ptimer_get_limit(s->timer)); + ptimer_transaction_commit(s->timer); cmsdk_apb_watchdog_update(s); break; case A_WDOGLOCK: @@ -299,8 +302,10 @@ static void cmsdk_apb_watchdog_reset(DeviceState *dev) s->itop = 0; s->resetstatus = 0; /* Set the limit and the count */ + ptimer_transaction_begin(s->timer); ptimer_set_limit(s->timer, 0xffffffff, 1); ptimer_run(s->timer, 0); + ptimer_transaction_commit(s->timer); } static void cmsdk_apb_watchdog_init(Object *obj) @@ -320,7 +325,6 @@ static void cmsdk_apb_watchdog_init(Object *obj) static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp) { CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev); - QEMUBH *bh; if (s->wdogclk_frq == 0) { error_setg(errp, @@ -328,14 +332,15 @@ static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp) return; } - bh = qemu_bh_new(cmsdk_apb_watchdog_tick, s); - s->timer = ptimer_init(bh, + s->timer = ptimer_init(cmsdk_apb_watchdog_tick, s, PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | PTIMER_POLICY_NO_IMMEDIATE_RELOAD | PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + ptimer_transaction_begin(s->timer); ptimer_set_freq(s->timer, s->wdogclk_frq); + ptimer_transaction_commit(s->timer); } static const VMStateDescription cmsdk_apb_watchdog_vmstate = { diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index 9b93213..145be6f 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -40,12 +40,14 @@ #define WDT_DRIVE_TYPE_MASK (0xFF << 24) #define WDT_PUSH_PULL_MAGIC (0xA8 << 24) #define WDT_OPEN_DRAIN_MAGIC (0x8A << 24) +#define WDT_RESET_MASK1 (0x1c / 4) #define WDT_TIMEOUT_STATUS (0x10 / 4) #define WDT_TIMEOUT_CLEAR (0x14 / 4) #define WDT_RESTART_MAGIC 0x4755 +#define AST2600_SCU_RESET_CONTROL1 (0x40 / 4) #define SCU_RESET_CONTROL1 (0x04 / 4) #define SCU_RESET_SDRAM BIT(0) @@ -54,21 +56,6 @@ static bool aspeed_wdt_is_enabled(const AspeedWDTState *s) return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE; } -static bool is_ast2500(const AspeedWDTState *s) -{ - switch (s->silicon_rev) { - case AST2500_A0_SILICON_REV: - case AST2500_A1_SILICON_REV: - return true; - case AST2400_A0_SILICON_REV: - case AST2400_A1_SILICON_REV: - default: - break; - } - - return false; -} - static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) { AspeedWDTState *s = ASPEED_WDT(opaque); @@ -89,6 +76,8 @@ static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) return s->regs[WDT_CTRL]; case WDT_RESET_WIDTH: return s->regs[WDT_RESET_WIDTH]; + case WDT_RESET_MASK1: + return s->regs[WDT_RESET_MASK1]; case WDT_TIMEOUT_STATUS: case WDT_TIMEOUT_CLEAR: qemu_log_mask(LOG_UNIMP, @@ -124,6 +113,7 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, unsigned size) { AspeedWDTState *s = ASPEED_WDT(opaque); + AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(s); bool enable = data & WDT_CTRL_ENABLE; offset >>= 2; @@ -140,7 +130,7 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, case WDT_RESTART: if ((data & 0xFFFF) == WDT_RESTART_MAGIC) { s->regs[WDT_STATUS] = s->regs[WDT_RELOAD_VALUE]; - aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); + aspeed_wdt_reload(s, !(s->regs[WDT_CTRL] & WDT_CTRL_1MHZ_CLK)); } break; case WDT_CTRL: @@ -153,24 +143,18 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, } break; case WDT_RESET_WIDTH: - { - uint32_t property = data & WDT_POLARITY_MASK; - - if (property && is_ast2500(s)) { - if (property == WDT_ACTIVE_HIGH_MAGIC) { - s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_ACTIVE_HIGH; - } else if (property == WDT_ACTIVE_LOW_MAGIC) { - s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_ACTIVE_HIGH; - } else if (property == WDT_PUSH_PULL_MAGIC) { - s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_PUSH_PULL; - } else if (property == WDT_OPEN_DRAIN_MAGIC) { - s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_PUSH_PULL; - } + if (awc->reset_pulse) { + awc->reset_pulse(s, data & WDT_POLARITY_MASK); } - s->regs[WDT_RESET_WIDTH] &= ~s->ext_pulse_width_mask; - s->regs[WDT_RESET_WIDTH] |= data & s->ext_pulse_width_mask; + s->regs[WDT_RESET_WIDTH] &= ~awc->ext_pulse_width_mask; + s->regs[WDT_RESET_WIDTH] |= data & awc->ext_pulse_width_mask; break; - } + + case WDT_RESET_MASK1: + /* TODO: implement */ + s->regs[WDT_RESET_MASK1] = data; + break; + case WDT_TIMEOUT_STATUS: case WDT_TIMEOUT_CLEAR: qemu_log_mask(LOG_UNIMP, @@ -226,9 +210,10 @@ static void aspeed_wdt_reset(DeviceState *dev) static void aspeed_wdt_timer_expired(void *dev) { AspeedWDTState *s = ASPEED_WDT(dev); + uint32_t reset_ctrl_reg = ASPEED_WDT_GET_CLASS(s)->reset_ctrl_reg; /* Do not reset on SDRAM controller reset */ - if (s->scu->regs[SCU_RESET_CONTROL1] & SCU_RESET_SDRAM) { + if (s->scu->regs[reset_ctrl_reg] & SCU_RESET_SDRAM) { timer_del(s->timer); s->regs[WDT_CTRL] = 0; return; @@ -256,25 +241,6 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) } s->scu = ASPEED_SCU(obj); - if (!is_supported_silicon_rev(s->silicon_rev)) { - error_setg(errp, "Unknown silicon revision: 0x%" PRIx32, - s->silicon_rev); - return; - } - - switch (s->silicon_rev) { - case AST2400_A0_SILICON_REV: - case AST2400_A1_SILICON_REV: - s->ext_pulse_width_mask = 0xff; - break; - case AST2500_A0_SILICON_REV: - case AST2500_A1_SILICON_REV: - s->ext_pulse_width_mask = 0xfffff; - break; - default: - g_assert_not_reached(); - } - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); /* FIXME: This setting should be derived from the SCU hw strapping @@ -287,20 +253,15 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static Property aspeed_wdt_properties[] = { - DEFINE_PROP_UINT32("silicon-rev", AspeedWDTState, silicon_rev, 0), - DEFINE_PROP_END_OF_LIST(), -}; - static void aspeed_wdt_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "ASPEED Watchdog Controller"; dc->realize = aspeed_wdt_realize; dc->reset = aspeed_wdt_reset; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->vmsd = &vmstate_aspeed_wdt; - dc->props = aspeed_wdt_properties; } static const TypeInfo aspeed_wdt_info = { @@ -308,12 +269,88 @@ static const TypeInfo aspeed_wdt_info = { .name = TYPE_ASPEED_WDT, .instance_size = sizeof(AspeedWDTState), .class_init = aspeed_wdt_class_init, + .class_size = sizeof(AspeedWDTClass), + .abstract = true, +}; + +static void aspeed_2400_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); + + dc->desc = "ASPEED 2400 Watchdog Controller"; + awc->offset = 0x20; + awc->ext_pulse_width_mask = 0xff; + awc->reset_ctrl_reg = SCU_RESET_CONTROL1; +} + +static const TypeInfo aspeed_2400_wdt_info = { + .name = TYPE_ASPEED_2400_WDT, + .parent = TYPE_ASPEED_WDT, + .instance_size = sizeof(AspeedWDTState), + .class_init = aspeed_2400_wdt_class_init, +}; + +static void aspeed_2500_wdt_reset_pulse(AspeedWDTState *s, uint32_t property) +{ + if (property) { + if (property == WDT_ACTIVE_HIGH_MAGIC) { + s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_ACTIVE_HIGH; + } else if (property == WDT_ACTIVE_LOW_MAGIC) { + s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_ACTIVE_HIGH; + } else if (property == WDT_PUSH_PULL_MAGIC) { + s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_PUSH_PULL; + } else if (property == WDT_OPEN_DRAIN_MAGIC) { + s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_PUSH_PULL; + } + } +} + +static void aspeed_2500_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); + + dc->desc = "ASPEED 2500 Watchdog Controller"; + awc->offset = 0x20; + awc->ext_pulse_width_mask = 0xfffff; + awc->reset_ctrl_reg = SCU_RESET_CONTROL1; + awc->reset_pulse = aspeed_2500_wdt_reset_pulse; +} + +static const TypeInfo aspeed_2500_wdt_info = { + .name = TYPE_ASPEED_2500_WDT, + .parent = TYPE_ASPEED_WDT, + .instance_size = sizeof(AspeedWDTState), + .class_init = aspeed_2500_wdt_class_init, +}; + +static void aspeed_2600_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); + + dc->desc = "ASPEED 2600 Watchdog Controller"; + awc->offset = 0x40; + awc->ext_pulse_width_mask = 0xfffff; /* TODO */ + awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; + awc->reset_pulse = aspeed_2500_wdt_reset_pulse; +} + +static const TypeInfo aspeed_2600_wdt_info = { + .name = TYPE_ASPEED_2600_WDT, + .parent = TYPE_ASPEED_WDT, + .instance_size = sizeof(AspeedWDTState), + .class_init = aspeed_2600_wdt_class_init, }; static void wdt_aspeed_register_types(void) { watchdog_add_model(&model); type_register_static(&aspeed_wdt_info); + type_register_static(&aspeed_2400_wdt_info); + type_register_static(&aspeed_2500_wdt_info); + type_register_static(&aspeed_2600_wdt_info); } type_init(wdt_aspeed_register_types) diff --git a/include/block/accounting.h b/include/block/accounting.h index d1f67b1..878b4c3 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -33,9 +33,11 @@ typedef struct BlockAcctTimedStats BlockAcctTimedStats; typedef struct BlockAcctStats BlockAcctStats; enum BlockAcctType { + BLOCK_ACCT_NONE = 0, BLOCK_ACCT_READ, BLOCK_ACCT_WRITE, BLOCK_ACCT_FLUSH, + BLOCK_ACCT_UNMAP, BLOCK_MAX_IOTYPE, }; diff --git a/include/block/aio_task.h b/include/block/aio_task.h new file mode 100644 index 0000000..50bc1e1 --- /dev/null +++ b/include/block/aio_task.h @@ -0,0 +1,54 @@ +/* + * Aio tasks loops + * + * Copyright (c) 2019 Virtuozzo International GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BLOCK_AIO_TASK_H +#define BLOCK_AIO_TASK_H + +#include "qemu/coroutine.h" + +typedef struct AioTaskPool AioTaskPool; +typedef struct AioTask AioTask; +typedef int coroutine_fn (*AioTaskFunc)(AioTask *task); +struct AioTask { + AioTaskPool *pool; + AioTaskFunc func; + int ret; +}; + +AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks); +void aio_task_pool_free(AioTaskPool *); + +/* error code of failed task or 0 if all is OK */ +int aio_task_pool_status(AioTaskPool *pool); + +bool aio_task_pool_empty(AioTaskPool *pool); + +/* User provides filled @task, however task->pool will be set automatically */ +void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task); + +void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool); +void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool); +void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool); + +#endif /* BLOCK_AIO_TASK_H */ diff --git a/include/block/block-copy.h b/include/block/block-copy.h new file mode 100644 index 0000000..e2e135ff --- /dev/null +++ b/include/block/block-copy.h @@ -0,0 +1,93 @@ +/* + * block_copy API + * + * Copyright (C) 2013 Proxmox Server Solutions + * Copyright (c) 2019 Virtuozzo International GmbH. + * + * Authors: + * Dietmar Maurer (dietmar@proxmox.com) + * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.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. + */ + +#ifndef BLOCK_COPY_H +#define BLOCK_COPY_H + +#include "block/block.h" + +typedef struct BlockCopyInFlightReq { + int64_t start_byte; + int64_t end_byte; + QLIST_ENTRY(BlockCopyInFlightReq) list; + CoQueue wait_queue; /* coroutines blocked on this request */ +} BlockCopyInFlightReq; + +typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque); +typedef void (*ProgressResetCallbackFunc)(void *opaque); +typedef struct BlockCopyState { + /* + * BdrvChild objects are not owned or managed by block-copy. They are + * provided by block-copy user and user is responsible for appropriate + * permissions on these children. + */ + BdrvChild *source; + BdrvChild *target; + BdrvDirtyBitmap *copy_bitmap; + int64_t cluster_size; + bool use_copy_range; + int64_t copy_range_size; + uint64_t len; + QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs; + + BdrvRequestFlags write_flags; + + /* + * skip_unallocated: + * + * Used by sync=top jobs, which first scan the source node for unallocated + * areas and clear them in the copy_bitmap. During this process, the bitmap + * is thus not fully initialized: It may still have bits set for areas that + * are unallocated and should actually not be copied. + * + * This is indicated by skip_unallocated. + * + * In this case, block_copy() will query the source’s allocation status, + * skip unallocated regions, clear them in the copy_bitmap, and invoke + * block_copy_reset_unallocated() every time it does. + */ + bool skip_unallocated; + + /* progress_bytes_callback: called when some copying progress is done. */ + ProgressBytesCallbackFunc progress_bytes_callback; + + /* + * progress_reset_callback: called when some bytes reset from copy_bitmap + * (see @skip_unallocated above). The callee is assumed to recalculate how + * many bytes remain based on the dirty bit count of copy_bitmap. + */ + ProgressResetCallbackFunc progress_reset_callback; + void *progress_opaque; +} BlockCopyState; + +BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, + int64_t cluster_size, + BdrvRequestFlags write_flags, + Error **errp); + +void block_copy_set_callbacks( + BlockCopyState *s, + ProgressBytesCallbackFunc progress_bytes_callback, + ProgressResetCallbackFunc progress_reset_callback, + void *progress_opaque); + +void block_copy_state_free(BlockCopyState *s); + +int64_t block_copy_reset_unallocated(BlockCopyState *s, + int64_t offset, int64_t *count); + +int coroutine_fn block_copy(BlockCopyState *s, int64_t start, uint64_t bytes, + bool *error_is_read); + +#endif /* BLOCK_COPY_H */ diff --git a/include/block/block.h b/include/block/block.h index 37c9de7..792bb82 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -501,6 +501,7 @@ int bdrv_get_flags(BlockDriverState *bs); int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, Error **errp); +BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs); void bdrv_round_to_clusters(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *cluster_offset, diff --git a/include/block/block_int.h b/include/block/block_int.h index 0422acd..05056b3 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -366,6 +366,7 @@ struct BlockDriver { int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs, Error **errp); + BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs); int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov, @@ -1196,6 +1197,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BdrvDirtyBitmap *sync_bitmap, BitmapSyncMode bitmap_mode, bool compress, + const char *filter_node_name, BlockdevOnError on_source_error, BlockdevOnError on_target_error, int creation_flags, diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 49db07b..04795c4 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -72,6 +72,23 @@ void QEMU_NORETURN cpu_loop_exit(CPUState *cpu); void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); void QEMU_NORETURN cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); +/** + * cpu_loop_exit_requested: + * @cpu: The CPU state to be tested + * + * Indicate if somebody asked for a return of the CPU to the main loop + * (e.g., via cpu_exit() or cpu_interrupt()). + * + * This is helpful for architectures that support interruptible + * instructions. After writing back all state to registers/memory, this + * call can be used to check if it makes sense to return to the main loop + * or to continue executing the interruptible instruction. + */ +static inline bool cpu_loop_exit_requested(CPUState *cpu) +{ + return (int32_t)atomic_read(&cpu_neg(cpu)->icount_decr.u32) < 0; +} + #if !defined(CONFIG_USER_ONLY) void cpu_reloading_memory_map(void); /** diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index e96e621..ad158bb 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -193,30 +193,29 @@ static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - rcu_read_lock(); - - blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + WITH_RCU_READ_LOCK_GUARD() { + blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); + + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + base = page - offset; + while (page < end) { + unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); + unsigned long num = next - base; + unsigned long found = find_next_bit(blocks->blocks[idx], + num, offset); + if (found < num) { + dirty = true; + break; + } - idx = page / DIRTY_MEMORY_BLOCK_SIZE; - offset = page % DIRTY_MEMORY_BLOCK_SIZE; - base = page - offset; - while (page < end) { - unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); - unsigned long num = next - base; - unsigned long found = find_next_bit(blocks->blocks[idx], num, offset); - if (found < num) { - dirty = true; - break; + page = next; + idx++; + offset = 0; + base += DIRTY_MEMORY_BLOCK_SIZE; } - - page = next; - idx++; - offset = 0; - base += DIRTY_MEMORY_BLOCK_SIZE; } - rcu_read_unlock(); - return dirty; } @@ -234,7 +233,7 @@ static inline bool cpu_physical_memory_all_dirty(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); @@ -256,8 +255,6 @@ static inline bool cpu_physical_memory_all_dirty(ram_addr_t start, base += DIRTY_MEMORY_BLOCK_SIZE; } - rcu_read_unlock(); - return dirty; } @@ -309,13 +306,11 @@ static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, idx = page / DIRTY_MEMORY_BLOCK_SIZE; offset = page % DIRTY_MEMORY_BLOCK_SIZE; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); set_bit_atomic(offset, blocks->blocks[idx]); - - rcu_read_unlock(); } static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, @@ -334,39 +329,37 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - rcu_read_lock(); + WITH_RCU_READ_LOCK_GUARD() { + for (i = 0; i < DIRTY_MEMORY_NUM; i++) { + blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i]); + } - for (i = 0; i < DIRTY_MEMORY_NUM; i++) { - blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i]); - } + idx = page / DIRTY_MEMORY_BLOCK_SIZE; + offset = page % DIRTY_MEMORY_BLOCK_SIZE; + base = page - offset; + while (page < end) { + unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); - idx = page / DIRTY_MEMORY_BLOCK_SIZE; - offset = page % DIRTY_MEMORY_BLOCK_SIZE; - base = page - offset; - while (page < end) { - unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE); + if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) { + bitmap_set_atomic(blocks[DIRTY_MEMORY_MIGRATION]->blocks[idx], + offset, next - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) { + bitmap_set_atomic(blocks[DIRTY_MEMORY_VGA]->blocks[idx], + offset, next - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) { + bitmap_set_atomic(blocks[DIRTY_MEMORY_CODE]->blocks[idx], + offset, next - page); + } - if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) { - bitmap_set_atomic(blocks[DIRTY_MEMORY_MIGRATION]->blocks[idx], - offset, next - page); + page = next; + idx++; + offset = 0; + base += DIRTY_MEMORY_BLOCK_SIZE; } - if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) { - bitmap_set_atomic(blocks[DIRTY_MEMORY_VGA]->blocks[idx], - offset, next - page); - } - if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) { - bitmap_set_atomic(blocks[DIRTY_MEMORY_CODE]->blocks[idx], - offset, next - page); - } - - page = next; - idx++; - offset = 0; - base += DIRTY_MEMORY_BLOCK_SIZE; } - rcu_read_unlock(); - xen_hvm_modified_memory(start, length); } @@ -396,36 +389,35 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, offset = BIT_WORD((start >> TARGET_PAGE_BITS) % DIRTY_MEMORY_BLOCK_SIZE); - rcu_read_lock(); + WITH_RCU_READ_LOCK_GUARD() { + for (i = 0; i < DIRTY_MEMORY_NUM; i++) { + blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i])->blocks; + } - for (i = 0; i < DIRTY_MEMORY_NUM; i++) { - blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i])->blocks; - } + for (k = 0; k < nr; k++) { + if (bitmap[k]) { + unsigned long temp = leul_to_cpu(bitmap[k]); - for (k = 0; k < nr; k++) { - if (bitmap[k]) { - unsigned long temp = leul_to_cpu(bitmap[k]); + atomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp); - atomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp); + if (global_dirty_log) { + atomic_or(&blocks[DIRTY_MEMORY_MIGRATION][idx][offset], + temp); + } - if (global_dirty_log) { - atomic_or(&blocks[DIRTY_MEMORY_MIGRATION][idx][offset], - temp); + if (tcg_enabled()) { + atomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], + temp); + } } - if (tcg_enabled()) { - atomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], temp); + if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) { + offset = 0; + idx++; } } - - if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) { - offset = 0; - idx++; - } } - rcu_read_unlock(); - xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS); } else { uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index 0ba90ef..a2a12af 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -13,6 +13,7 @@ typedef enum { ACPI_MEMORY_HOTPLUG_STATUS = 8, ACPI_NVDIMM_HOTPLUG_STATUS = 16, ACPI_VMGENID_CHANGE_STATUS = 32, + ACPI_POWER_DOWN_STATUS = 64, } AcpiEventStatusBits; #define TYPE_ACPI_DEVICE_IF "acpi-device-interface" diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h new file mode 100644 index 0000000..d157eac --- /dev/null +++ b/include/hw/acpi/generic_event_device.h @@ -0,0 +1,103 @@ +/* + * + * Copyright (c) 2018 Intel Corporation + * Copyright (c) 2019 Huawei Technologies R & D (UK) Ltd + * Written by Samuel Ortiz, Shameer Kolothum + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * The ACPI Generic Event Device (GED) is a hardware-reduced specific + * device[ACPI v6.1 Section 5.6.9] that handles all platform events, + * including the hotplug ones. Generic Event Device allows platforms + * to handle interrupts in ACPI ASL statements. It follows a very + * similar approach like the _EVT method from GPIO events. All + * interrupts are listed in _CRS and the handler is written in _EVT + * method. Here, we use a single interrupt for the GED device, relying + * on IO memory region to communicate the type of device affected by + * the interrupt. This way, we can support up to 32 events with a + * unique interrupt. + * + * Here is an example. + * + * Device (\_SB.GED) + * { + * Name (_HID, "ACPI0013") + * Name (_UID, Zero) + * Name (_CRS, ResourceTemplate () + * { + * Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, ) + * { + * 0x00000029, + * } + * }) + * OperationRegion (EREG, SystemMemory, 0x09080000, 0x04) + * Field (EREG, DWordAcc, NoLock, WriteAsZeros) + * { + * ESEL, 32 + * } + * + * Method (_EVT, 1, Serialized) // _EVT: Event + * { + * Local0 = ESEL // ESEL = IO memory region which specifies the + * // device type. + * If (((Local0 & One) == One)) + * { + * MethodEvent1() + * } + * If ((Local0 & 0x2) == 0x2) + * { + * MethodEvent2() + * } + * ... + * } + * } + * + */ + +#ifndef HW_ACPI_GED_H +#define HW_ACPI_GED_H + +#include "hw/sysbus.h" +#include "hw/acpi/memory_hotplug.h" + +#define ACPI_POWER_BUTTON_DEVICE "PWRB" + +#define TYPE_ACPI_GED "acpi-ged" +#define ACPI_GED(obj) \ + OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED) + +#define ACPI_GED_EVT_SEL_OFFSET 0x0 +#define ACPI_GED_EVT_SEL_LEN 0x4 + +#define GED_DEVICE "GED" +#define AML_GED_EVT_REG "EREG" +#define AML_GED_EVT_SEL "ESEL" + +/* + * Platforms need to specify the GED event bitmap + * to describe what kind of events they want to support + * through GED. + */ +#define ACPI_GED_MEM_HOTPLUG_EVT 0x1 +#define ACPI_GED_PWR_DOWN_EVT 0x2 + +typedef struct GEDState { + MemoryRegion io; + uint32_t sel; +} GEDState; + +typedef struct AcpiGedState { + SysBusDevice parent_obj; + MemHotplugState memhp_state; + MemoryRegion container_memhp; + GEDState ged_state; + uint32_t ged_event_bitmap; + qemu_irq irq; +} AcpiGedState; + +void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev, + uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base); + +#endif diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h index 77c6576..dfe9cf3 100644 --- a/include/hw/acpi/memory_hotplug.h +++ b/include/hw/acpi/memory_hotplug.h @@ -5,6 +5,10 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" +#define MEMORY_SLOT_SCAN_METHOD "MSCN" +#define MEMORY_DEVICES_CONTAINER "\\_SB.MHPC" +#define MEMORY_HOTPLUG_IO_LEN 24 + /** * MemStatus: * @is_removing: the memory device in slot has been requested to be ejected. @@ -29,7 +33,7 @@ typedef struct MemHotplugState { } MemHotplugState; void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, - MemHotplugState *state, uint16_t io_base); + MemHotplugState *state, hwaddr io_base); void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp); @@ -48,5 +52,6 @@ void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list); void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem, const char *res_root, - const char *event_handler_method); + const char *event_handler_method, + AmlRegionSpace rs, hwaddr memhp_io_base); #endif diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index ab5052b..cccb684 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -12,6 +12,7 @@ #ifndef ASPEED_SOC_H #define ASPEED_SOC_H +#include "hw/cpu/a15mpcore.h" #include "hw/intc/aspeed_vic.h" #include "hw/misc/aspeed_scu.h" #include "hw/misc/aspeed_sdmc.h" @@ -24,11 +25,12 @@ #include "hw/net/ftgmac100.h" #include "target/arm/cpu.h" #include "hw/gpio/aspeed_gpio.h" +#include "hw/sd/aspeed_sdhci.h" #define ASPEED_SPIS_NUM 2 -#define ASPEED_WDTS_NUM 3 +#define ASPEED_WDTS_NUM 4 #define ASPEED_CPUS_NUM 2 -#define ASPEED_MACS_NUM 2 +#define ASPEED_MACS_NUM 4 typedef struct AspeedSoCState { /*< private >*/ @@ -37,6 +39,7 @@ typedef struct AspeedSoCState { /*< public >*/ ARMCPU cpu[ASPEED_CPUS_NUM]; uint32_t num_cpus; + A15MPPrivState a7mpcore; MemoryRegion sram; AspeedVICState vic; AspeedRtcState rtc; @@ -49,27 +52,28 @@ typedef struct AspeedSoCState { AspeedSDMCState sdmc; AspeedWDTState wdt[ASPEED_WDTS_NUM]; FTGMAC100State ftgmac100[ASPEED_MACS_NUM]; + AspeedMiiState mii[ASPEED_MACS_NUM]; AspeedGPIOState gpio; + AspeedGPIOState gpio_1_8v; + AspeedSDHCIState sdhci; } AspeedSoCState; #define TYPE_ASPEED_SOC "aspeed-soc" #define ASPEED_SOC(obj) OBJECT_CHECK(AspeedSoCState, (obj), TYPE_ASPEED_SOC) -typedef struct AspeedSoCInfo { +typedef struct AspeedSoCClass { + DeviceClass parent_class; + const char *name; const char *cpu_type; uint32_t silicon_rev; uint64_t sram_size; int spis_num; int wdts_num; + int macs_num; const int *irqmap; const hwaddr *memmap; uint32_t num_cpus; -} AspeedSoCInfo; - -typedef struct AspeedSoCClass { - DeviceClass parent_class; - AspeedSoCInfo *info; } AspeedSoCClass; #define ASPEED_SOC_CLASS(klass) \ @@ -92,8 +96,11 @@ enum { ASPEED_SDMC, ASPEED_SCU, ASPEED_ADC, + ASPEED_VIDEO, ASPEED_SRAM, + ASPEED_SDHCI, ASPEED_GPIO, + ASPEED_GPIO_1_8V, ASPEED_RTC, ASPEED_TIMER1, ASPEED_TIMER2, @@ -110,6 +117,12 @@ enum { ASPEED_I2C, ASPEED_ETH1, ASPEED_ETH2, + ASPEED_ETH3, + ASPEED_ETH4, + ASPEED_MII1, + ASPEED_MII2, + ASPEED_MII3, + ASPEED_MII4, ASPEED_SDRAM, ASPEED_XDMA, }; diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index 6b17f6a..62a4c7b 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -23,6 +23,7 @@ #include "hw/sd/sdhci.h" #include "hw/sd/bcm2835_sdhost.h" #include "hw/gpio/bcm2835_gpio.h" +#include "hw/misc/unimp.h" #define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals" #define BCM2835_PERIPHERALS(obj) \ @@ -37,6 +38,10 @@ typedef struct BCM2835PeripheralState { MemoryRegion ram_alias[4]; qemu_irq irq, fiq; + UnimplementedDeviceState systmr; + UnimplementedDeviceState armtmr; + UnimplementedDeviceState cprman; + UnimplementedDeviceState a2w; PL011State uart0; BCM2835AuxState aux; BCM2835FBState fb; @@ -48,6 +53,16 @@ typedef struct BCM2835PeripheralState { SDHCIState sdhci; BCM2835SDHostState sdhost; BCM2835GpioState gpio; + UnimplementedDeviceState i2s; + UnimplementedDeviceState spi[1]; + UnimplementedDeviceState i2c[3]; + UnimplementedDeviceState otp; + UnimplementedDeviceState dbus; + UnimplementedDeviceState ave0; + UnimplementedDeviceState bscsl; + UnimplementedDeviceState smi; + UnimplementedDeviceState dwc2; + UnimplementedDeviceState sdramc; } BCM2835PeripheralState; #endif /* BCM2835_PERIPHERALS_H */ diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h index 10083d3..cdcbca9 100644 --- a/include/hw/arm/raspi_platform.h +++ b/include/hw/arm/raspi_platform.h @@ -25,8 +25,7 @@ #ifndef HW_ARM_RASPI_PLATFORM_H #define HW_ARM_RASPI_PLATFORM_H -#define MCORE_OFFSET 0x0000 /* Fake frame buffer device - * (the multicore sync block) */ +#define MSYNC_OFFSET 0x0000 /* Multicore Sync Block */ #define IC0_OFFSET 0x2000 #define ST_OFFSET 0x3000 /* System Timer */ #define MPHI_OFFSET 0x6000 /* Message-based Parallel Host Intf. */ @@ -37,9 +36,10 @@ #define ARMCTRL_TIMER0_1_OFFSET (ARM_OFFSET + 0x400) /* Timer 0 and 1 */ #define ARMCTRL_0_SBM_OFFSET (ARM_OFFSET + 0x800) /* User 0 (ARM) Semaphores * Doorbells & Mailboxes */ -#define PM_OFFSET 0x100000 /* Power Management, Reset controller - * and Watchdog registers */ -#define PCM_CLOCK_OFFSET 0x101098 +#define CPRMAN_OFFSET 0x100000 /* Power Management, Watchdog */ +#define CM_OFFSET 0x101000 /* Clock Management */ +#define A2W_OFFSET 0x102000 /* Reset controller */ +#define AVS_OFFSET 0x103000 /* Audio Video Standard */ #define RNG_OFFSET 0x104000 #define GPIO_OFFSET 0x200000 #define UART0_OFFSET 0x201000 @@ -47,11 +47,17 @@ #define I2S_OFFSET 0x203000 #define SPI0_OFFSET 0x204000 #define BSC0_OFFSET 0x205000 /* BSC0 I2C/TWI */ -#define UART1_OFFSET 0x215000 -#define EMMC_OFFSET 0x300000 +#define OTP_OFFSET 0x20f000 +#define BSC_SL_OFFSET 0x214000 /* SPI slave */ +#define AUX_OFFSET 0x215000 /* AUX: UART1/SPI1/SPI2 */ +#define EMMC1_OFFSET 0x300000 #define SMI_OFFSET 0x600000 #define BSC1_OFFSET 0x804000 /* BSC1 I2C/TWI */ -#define USB_OFFSET 0x980000 /* DTC_OTG USB controller */ +#define BSC2_OFFSET 0x805000 /* BSC2 I2C/TWI */ +#define DBUS_OFFSET 0x900000 +#define AVE0_OFFSET 0x910000 +#define USB_OTG_OFFSET 0x980000 /* DTC_OTG USB controller */ +#define SDRAMC_OFFSET 0xe00000 #define DMA15_OFFSET 0xE05000 /* DMA controller, channel 15 */ /* GPU interrupts */ @@ -112,7 +118,7 @@ #define INTERRUPT_SPI 54 #define INTERRUPT_I2SPCM 55 #define INTERRUPT_SDIO 56 -#define INTERRUPT_UART 57 +#define INTERRUPT_UART0 57 #define INTERRUPT_SLIMBUS 58 #define INTERRUPT_VEC 59 #define INTERRUPT_CPG 60 diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index a720942..0b41083 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -77,6 +77,8 @@ enum { VIRT_GPIO, VIRT_SECURE_UART, VIRT_SECURE_MEM, + VIRT_PCDIMM_ACPI, + VIRT_ACPI_GED, VIRT_LOWMEMMAP_LAST, }; @@ -106,6 +108,7 @@ typedef struct { bool claim_edge_triggered_timers; bool smbios_old_sys_ver; bool no_highmem_ecam; + bool no_ged; /* Machines < 4.2 has no support for ACPI GED device */ } VirtMachineClass; typedef struct { @@ -133,6 +136,8 @@ typedef struct { uint32_t iommu_phandle; int psci_conduit; hwaddr highest_gpa; + DeviceState *acpi_dev; + Notifier powerdown_notifier; } VirtMachineState; #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM) diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index a2753f0..13e0105 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -25,10 +25,13 @@ #include "hw/sysbus.h" #define TYPE_ASPEED_I2C "aspeed.i2c" +#define TYPE_ASPEED_2400_I2C TYPE_ASPEED_I2C "-ast2400" +#define TYPE_ASPEED_2500_I2C TYPE_ASPEED_I2C "-ast2500" +#define TYPE_ASPEED_2600_I2C TYPE_ASPEED_I2C "-ast2600" #define ASPEED_I2C(obj) \ OBJECT_CHECK(AspeedI2CState, (obj), TYPE_ASPEED_I2C) -#define ASPEED_I2C_NR_BUSSES 14 +#define ASPEED_I2C_NR_BUSSES 16 struct AspeedI2CState; @@ -39,6 +42,7 @@ typedef struct AspeedI2CBus { I2CBus *bus; uint8_t id; + qemu_irq irq; uint32_t ctrl; uint32_t timing[2]; @@ -59,6 +63,20 @@ typedef struct AspeedI2CState { AspeedI2CBus busses[ASPEED_I2C_NR_BUSSES]; } AspeedI2CState; +#define ASPEED_I2C_CLASS(klass) \ + OBJECT_CLASS_CHECK(AspeedI2CClass, (klass), TYPE_ASPEED_I2C) +#define ASPEED_I2C_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AspeedI2CClass, (obj), TYPE_ASPEED_I2C) + +typedef struct AspeedI2CClass { + SysBusDeviceClass parent_class; + + uint8_t num_busses; + uint8_t reg_size; + uint8_t gap; + qemu_irq (*bus_get_irq)(AspeedI2CBus *); +} AspeedI2CClass; + I2CBus *aspeed_i2c_get_bus(DeviceState *dev, int busnr); #endif /* ASPEED_I2C_H */ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 6df4f4b..37bfd95 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -70,6 +70,9 @@ struct PCMachineState { /* Address space used by IOAPIC device. All IOAPIC interrupts * will be translated to MSI messages in the address space. */ AddressSpace *ioapic_as; + + /* ACPI Memory hotplug IO base address */ + hwaddr memhp_io_base; }; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index 239e94f..1d7f7ff 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -17,8 +17,10 @@ #define ASPEED_SCU(obj) OBJECT_CHECK(AspeedSCUState, (obj), TYPE_ASPEED_SCU) #define TYPE_ASPEED_2400_SCU TYPE_ASPEED_SCU "-ast2400" #define TYPE_ASPEED_2500_SCU TYPE_ASPEED_SCU "-ast2500" +#define TYPE_ASPEED_2600_SCU TYPE_ASPEED_SCU "-ast2600" #define ASPEED_SCU_NR_REGS (0x1A8 >> 2) +#define ASPEED_AST2600_SCU_NR_REGS (0xE20 >> 2) typedef struct AspeedSCUState { /*< private >*/ @@ -27,7 +29,7 @@ typedef struct AspeedSCUState { /*< public >*/ MemoryRegion iomem; - uint32_t regs[ASPEED_SCU_NR_REGS]; + uint32_t regs[ASPEED_AST2600_SCU_NR_REGS]; uint32_t silicon_rev; uint32_t hw_strap1; uint32_t hw_strap2; @@ -38,6 +40,7 @@ typedef struct AspeedSCUState { #define AST2400_A1_SILICON_REV 0x02010303U #define AST2500_A0_SILICON_REV 0x04000303U #define AST2500_A1_SILICON_REV 0x04010303U +#define AST2600_A0_SILICON_REV 0x05000303U #define ASPEED_IS_AST2500(si_rev) ((((si_rev) >> 24) & 0xff) == 0x04) @@ -54,6 +57,8 @@ typedef struct AspeedSCUClass { const uint32_t *resets; uint32_t (*calc_hpll)(AspeedSCUState *s, uint32_t hpll_reg); uint32_t apb_divider; + uint32_t nr_regs; + const MemoryRegionOps *ops; } AspeedSCUClass; #define ASPEED_SCU_PROT_KEY 0x1688A8A8 diff --git a/include/hw/misc/aspeed_sdmc.h b/include/hw/misc/aspeed_sdmc.h index b3c926a..5dbde59 100644 --- a/include/hw/misc/aspeed_sdmc.h +++ b/include/hw/misc/aspeed_sdmc.h @@ -13,6 +13,9 @@ #define TYPE_ASPEED_SDMC "aspeed.sdmc" #define ASPEED_SDMC(obj) OBJECT_CHECK(AspeedSDMCState, (obj), TYPE_ASPEED_SDMC) +#define TYPE_ASPEED_2400_SDMC TYPE_ASPEED_SDMC "-ast2400" +#define TYPE_ASPEED_2500_SDMC TYPE_ASPEED_SDMC "-ast2500" +#define TYPE_ASPEED_2600_SDMC TYPE_ASPEED_SDMC "-ast2600" #define ASPEED_SDMC_NR_REGS (0x174 >> 2) @@ -24,12 +27,21 @@ typedef struct AspeedSDMCState { MemoryRegion iomem; uint32_t regs[ASPEED_SDMC_NR_REGS]; - uint32_t silicon_rev; - uint32_t ram_bits; uint64_t ram_size; uint64_t max_ram_size; - uint32_t fixed_conf; - } AspeedSDMCState; +#define ASPEED_SDMC_CLASS(klass) \ + OBJECT_CLASS_CHECK(AspeedSDMCClass, (klass), TYPE_ASPEED_SDMC) +#define ASPEED_SDMC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AspeedSDMCClass, (obj), TYPE_ASPEED_SDMC) + +typedef struct AspeedSDMCClass { + SysBusDeviceClass parent_class; + + uint64_t max_ram_size; + uint32_t (*compute_conf)(AspeedSDMCState *s, uint32_t data); + void (*write)(AspeedSDMCState *s, uint32_t reg, uint32_t data); +} AspeedSDMCClass; + #endif /* ASPEED_SDMC_H */ diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h index 94cfe05..ab37e7b 100644 --- a/include/hw/net/ftgmac100.h +++ b/include/hw/net/ftgmac100.h @@ -66,4 +66,21 @@ typedef struct FTGMAC100State { uint32_t rxdes0_edorr; } FTGMAC100State; +#define TYPE_ASPEED_MII "aspeed-mmi" +#define ASPEED_MII(obj) OBJECT_CHECK(AspeedMiiState, (obj), TYPE_ASPEED_MII) + +/* + * AST2600 MII controller + */ +typedef struct AspeedMiiState { + /*< private >*/ + SysBusDevice parent_obj; + + FTGMAC100State *nic; + + MemoryRegion iomem; + uint32_t phycr; + uint32_t phydata; +} AspeedMiiState; + #endif diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h index 9c77055..4c321f6 100644 --- a/include/hw/ptimer.h +++ b/include/hw/ptimer.h @@ -72,7 +72,7 @@ * ptimer_set_count() or ptimer_set_limit() will not trigger the timer * (though it will cause a reload). Only a counter decrement to "0" * will cause a trigger. Not compatible with NO_IMMEDIATE_TRIGGER; - * ptimer_init() will assert() that you don't set both. + * ptimer_init_with_bh() will assert() that you don't set both. */ #define PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT (1 << 5) @@ -81,7 +81,7 @@ typedef struct ptimer_state ptimer_state; typedef void (*ptimer_cb)(void *opaque); /** - * ptimer_init - Allocate and return a new ptimer + * ptimer_init_with_bh - Allocate and return a new ptimer * @bh: QEMU bottom half which is run on timer expiry * @policy: PTIMER_POLICY_* bits specifying behaviour * @@ -89,18 +89,72 @@ typedef void (*ptimer_cb)(void *opaque); * The ptimer takes ownership of @bh and will delete it * when the ptimer is eventually freed. */ -ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask); +ptimer_state *ptimer_init_with_bh(QEMUBH *bh, uint8_t policy_mask); + +/** + * ptimer_init - Allocate and return a new ptimer + * @callback: function to call on ptimer expiry + * @callback_opaque: opaque pointer passed to @callback + * @policy: PTIMER_POLICY_* bits specifying behaviour + * + * The ptimer returned must be freed using ptimer_free(). + * + * If a ptimer is created using this API then will use the + * transaction-based API for modifying ptimer state: all calls + * to functions which modify ptimer state: + * - ptimer_set_period() + * - ptimer_set_freq() + * - ptimer_set_limit() + * - ptimer_set_count() + * - ptimer_run() + * - ptimer_stop() + * must be between matched calls to ptimer_transaction_begin() + * and ptimer_transaction_commit(). When ptimer_transaction_commit() + * is called it will evaluate the state of the timer after all the + * changes in the transaction, and call the callback if necessary. + * + * The callback function is always called from within a transaction + * begin/commit block, so the callback should not call the + * ptimer_transaction_begin() function itself. If the callback changes + * the ptimer state such that another ptimer expiry is triggered, then + * the callback will be called a second time after the first call returns. + */ +ptimer_state *ptimer_init(ptimer_cb callback, + void *callback_opaque, + uint8_t policy_mask); /** * ptimer_free - Free a ptimer * @s: timer to free * - * Free a ptimer created using ptimer_init() (including + * Free a ptimer created using ptimer_init_with_bh() (including * deleting the bottom half which it is using). */ void ptimer_free(ptimer_state *s); /** + * ptimer_transaction_begin() - Start a ptimer modification transaction + * + * This function must be called before making any calls to functions + * which modify the ptimer's state (see the ptimer_init() documentation + * for a list of these), and must always have a matched call to + * ptimer_transaction_commit(). + * It is an error to call this function for a BH-based ptimer; + * attempting to do this will trigger an assert. + */ +void ptimer_transaction_begin(ptimer_state *s); + +/** + * ptimer_transaction_commit() - Commit a ptimer modification transaction + * + * This function must be called after calls to functions which modify + * the ptimer's state, and completes the update of the ptimer. If the + * ptimer state now means that we should trigger the timer expiry + * callback, it will be called directly. + */ +void ptimer_transaction_commit(ptimer_state *s); + +/** * ptimer_set_period - Set counter increment interval in nanoseconds * @s: ptimer to configure * @period: period of the counter in nanoseconds @@ -108,6 +162,9 @@ void ptimer_free(ptimer_state *s); * Note that if your counter behaviour is specified as having a * particular frequency rather than a period then ptimer_set_freq() * may be more appropriate. + * + * This function will assert if it is called outside a + * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer. */ void ptimer_set_period(ptimer_state *s, int64_t period); @@ -121,6 +178,9 @@ void ptimer_set_period(ptimer_state *s, int64_t period); * as setting the frequency then this function is more appropriate, * because it allows specifying an effective period which is * precise to fractions of a nanosecond, avoiding rounding errors. + * + * This function will assert if it is called outside a + * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer. */ void ptimer_set_freq(ptimer_state *s, uint32_t freq); @@ -148,6 +208,9 @@ uint64_t ptimer_get_limit(ptimer_state *s); * Set the limit value of the down-counter. The @reload flag can * be used to emulate the behaviour of timers which immediately * reload the counter when their reload register is written to. + * + * This function will assert if it is called outside a + * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer. */ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload); @@ -169,6 +232,9 @@ uint64_t ptimer_get_count(ptimer_state *s); * Set the value of the down-counter. If the counter is currently * enabled this will arrange for a timer callback at the appropriate * point in the future. + * + * This function will assert if it is called outside a + * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer. */ void ptimer_set_count(ptimer_state *s, uint64_t count); @@ -178,10 +244,14 @@ void ptimer_set_count(ptimer_state *s, uint64_t count); * @oneshot: non-zero if this timer should only count down once * * Start a ptimer counting down; when it reaches zero the bottom half - * passed to ptimer_init() will be invoked. If the @oneshot argument is zero, + * passed to ptimer_init_with_bh() will be invoked. + * If the @oneshot argument is zero, * the counter value will then be reloaded from the limit and it will * start counting down again. If @oneshot is non-zero, then the counter * will disable itself when it reaches zero. + * + * This function will assert if it is called outside a + * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer. */ void ptimer_run(ptimer_state *s, int oneshot); @@ -194,6 +264,9 @@ void ptimer_run(ptimer_state *s, int oneshot); * * Note that this can cause it to "lose" time, even if it is immediately * restarted. + * + * This function will assert if it is called outside a + * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer. */ void ptimer_stop(ptimer_state *s); diff --git a/include/hw/sd/aspeed_sdhci.h b/include/hw/sd/aspeed_sdhci.h new file mode 100644 index 0000000..dfdab43 --- /dev/null +++ b/include/hw/sd/aspeed_sdhci.h @@ -0,0 +1,34 @@ +/* + * Aspeed SD Host Controller + * Eddie James <eajames@linux.ibm.com> + * + * Copyright (C) 2019 IBM Corp + * SPDX-License-Identifer: GPL-2.0-or-later + */ + +#ifndef ASPEED_SDHCI_H +#define ASPEED_SDHCI_H + +#include "hw/sd/sdhci.h" + +#define TYPE_ASPEED_SDHCI "aspeed.sdhci" +#define ASPEED_SDHCI(obj) OBJECT_CHECK(AspeedSDHCIState, (obj), \ + TYPE_ASPEED_SDHCI) + +#define ASPEED_SDHCI_CAPABILITIES 0x01E80080 +#define ASPEED_SDHCI_NUM_SLOTS 2 +#define ASPEED_SDHCI_NUM_REGS (ASPEED_SDHCI_REG_SIZE / sizeof(uint32_t)) +#define ASPEED_SDHCI_REG_SIZE 0x100 + +typedef struct AspeedSDHCIState { + SysBusDevice parent; + + SDHCIState slots[ASPEED_SDHCI_NUM_SLOTS]; + + MemoryRegion iomem; + qemu_irq irq; + + uint32_t regs[ASPEED_SDHCI_NUM_REGS]; +} AspeedSDHCIState; + +#endif /* ASPEED_SDHCI_H */ diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 5176ff6..684d16e 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -49,6 +49,10 @@ typedef struct AspeedSMCController { hwaddr dma_flash_mask; hwaddr dma_dram_mask; uint32_t nregs; + uint32_t (*segment_to_reg)(const struct AspeedSMCState *s, + const AspeedSegments *seg); + void (*reg_to_segment)(const struct AspeedSMCState *s, uint32_t reg, + AspeedSegments *seg); } AspeedSMCController; typedef struct AspeedSMCFlash { diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h index 1fb949e..9483298 100644 --- a/include/hw/timer/aspeed_timer.h +++ b/include/hw/timer/aspeed_timer.h @@ -28,6 +28,10 @@ #define ASPEED_TIMER(obj) \ OBJECT_CHECK(AspeedTimerCtrlState, (obj), TYPE_ASPEED_TIMER); #define TYPE_ASPEED_TIMER "aspeed.timer" +#define TYPE_ASPEED_2400_TIMER TYPE_ASPEED_TIMER "-ast2400" +#define TYPE_ASPEED_2500_TIMER TYPE_ASPEED_TIMER "-ast2500" +#define TYPE_ASPEED_2600_TIMER TYPE_ASPEED_TIMER "-ast2600" + #define ASPEED_TIMER_NR_TIMERS 8 typedef struct AspeedTimer { @@ -55,9 +59,23 @@ typedef struct AspeedTimerCtrlState { uint32_t ctrl; uint32_t ctrl2; + uint32_t ctrl3; + uint32_t irq_sts; AspeedTimer timers[ASPEED_TIMER_NR_TIMERS]; AspeedSCUState *scu; } AspeedTimerCtrlState; +#define ASPEED_TIMER_CLASS(klass) \ + OBJECT_CLASS_CHECK(AspeedTimerClass, (klass), TYPE_ASPEED_TIMER) +#define ASPEED_TIMER_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AspeedTimerClass, (obj), TYPE_ASPEED_TIMER) + +typedef struct AspeedTimerClass { + SysBusDeviceClass parent_class; + + uint64_t (*read)(AspeedTimerCtrlState *s, hwaddr offset); + void (*write)(AspeedTimerCtrlState *s, hwaddr offset, uint64_t value); +} AspeedTimerClass; + #endif /* ASPEED_TIMER_H */ diff --git a/include/hw/timer/mss-timer.h b/include/hw/timer/mss-timer.h index d15d173..e5a784b 100644 --- a/include/hw/timer/mss-timer.h +++ b/include/hw/timer/mss-timer.h @@ -46,7 +46,6 @@ #define R_TIM1_MAX 6 struct Msf2Timer { - QEMUBH *bh; ptimer_state *ptimer; uint32_t regs[R_TIM1_MAX]; diff --git a/include/hw/virtio/vhost-user-fs.h b/include/hw/virtio/vhost-user-fs.h new file mode 100644 index 0000000..539885b --- /dev/null +++ b/include/hw/virtio/vhost-user-fs.h @@ -0,0 +1,45 @@ +/* + * Vhost-user filesystem virtio device + * + * Copyright 2018-2019 Red Hat, Inc. + * + * Authors: + * Stefan Hajnoczi <stefanha@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef _QEMU_VHOST_USER_FS_H +#define _QEMU_VHOST_USER_FS_H + +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "chardev/char-fe.h" + +#define TYPE_VHOST_USER_FS "vhost-user-fs-device" +#define VHOST_USER_FS(obj) \ + OBJECT_CHECK(VHostUserFS, (obj), TYPE_VHOST_USER_FS) + +typedef struct { + CharBackend chardev; + char *tag; + uint16_t num_request_queues; + uint16_t queue_size; + char *vhostfd; +} VHostUserFSConf; + +typedef struct { + /*< private >*/ + VirtIODevice parent; + VHostUserFSConf conf; + struct vhost_virtqueue *vhost_vqs; + struct vhost_dev vhost_dev; + VhostUserState vhost_user; + + /*< public >*/ +} VHostUserFS; + +#endif /* _QEMU_VHOST_USER_FS_H */ diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h index 8c5691c..dfedd76 100644 --- a/include/hw/watchdog/wdt_aspeed.h +++ b/include/hw/watchdog/wdt_aspeed.h @@ -16,6 +16,9 @@ #define TYPE_ASPEED_WDT "aspeed.wdt" #define ASPEED_WDT(obj) \ OBJECT_CHECK(AspeedWDTState, (obj), TYPE_ASPEED_WDT) +#define TYPE_ASPEED_2400_WDT TYPE_ASPEED_WDT "-ast2400" +#define TYPE_ASPEED_2500_WDT TYPE_ASPEED_WDT "-ast2500" +#define TYPE_ASPEED_2600_WDT TYPE_ASPEED_WDT "-ast2600" #define ASPEED_WDT_REGS_MAX (0x20 / 4) @@ -30,8 +33,20 @@ typedef struct AspeedWDTState { AspeedSCUState *scu; uint32_t pclk_freq; - uint32_t silicon_rev; - uint32_t ext_pulse_width_mask; } AspeedWDTState; +#define ASPEED_WDT_CLASS(klass) \ + OBJECT_CLASS_CHECK(AspeedWDTClass, (klass), TYPE_ASPEED_WDT) +#define ASPEED_WDT_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AspeedWDTClass, (obj), TYPE_ASPEED_WDT) + +typedef struct AspeedWDTClass { + SysBusDeviceClass parent_class; + + uint32_t offset; + uint32_t ext_pulse_width_mask; + uint32_t reset_ctrl_reg; + void (*reset_pulse)(AspeedWDTState *s, uint32_t property); +} AspeedWDTClass; + #endif /* WDT_ASPEED_H */ diff --git a/include/migration/misc.h b/include/migration/misc.h index b9d8e78..d276225 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -60,6 +60,7 @@ void migration_object_init(void); void migration_shutdown(void); void qemu_start_incoming_migration(const char *uri, Error **errp); bool migration_is_idle(void); +bool migration_is_active(MigrationState *); void add_migration_state_change_notifier(Notifier *notify); void remove_migration_state_change_notifier(Notifier *notify); bool migration_in_setup(MigrationState *); diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1fbfd09..b9ee563 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -224,6 +224,7 @@ extern const VMStateInfo vmstate_info_unused_buffer; extern const VMStateInfo vmstate_info_tmp; extern const VMStateInfo vmstate_info_bitmap; extern const VMStateInfo vmstate_info_qtailq; +extern const VMStateInfo vmstate_info_gtree; #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0) /* @@ -754,6 +755,45 @@ extern const VMStateInfo vmstate_info_qtailq; .start = offsetof(_type, _next), \ } +/* + * For migrating a GTree whose key is a pointer to _key_type and the + * value, a pointer to _val_type + * The target tree must have been properly initialized + * _vmsd: Start address of the 2 element array containing the data vmsd + * and the key vmsd, in that order + * _key_type: type of the key + * _val_type: type of the value + */ +#define VMSTATE_GTREE_V(_field, _state, _version, _vmsd, \ + _key_type, _val_type) \ +{ \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .vmsd = (_vmsd), \ + .info = &vmstate_info_gtree, \ + .start = sizeof(_key_type), \ + .size = sizeof(_val_type), \ + .offset = offsetof(_state, _field), \ +} + +/* + * For migrating a GTree with direct key and the value a pointer + * to _val_type + * The target tree must have been properly initialized + * _vmsd: data vmsd + * _val_type: type of the value + */ +#define VMSTATE_GTREE_DIRECT_KEY_V(_field, _state, _version, _vmsd, _val_type) \ +{ \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .vmsd = (_vmsd), \ + .info = &vmstate_info_gtree, \ + .start = 0, \ + .size = sizeof(_val_type), \ + .offset = offsetof(_state, _field), \ +} + /* _f : field name _f_n : num of elements field_name _n : num of elements diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h index 22876d1..9c82683 100644 --- a/include/qemu/rcu.h +++ b/include/qemu/rcu.h @@ -154,6 +154,31 @@ extern void call_rcu1(struct rcu_head *head, RCUCBFunc *func); }), \ (RCUCBFunc *)g_free); +typedef void RCUReadAuto; +static inline RCUReadAuto *rcu_read_auto_lock(void) +{ + rcu_read_lock(); + /* Anything non-NULL causes the cleanup function to be called */ + return (void *)(uintptr_t)0x1; +} + +static inline void rcu_read_auto_unlock(RCUReadAuto *r) +{ + rcu_read_unlock(); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(RCUReadAuto, rcu_read_auto_unlock) + +#define WITH_RCU_READ_LOCK_GUARD() \ + WITH_RCU_READ_LOCK_GUARD_(_rcu_read_auto##__COUNTER__) + +#define WITH_RCU_READ_LOCK_GUARD_(var) \ + for (g_autoptr(RCUReadAuto) var = rcu_read_auto_lock(); \ + (var); rcu_read_auto_unlock(var), (var) = NULL) + +#define RCU_READ_LOCK_GUARD() \ + g_autoptr(RCUReadAuto) _rcu_read_auto __attribute__((unused)) = rcu_read_auto_lock() + #ifdef __cplusplus } #endif diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index 682ba1d..3e4e1d9 100644 --- a/include/qom/object_interfaces.h +++ b/include/qom/object_interfaces.h @@ -133,6 +133,18 @@ int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp); /** + * user_creatable_print_help: + * @type: the QOM type to be added + * @opts: options to create + * + * Prints help if requested in @opts. + * + * Returns: true if @opts contained a help option and help was printed, false + * if no help option was found. + */ +bool user_creatable_print_help(const char *type, QemuOpts *opts); + +/** * user_creatable_del: * @id: the unique ID for the object * @errp: if an error occurs, a pointer to an area to store the error diff --git a/include/standard-headers/asm-x86/bootparam.h b/include/standard-headers/asm-x86/bootparam.h index 67d4f01..a6f7cf5 100644 --- a/include/standard-headers/asm-x86/bootparam.h +++ b/include/standard-headers/asm-x86/bootparam.h @@ -29,6 +29,8 @@ #define XLF_EFI_HANDOVER_32 (1<<2) #define XLF_EFI_HANDOVER_64 (1<<3) #define XLF_EFI_KEXEC (1<<4) +#define XLF_5LEVEL (1<<5) +#define XLF_5LEVEL_ENABLED (1<<6) #endif /* _ASM_X86_BOOTPARAM_H */ diff --git a/include/standard-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h index e171514..90604a8 100644 --- a/include/standard-headers/asm-x86/kvm_para.h +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -30,6 +30,7 @@ #define KVM_FEATURE_ASYNC_PF_VMEXIT 10 #define KVM_FEATURE_PV_SEND_IPI 11 #define KVM_FEATURE_POLL_CONTROL 12 +#define KVM_FEATURE_PV_SCHED_YIELD 13 #define KVM_HINTS_REALTIME 0 diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 9b9919a..4ff422b 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -259,10 +259,32 @@ struct ethtool_tunable { #define ETHTOOL_PHY_FAST_LINK_DOWN_ON 0 #define ETHTOOL_PHY_FAST_LINK_DOWN_OFF 0xff +/* Energy Detect Power Down (EDPD) is a feature supported by some PHYs, where + * the PHY's RX & TX blocks are put into a low-power mode when there is no + * link detected (typically cable is un-plugged). For RX, only a minimal + * link-detection is available, and for TX the PHY wakes up to send link pulses + * to avoid any lock-ups in case the peer PHY may also be running in EDPD mode. + * + * Some PHYs may support configuration of the wake-up interval for TX pulses, + * and some PHYs may support only disabling TX pulses entirely. For the latter + * a special value is required (ETHTOOL_PHY_EDPD_NO_TX) so that this can be + * configured from userspace (should the user want it). + * + * The interval units for TX wake-up are in milliseconds, since this should + * cover a reasonable range of intervals: + * - from 1 millisecond, which does not sound like much of a power-saver + * - to ~65 seconds which is quite a lot to wait for a link to come up when + * plugging a cable + */ +#define ETHTOOL_PHY_EDPD_DFLT_TX_MSECS 0xffff +#define ETHTOOL_PHY_EDPD_NO_TX 0xfffe +#define ETHTOOL_PHY_EDPD_DISABLE 0 + enum phy_tunable_id { ETHTOOL_PHY_ID_UNSPEC, ETHTOOL_PHY_DOWNSHIFT, ETHTOOL_PHY_FAST_LINK_DOWN, + ETHTOOL_PHY_EDPD, /* * Add your fresh new phy tunable attribute above and remember to update * phy_tunable_strings[] in net/core/ethtool.c @@ -1483,6 +1505,8 @@ enum ethtool_link_mode_bit_indices { ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT = 64, ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT = 65, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66, + ETHTOOL_LINK_MODE_100baseT1_Full_BIT = 67, + ETHTOOL_LINK_MODE_1000baseT1_Full_BIT = 68, /* must be last entry */ __ETHTOOL_LINK_MODE_MASK_NBITS diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 2716476..29d6e93 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -528,6 +528,7 @@ #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ #define PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */ +#define PCI_EXP_LNKCAP_SLS_32_0GB 0x00000005 /* LNKCAP2 SLS Vector bit 4 */ #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ @@ -556,6 +557,7 @@ #define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */ #define PCI_EXP_LNKSTA_CLS_16_0GB 0x0004 /* Current Link Speed 16.0GT/s */ +#define PCI_EXP_LNKSTA_CLS_32_0GB 0x0005 /* Current Link Speed 32.0GT/s */ #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */ #define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */ #define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */ @@ -589,6 +591,7 @@ #define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */ #define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */ #define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */ +#define PCI_EXP_SLTCTL_ATTN_IND_SHIFT 6 /* Attention Indicator shift */ #define PCI_EXP_SLTCTL_ATTN_IND_ON 0x0040 /* Attention Indicator on */ #define PCI_EXP_SLTCTL_ATTN_IND_BLINK 0x0080 /* Attention Indicator blinking */ #define PCI_EXP_SLTCTL_ATTN_IND_OFF 0x00c0 /* Attention Indicator off */ @@ -661,6 +664,7 @@ #define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ #define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ #define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */ +#define PCI_EXP_LNKCAP2_SLS_32_0GB 0x00000020 /* Supported Speed 32GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_LNKCTL2_TLS 0x000f @@ -668,6 +672,7 @@ #define PCI_EXP_LNKCTL2_TLS_5_0GT 0x0002 /* Supported Speed 5GT/s */ #define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed 8GT/s */ #define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed 16GT/s */ +#define PCI_EXP_LNKCTL2_TLS_32_0GT 0x0005 /* Supported Speed 32GT/s */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ @@ -709,7 +714,9 @@ #define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */ #define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */ #define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */ -#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM +#define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */ +#define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */ +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_16GT #define PCI_EXT_CAP_DSN_SIZEOF 12 #define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40 @@ -1049,4 +1056,14 @@ #define PCI_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */ #define PCI_L1SS_CTL2 0x0c /* Control 2 Register */ +/* Data Link Feature */ +#define PCI_DLF_CAP 0x04 /* Capabilities Register */ +#define PCI_DLF_EXCHANGE_ENABLE 0x80000000 /* Data Link Feature Exchange Enable */ + +/* Physical Layer 16.0 GT/s */ +#define PCI_PL_16GT_LE_CTRL 0x20 /* Lane Equalization Control Register */ +#define PCI_PL_16GT_LE_CTRL_DSP_TX_PRESET_MASK 0x0000000F +#define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK 0x000000F0 +#define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT 4 + #endif /* LINUX_PCI_REGS_H */ diff --git a/include/standard-headers/linux/virtio_fs.h b/include/standard-headers/linux/virtio_fs.h new file mode 100644 index 0000000..9d88817 --- /dev/null +++ b/include/standard-headers/linux/virtio_fs.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ + +#ifndef _LINUX_VIRTIO_FS_H +#define _LINUX_VIRTIO_FS_H + +#include "standard-headers/linux/types.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" +#include "standard-headers/linux/virtio_types.h" + +struct virtio_fs_config { + /* Filesystem name (UTF-8, not NUL-terminated, padded with NULs) */ + uint8_t tag[36]; + + /* Number of request queues */ + uint32_t num_request_queues; +} QEMU_PACKED; + +#endif /* _LINUX_VIRTIO_FS_H */ diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h index 32b2f94..585e07b 100644 --- a/include/standard-headers/linux/virtio_ids.h +++ b/include/standard-headers/linux/virtio_ids.h @@ -43,6 +43,8 @@ #define VIRTIO_ID_INPUT 18 /* virtio input */ #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */ #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */ +#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */ +#define VIRTIO_ID_FS 26 /* virtio filesystem */ #define VIRTIO_ID_PMEM 27 /* virtio pmem */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/standard-headers/linux/virtio_iommu.h b/include/standard-headers/linux/virtio_iommu.h new file mode 100644 index 0000000..b9443b8 --- /dev/null +++ b/include/standard-headers/linux/virtio_iommu.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Virtio-iommu definition v0.12 + * + * Copyright (C) 2019 Arm Ltd. + */ +#ifndef _LINUX_VIRTIO_IOMMU_H +#define _LINUX_VIRTIO_IOMMU_H + +#include "standard-headers/linux/types.h" + +/* Feature bits */ +#define VIRTIO_IOMMU_F_INPUT_RANGE 0 +#define VIRTIO_IOMMU_F_DOMAIN_RANGE 1 +#define VIRTIO_IOMMU_F_MAP_UNMAP 2 +#define VIRTIO_IOMMU_F_BYPASS 3 +#define VIRTIO_IOMMU_F_PROBE 4 +#define VIRTIO_IOMMU_F_MMIO 5 + +struct virtio_iommu_range_64 { + uint64_t start; + uint64_t end; +}; + +struct virtio_iommu_range_32 { + uint32_t start; + uint32_t end; +}; + +struct virtio_iommu_config { + /* Supported page sizes */ + uint64_t page_size_mask; + /* Supported IOVA range */ + struct virtio_iommu_range_64 input_range; + /* Max domain ID size */ + struct virtio_iommu_range_32 domain_range; + /* Probe buffer size */ + uint32_t probe_size; +}; + +/* Request types */ +#define VIRTIO_IOMMU_T_ATTACH 0x01 +#define VIRTIO_IOMMU_T_DETACH 0x02 +#define VIRTIO_IOMMU_T_MAP 0x03 +#define VIRTIO_IOMMU_T_UNMAP 0x04 +#define VIRTIO_IOMMU_T_PROBE 0x05 + +/* Status types */ +#define VIRTIO_IOMMU_S_OK 0x00 +#define VIRTIO_IOMMU_S_IOERR 0x01 +#define VIRTIO_IOMMU_S_UNSUPP 0x02 +#define VIRTIO_IOMMU_S_DEVERR 0x03 +#define VIRTIO_IOMMU_S_INVAL 0x04 +#define VIRTIO_IOMMU_S_RANGE 0x05 +#define VIRTIO_IOMMU_S_NOENT 0x06 +#define VIRTIO_IOMMU_S_FAULT 0x07 +#define VIRTIO_IOMMU_S_NOMEM 0x08 + +struct virtio_iommu_req_head { + uint8_t type; + uint8_t reserved[3]; +}; + +struct virtio_iommu_req_tail { + uint8_t status; + uint8_t reserved[3]; +}; + +struct virtio_iommu_req_attach { + struct virtio_iommu_req_head head; + uint32_t domain; + uint32_t endpoint; + uint8_t reserved[8]; + struct virtio_iommu_req_tail tail; +}; + +struct virtio_iommu_req_detach { + struct virtio_iommu_req_head head; + uint32_t domain; + uint32_t endpoint; + uint8_t reserved[8]; + struct virtio_iommu_req_tail tail; +}; + +#define VIRTIO_IOMMU_MAP_F_READ (1 << 0) +#define VIRTIO_IOMMU_MAP_F_WRITE (1 << 1) +#define VIRTIO_IOMMU_MAP_F_MMIO (1 << 2) + +#define VIRTIO_IOMMU_MAP_F_MASK (VIRTIO_IOMMU_MAP_F_READ | \ + VIRTIO_IOMMU_MAP_F_WRITE | \ + VIRTIO_IOMMU_MAP_F_MMIO) + +struct virtio_iommu_req_map { + struct virtio_iommu_req_head head; + uint32_t domain; + uint64_t virt_start; + uint64_t virt_end; + uint64_t phys_start; + uint32_t flags; + struct virtio_iommu_req_tail tail; +}; + +struct virtio_iommu_req_unmap { + struct virtio_iommu_req_head head; + uint32_t domain; + uint64_t virt_start; + uint64_t virt_end; + uint8_t reserved[4]; + struct virtio_iommu_req_tail tail; +}; + +#define VIRTIO_IOMMU_PROBE_T_NONE 0 +#define VIRTIO_IOMMU_PROBE_T_RESV_MEM 1 + +#define VIRTIO_IOMMU_PROBE_T_MASK 0xfff + +struct virtio_iommu_probe_property { + uint16_t type; + uint16_t length; +}; + +#define VIRTIO_IOMMU_RESV_MEM_T_RESERVED 0 +#define VIRTIO_IOMMU_RESV_MEM_T_MSI 1 + +struct virtio_iommu_probe_resv_mem { + struct virtio_iommu_probe_property head; + uint8_t subtype; + uint8_t reserved[3]; + uint64_t start; + uint64_t end; +}; + +struct virtio_iommu_req_probe { + struct virtio_iommu_req_head head; + uint32_t endpoint; + uint8_t reserved[64]; + + uint8_t properties[]; + + /* + * Tail follows the variable-length properties array. No padding, + * property lengths are all aligned on 8 bytes. + */ +}; + +/* Fault types */ +#define VIRTIO_IOMMU_FAULT_R_UNKNOWN 0 +#define VIRTIO_IOMMU_FAULT_R_DOMAIN 1 +#define VIRTIO_IOMMU_FAULT_R_MAPPING 2 + +#define VIRTIO_IOMMU_FAULT_F_READ (1 << 0) +#define VIRTIO_IOMMU_FAULT_F_WRITE (1 << 1) +#define VIRTIO_IOMMU_FAULT_F_EXEC (1 << 2) +#define VIRTIO_IOMMU_FAULT_F_ADDRESS (1 << 8) + +struct virtio_iommu_fault { + uint8_t reason; + uint8_t reserved[3]; + uint32_t flags; + uint32_t endpoint; + uint8_t reserved2[4]; + uint64_t address; +}; + +#endif diff --git a/include/standard-headers/linux/virtio_pmem.h b/include/standard-headers/linux/virtio_pmem.h index 7e3d43b..fc029de 100644 --- a/include/standard-headers/linux/virtio_pmem.h +++ b/include/standard-headers/linux/virtio_pmem.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause */ /* * Definitions for virtio-pmem devices. * @@ -7,8 +7,8 @@ * Author(s): Pankaj Gupta <pagupta@redhat.com> */ -#ifndef _UAPI_LINUX_VIRTIO_PMEM_H -#define _UAPI_LINUX_VIRTIO_PMEM_H +#ifndef _LINUX_VIRTIO_PMEM_H +#define _LINUX_VIRTIO_PMEM_H #include "standard-headers/linux/types.h" #include "standard-headers/linux/virtio_ids.h" diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index dfc7a31..8df5172 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -15,6 +15,7 @@ #include "qapi/qapi-types-misc.h" #include "qapi/qapi-types-run-state.h" #include "qapi/qapi-types-ui.h" +#include "block/aio.h" /* replay clock kinds */ enum ReplayClockKind { @@ -140,6 +141,9 @@ void replay_enable_events(void); bool replay_events_enabled(void); /*! Adds bottom half event to the queue */ void replay_bh_schedule_event(QEMUBH *bh); +/* Adds oneshot bottom half event to the queue */ +void replay_bh_schedule_oneshot_event(AioContext *ctx, + QEMUBHFunc *cb, void *opaque); /*! Adds input event to the queue */ void replay_input_event(QemuConsole *src, InputEvent *evt); /*! Adds input sync event to the queue */ diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index e1f8b74..9d379d3 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -214,6 +214,18 @@ struct kvm_vcpu_events { #define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \ KVM_REG_ARM_FW | ((r) & 0xffff)) #define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 KVM_REG_ARM_FW_REG(1) + /* Higher values mean better protection. */ +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL 0 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL 1 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED 2 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 KVM_REG_ARM_FW_REG(2) + /* Higher values mean better protection. */ +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL 0 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN 1 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL 2 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4) /* Device Control API: ARM VGIC */ #define KVM_DEV_ARM_VGIC_GRP_ADDR 0 @@ -254,8 +266,10 @@ struct kvm_vcpu_events { #define KVM_DEV_ARM_ITS_CTRL_RESET 4 /* KVM_IRQ_LINE irq field index values */ +#define KVM_ARM_IRQ_VCPU2_SHIFT 28 +#define KVM_ARM_IRQ_VCPU2_MASK 0xf #define KVM_ARM_IRQ_TYPE_SHIFT 24 -#define KVM_ARM_IRQ_TYPE_MASK 0xff +#define KVM_ARM_IRQ_TYPE_MASK 0xf #define KVM_ARM_IRQ_VCPU_SHIFT 16 #define KVM_ARM_IRQ_VCPU_MASK 0xff #define KVM_ARM_IRQ_NUM_SHIFT 0 diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h index 27a9b6d..eb5d361 100644 --- a/linux-headers/asm-arm/unistd-common.h +++ b/linux-headers/asm-arm/unistd-common.h @@ -388,5 +388,7 @@ #define __NR_fsconfig (__NR_SYSCALL_BASE + 431) #define __NR_fsmount (__NR_SYSCALL_BASE + 432) #define __NR_fspick (__NR_SYSCALL_BASE + 433) +#define __NR_pidfd_open (__NR_SYSCALL_BASE + 434) +#define __NR_clone3 (__NR_SYSCALL_BASE + 435) #endif /* _ASM_ARM_UNISTD_COMMON_H */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 2431ec3..0ce6e49 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -229,6 +229,16 @@ struct kvm_vcpu_events { #define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ KVM_REG_ARM_FW | ((r) & 0xffff)) #define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 KVM_REG_ARM_FW_REG(1) +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL 0 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL 1 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED 2 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 KVM_REG_ARM_FW_REG(2) +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL 0 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN 1 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL 2 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4) /* SVE registers */ #define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT) @@ -260,6 +270,13 @@ struct kvm_vcpu_events { KVM_REG_SIZE_U256 | \ ((i) & (KVM_ARM64_SVE_MAX_SLICES - 1))) +/* + * Register values for KVM_REG_ARM64_SVE_ZREG(), KVM_REG_ARM64_SVE_PREG() and + * KVM_REG_ARM64_SVE_FFR() are represented in memory in an endianness- + * invariant layout which differs from the layout used for the FPSIMD + * V-registers on big-endian systems: see sigcontext.h for more explanation. + */ + #define KVM_ARM64_SVE_VQ_MIN __SVE_VQ_MIN #define KVM_ARM64_SVE_VQ_MAX __SVE_VQ_MAX @@ -308,8 +325,10 @@ struct kvm_vcpu_events { #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 /* KVM_IRQ_LINE irq field index values */ +#define KVM_ARM_IRQ_VCPU2_SHIFT 28 +#define KVM_ARM_IRQ_VCPU2_MASK 0xf #define KVM_ARM_IRQ_TYPE_SHIFT 24 -#define KVM_ARM_IRQ_TYPE_MASK 0xff +#define KVM_ARM_IRQ_TYPE_MASK 0xf #define KVM_ARM_IRQ_VCPU_SHIFT 16 #define KVM_ARM_IRQ_VCPU_MASK 0xff #define KVM_ARM_IRQ_NUM_SHIFT 0 diff --git a/linux-headers/asm-generic/mman-common.h b/linux-headers/asm-generic/mman-common.h index abd238d..c160a53 100644 --- a/linux-headers/asm-generic/mman-common.h +++ b/linux-headers/asm-generic/mman-common.h @@ -19,15 +19,18 @@ #define MAP_TYPE 0x0f /* Mask for type of mapping */ #define MAP_FIXED 0x10 /* Interpret addr exactly */ #define MAP_ANONYMOUS 0x20 /* don't use a file */ -#ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED -# define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be uninitialized */ -#else -# define MAP_UNINITIALIZED 0x0 /* Don't support this flag */ -#endif -/* 0x0100 - 0x80000 flags are defined in asm-generic/mman.h */ +/* 0x0100 - 0x4000 flags are defined in asm-generic/mman.h */ +#define MAP_POPULATE 0x008000 /* populate (prefault) pagetables */ +#define MAP_NONBLOCK 0x010000 /* do not block on IO */ +#define MAP_STACK 0x020000 /* give out an address that is best suited for process/thread stacks */ +#define MAP_HUGETLB 0x040000 /* create a huge page mapping */ +#define MAP_SYNC 0x080000 /* perform synchronous page faults for the mapping */ #define MAP_FIXED_NOREPLACE 0x100000 /* MAP_FIXED which doesn't unmap underlying mapping */ +#define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be + * uninitialized */ + /* * Flags for mlock */ @@ -64,6 +67,9 @@ #define MADV_WIPEONFORK 18 /* Zero memory on fork, child only */ #define MADV_KEEPONFORK 19 /* Undo MADV_WIPEONFORK */ +#define MADV_COLD 20 /* deactivate these pages */ +#define MADV_PAGEOUT 21 /* reclaim these pages */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/linux-headers/asm-generic/mman.h b/linux-headers/asm-generic/mman.h index 653687d..57e8195 100644 --- a/linux-headers/asm-generic/mman.h +++ b/linux-headers/asm-generic/mman.h @@ -9,13 +9,11 @@ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ #define MAP_LOCKED 0x2000 /* pages are locked */ #define MAP_NORESERVE 0x4000 /* don't check for reservations */ -#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ -#define MAP_NONBLOCK 0x10000 /* do not block on IO */ -#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */ -#define MAP_HUGETLB 0x40000 /* create a huge page mapping */ -#define MAP_SYNC 0x80000 /* perform synchronous page faults for the mapping */ -/* Bits [26:31] are reserved, see mman-common.h for MAP_HUGETLB usage */ +/* + * Bits [26:31] are reserved, see asm-generic/hugetlb_encode.h + * for MAP_HUGETLB usage + */ #define MCL_CURRENT 1 /* lock all current mappings */ #define MCL_FUTURE 2 /* lock all future mappings */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index a87904d..1fc8faa 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -569,7 +569,7 @@ __SYSCALL(__NR_semget, sys_semget) __SC_COMP(__NR_semctl, sys_semctl, compat_sys_semctl) #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_semtimedop 192 -__SC_COMP(__NR_semtimedop, sys_semtimedop, sys_semtimedop_time32) +__SC_3264(__NR_semtimedop, sys_semtimedop_time32, sys_semtimedop) #endif #define __NR_semop 193 __SYSCALL(__NR_semop, sys_semop) @@ -844,9 +844,15 @@ __SYSCALL(__NR_fsconfig, sys_fsconfig) __SYSCALL(__NR_fsmount, sys_fsmount) #define __NR_fspick 433 __SYSCALL(__NR_fspick, sys_fspick) +#define __NR_pidfd_open 434 +__SYSCALL(__NR_pidfd_open, sys_pidfd_open) +#ifdef __ARCH_WANT_SYS_CLONE3 +#define __NR_clone3 435 +__SYSCALL(__NR_clone3, sys_clone3) +#endif #undef __NR_syscalls -#define __NR_syscalls 434 +#define __NR_syscalls 436 /* * 32 bit systems traditionally used different diff --git a/linux-headers/asm-mips/mman.h b/linux-headers/asm-mips/mman.h index c2b4096..57dc2ac 100644 --- a/linux-headers/asm-mips/mman.h +++ b/linux-headers/asm-mips/mman.h @@ -95,6 +95,9 @@ #define MADV_WIPEONFORK 18 /* Zero memory on fork, child only */ #define MADV_KEEPONFORK 19 /* Undo MADV_WIPEONFORK */ +#define MADV_COLD 20 /* deactivate these pages */ +#define MADV_PAGEOUT 21 /* reclaim these pages */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h index fb988de..7dffe8e 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -363,6 +363,7 @@ #define __NR_fsconfig (__NR_Linux + 431) #define __NR_fsmount (__NR_Linux + 432) #define __NR_fspick (__NR_Linux + 433) +#define __NR_pidfd_open (__NR_Linux + 434) #endif /* _ASM_MIPS_UNISTD_N32_H */ diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h index 1735916..f4592d6 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -339,6 +339,7 @@ #define __NR_fsconfig (__NR_Linux + 431) #define __NR_fsmount (__NR_Linux + 432) #define __NR_fspick (__NR_Linux + 433) +#define __NR_pidfd_open (__NR_Linux + 434) #endif /* _ASM_MIPS_UNISTD_N64_H */ diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h index 83c8d8f..04c6728 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -409,6 +409,7 @@ #define __NR_fsconfig (__NR_Linux + 431) #define __NR_fsmount (__NR_Linux + 432) #define __NR_fspick (__NR_Linux + 433) +#define __NR_pidfd_open (__NR_Linux + 434) #endif /* _ASM_MIPS_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/mman.h b/linux-headers/asm-powerpc/mman.h index 1c2b3fc..8db7c2a 100644 --- a/linux-headers/asm-powerpc/mman.h +++ b/linux-headers/asm-powerpc/mman.h @@ -21,15 +21,11 @@ #define MAP_DENYWRITE 0x0800 /* ETXTBSY */ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ + #define MCL_CURRENT 0x2000 /* lock all currently mapped pages */ #define MCL_FUTURE 0x4000 /* lock all additions to address space */ #define MCL_ONFAULT 0x8000 /* lock all pages that are faulted in */ -#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ -#define MAP_NONBLOCK 0x10000 /* do not block on IO */ -#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */ -#define MAP_HUGETLB 0x40000 /* create a huge page mapping */ - /* Override any generic PKEY permission defines */ #define PKEY_DISABLE_EXECUTE 0x4 #undef PKEY_ACCESS_MASK diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h index 04cb2d3..5584cc1 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -416,6 +416,8 @@ #define __NR_fsconfig 431 #define __NR_fsmount 432 #define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 #endif /* _ASM_POWERPC_UNISTD_32_H */ diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h index b1e6921..251bcff 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -388,6 +388,8 @@ #define __NR_fsconfig 431 #define __NR_fsmount 432 #define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 #endif /* _ASM_POWERPC_UNISTD_64_H */ diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index 03ab596..0138ccb 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -231,6 +231,12 @@ struct kvm_guest_debug_arch { #define KVM_SYNC_GSCB (1UL << 9) #define KVM_SYNC_BPBC (1UL << 10) #define KVM_SYNC_ETOKEN (1UL << 11) + +#define KVM_SYNC_S390_VALID_FIELDS \ + (KVM_SYNC_PREFIX | KVM_SYNC_GPRS | KVM_SYNC_ACRS | KVM_SYNC_CRS | \ + KVM_SYNC_ARCH0 | KVM_SYNC_PFAULT | KVM_SYNC_VRS | KVM_SYNC_RICCB | \ + KVM_SYNC_FPRS | KVM_SYNC_GSCB | KVM_SYNC_BPBC | KVM_SYNC_ETOKEN) + /* length and alignment of the sdnx as a power of two */ #define SDNXC 8 #define SDNXL (1UL << SDNXC) diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index 941853f..7cce3ee 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -406,5 +406,7 @@ #define __NR_fsconfig 431 #define __NR_fsmount 432 #define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 #endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index 90271d7..2371ff1 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -354,5 +354,7 @@ #define __NR_fsconfig 431 #define __NR_fsmount 432 #define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 6e7dd79..503d3f4 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -378,23 +378,24 @@ struct kvm_sync_regs { struct kvm_vcpu_events events; }; -#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0) -#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1) -#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2) -#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3) +#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0) +#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1) +#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2) +#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3) +#define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4) #define KVM_STATE_NESTED_FORMAT_VMX 0 -#define KVM_STATE_NESTED_FORMAT_SVM 1 +#define KVM_STATE_NESTED_FORMAT_SVM 1 /* unused */ #define KVM_STATE_NESTED_GUEST_MODE 0x00000001 #define KVM_STATE_NESTED_RUN_PENDING 0x00000002 #define KVM_STATE_NESTED_EVMCS 0x00000004 -#define KVM_STATE_NESTED_VMX_VMCS_SIZE 0x1000 - #define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001 #define KVM_STATE_NESTED_SMM_VMXON 0x00000002 +#define KVM_STATE_NESTED_VMX_VMCS_SIZE 0x1000 + struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; __u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; @@ -432,4 +433,17 @@ struct kvm_nested_state { } data; }; +/* for KVM_CAP_PMU_EVENT_FILTER */ +struct kvm_pmu_event_filter { + __u32 action; + __u32 nevents; + __u32 fixed_counter_bitmap; + __u32 flags; + __u32 pad[4]; + __u64 events[0]; +}; + +#define KVM_PMU_EVENT_ALLOW 0 +#define KVM_PMU_EVENT_DENY 1 + #endif /* _ASM_X86_KVM_H */ diff --git a/linux-headers/asm-x86/unistd.h b/linux-headers/asm-x86/unistd.h index c04f638..498d151 100644 --- a/linux-headers/asm-x86/unistd.h +++ b/linux-headers/asm-x86/unistd.h @@ -3,7 +3,7 @@ #define _ASM_X86_UNISTD_H /* x32 syscall flag bit */ -#define __X32_SYSCALL_BIT 0x40000000 +#define __X32_SYSCALL_BIT 0x40000000UL # ifdef __i386__ # include <asm/unistd_32.h> diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index 57bb488..e8ebec1 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -424,5 +424,7 @@ #define __NR_fsconfig 431 #define __NR_fsmount 432 #define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 #endif /* _ASM_X86_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index fe6aa06..a2f863d 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -346,5 +346,7 @@ #define __NR_fsconfig 431 #define __NR_fsmount 432 #define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 #endif /* _ASM_X86_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index 09cca49..4cdc67d 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -299,6 +299,8 @@ #define __NR_fsconfig (__X32_SYSCALL_BIT + 431) #define __NR_fsmount (__X32_SYSCALL_BIT + 432) #define __NR_fspick (__X32_SYSCALL_BIT + 433) +#define __NR_pidfd_open (__X32_SYSCALL_BIT + 434) +#define __NR_clone3 (__X32_SYSCALL_BIT + 435) #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) #define __NR_ioctl (__X32_SYSCALL_BIT + 514) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 18892d6..3d9b18f 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -116,7 +116,7 @@ struct kvm_irq_level { * ACPI gsi notion of irq. * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47.. * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23.. - * For ARM: See Documentation/virtual/kvm/api.txt + * For ARM: See Documentation/virt/kvm/api.txt */ union { __u32 irq; @@ -243,6 +243,8 @@ struct kvm_hyperv_exit { #define KVM_INTERNAL_ERROR_SIMUL_EX 2 /* Encounter unexpected vm-exit due to delivery event. */ #define KVM_INTERNAL_ERROR_DELIVERY_EV 3 +/* Encounter unexpected vm-exit reason */ +#define KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON 4 /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ struct kvm_run { @@ -995,6 +997,9 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_ARM_SVE 170 #define KVM_CAP_ARM_PTRAUTH_ADDRESS 171 #define KVM_CAP_ARM_PTRAUTH_GENERIC 172 +#define KVM_CAP_PMU_EVENT_FILTER 173 +#define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174 +#define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175 #ifdef KVM_CAP_IRQ_ROUTING @@ -1085,7 +1090,7 @@ struct kvm_xen_hvm_config { * * KVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies * the irqfd to operate in resampling mode for level triggered interrupt - * emulation. See Documentation/virtual/kvm/api.txt. + * emulation. See Documentation/virt/kvm/api.txt. */ #define KVM_IRQFD_FLAG_RESAMPLE (1 << 1) @@ -1141,6 +1146,7 @@ struct kvm_dirty_tlb { #define KVM_REG_S390 0x5000000000000000ULL #define KVM_REG_ARM64 0x6000000000000000ULL #define KVM_REG_MIPS 0x7000000000000000ULL +#define KVM_REG_RISCV 0x8000000000000000ULL #define KVM_REG_SIZE_SHIFT 52 #define KVM_REG_SIZE_MASK 0x00f0000000000000ULL @@ -1329,6 +1335,8 @@ struct kvm_s390_ucas_mapping { #define KVM_PPC_GET_RMMU_INFO _IOW(KVMIO, 0xb0, struct kvm_ppc_rmmu_info) /* Available with KVM_CAP_PPC_GET_CPU_CHAR */ #define KVM_PPC_GET_CPU_CHAR _IOR(KVMIO, 0xb1, struct kvm_ppc_cpu_char) +/* Available with KVM_CAP_PMU_EVENT_FILTER */ +#define KVM_SET_PMU_EVENT_FILTER _IOW(KVMIO, 0xb2, struct kvm_pmu_event_filter) /* ioctl for vm fd */ #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device) diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h index 36bbe17..34c3969 100644 --- a/linux-headers/linux/psp-sev.h +++ b/linux-headers/linux/psp-sev.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ /* * Userspace interface for AMD Secure Encrypted Virtualization (SEV) * platform management commands. @@ -7,10 +8,6 @@ * Author: Brijesh Singh <brijesh.singh@amd.com> * * SEV API specification is available at: https://developer.amd.com/sev/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __PSP_SEV_USER_H__ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 24f5051..fb10370 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -295,15 +295,38 @@ struct vfio_region_info_cap_type { __u32 subtype; /* type specific */ }; +/* + * List of region types, global per bus driver. + * If you introduce a new type, please add it here. + */ + +/* PCI region type containing a PCI vendor part */ #define VFIO_REGION_TYPE_PCI_VENDOR_TYPE (1 << 31) #define VFIO_REGION_TYPE_PCI_VENDOR_MASK (0xffff) +#define VFIO_REGION_TYPE_GFX (1) +#define VFIO_REGION_TYPE_CCW (2) + +/* sub-types for VFIO_REGION_TYPE_PCI_* */ -/* 8086 Vendor sub-types */ +/* 8086 vendor PCI sub-types */ #define VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION (1) #define VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG (2) #define VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG (3) -#define VFIO_REGION_TYPE_GFX (1) +/* 10de vendor PCI sub-types */ +/* + * NVIDIA GPU NVlink2 RAM is coherent RAM mapped onto the host address space. + */ +#define VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM (1) + +/* 1014 vendor PCI sub-types */ +/* + * IBM NPU NVlink2 ATSD (Address Translation Shootdown) register of NPU + * to do TLB invalidation on a GPU. + */ +#define VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD (1) + +/* sub-types for VFIO_REGION_TYPE_GFX */ #define VFIO_REGION_SUBTYPE_GFX_EDID (1) /** @@ -353,26 +376,10 @@ struct vfio_region_gfx_edid { #define VFIO_DEVICE_GFX_LINK_STATE_DOWN 2 }; -#define VFIO_REGION_TYPE_CCW (2) -/* ccw sub-types */ +/* sub-types for VFIO_REGION_TYPE_CCW */ #define VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD (1) /* - * 10de vendor sub-type - * - * NVIDIA GPU NVlink2 RAM is coherent RAM mapped onto the host address space. - */ -#define VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM (1) - -/* - * 1014 vendor sub-type - * - * IBM NPU NVlink2 ATSD (Address Translation Shootdown) register of NPU - * to do TLB invalidation on a GPU. - */ -#define VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD (1) - -/* * The MSIX mappable capability informs that MSIX data of a BAR can be mmapped * which allows direct access to non-MSIX registers which happened to be within * the same system page. @@ -714,7 +721,31 @@ struct vfio_iommu_type1_info { __u32 argsz; __u32 flags; #define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */ - __u64 iova_pgsizes; /* Bitmap of supported page sizes */ +#define VFIO_IOMMU_INFO_CAPS (1 << 1) /* Info supports caps */ + __u64 iova_pgsizes; /* Bitmap of supported page sizes */ + __u32 cap_offset; /* Offset within info struct of first cap */ +}; + +/* + * The IOVA capability allows to report the valid IOVA range(s) + * excluding any non-relaxable reserved regions exposed by + * devices attached to the container. Any DMA map attempt + * outside the valid iova range will return error. + * + * The structures below define version 1 of this capability. + */ +#define VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE 1 + +struct vfio_iova_range { + __u64 start; + __u64 end; +}; + +struct vfio_iommu_type1_info_cap_iova_range { + struct vfio_info_cap_header header; + __u32 nr_iovas; + __u32 reserved; + struct vfio_iova_range iova_ranges[]; }; #define VFIO_IOMMU_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12) @@ -779,14 +779,13 @@ FlatView *address_space_get_flatview(AddressSpace *as) { FlatView *view; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); do { view = address_space_to_flatview(as); /* If somebody has replaced as->current_map concurrently, * flatview_ref returns false. */ } while (!flatview_ref(view)); - rcu_read_unlock(); return view; } @@ -2166,12 +2165,11 @@ int memory_region_get_fd(MemoryRegion *mr) { int fd; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); while (mr->alias) { mr = mr->alias; } fd = mr->ram_block->fd; - rcu_read_unlock(); return fd; } @@ -2181,14 +2179,13 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr) void *ptr; uint64_t offset = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); while (mr->alias) { offset += mr->alias_offset; mr = mr->alias; } assert(mr->ram_block); ptr = qemu_map_ram_ptr(mr->ram_block, offset); - rcu_read_unlock(); return ptr; } @@ -2578,12 +2575,11 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, hwaddr addr, uint64_t size) { MemoryRegionSection ret; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); ret = memory_region_find_rcu(mr, addr, size); if (ret.mr) { memory_region_ref(ret.mr); } - rcu_read_unlock(); return ret; } @@ -2591,9 +2587,8 @@ bool memory_region_present(MemoryRegion *container, hwaddr addr) { MemoryRegion *mr; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); mr = memory_region_find_rcu(container, addr, 1).mr; - rcu_read_unlock(); return mr && mr != container; } diff --git a/migration/migration.c b/migration/migration.c index 5f7e4d1..3febd0f 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1533,8 +1533,7 @@ static void migrate_fd_cleanup(MigrationState *s) qemu_fclose(tmp); } - assert((s->state != MIGRATION_STATUS_ACTIVE) && - (s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE)); + assert(!migration_is_active(s)); if (s->state == MIGRATION_STATUS_CANCELLING) { migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING, @@ -1703,6 +1702,12 @@ bool migration_is_idle(void) return false; } +bool migration_is_active(MigrationState *s) +{ + return (s->state == MIGRATION_STATUS_ACTIVE || + s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); +} + void migrate_init(MigrationState *s) { /* @@ -2481,7 +2486,7 @@ retry: out: res = qemu_file_get_error(rp); if (res) { - if (res == -EIO) { + if (res == -EIO && migration_in_postcopy()) { /* * Maybe there is something we can do: it looks like a * network down issue, and we pause for a recovery. @@ -3144,8 +3149,7 @@ static MigIterateState migration_iteration_run(MigrationState *s) return MIG_ITERATE_SKIP; } /* Just another iteration step */ - qemu_savevm_state_iterate(s->to_dst_file, - s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); + qemu_savevm_state_iterate(s->to_dst_file, in_postcopy); } else { trace_migration_thread_low_pending(pending_size); migration_completion(s); @@ -3266,8 +3270,7 @@ static void *migration_thread(void *opaque) trace_migration_thread_setup_complete(); - while (s->state == MIGRATION_STATUS_ACTIVE || - s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + while (migration_is_active(s)) { int64_t current_time; if (urgent || !qemu_file_rate_limit(s->to_dst_file)) { diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 1f63e65..abccafc 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -577,8 +577,6 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) } } - postcopy_state_set(POSTCOPY_INCOMING_END); - if (mis->postcopy_tmp_page) { munmap(mis->postcopy_tmp_page, mis->largest_page_size); mis->postcopy_tmp_page = NULL; @@ -768,9 +766,11 @@ static void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, atomic_xchg(&dc->page_fault_vcpu_time[cpu], low_time_offset); atomic_xchg(&dc->vcpu_addr[cpu], addr); - /* check it here, not at the begining of the function, - * due to, check could accur early than bitmap_set in - * qemu_ufd_copy_ioctl */ + /* + * check it here, not at the beginning of the function, + * due to, check could occur early than bitmap_set in + * qemu_ufd_copy_ioctl + */ already_received = ramblock_recv_bitmap_test(rb, (void *)addr); if (already_received) { atomic_xchg(&dc->vcpu_addr[cpu], 0); @@ -1094,7 +1094,7 @@ retry: return NULL; } -int postcopy_ram_enable_notify(MigrationIncomingState *mis) +int postcopy_ram_incoming_setup(MigrationIncomingState *mis) { /* Open the fd for the kernel to give us userfaults */ mis->userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); @@ -1134,6 +1134,32 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis) return -1; } + mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size, + PROT_READ | PROT_WRITE, MAP_PRIVATE | + MAP_ANONYMOUS, -1, 0); + if (mis->postcopy_tmp_page == MAP_FAILED) { + mis->postcopy_tmp_page = NULL; + error_report("%s: Failed to map postcopy_tmp_page %s", + __func__, strerror(errno)); + return -1; + } + + /* + * Map large zero page when kernel can't use UFFDIO_ZEROPAGE for hugepages + */ + mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + if (mis->postcopy_tmp_zero_page == MAP_FAILED) { + int e = errno; + mis->postcopy_tmp_zero_page = NULL; + error_report("%s: Failed to map large zero page %s", + __func__, strerror(e)); + return -e; + } + memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size); + /* * Ballooning can mark pages as absent while we're postcopying * that would cause false userfaults. @@ -1240,50 +1266,10 @@ int postcopy_place_page_zero(MigrationIncomingState *mis, void *host, qemu_ram_block_host_offset(rb, host)); } else { - /* The kernel can't use UFFDIO_ZEROPAGE for hugepages */ - if (!mis->postcopy_tmp_zero_page) { - mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0); - if (mis->postcopy_tmp_zero_page == MAP_FAILED) { - int e = errno; - mis->postcopy_tmp_zero_page = NULL; - error_report("%s: %s mapping large zero page", - __func__, strerror(e)); - return -e; - } - memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size); - } - return postcopy_place_page(mis, host, mis->postcopy_tmp_zero_page, - rb); + return postcopy_place_page(mis, host, mis->postcopy_tmp_zero_page, rb); } } -/* - * Returns a target page of memory that can be mapped at a later point in time - * using postcopy_place_page - * The same address is used repeatedly, postcopy_place_page just takes the - * backing page away. - * Returns: Pointer to allocated page - * - */ -void *postcopy_get_tmp_page(MigrationIncomingState *mis) -{ - if (!mis->postcopy_tmp_page) { - mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size, - PROT_READ | PROT_WRITE, MAP_PRIVATE | - MAP_ANONYMOUS, -1, 0); - if (mis->postcopy_tmp_page == MAP_FAILED) { - mis->postcopy_tmp_page = NULL; - error_report("%s: %s", __func__, strerror(errno)); - return NULL; - } - } - - return mis->postcopy_tmp_page; -} - #else /* No target OS support, stubs just fail */ void fill_destination_postcopy_migration_info(MigrationInfo *info) @@ -1321,7 +1307,7 @@ int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, return -1; } -int postcopy_ram_enable_notify(MigrationIncomingState *mis) +int postcopy_ram_incoming_setup(MigrationIncomingState *mis) { assert(0); return -1; @@ -1341,12 +1327,6 @@ int postcopy_place_page_zero(MigrationIncomingState *mis, void *host, return -1; } -void *postcopy_get_tmp_page(MigrationIncomingState *mis) -{ - assert(0); - return NULL; -} - int postcopy_wake_shared(struct PostCopyFD *pcfd, uint64_t client_addr, RAMBlock *rb) diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index 9c8bd2b..9941feb 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -20,7 +20,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis); * Make all of RAM sensitive to accesses to areas that haven't yet been written * and wire up anything necessary to deal with it. */ -int postcopy_ram_enable_notify(MigrationIncomingState *mis); +int postcopy_ram_incoming_setup(MigrationIncomingState *mis); /* * Initialise postcopy-ram, setting the RAM to a state where we can go into @@ -100,13 +100,6 @@ typedef enum { POSTCOPY_INCOMING_END } PostcopyState; -/* - * Allocate a page of memory that can be mapped at a later point in time - * using postcopy_place_page - * Returns: Pointer to allocated page - */ -void *postcopy_get_tmp_page(MigrationIncomingState *mis); - PostcopyState postcopy_state_get(void); /* Set the state and return the old state */ PostcopyState postcopy_state_set(PostcopyState new_state); diff --git a/migration/ram.c b/migration/ram.c index 22423f0..5078f94 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -181,14 +181,14 @@ int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque) RAMBlock *block; int ret = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); + RAMBLOCK_FOREACH_NOT_IGNORED(block) { ret = func(block, opaque); if (ret) { break; } } - rcu_read_unlock(); return ret; } @@ -791,13 +791,10 @@ static void multifd_pages_clear(MultiFDPages_t *pages) static void multifd_send_fill_packet(MultiFDSendParams *p) { MultiFDPacket_t *packet = p->packet; - uint32_t page_max = MULTIFD_PACKET_SIZE / qemu_target_page_size(); int i; - packet->magic = cpu_to_be32(MULTIFD_MAGIC); - packet->version = cpu_to_be32(MULTIFD_VERSION); packet->flags = cpu_to_be32(p->flags); - packet->pages_alloc = cpu_to_be32(page_max); + packet->pages_alloc = cpu_to_be32(p->pages->allocated); packet->pages_used = cpu_to_be32(p->pages->used); packet->next_packet_size = cpu_to_be32(p->next_packet_size); packet->packet_num = cpu_to_be64(p->packet_num); @@ -838,7 +835,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) packet->pages_alloc = be32_to_cpu(packet->pages_alloc); /* - * If we recevied a packet that is 100 times bigger than expected + * If we received a packet that is 100 times bigger than expected * just stop migration. It is a magic number. */ if (packet->pages_alloc > pages_max * 100) { @@ -1132,7 +1129,6 @@ static void *multifd_send_thread(void *opaque) p->flags = 0; p->num_packets++; p->num_pages += used; - p->pages->used = 0; qemu_mutex_unlock(&p->mutex); trace_multifd_send(p->id, packet_num, used, flags, @@ -1241,6 +1237,8 @@ int multifd_save_setup(void) p->packet_len = sizeof(MultiFDPacket_t) + sizeof(ram_addr_t) * page_count; p->packet = g_malloc0(p->packet_len); + p->packet->magic = cpu_to_be32(MULTIFD_MAGIC); + p->packet->version = cpu_to_be32(MULTIFD_VERSION); p->name = g_strdup_printf("multifdsend_%d", i); socket_send_channel_create(multifd_new_send_channel_async, p); } @@ -1848,12 +1846,12 @@ static void migration_bitmap_sync(RAMState *rs) memory_global_dirty_log_sync(); qemu_mutex_lock(&rs->bitmap_mutex); - rcu_read_lock(); - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - ramblock_sync_dirty_bitmap(rs, block); + WITH_RCU_READ_LOCK_GUARD() { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + ramblock_sync_dirty_bitmap(rs, block); + } + ram_counters.remaining = ram_bytes_remaining(); } - ram_counters.remaining = ram_bytes_remaining(); - rcu_read_unlock(); qemu_mutex_unlock(&rs->bitmap_mutex); memory_global_after_dirty_log_sync(); @@ -2397,13 +2395,12 @@ static void migration_page_queue_free(RAMState *rs) /* This queue generally should be empty - but in the case of a failed * migration might have some droppings in. */ - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); QSIMPLEQ_FOREACH_SAFE(mspr, &rs->src_page_requests, next_req, next_mspr) { memory_region_unref(mspr->rb->mr); QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req); g_free(mspr); } - rcu_read_unlock(); } /** @@ -2424,7 +2421,8 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) RAMState *rs = ram_state; ram_counters.postcopy_requests++; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); + if (!rbname) { /* Reuse last RAMBlock */ ramblock = rs->last_req_rb; @@ -2466,12 +2464,10 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) QSIMPLEQ_INSERT_TAIL(&rs->src_page_requests, new_entry, next_req); migration_make_urgent_request(); qemu_mutex_unlock(&rs->src_page_req_mutex); - rcu_read_unlock(); return 0; err: - rcu_read_unlock(); return -1; } @@ -2700,7 +2696,8 @@ static uint64_t ram_bytes_total_common(bool count_ignored) RAMBlock *block; uint64_t total = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); + if (count_ignored) { RAMBLOCK_FOREACH_MIGRATABLE(block) { total += block->used_length; @@ -2710,7 +2707,6 @@ static uint64_t ram_bytes_total_common(bool count_ignored) total += block->used_length; } } - rcu_read_unlock(); return total; } @@ -3034,7 +3030,7 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) RAMBlock *block; int ret; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); /* This should be our last sync, the src is now paused */ migration_bitmap_sync(rs); @@ -3048,7 +3044,6 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) /* Deal with TPS != HPS and huge pages */ ret = postcopy_chunk_hostpages(ms, block); if (ret) { - rcu_read_unlock(); return ret; } @@ -3060,7 +3055,6 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) trace_ram_postcopy_send_discard_bitmap(); ret = postcopy_each_ram_send_discard(ms); - rcu_read_unlock(); return ret; } @@ -3081,7 +3075,7 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length) trace_ram_discard_range(rbname, start, length); - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); RAMBlock *rb = qemu_ram_block_by_name(rbname); if (!rb) { @@ -3101,8 +3095,6 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length) ret = ram_block_discard_range(rb, start, length); err: - rcu_read_unlock(); - return ret; } @@ -3231,13 +3223,12 @@ static void ram_init_bitmaps(RAMState *rs) /* For memory_global_dirty_log_start below. */ qemu_mutex_lock_iothread(); qemu_mutex_lock_ramlist(); - rcu_read_lock(); - - ram_list_init_bitmaps(); - memory_global_dirty_log_start(); - migration_bitmap_sync_precopy(rs); - rcu_read_unlock(); + WITH_RCU_READ_LOCK_GUARD() { + ram_list_init_bitmaps(); + memory_global_dirty_log_start(); + migration_bitmap_sync_precopy(rs); + } qemu_mutex_unlock_ramlist(); qemu_mutex_unlock_iothread(); } @@ -3373,24 +3364,23 @@ static int ram_save_setup(QEMUFile *f, void *opaque) } (*rsp)->f = f; - rcu_read_lock(); - - qemu_put_be64(f, ram_bytes_total_common(true) | RAM_SAVE_FLAG_MEM_SIZE); + WITH_RCU_READ_LOCK_GUARD() { + qemu_put_be64(f, ram_bytes_total_common(true) | RAM_SAVE_FLAG_MEM_SIZE); - RAMBLOCK_FOREACH_MIGRATABLE(block) { - qemu_put_byte(f, strlen(block->idstr)); - qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); - qemu_put_be64(f, block->used_length); - if (migrate_postcopy_ram() && block->page_size != qemu_host_page_size) { - qemu_put_be64(f, block->page_size); - } - if (migrate_ignore_shared()) { - qemu_put_be64(f, block->mr->addr); + RAMBLOCK_FOREACH_MIGRATABLE(block) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); + qemu_put_be64(f, block->used_length); + if (migrate_postcopy_ram() && block->page_size != + qemu_host_page_size) { + qemu_put_be64(f, block->page_size); + } + if (migrate_ignore_shared()) { + qemu_put_be64(f, block->mr->addr); + } } } - rcu_read_unlock(); - ram_control_before_iterate(f, RAM_CONTROL_SETUP); ram_control_after_iterate(f, RAM_CONTROL_SETUP); @@ -3425,55 +3415,57 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) goto out; } - rcu_read_lock(); - if (ram_list.version != rs->last_version) { - ram_state_reset(rs); - } - - /* Read version before ram_list.blocks */ - smp_rmb(); + WITH_RCU_READ_LOCK_GUARD() { + if (ram_list.version != rs->last_version) { + ram_state_reset(rs); + } - ram_control_before_iterate(f, RAM_CONTROL_ROUND); + /* Read version before ram_list.blocks */ + smp_rmb(); - t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - i = 0; - while ((ret = qemu_file_rate_limit(f)) == 0 || - !QSIMPLEQ_EMPTY(&rs->src_page_requests)) { - int pages; + ram_control_before_iterate(f, RAM_CONTROL_ROUND); - if (qemu_file_get_error(f)) { - break; - } + t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + i = 0; + while ((ret = qemu_file_rate_limit(f)) == 0 || + !QSIMPLEQ_EMPTY(&rs->src_page_requests)) { + int pages; - pages = ram_find_and_save_block(rs, false); - /* no more pages to sent */ - if (pages == 0) { - done = 1; - break; - } + if (qemu_file_get_error(f)) { + break; + } - if (pages < 0) { - qemu_file_set_error(f, pages); - break; - } + pages = ram_find_and_save_block(rs, false); + /* no more pages to sent */ + if (pages == 0) { + done = 1; + break; + } - rs->target_page_count += pages; - - /* we want to check in the 1st loop, just in case it was the 1st time - and we had to sync the dirty bitmap. - qemu_clock_get_ns() is a bit expensive, so we only check each some - iterations - */ - if ((i & 63) == 0) { - uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / 1000000; - if (t1 > MAX_WAIT) { - trace_ram_save_iterate_big_wait(t1, i); + if (pages < 0) { + qemu_file_set_error(f, pages); break; } + + rs->target_page_count += pages; + + /* + * we want to check in the 1st loop, just in case it was the 1st + * time and we had to sync the dirty bitmap. + * qemu_clock_get_ns() is a bit expensive, so we only check each + * some iterations + */ + if ((i & 63) == 0) { + uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / + 1000000; + if (t1 > MAX_WAIT) { + trace_ram_save_iterate_big_wait(t1, i); + break; + } + } + i++; } - i++; } - rcu_read_unlock(); /* * Must occur before EOS (or any QEMUFile operation) @@ -3511,35 +3503,33 @@ static int ram_save_complete(QEMUFile *f, void *opaque) RAMState *rs = *temp; int ret = 0; - rcu_read_lock(); - - if (!migration_in_postcopy()) { - migration_bitmap_sync_precopy(rs); - } + WITH_RCU_READ_LOCK_GUARD() { + if (!migration_in_postcopy()) { + migration_bitmap_sync_precopy(rs); + } - ram_control_before_iterate(f, RAM_CONTROL_FINISH); + ram_control_before_iterate(f, RAM_CONTROL_FINISH); - /* try transferring iterative blocks of memory */ + /* try transferring iterative blocks of memory */ - /* flush all remaining blocks regardless of rate limiting */ - while (true) { - int pages; + /* flush all remaining blocks regardless of rate limiting */ + while (true) { + int pages; - pages = ram_find_and_save_block(rs, !migration_in_colo_state()); - /* no more blocks to sent */ - if (pages == 0) { - break; - } - if (pages < 0) { - ret = pages; - break; + pages = ram_find_and_save_block(rs, !migration_in_colo_state()); + /* no more blocks to sent */ + if (pages == 0) { + break; + } + if (pages < 0) { + ret = pages; + break; + } } - } - flush_compressed_data(rs); - ram_control_after_iterate(f, RAM_CONTROL_FINISH); - - rcu_read_unlock(); + flush_compressed_data(rs); + ram_control_after_iterate(f, RAM_CONTROL_FINISH); + } multifd_send_sync_main(rs); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); @@ -3562,9 +3552,9 @@ static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, if (!migration_in_postcopy() && remaining_size < max_size) { qemu_mutex_lock_iothread(); - rcu_read_lock(); - migration_bitmap_sync_precopy(rs); - rcu_read_unlock(); + WITH_RCU_READ_LOCK_GUARD() { + migration_bitmap_sync_precopy(rs); + } qemu_mutex_unlock_iothread(); remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; } @@ -3908,7 +3898,13 @@ int colo_init_ram_cache(void) error_report("%s: Can't alloc memory for COLO cache of block %s," "size 0x" RAM_ADDR_FMT, __func__, block->idstr, block->used_length); - goto out_locked; + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + if (block->colo_cache) { + qemu_anon_ram_free(block->colo_cache, block->used_length); + block->colo_cache = NULL; + } + } + return -errno; } memcpy(block->colo_cache, block->host, block->used_length); } @@ -3934,18 +3930,6 @@ int colo_init_ram_cache(void) memory_global_dirty_log_start(); return 0; - -out_locked: - - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - if (block->colo_cache) { - qemu_anon_ram_free(block->colo_cache, block->used_length); - block->colo_cache = NULL; - } - } - - rcu_read_unlock(); - return -errno; } /* It is need to hold the global lock to call this helper */ @@ -3959,16 +3943,14 @@ void colo_release_ram_cache(void) block->bmap = NULL; } - rcu_read_lock(); - - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - if (block->colo_cache) { - qemu_anon_ram_free(block->colo_cache, block->used_length); - block->colo_cache = NULL; + WITH_RCU_READ_LOCK_GUARD() { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + if (block->colo_cache) { + qemu_anon_ram_free(block->colo_cache, block->used_length); + block->colo_cache = NULL; + } } } - - rcu_read_unlock(); qemu_mutex_destroy(&ram_state->bitmap_mutex); g_free(ram_state); ram_state = NULL; @@ -4048,7 +4030,7 @@ static int ram_load_postcopy(QEMUFile *f) bool matches_target_page_size = false; MigrationIncomingState *mis = migration_incoming_get_current(); /* Temporary page that is later 'placed' */ - void *postcopy_host_page = postcopy_get_tmp_page(mis); + void *postcopy_host_page = mis->postcopy_tmp_page; void *last_host = NULL; bool all_zero = false; @@ -4206,31 +4188,30 @@ static void colo_flush_ram_cache(void) unsigned long offset = 0; memory_global_dirty_log_sync(); - rcu_read_lock(); - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - ramblock_sync_dirty_bitmap(ram_state, block); + WITH_RCU_READ_LOCK_GUARD() { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + ramblock_sync_dirty_bitmap(ram_state, block); + } } - rcu_read_unlock(); trace_colo_flush_ram_cache_begin(ram_state->migration_dirty_pages); - rcu_read_lock(); - block = QLIST_FIRST_RCU(&ram_list.blocks); + WITH_RCU_READ_LOCK_GUARD() { + block = QLIST_FIRST_RCU(&ram_list.blocks); - while (block) { - offset = migration_bitmap_find_dirty(ram_state, block, offset); + while (block) { + offset = migration_bitmap_find_dirty(ram_state, block, offset); - if (offset << TARGET_PAGE_BITS >= block->used_length) { - offset = 0; - block = QLIST_NEXT_RCU(block, next); - } else { - migration_bitmap_clear_dirty(ram_state, block, offset); - dst_host = block->host + (offset << TARGET_PAGE_BITS); - src_host = block->colo_cache + (offset << TARGET_PAGE_BITS); - memcpy(dst_host, src_host, TARGET_PAGE_SIZE); + if (offset << TARGET_PAGE_BITS >= block->used_length) { + offset = 0; + block = QLIST_NEXT_RCU(block, next); + } else { + migration_bitmap_clear_dirty(ram_state, block, offset); + dst_host = block->host + (offset << TARGET_PAGE_BITS); + src_host = block->colo_cache + (offset << TARGET_PAGE_BITS); + memcpy(dst_host, src_host, TARGET_PAGE_SIZE); + } } } - - rcu_read_unlock(); trace_colo_flush_ram_cache_end(); } @@ -4429,16 +4410,15 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) * it will be necessary to reduce the granularity of this * critical section. */ - rcu_read_lock(); + WITH_RCU_READ_LOCK_GUARD() { + if (postcopy_running) { + ret = ram_load_postcopy(f); + } else { + ret = ram_load_precopy(f); + } - if (postcopy_running) { - ret = ram_load_postcopy(f); - } else { - ret = ram_load_precopy(f); + ret |= wait_for_decompress_done(); } - - ret |= wait_for_decompress_done(); - rcu_read_unlock(); trace_ram_load_complete(ret, seq_iter); if (!ret && migration_incoming_in_colo_state()) { diff --git a/migration/rdma.c b/migration/rdma.c index 4c74e88..e241dcb 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -88,7 +88,6 @@ static uint32_t known_capabilities = RDMA_CAPABILITY_PIN_ALL; " to abort!"); \ rdma->error_reported = 1; \ } \ - rcu_read_unlock(); \ return rdma->error_state; \ } \ } while (0) @@ -2678,11 +2677,10 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, size_t i; size_t len = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); rdma = atomic_rcu_read(&rioc->rdmaout); if (!rdma) { - rcu_read_unlock(); return -EIO; } @@ -2695,7 +2693,6 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, ret = qemu_rdma_write_flush(f, rdma); if (ret < 0) { rdma->error_state = ret; - rcu_read_unlock(); return ret; } @@ -2715,7 +2712,6 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, if (ret < 0) { rdma->error_state = ret; - rcu_read_unlock(); return ret; } @@ -2724,7 +2720,6 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, } } - rcu_read_unlock(); return done; } @@ -2764,11 +2759,10 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, ssize_t i; size_t done = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); rdma = atomic_rcu_read(&rioc->rdmain); if (!rdma) { - rcu_read_unlock(); return -EIO; } @@ -2805,7 +2799,6 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, if (ret < 0) { rdma->error_state = ret; - rcu_read_unlock(); return ret; } @@ -2819,14 +2812,12 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, /* Still didn't get enough, so lets just return */ if (want) { if (done == 0) { - rcu_read_unlock(); return QIO_CHANNEL_ERR_BLOCK; } else { break; } } } - rcu_read_unlock(); return done; } @@ -2882,7 +2873,7 @@ qio_channel_rdma_source_prepare(GSource *source, GIOCondition cond = 0; *timeout = -1; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); if (rsource->condition == G_IO_IN) { rdma = atomic_rcu_read(&rsource->rioc->rdmain); } else { @@ -2891,7 +2882,6 @@ qio_channel_rdma_source_prepare(GSource *source, if (!rdma) { error_report("RDMAContext is NULL when prepare Gsource"); - rcu_read_unlock(); return FALSE; } @@ -2900,7 +2890,6 @@ qio_channel_rdma_source_prepare(GSource *source, } cond |= G_IO_OUT; - rcu_read_unlock(); return cond & rsource->condition; } @@ -2911,7 +2900,7 @@ qio_channel_rdma_source_check(GSource *source) RDMAContext *rdma; GIOCondition cond = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); if (rsource->condition == G_IO_IN) { rdma = atomic_rcu_read(&rsource->rioc->rdmain); } else { @@ -2920,7 +2909,6 @@ qio_channel_rdma_source_check(GSource *source) if (!rdma) { error_report("RDMAContext is NULL when check Gsource"); - rcu_read_unlock(); return FALSE; } @@ -2929,7 +2917,6 @@ qio_channel_rdma_source_check(GSource *source) } cond |= G_IO_OUT; - rcu_read_unlock(); return cond & rsource->condition; } @@ -2943,7 +2930,7 @@ qio_channel_rdma_source_dispatch(GSource *source, RDMAContext *rdma; GIOCondition cond = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); if (rsource->condition == G_IO_IN) { rdma = atomic_rcu_read(&rsource->rioc->rdmain); } else { @@ -2952,7 +2939,6 @@ qio_channel_rdma_source_dispatch(GSource *source, if (!rdma) { error_report("RDMAContext is NULL when dispatch Gsource"); - rcu_read_unlock(); return FALSE; } @@ -2961,7 +2947,6 @@ qio_channel_rdma_source_dispatch(GSource *source, } cond |= G_IO_OUT; - rcu_read_unlock(); return (*func)(QIO_CHANNEL(rsource->rioc), (cond & rsource->condition), user_data); @@ -3073,7 +3058,7 @@ qio_channel_rdma_shutdown(QIOChannel *ioc, QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); RDMAContext *rdmain, *rdmaout; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); rdmain = atomic_rcu_read(&rioc->rdmain); rdmaout = atomic_rcu_read(&rioc->rdmain); @@ -3100,7 +3085,6 @@ qio_channel_rdma_shutdown(QIOChannel *ioc, break; } - rcu_read_unlock(); return 0; } @@ -3146,18 +3130,16 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, RDMAContext *rdma; int ret; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); rdma = atomic_rcu_read(&rioc->rdmaout); if (!rdma) { - rcu_read_unlock(); return -EIO; } CHECK_ERROR_STATE(); if (migration_in_postcopy()) { - rcu_read_unlock(); return RAM_SAVE_CONTROL_NOT_SUPP; } @@ -3242,11 +3224,9 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, } } - rcu_read_unlock(); return RAM_SAVE_CONTROL_DELAYED; err: rdma->error_state = ret; - rcu_read_unlock(); return ret; } @@ -3470,11 +3450,10 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) int count = 0; int i = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); rdma = atomic_rcu_read(&rioc->rdmain); if (!rdma) { - rcu_read_unlock(); return -EIO; } @@ -3717,7 +3696,6 @@ out: if (ret < 0) { rdma->error_state = ret; } - rcu_read_unlock(); return ret; } @@ -3735,11 +3713,10 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) int curr; int found = -1; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); rdma = atomic_rcu_read(&rioc->rdmain); if (!rdma) { - rcu_read_unlock(); return -EIO; } @@ -3753,7 +3730,6 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) if (found == -1) { error_report("RAMBlock '%s' not found on destination", name); - rcu_read_unlock(); return -ENOENT; } @@ -3761,7 +3737,6 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) trace_rdma_block_notification_handle(name, rdma->next_src_index); rdma->next_src_index++; - rcu_read_unlock(); return 0; } @@ -3786,17 +3761,15 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque, QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); RDMAContext *rdma; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); rdma = atomic_rcu_read(&rioc->rdmaout); if (!rdma) { - rcu_read_unlock(); return -EIO; } CHECK_ERROR_STATE(); if (migration_in_postcopy()) { - rcu_read_unlock(); return 0; } @@ -3804,7 +3777,6 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque, qemu_put_be64(f, RAM_SAVE_FLAG_HOOK); qemu_fflush(f); - rcu_read_unlock(); return 0; } @@ -3821,17 +3793,15 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, RDMAControlHeader head = { .len = 0, .repeat = 1 }; int ret = 0; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); rdma = atomic_rcu_read(&rioc->rdmaout); if (!rdma) { - rcu_read_unlock(); return -EIO; } CHECK_ERROR_STATE(); if (migration_in_postcopy()) { - rcu_read_unlock(); return 0; } @@ -3863,7 +3833,6 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, qemu_rdma_reg_whole_ram_blocks : NULL); if (ret < 0) { ERROR(errp, "receiving remote info!"); - rcu_read_unlock(); return ret; } @@ -3887,7 +3856,6 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, "not identical on both the source and destination.", local->nb_blocks, nb_dest_blocks); rdma->error_state = -EINVAL; - rcu_read_unlock(); return -EINVAL; } @@ -3904,7 +3872,6 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, local->block[i].length, rdma->dest_blocks[i].length); rdma->error_state = -EINVAL; - rcu_read_unlock(); return -EINVAL; } local->block[i].remote_host_addr = @@ -3922,11 +3889,9 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, goto err; } - rcu_read_unlock(); return 0; err: rdma->error_state = ret; - rcu_read_unlock(); return ret; } diff --git a/migration/savevm.c b/migration/savevm.c index bb9462a..8d95e26 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1215,6 +1215,8 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) save_section_footer(f, se); if (ret < 0) { + error_report("failed to save SaveStateEntry with id(name): %d(%s)", + se->section_id, se->idstr); qemu_file_set_error(f, ret); } if (ret <= 0) { @@ -1835,6 +1837,8 @@ static void *postcopy_ram_listen_thread(void *opaque) rcu_unregister_thread(); mis->have_listen_thread = false; + postcopy_state_set(POSTCOPY_INCOMING_END); + return NULL; } @@ -1865,7 +1869,7 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) * shouldn't be doing anything yet so don't actually expect requests */ if (migrate_postcopy_ram()) { - if (postcopy_ram_enable_notify(mis)) { + if (postcopy_ram_incoming_setup(mis)) { postcopy_ram_incoming_cleanup(mis); return -1; } @@ -1876,11 +1880,6 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) return -1; } - if (mis->have_listen_thread) { - error_report("CMD_POSTCOPY_RAM_LISTEN already has a listen thread"); - return -1; - } - mis->have_listen_thread = true; /* Start up the listening thread and wait for it to signal ready */ qemu_sem_init(&mis->listen_thread_sem, 0); @@ -1934,7 +1933,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) /* After all discards we can start running and asking for pages */ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) { - PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_RUNNING); + PostcopyState ps = postcopy_state_get(); trace_loadvm_postcopy_handle_run(); if (ps != POSTCOPY_INCOMING_LISTENING) { @@ -1942,6 +1941,7 @@ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) return -1; } + postcopy_state_set(POSTCOPY_INCOMING_RUNNING); mis->bh = qemu_bh_new(loadvm_postcopy_handle_run_bh, mis); qemu_bh_schedule(mis->bh); diff --git a/migration/trace-events b/migration/trace-events index 858d415..6dee7b5 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -71,6 +71,11 @@ get_qtailq_end(const char *name, const char *reason, int val) "%s %s/%d" put_qtailq(const char *name, int version_id) "%s v%d" put_qtailq_end(const char *name, const char *reason) "%s %s" +get_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d" +get_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d" +put_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d" +put_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d" + # qemu-file.c qemu_file_fclose(void) "" diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index bee658a..7236cf9 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -691,3 +691,155 @@ const VMStateInfo vmstate_info_qtailq = { .get = get_qtailq, .put = put_qtailq, }; + +struct put_gtree_data { + QEMUFile *f; + const VMStateDescription *key_vmsd; + const VMStateDescription *val_vmsd; + QJSON *vmdesc; + int ret; +}; + +static gboolean put_gtree_elem(gpointer key, gpointer value, gpointer data) +{ + struct put_gtree_data *capsule = (struct put_gtree_data *)data; + QEMUFile *f = capsule->f; + int ret; + + qemu_put_byte(f, true); + + /* put the key */ + if (!capsule->key_vmsd) { + qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */ + } else { + ret = vmstate_save_state(f, capsule->key_vmsd, key, capsule->vmdesc); + if (ret) { + capsule->ret = ret; + return true; + } + } + + /* put the data */ + ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc); + if (ret) { + capsule->ret = ret; + return true; + } + return false; +} + +static int put_gtree(QEMUFile *f, void *pv, size_t unused_size, + const VMStateField *field, QJSON *vmdesc) +{ + bool direct_key = (!field->start); + const VMStateDescription *key_vmsd = direct_key ? NULL : &field->vmsd[1]; + const VMStateDescription *val_vmsd = &field->vmsd[0]; + const char *key_vmsd_name = direct_key ? "direct" : key_vmsd->name; + struct put_gtree_data capsule = { + .f = f, + .key_vmsd = key_vmsd, + .val_vmsd = val_vmsd, + .vmdesc = vmdesc, + .ret = 0}; + GTree **pval = pv; + GTree *tree = *pval; + uint32_t nnodes = g_tree_nnodes(tree); + int ret; + + trace_put_gtree(field->name, key_vmsd_name, val_vmsd->name, nnodes); + qemu_put_be32(f, nnodes); + g_tree_foreach(tree, put_gtree_elem, (gpointer)&capsule); + qemu_put_byte(f, false); + ret = capsule.ret; + if (ret) { + error_report("%s : failed to save gtree (%d)", field->name, ret); + } + trace_put_gtree_end(field->name, key_vmsd_name, val_vmsd->name, ret); + return ret; +} + +static int get_gtree(QEMUFile *f, void *pv, size_t unused_size, + const VMStateField *field) +{ + bool direct_key = (!field->start); + const VMStateDescription *key_vmsd = direct_key ? NULL : &field->vmsd[1]; + const VMStateDescription *val_vmsd = &field->vmsd[0]; + const char *key_vmsd_name = direct_key ? "direct" : key_vmsd->name; + int version_id = field->version_id; + size_t key_size = field->start; + size_t val_size = field->size; + int nnodes, count = 0; + GTree **pval = pv; + GTree *tree = *pval; + void *key, *val; + int ret = 0; + + /* in case of direct key, the key vmsd can be {}, ie. check fields */ + if (!direct_key && version_id > key_vmsd->version_id) { + error_report("%s %s", key_vmsd->name, "too new"); + return -EINVAL; + } + if (!direct_key && version_id < key_vmsd->minimum_version_id) { + error_report("%s %s", key_vmsd->name, "too old"); + return -EINVAL; + } + if (version_id > val_vmsd->version_id) { + error_report("%s %s", val_vmsd->name, "too new"); + return -EINVAL; + } + if (version_id < val_vmsd->minimum_version_id) { + error_report("%s %s", val_vmsd->name, "too old"); + return -EINVAL; + } + + nnodes = qemu_get_be32(f); + trace_get_gtree(field->name, key_vmsd_name, val_vmsd->name, nnodes); + + while (qemu_get_byte(f)) { + if ((++count) > nnodes) { + ret = -EINVAL; + break; + } + if (direct_key) { + key = (void *)(uintptr_t)qemu_get_be64(f); + } else { + key = g_malloc0(key_size); + ret = vmstate_load_state(f, key_vmsd, key, version_id); + if (ret) { + error_report("%s : failed to load %s (%d)", + field->name, key_vmsd->name, ret); + goto key_error; + } + } + val = g_malloc0(val_size); + ret = vmstate_load_state(f, val_vmsd, val, version_id); + if (ret) { + error_report("%s : failed to load %s (%d)", + field->name, val_vmsd->name, ret); + goto val_error; + } + g_tree_insert(tree, key, val); + } + if (count != nnodes) { + error_report("%s inconsistent stream when loading the gtree", + field->name); + return -EINVAL; + } + trace_get_gtree_end(field->name, key_vmsd_name, val_vmsd->name, ret); + return ret; +val_error: + g_free(val); +key_error: + if (!direct_key) { + g_free(key); + } + trace_get_gtree_end(field->name, key_vmsd_name, val_vmsd->name, ret); + return ret; +} + + +const VMStateInfo vmstate_info_gtree = { + .name = "gtree", + .get = get_gtree, + .put = put_gtree, +}; diff --git a/net/vhost-user.c b/net/vhost-user.c index 51921de..014199d 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -235,6 +235,10 @@ static void chr_closed_bh(void *opaque) s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); + if (s->vhost_net) { + s->acked_features = vhost_net_get_acked_features(s->vhost_net); + } + qmp_set_link(name, false, &err); qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc Binary files differindex aaeb2c6..28f6d4b 100644 --- a/pc-bios/openbios-ppc +++ b/pc-bios/openbios-ppc diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 Binary files differindex b471b87..964893f 100644 --- a/pc-bios/openbios-sparc32 +++ b/pc-bios/openbios-sparc32 diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 Binary files differindex 8c77a06..0e19138 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 diff --git a/qapi/block-core.json b/qapi/block-core.json index e6edd64..f66553a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -860,6 +860,8 @@ # # @wr_bytes: The number of bytes written by the device. # +# @unmap_bytes: The number of bytes unmapped by the device (Since 4.2) +# # @rd_operations: The number of read operations performed by the device. # # @wr_operations: The number of write operations performed by the device. @@ -867,12 +869,18 @@ # @flush_operations: The number of cache flush operations performed by the # device (since 0.15.0) # -# @flush_total_time_ns: Total time spend on cache flushes in nano-seconds -# (since 0.15.0). +# @unmap_operations: The number of unmap operations performed by the device +# (Since 4.2) +# +# @rd_total_time_ns: Total time spent on reads in nanoseconds (since 0.15.0). +# +# @wr_total_time_ns: Total time spent on writes in nanoseconds (since 0.15.0). # -# @wr_total_time_ns: Total time spend on writes in nano-seconds (since 0.15.0). +# @flush_total_time_ns: Total time spent on cache flushes in nanoseconds +# (since 0.15.0). # -# @rd_total_time_ns: Total_time_spend on reads in nano-seconds (since 0.15.0). +# @unmap_total_time_ns: Total time spent on unmap operations in nanoseconds +# (Since 4.2) # # @wr_highest_offset: The offset after the greatest byte written to the # device. The intended use of this information is for @@ -885,6 +893,9 @@ # @wr_merged: Number of write requests that have been merged into another # request (Since 2.3). # +# @unmap_merged: Number of unmap requests that have been merged into another +# request (Since 4.2) +# # @idle_time_ns: Time since the last I/O operation, in # nanoseconds. If the field is absent it means that # there haven't been any operations yet (Since 2.5). @@ -898,6 +909,9 @@ # @failed_flush_operations: The number of failed flush operations # performed by the device (Since 2.5) # +# @failed_unmap_operations: The number of failed unmap operations performed +# by the device (Since 4.2) +# # @invalid_rd_operations: The number of invalid read operations # performed by the device (Since 2.5) # @@ -907,6 +921,9 @@ # @invalid_flush_operations: The number of invalid flush operations # performed by the device (Since 2.5) # +# @invalid_unmap_operations: The number of invalid unmap operations performed +# by the device (Since 4.2) +# # @account_invalid: Whether invalid operations are included in the # last access statistics (Since 2.5) # @@ -925,14 +942,18 @@ # Since: 0.14.0 ## { 'struct': 'BlockDeviceStats', - 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'rd_operations': 'int', - 'wr_operations': 'int', 'flush_operations': 'int', - 'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int', - 'rd_total_time_ns': 'int', 'wr_highest_offset': 'int', - 'rd_merged': 'int', 'wr_merged': 'int', '*idle_time_ns': 'int', + 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'unmap_bytes' : 'int', + 'rd_operations': 'int', 'wr_operations': 'int', + 'flush_operations': 'int', 'unmap_operations': 'int', + 'rd_total_time_ns': 'int', 'wr_total_time_ns': 'int', + 'flush_total_time_ns': 'int', 'unmap_total_time_ns': 'int', + 'wr_highest_offset': 'int', + 'rd_merged': 'int', 'wr_merged': 'int', 'unmap_merged': 'int', + '*idle_time_ns': 'int', 'failed_rd_operations': 'int', 'failed_wr_operations': 'int', - 'failed_flush_operations': 'int', 'invalid_rd_operations': 'int', - 'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int', + 'failed_flush_operations': 'int', 'failed_unmap_operations': 'int', + 'invalid_rd_operations': 'int', 'invalid_wr_operations': 'int', + 'invalid_flush_operations': 'int', 'invalid_unmap_operations': 'int', 'account_invalid': 'bool', 'account_failed': 'bool', 'timed_stats': ['BlockDeviceTimedStats'], '*rd_latency_histogram': 'BlockLatencyHistogramInfo', @@ -940,6 +961,41 @@ '*flush_latency_histogram': 'BlockLatencyHistogramInfo' } } ## +# @BlockStatsSpecificFile: +# +# File driver statistics +# +# @discard-nb-ok: The number of successful discard operations performed by +# the driver. +# +# @discard-nb-failed: The number of failed discard operations performed by +# the driver. +# +# @discard-bytes-ok: The number of bytes discarded by the driver. +# +# Since: 4.2 +## +{ 'struct': 'BlockStatsSpecificFile', + 'data': { + 'discard-nb-ok': 'uint64', + 'discard-nb-failed': 'uint64', + 'discard-bytes-ok': 'uint64' } } + +## +# @BlockStatsSpecific: +# +# Block driver specific statistics +# +# Since: 4.2 +## +{ 'union': 'BlockStatsSpecific', + 'base': { 'driver': 'BlockdevDriver' }, + 'discriminator': 'driver', + 'data': { + 'file': 'BlockStatsSpecificFile', + 'host_device': 'BlockStatsSpecificFile' } } + +## # @BlockStats: # # Statistics of a virtual block device or a block backing device. @@ -954,6 +1010,8 @@ # # @stats: A @BlockDeviceStats for the device. # +# @driver-specific: Optional driver-specific stats. (Since 4.2) +# # @parent: This describes the file block device if it has one. # Contains recursively the statistics of the underlying # protocol (e.g. the host file for a qcow2 image). If there is @@ -967,6 +1025,7 @@ { 'struct': 'BlockStats', 'data': {'*device': 'str', '*qdev': 'str', '*node-name': 'str', 'stats': 'BlockDeviceStats', + '*driver-specific': 'BlockStatsSpecific', '*parent': 'BlockStats', '*backing': 'BlockStats'} } @@ -1391,6 +1450,11 @@ # list without user intervention. # Defaults to true. (Since 2.12) # +# @filter-node-name: the node name that should be assigned to the +# filter driver that the backup job inserts into the graph +# above node specified by @drive. If this option is not given, +# a node name is autogenerated. (Since: 4.2) +# # Note: @on-source-error and @on-target-error only affect background # I/O. If an error occurs during a guest write request, the device's # rerror/werror actions will be used. @@ -1404,7 +1468,8 @@ '*compress': 'bool', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool', + '*filter-node-name': 'str' } } ## # @DriveBackup: @@ -214,6 +214,14 @@ static QemuOptsList qemu_object_opts = { }, }; +static bool qemu_img_object_print_help(const char *type, QemuOpts *opts) +{ + if (user_creatable_print_help(type, opts)) { + exit(0); + } + return true; +} + static QemuOptsList qemu_source_opts = { .name = "source", .implied_opt_name = "file", @@ -516,7 +524,7 @@ static int img_create(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { goto fail; } @@ -766,7 +774,7 @@ static int img_check(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { return 1; } @@ -979,7 +987,7 @@ static int img_commit(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { return 1; } @@ -1362,7 +1370,7 @@ static int img_compare(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { ret = 2; goto out4; } @@ -2210,7 +2218,7 @@ static int img_convert(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { goto fail_getopt; } @@ -2776,7 +2784,7 @@ static int img_info(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { return 1; } @@ -3002,7 +3010,7 @@ static int img_map(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { return 1; } @@ -3154,7 +3162,7 @@ static int img_snapshot(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { return 1; } @@ -3321,7 +3329,7 @@ static int img_rebase(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { return 1; } @@ -3742,7 +3750,7 @@ static int img_resize(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { return 1; } @@ -3986,7 +3994,7 @@ static int img_amend(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { ret = -1; goto out_no_progress; } @@ -4630,7 +4638,7 @@ static int img_dd(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { ret = -1; goto out; } @@ -4907,7 +4915,7 @@ static int img_measure(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal)) { + qemu_img_object_print_help, &error_fatal)) { goto out; } @@ -475,6 +475,13 @@ static QemuOptsList qemu_object_opts = { }, }; +static bool qemu_io_object_print_help(const char *type, QemuOpts *opts) +{ + if (user_creatable_print_help(type, opts)) { + exit(0); + } + return true; +} static QemuOptsList file_opts = { .name = "file", @@ -622,7 +629,7 @@ int main(int argc, char **argv) qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal); + qemu_io_object_print_help, &error_fatal); if (!trace_init_backends()) { exit(1); @@ -507,6 +507,13 @@ static QemuOptsList qemu_object_opts = { }, }; +static bool qemu_nbd_object_print_help(const char *type, QemuOpts *opts) +{ + if (user_creatable_print_help(type, opts)) { + exit(0); + } + return true; +} static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list, @@ -902,7 +909,7 @@ int main(int argc, char **argv) qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, &error_fatal); + qemu_nbd_object_print_help, &error_fatal); if (!trace_init_backends()) { exit(1); diff --git a/qemu-options.hx b/qemu-options.hx index 2a04ca6..793d70f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1339,7 +1339,7 @@ ETEXI DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, "-virtfs local,path=path,mount_tag=tag,security_model=mapped-xattr|mapped-file|passthrough|none\n" - " [,id=id][,writeout=immediate][,readonly][,fmode=fmode][,dmode=dmode]\n" + " [,id=id][,writeout=immediate][,readonly][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn]\n" "-virtfs proxy,mount_tag=tag,socket=socket[,id=id][,writeout=immediate][,readonly]\n" "-virtfs proxy,mount_tag=tag,sock_fd=sock_fd[,id=id][,writeout=immediate][,readonly]\n" "-virtfs synth,mount_tag=tag[,id=id][,readonly]\n", @@ -1347,7 +1347,7 @@ DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, STEXI -@item -virtfs local,path=@var{path},mount_tag=@var{mount_tag} ,security_model=@var{security_model}[,writeout=@var{writeout}][,readonly] [,fmode=@var{fmode}][,dmode=@var{dmode}] +@item -virtfs local,path=@var{path},mount_tag=@var{mount_tag} ,security_model=@var{security_model}[,writeout=@var{writeout}][,readonly] [,fmode=@var{fmode}][,dmode=@var{dmode}][,multidevs=@var{multidevs}] @itemx -virtfs proxy,socket=@var{socket},mount_tag=@var{mount_tag} [,writeout=@var{writeout}][,readonly] @itemx -virtfs proxy,sock_fd=@var{sock_fd},mount_tag=@var{mount_tag} [,writeout=@var{writeout}][,readonly] @itemx -virtfs synth,mount_tag=@var{mount_tag} @@ -1403,6 +1403,28 @@ Specifies the default mode for newly created directories on the host. Works only with security models "mapped-xattr" and "mapped-file". @item mount_tag=@var{mount_tag} Specifies the tag name to be used by the guest to mount this export point. +@item multidevs=@var{multidevs} +Specifies how to deal with multiple devices being shared with a 9p export. +Supported behaviours are either "remap", "forbid" or "warn". The latter is +the default behaviour on which virtfs 9p expects only one device to be +shared with the same export, and if more than one device is shared and +accessed via the same 9p export then only a warning message is logged +(once) by qemu on host side. In order to avoid file ID collisions on guest +you should either create a separate virtfs export for each device to be +shared with guests (recommended way) or you might use "remap" instead which +allows you to share multiple devices with only one export instead, which is +achieved by remapping the original inode numbers from host to guest in a +way that would prevent such collisions. Remapping inodes in such use cases +is required because the original device IDs from host are never passed and +exposed on guest. Instead all files of an export shared with virtfs always +share the same device id on guest. So two files with identical inode +numbers but from actually different devices on host would otherwise cause a +file ID collision and hence potential misbehaviours on guest. "forbid" on +the other hand assumes like "warn" that only one device is shared by the +same export, however it will not only log a warning message but also +deny access to additional devices on guest. Note though that "forbid" does +currently not block all possible file access operations (e.g. readdir() +would still return entries from other devices). @end table ETEXI diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index cb58099..46cd6ea 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -1,8 +1,11 @@ #include "qemu/osdep.h" + +#include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "qom/object_interfaces.h" +#include "qemu/help_option.h" #include "qemu/module.h" #include "qemu/option.h" #include "qapi/opts-visitor.h" @@ -155,6 +158,64 @@ int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp) return 0; } +bool user_creatable_print_help(const char *type, QemuOpts *opts) +{ + ObjectClass *klass; + + if (is_help_option(type)) { + GSList *l, *list; + + printf("List of user creatable objects:\n"); + list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false); + for (l = list; l != NULL; l = l->next) { + ObjectClass *oc = OBJECT_CLASS(l->data); + printf(" %s\n", object_class_get_name(oc)); + } + g_slist_free(list); + return true; + } + + klass = object_class_by_name(type); + if (klass && qemu_opt_has_help_opt(opts)) { + ObjectPropertyIterator iter; + ObjectProperty *prop; + GPtrArray *array = g_ptr_array_new(); + int i; + + object_class_property_iter_init(&iter, klass); + while ((prop = object_property_iter_next(&iter))) { + GString *str; + + if (!prop->set) { + continue; + } + + str = g_string_new(NULL); + g_string_append_printf(str, " %s=<%s>", prop->name, prop->type); + if (prop->description) { + if (str->len < 24) { + g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); + } + g_string_append_printf(str, " - %s", prop->description); + } + g_ptr_array_add(array, g_string_free(str, false)); + } + g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); + if (array->len > 0) { + printf("%s options:\n", type); + } else { + printf("There are no options for %s.\n", type); + } + for (i = 0; i < array->len; i++) { + printf("%s\n", (char *)array->pdata[i]); + } + g_ptr_array_set_free_func(array, g_free); + g_ptr_array_free(array, true); + return true; + } + + return false; +} void user_creatable_del(const char *id, Error **errp) { diff --git a/replay/replay-events.c b/replay/replay-events.c index 008e80f..302b840 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -36,6 +36,9 @@ static void replay_run_event(Event *event) case REPLAY_ASYNC_EVENT_BH: aio_bh_call(event->opaque); break; + case REPLAY_ASYNC_EVENT_BH_ONESHOT: + ((QEMUBHFunc *)event->opaque)(event->opaque2); + break; case REPLAY_ASYNC_EVENT_INPUT: qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque); qapi_free_InputEvent((InputEvent *)event->opaque); @@ -131,6 +134,17 @@ void replay_bh_schedule_event(QEMUBH *bh) } } +void replay_bh_schedule_oneshot_event(AioContext *ctx, + QEMUBHFunc *cb, void *opaque) +{ + if (events_enabled) { + uint64_t id = replay_get_current_icount(); + replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id); + } else { + aio_bh_schedule_oneshot(ctx, cb, opaque); + } +} + void replay_add_input_event(struct InputEvent *event) { replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0); @@ -161,6 +175,7 @@ static void replay_save_event(Event *event, int checkpoint) /* save event-specific data */ switch (event->event_kind) { case REPLAY_ASYNC_EVENT_BH: + case REPLAY_ASYNC_EVENT_BH_ONESHOT: replay_put_qword(event->id); break; case REPLAY_ASYNC_EVENT_INPUT: @@ -216,6 +231,7 @@ static Event *replay_read_event(int checkpoint) /* Events that has not to be in the queue */ switch (replay_state.read_event_kind) { case REPLAY_ASYNC_EVENT_BH: + case REPLAY_ASYNC_EVENT_BH_ONESHOT: if (replay_state.read_event_id == -1) { replay_state.read_event_id = replay_get_qword(); } diff --git a/replay/replay-internal.h b/replay/replay-internal.h index afba9a3..55fca1a 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -51,6 +51,7 @@ enum ReplayEvents { enum ReplayAsyncEventKind { REPLAY_ASYNC_EVENT_BH, + REPLAY_ASYNC_EVENT_BH_ONESHOT, REPLAY_ASYNC_EVENT_INPUT, REPLAY_ASYNC_EVENT_INPUT_SYNC, REPLAY_ASYNC_EVENT_CHAR_READ, diff --git a/replay/replay.c b/replay/replay.c index 713395b..5cc25bd2 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -385,6 +385,8 @@ void replay_finish(void) g_free(replay_snapshot); replay_snapshot = NULL; + replay_mode = REPLAY_MODE_NONE; + replay_finish_events(); } diff --git a/roms/openbios b/roms/openbios -Subproject c79e0ecb84f4f1ee3f73f521622e264edd1bf17 +Subproject f28e16f9aab36f723df525e8a2a1a798b18e19b diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 04279fa..44c118b 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -456,12 +456,12 @@ def generate(events, group, format, backends, import tracetool format = str(format) - if len(format) is 0: + if len(format) == 0: raise TracetoolError("format not set") if not tracetool.format.exists(format): raise TracetoolError("unknown format: %s" % format) - if len(backends) is 0: + if len(backends) == 0: raise TracetoolError("no backends specified") for backend in backends: if not tracetool.backend.exists(backend): diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 9c7393b..4a50e95 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -20,6 +20,7 @@ stub-obj-y += monitor.o stub-obj-y += notify-event.o stub-obj-y += qtest.o stub-obj-y += replay.o +stub-obj-y += replay-user.o stub-obj-y += runstate-check.o stub-obj-y += set-fd-handler.o stub-obj-y += sysbus.o diff --git a/stubs/replay-user.c b/stubs/replay-user.c new file mode 100644 index 0000000..2ad9e27 --- /dev/null +++ b/stubs/replay-user.c @@ -0,0 +1,9 @@ +#include "qemu/osdep.h" +#include "sysemu/replay.h" +#include "sysemu/sysemu.h" + +void replay_bh_schedule_oneshot_event(AioContext *ctx, + QEMUBHFunc *cb, void *opaque) +{ + aio_bh_schedule_oneshot(ctx, cb, opaque); +} diff --git a/target/arm/arm-semi.c b/target/arm/arm-semi.c index 90423a3..6f7b6d8 100644 --- a/target/arm/arm-semi.c +++ b/target/arm/arm-semi.c @@ -59,6 +59,7 @@ #define TARGET_SYS_HEAPINFO 0x16 #define TARGET_SYS_EXIT 0x18 #define TARGET_SYS_SYNCCACHE 0x19 +#define TARGET_SYS_EXIT_EXTENDED 0x20 /* ADP_Stopped_ApplicationExit is used for exit(0), * anything else is implemented as exit(1) */ @@ -106,43 +107,169 @@ static int open_modeflags[12] = { O_RDWR | O_CREAT | O_APPEND | O_BINARY }; -#ifdef CONFIG_USER_ONLY -static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code) +typedef enum GuestFDType { + GuestFDUnused = 0, + GuestFDHost = 1, + GuestFDGDB = 2, + GuestFDFeatureFile = 3, +} GuestFDType; + +/* + * Guest file descriptors are integer indexes into an array of + * these structures (we will dynamically resize as necessary). + */ +typedef struct GuestFD { + GuestFDType type; + union { + int hostfd; + target_ulong featurefile_offset; + }; +} GuestFD; + +static GArray *guestfd_array; + +/* + * Allocate a new guest file descriptor and return it; if we + * couldn't allocate a new fd then return -1. + * This is a fairly simplistic implementation because we don't + * expect that most semihosting guest programs will make very + * heavy use of opening and closing fds. + */ +static int alloc_guestfd(void) { - if (code == (uint32_t)-1) - ts->swi_errno = errno; - return code; + guint i; + + if (!guestfd_array) { + /* New entries zero-initialized, i.e. type GuestFDUnused */ + guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); + } + + for (i = 0; i < guestfd_array->len; i++) { + GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i); + + if (gf->type == GuestFDUnused) { + return i; + } + } + + /* All elements already in use: expand the array */ + g_array_set_size(guestfd_array, i + 1); + return i; } -#else -static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code) + +/* + * Look up the guestfd in the data structure; return NULL + * for out of bounds, but don't check whether the slot is unused. + * This is used internally by the other guestfd functions. + */ +static GuestFD *do_get_guestfd(int guestfd) { - return code; + if (!guestfd_array) { + return NULL; + } + + if (guestfd < 0 || guestfd >= guestfd_array->len) { + return NULL; + } + + return &g_array_index(guestfd_array, GuestFD, guestfd); +} + +/* + * Associate the specified guest fd (which must have been + * allocated via alloc_fd() and not previously used) with + * the specified host/gdb fd. + */ +static void associate_guestfd(int guestfd, int hostfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost; + gf->hostfd = hostfd; +} + +/* + * Deallocate the specified guest file descriptor. This doesn't + * close the host fd, it merely undoes the work of alloc_fd(). + */ +static void dealloc_guestfd(int guestfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = GuestFDUnused; +} + +/* + * Given a guest file descriptor, get the associated struct. + * If the fd is not valid, return NULL. This is the function + * used by the various semihosting calls to validate a handle + * from the guest. + * Note: calling alloc_guestfd() or dealloc_guestfd() will + * invalidate any GuestFD* obtained by calling this function. + */ +static GuestFD *get_guestfd(int guestfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + if (!gf || gf->type == GuestFDUnused) { + return NULL; + } + return gf; } +/* + * The semihosting API has no concept of its errno being thread-safe, + * as the API design predates SMP CPUs and was intended as a simple + * real-hardware set of debug functionality. For QEMU, we make the + * errno be per-thread in linux-user mode; in softmmu it is a simple + * global, and we assume that the guest takes care of avoiding any races. + */ +#ifndef CONFIG_USER_ONLY +static target_ulong syscall_err; + #include "exec/softmmu-semi.h" #endif -static target_ulong arm_semi_syscall_len; +static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code) +{ + if (code == (uint32_t)-1) { +#ifdef CONFIG_USER_ONLY + CPUState *cs = env_cpu(env); + TaskState *ts = cs->opaque; -#if !defined(CONFIG_USER_ONLY) -static target_ulong syscall_err; + ts->swi_errno = errno; +#else + syscall_err = errno; #endif + } + return code; +} -static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) +static inline uint32_t get_swi_errno(CPUARMState *env) { - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; #ifdef CONFIG_USER_ONLY + CPUState *cs = env_cpu(env); TaskState *ts = cs->opaque; + + return ts->swi_errno; +#else + return syscall_err; #endif +} + +static target_ulong arm_semi_syscall_len; + +static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; target_ulong reg0 = is_a64(env) ? env->xregs[0] : env->regs[0]; if (ret == (target_ulong)-1) { -#ifdef CONFIG_USER_ONLY - ts->swi_errno = err; -#else - syscall_err = err; -#endif + errno = err; + set_swi_errno(env, -1); reg0 = ret; } else { /* Fixup syscalls that use nonstardard return conventions. */ @@ -199,11 +326,30 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) } else { env->regs[0] = size; } -#ifdef CONFIG_USER_ONLY - ((TaskState *)cs->opaque)->swi_errno = err; -#else - syscall_err = err; -#endif + errno = err; + set_swi_errno(env, -1); +} + +static int arm_semi_open_guestfd; + +static void arm_semi_open_cb(CPUState *cs, target_ulong ret, target_ulong err) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + if (ret == (target_ulong)-1) { + errno = err; + set_swi_errno(env, -1); + dealloc_guestfd(arm_semi_open_guestfd); + } else { + associate_guestfd(arm_semi_open_guestfd, ret); + ret = arm_semi_open_guestfd; + } + + if (is_a64(env)) { + env->xregs[0] = ret; + } else { + env->regs[0] = ret; + } } static target_ulong arm_gdb_syscall(ARMCPU *cpu, gdb_syscall_complete_cb cb, @@ -216,26 +362,289 @@ static target_ulong arm_gdb_syscall(ARMCPU *cpu, gdb_syscall_complete_cb cb, gdb_do_syscallv(cb, fmt, va); va_end(va); - /* FIXME: we are implicitly relying on the syscall completing - * before this point, which is not guaranteed. We should - * put in an explicit synchronization between this and - * the callback function. + /* + * FIXME: in softmmu mode, the gdbstub will schedule our callback + * to occur, but will not actually call it to complete the syscall + * until after this function has returned and we are back in the + * CPU main loop. Therefore callers to this function must not + * do anything with its return value, because it is not necessarily + * the result of the syscall, but could just be the old value of X0. + * The only thing safe to do with this is that the callers of + * do_arm_semihosting() will write it straight back into X0. + * (In linux-user mode, the callback will have happened before + * gdb_do_syscallv() returns.) + * + * We should tidy this up so neither this function nor + * do_arm_semihosting() return a value, so the mistake of + * doing something with the return value is not possible to make. */ return is_a64(env) ? env->xregs[0] : env->regs[0]; } +/* + * Types for functions implementing various semihosting calls + * for specific types of guest file descriptor. These must all + * do the work and return the required return value for the guest, + * setting the guest errno if appropriate. + */ +typedef uint32_t sys_closefn(ARMCPU *cpu, GuestFD *gf); +typedef uint32_t sys_writefn(ARMCPU *cpu, GuestFD *gf, + target_ulong buf, uint32_t len); +typedef uint32_t sys_readfn(ARMCPU *cpu, GuestFD *gf, + target_ulong buf, uint32_t len); +typedef uint32_t sys_isattyfn(ARMCPU *cpu, GuestFD *gf); +typedef uint32_t sys_seekfn(ARMCPU *cpu, GuestFD *gf, + target_ulong offset); +typedef uint32_t sys_flenfn(ARMCPU *cpu, GuestFD *gf); + +static uint32_t host_closefn(ARMCPU *cpu, GuestFD *gf) +{ + CPUARMState *env = &cpu->env; + + return set_swi_errno(env, close(gf->hostfd)); +} + +static uint32_t host_writefn(ARMCPU *cpu, GuestFD *gf, + target_ulong buf, uint32_t len) +{ + uint32_t ret; + CPUARMState *env = &cpu->env; + char *s = lock_user(VERIFY_READ, buf, len, 1); + if (!s) { + /* Return bytes not written on error */ + return len; + } + ret = set_swi_errno(env, write(gf->hostfd, s, len)); + unlock_user(s, buf, 0); + if (ret == (uint32_t)-1) { + ret = 0; + } + /* Return bytes not written */ + return len - ret; +} + +static uint32_t host_readfn(ARMCPU *cpu, GuestFD *gf, + target_ulong buf, uint32_t len) +{ + uint32_t ret; + CPUARMState *env = &cpu->env; + char *s = lock_user(VERIFY_WRITE, buf, len, 0); + if (!s) { + /* return bytes not read */ + return len; + } + do { + ret = set_swi_errno(env, read(gf->hostfd, s, len)); + } while (ret == -1 && errno == EINTR); + unlock_user(s, buf, len); + if (ret == (uint32_t)-1) { + ret = 0; + } + /* Return bytes not read */ + return len - ret; +} + +static uint32_t host_isattyfn(ARMCPU *cpu, GuestFD *gf) +{ + return isatty(gf->hostfd); +} + +static uint32_t host_seekfn(ARMCPU *cpu, GuestFD *gf, target_ulong offset) +{ + CPUARMState *env = &cpu->env; + uint32_t ret = set_swi_errno(env, lseek(gf->hostfd, offset, SEEK_SET)); + if (ret == (uint32_t)-1) { + return -1; + } + return 0; +} + +static uint32_t host_flenfn(ARMCPU *cpu, GuestFD *gf) +{ + CPUARMState *env = &cpu->env; + struct stat buf; + uint32_t ret = set_swi_errno(env, fstat(gf->hostfd, &buf)); + if (ret == (uint32_t)-1) { + return -1; + } + return buf.st_size; +} + +static uint32_t gdb_closefn(ARMCPU *cpu, GuestFD *gf) +{ + return arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", gf->hostfd); +} + +static uint32_t gdb_writefn(ARMCPU *cpu, GuestFD *gf, + target_ulong buf, uint32_t len) +{ + arm_semi_syscall_len = len; + return arm_gdb_syscall(cpu, arm_semi_cb, "write,%x,%x,%x", + gf->hostfd, buf, len); +} + +static uint32_t gdb_readfn(ARMCPU *cpu, GuestFD *gf, + target_ulong buf, uint32_t len) +{ + arm_semi_syscall_len = len; + return arm_gdb_syscall(cpu, arm_semi_cb, "read,%x,%x,%x", + gf->hostfd, buf, len); +} + +static uint32_t gdb_isattyfn(ARMCPU *cpu, GuestFD *gf) +{ + return arm_gdb_syscall(cpu, arm_semi_cb, "isatty,%x", gf->hostfd); +} + +static uint32_t gdb_seekfn(ARMCPU *cpu, GuestFD *gf, target_ulong offset) +{ + return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0", + gf->hostfd, offset); +} + +static uint32_t gdb_flenfn(ARMCPU *cpu, GuestFD *gf) +{ + return arm_gdb_syscall(cpu, arm_semi_flen_cb, "fstat,%x,%x", + gf->hostfd, arm_flen_buf(cpu)); +} + +#define SHFB_MAGIC_0 0x53 +#define SHFB_MAGIC_1 0x48 +#define SHFB_MAGIC_2 0x46 +#define SHFB_MAGIC_3 0x42 + +/* Feature bits reportable in feature byte 0 */ +#define SH_EXT_EXIT_EXTENDED (1 << 0) +#define SH_EXT_STDOUT_STDERR (1 << 1) + +static const uint8_t featurefile_data[] = { + SHFB_MAGIC_0, + SHFB_MAGIC_1, + SHFB_MAGIC_2, + SHFB_MAGIC_3, + SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ +}; + +static void init_featurefile_guestfd(int guestfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = GuestFDFeatureFile; + gf->featurefile_offset = 0; +} + +static uint32_t featurefile_closefn(ARMCPU *cpu, GuestFD *gf) +{ + /* Nothing to do */ + return 0; +} + +static uint32_t featurefile_writefn(ARMCPU *cpu, GuestFD *gf, + target_ulong buf, uint32_t len) +{ + /* This fd can never be open for writing */ + CPUARMState *env = &cpu->env; + + errno = EBADF; + return set_swi_errno(env, -1); +} + +static uint32_t featurefile_readfn(ARMCPU *cpu, GuestFD *gf, + target_ulong buf, uint32_t len) +{ + uint32_t i; +#ifndef CONFIG_USER_ONLY + CPUARMState *env = &cpu->env; +#endif + char *s; + + s = lock_user(VERIFY_WRITE, buf, len, 0); + if (!s) { + return len; + } + + for (i = 0; i < len; i++) { + if (gf->featurefile_offset >= sizeof(featurefile_data)) { + break; + } + s[i] = featurefile_data[gf->featurefile_offset]; + gf->featurefile_offset++; + } + + unlock_user(s, buf, len); + + /* Return number of bytes not read */ + return len - i; +} + +static uint32_t featurefile_isattyfn(ARMCPU *cpu, GuestFD *gf) +{ + return 0; +} + +static uint32_t featurefile_seekfn(ARMCPU *cpu, GuestFD *gf, + target_ulong offset) +{ + gf->featurefile_offset = offset; + return 0; +} + +static uint32_t featurefile_flenfn(ARMCPU *cpu, GuestFD *gf) +{ + return sizeof(featurefile_data); +} + +typedef struct GuestFDFunctions { + sys_closefn *closefn; + sys_writefn *writefn; + sys_readfn *readfn; + sys_isattyfn *isattyfn; + sys_seekfn *seekfn; + sys_flenfn *flenfn; +} GuestFDFunctions; + +static const GuestFDFunctions guestfd_fns[] = { + [GuestFDHost] = { + .closefn = host_closefn, + .writefn = host_writefn, + .readfn = host_readfn, + .isattyfn = host_isattyfn, + .seekfn = host_seekfn, + .flenfn = host_flenfn, + }, + [GuestFDGDB] = { + .closefn = gdb_closefn, + .writefn = gdb_writefn, + .readfn = gdb_readfn, + .isattyfn = gdb_isattyfn, + .seekfn = gdb_seekfn, + .flenfn = gdb_flenfn, + }, + [GuestFDFeatureFile] = { + .closefn = featurefile_closefn, + .writefn = featurefile_writefn, + .readfn = featurefile_readfn, + .isattyfn = featurefile_isattyfn, + .seekfn = featurefile_seekfn, + .flenfn = featurefile_flenfn, + }, +}; + /* Read the input value from the argument block; fail the semihosting * call if the memory read fails. */ #define GET_ARG(n) do { \ if (is_a64(env)) { \ if (get_user_u64(arg ## n, args + (n) * 8)) { \ - return -1; \ + errno = EFAULT; \ + return set_swi_errno(env, -1); \ } \ } else { \ if (get_user_u32(arg ## n, args + (n) * 4)) { \ - return -1; \ + errno = EFAULT; \ + return set_swi_errno(env, -1); \ } \ } \ } while (0) @@ -264,11 +673,7 @@ target_ulong do_arm_semihosting(CPUARMState *env) int nr; uint32_t ret; uint32_t len; -#ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; -#else - CPUARMState *ts = env; -#endif + GuestFD *gf; if (is_a64(env)) { /* Note that the syscall number is in W0, not X0 */ @@ -281,38 +686,90 @@ target_ulong do_arm_semihosting(CPUARMState *env) switch (nr) { case TARGET_SYS_OPEN: + { + int guestfd; + GET_ARG(0); GET_ARG(1); GET_ARG(2); s = lock_user_string(arg0); if (!s) { - /* FIXME - should this error code be -TARGET_EFAULT ? */ - return (uint32_t)-1; + errno = EFAULT; + return set_swi_errno(env, -1); } if (arg1 >= 12) { unlock_user(s, arg0, 0); - return (uint32_t)-1; + errno = EINVAL; + return set_swi_errno(env, -1); + } + + guestfd = alloc_guestfd(); + if (guestfd < 0) { + unlock_user(s, arg0, 0); + errno = EMFILE; + return set_swi_errno(env, -1); } + if (strcmp(s, ":tt") == 0) { - int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO; + int result_fileno; + + /* + * We implement SH_EXT_STDOUT_STDERR, so: + * open for read == stdin + * open for write == stdout + * open for append == stderr + */ + if (arg1 < 4) { + result_fileno = STDIN_FILENO; + } else if (arg1 < 8) { + result_fileno = STDOUT_FILENO; + } else { + result_fileno = STDERR_FILENO; + } + associate_guestfd(guestfd, result_fileno); unlock_user(s, arg0, 0); - return result_fileno; + return guestfd; } + if (strcmp(s, ":semihosting-features") == 0) { + unlock_user(s, arg0, 0); + /* We must fail opens for modes other than 0 ('r') or 1 ('rb') */ + if (arg1 != 0 && arg1 != 1) { + dealloc_guestfd(guestfd); + errno = EACCES; + return set_swi_errno(env, -1); + } + init_featurefile_guestfd(guestfd); + return guestfd; + } + if (use_gdb_syscalls()) { - ret = arm_gdb_syscall(cpu, arm_semi_cb, "open,%s,%x,1a4", arg0, + arm_semi_open_guestfd = guestfd; + ret = arm_gdb_syscall(cpu, arm_semi_open_cb, "open,%s,%x,1a4", arg0, (int)arg2+1, gdb_open_modeflags[arg1]); } else { - ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644)); + ret = set_swi_errno(env, open(s, open_modeflags[arg1], 0644)); + if (ret == (uint32_t)-1) { + dealloc_guestfd(guestfd); + } else { + associate_guestfd(guestfd, ret); + ret = guestfd; + } } unlock_user(s, arg0, 0); return ret; + } case TARGET_SYS_CLOSE: GET_ARG(0); - if (use_gdb_syscalls()) { - return arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", arg0); - } else { - return set_swi_errno(ts, close(arg0)); + + gf = get_guestfd(arg0); + if (!gf) { + errno = EBADF; + return set_swi_errno(env, -1); } + + ret = guestfd_fns[gf->type].closefn(cpu, gf); + dealloc_guestfd(arg0); + return ret; case TARGET_SYS_WRITEC: qemu_semihosting_console_outc(env, args); return 0xdeadbeef; @@ -323,83 +780,61 @@ target_ulong do_arm_semihosting(CPUARMState *env) GET_ARG(1); GET_ARG(2); len = arg2; - if (use_gdb_syscalls()) { - arm_semi_syscall_len = len; - return arm_gdb_syscall(cpu, arm_semi_cb, "write,%x,%x,%x", - arg0, arg1, len); - } else { - s = lock_user(VERIFY_READ, arg1, len, 1); - if (!s) { - /* Return bytes not written on error */ - return len; - } - ret = set_swi_errno(ts, write(arg0, s, len)); - unlock_user(s, arg1, 0); - if (ret == (uint32_t)-1) { - ret = 0; - } - /* Return bytes not written */ - return len - ret; + + gf = get_guestfd(arg0); + if (!gf) { + errno = EBADF; + return set_swi_errno(env, -1); } + + return guestfd_fns[gf->type].writefn(cpu, gf, arg1, len); case TARGET_SYS_READ: GET_ARG(0); GET_ARG(1); GET_ARG(2); len = arg2; - if (use_gdb_syscalls()) { - arm_semi_syscall_len = len; - return arm_gdb_syscall(cpu, arm_semi_cb, "read,%x,%x,%x", - arg0, arg1, len); - } else { - s = lock_user(VERIFY_WRITE, arg1, len, 0); - if (!s) { - /* return bytes not read */ - return len; - } - do { - ret = set_swi_errno(ts, read(arg0, s, len)); - } while (ret == -1 && errno == EINTR); - unlock_user(s, arg1, len); - if (ret == (uint32_t)-1) { - ret = 0; - } - /* Return bytes not read */ - return len - ret; + + gf = get_guestfd(arg0); + if (!gf) { + errno = EBADF; + return set_swi_errno(env, -1); } + + return guestfd_fns[gf->type].readfn(cpu, gf, arg1, len); case TARGET_SYS_READC: qemu_log_mask(LOG_UNIMP, "%s: SYS_READC not implemented", __func__); return 0; case TARGET_SYS_ISTTY: GET_ARG(0); - if (use_gdb_syscalls()) { - return arm_gdb_syscall(cpu, arm_semi_cb, "isatty,%x", arg0); - } else { - return isatty(arg0); + + gf = get_guestfd(arg0); + if (!gf) { + errno = EBADF; + return set_swi_errno(env, -1); } + + return guestfd_fns[gf->type].isattyfn(cpu, gf); case TARGET_SYS_SEEK: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0", - arg0, arg1); - } else { - ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET)); - if (ret == (uint32_t)-1) - return -1; - return 0; + + gf = get_guestfd(arg0); + if (!gf) { + errno = EBADF; + return set_swi_errno(env, -1); } + + return guestfd_fns[gf->type].seekfn(cpu, gf, arg1); case TARGET_SYS_FLEN: GET_ARG(0); - if (use_gdb_syscalls()) { - return arm_gdb_syscall(cpu, arm_semi_flen_cb, "fstat,%x,%x", - arg0, arm_flen_buf(cpu)); - } else { - struct stat buf; - ret = set_swi_errno(ts, fstat(arg0, &buf)); - if (ret == (uint32_t)-1) - return -1; - return buf.st_size; + + gf = get_guestfd(arg0); + if (!gf) { + errno = EBADF; + return set_swi_errno(env, -1); } + + return guestfd_fns[gf->type].flenfn(cpu, gf); case TARGET_SYS_TMPNAM: qemu_log_mask(LOG_UNIMP, "%s: SYS_TMPNAM not implemented", __func__); return -1; @@ -412,10 +847,10 @@ target_ulong do_arm_semihosting(CPUARMState *env) } else { s = lock_user_string(arg0); if (!s) { - /* FIXME - should this error code be -TARGET_EFAULT ? */ - return (uint32_t)-1; + errno = EFAULT; + return set_swi_errno(env, -1); } - ret = set_swi_errno(ts, remove(s)); + ret = set_swi_errno(env, remove(s)); unlock_user(s, arg0, 0); } return ret; @@ -431,11 +866,12 @@ target_ulong do_arm_semihosting(CPUARMState *env) char *s2; s = lock_user_string(arg0); s2 = lock_user_string(arg2); - if (!s || !s2) - /* FIXME - should this error code be -TARGET_EFAULT ? */ - ret = (uint32_t)-1; - else - ret = set_swi_errno(ts, rename(s, s2)); + if (!s || !s2) { + errno = EFAULT; + ret = set_swi_errno(env, -1); + } else { + ret = set_swi_errno(env, rename(s, s2)); + } if (s2) unlock_user(s2, arg2, 0); if (s) @@ -445,7 +881,7 @@ target_ulong do_arm_semihosting(CPUARMState *env) case TARGET_SYS_CLOCK: return clock() / (CLOCKS_PER_SEC / 100); case TARGET_SYS_TIME: - return set_swi_errno(ts, time(NULL)); + return set_swi_errno(env, time(NULL)); case TARGET_SYS_SYSTEM: GET_ARG(0); GET_ARG(1); @@ -455,19 +891,15 @@ target_ulong do_arm_semihosting(CPUARMState *env) } else { s = lock_user_string(arg0); if (!s) { - /* FIXME - should this error code be -TARGET_EFAULT ? */ - return (uint32_t)-1; + errno = EFAULT; + return set_swi_errno(env, -1); } - ret = set_swi_errno(ts, system(s)); + ret = set_swi_errno(env, system(s)); unlock_user(s, arg0, 0); return ret; } case TARGET_SYS_ERRNO: -#ifdef CONFIG_USER_ONLY - return ts->swi_errno; -#else - return syscall_err; -#endif + return get_swi_errno(env); case TARGET_SYS_GET_CMDLINE: { /* Build a command-line from the original argv. @@ -490,6 +922,8 @@ target_ulong do_arm_semihosting(CPUARMState *env) int status = 0; #if !defined(CONFIG_USER_ONLY) const char *cmdline; +#else + TaskState *ts = cs->opaque; #endif GET_ARG(0); GET_ARG(1); @@ -516,19 +950,22 @@ target_ulong do_arm_semihosting(CPUARMState *env) if (output_size > input_size) { /* Not enough space to store command-line arguments. */ - return -1; + errno = E2BIG; + return set_swi_errno(env, -1); } /* Adjust the command-line length. */ if (SET_ARG(1, output_size - 1)) { /* Couldn't write back to argument block */ - return -1; + errno = EFAULT; + return set_swi_errno(env, -1); } /* Lock the buffer on the ARM side. */ output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0); if (!output_buffer) { - return -1; + errno = EFAULT; + return set_swi_errno(env, -1); } /* Copy the command-line arguments. */ @@ -543,7 +980,8 @@ target_ulong do_arm_semihosting(CPUARMState *env) if (copy_from_user(output_buffer, ts->info->arg_start, output_size)) { - status = -1; + errno = EFAULT; + status = set_swi_errno(env, -1); goto out; } @@ -565,6 +1003,9 @@ target_ulong do_arm_semihosting(CPUARMState *env) target_ulong retvals[4]; target_ulong limit; int i; +#ifdef CONFIG_USER_ONLY + TaskState *ts = cs->opaque; +#endif GET_ARG(0); @@ -613,17 +1054,21 @@ target_ulong do_arm_semihosting(CPUARMState *env) if (fail) { /* Couldn't write back to argument block */ - return -1; + errno = EFAULT; + return set_swi_errno(env, -1); } } return 0; } case TARGET_SYS_EXIT: - if (is_a64(env)) { + case TARGET_SYS_EXIT_EXTENDED: + if (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(env)) { /* - * The A64 version of this call takes a parameter block, + * The A64 version of SYS_EXIT takes a parameter block, * so the application-exit type can return a subcode which * is the exit status code from the application. + * SYS_EXIT_EXTENDED is an a new-in-v2.0 optional function + * which allows A32/T32 guests to also provide a status code. */ GET_ARG(0); GET_ARG(1); @@ -635,8 +1080,10 @@ target_ulong do_arm_semihosting(CPUARMState *env) } } else { /* - * ARM specifies only Stopped_ApplicationExit as normal - * exit, everything else is considered an error + * The A32/T32 version of SYS_EXIT specifies only + * Stopped_ApplicationExit as normal exit, but does not + * allow the guest to specify the exit status code. + * Everything else is considered an error. */ ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1; } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 2399c14..13813fb 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -576,16 +576,16 @@ static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); - int kvm_irq = KVM_ARM_IRQ_TYPE_CPU << KVM_ARM_IRQ_TYPE_SHIFT; uint32_t linestate_bit; + int irq_id; switch (irq) { case ARM_CPU_IRQ: - kvm_irq |= KVM_ARM_IRQ_CPU_IRQ; + irq_id = KVM_ARM_IRQ_CPU_IRQ; linestate_bit = CPU_INTERRUPT_HARD; break; case ARM_CPU_FIQ: - kvm_irq |= KVM_ARM_IRQ_CPU_FIQ; + irq_id = KVM_ARM_IRQ_CPU_FIQ; linestate_bit = CPU_INTERRUPT_FIQ; break; default: @@ -597,9 +597,7 @@ static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) } else { env->irq_line_state &= ~linestate_bit; } - - kvm_irq |= cs->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT; - kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0); + kvm_arm_set_irq(cs->cpu_index, KVM_ARM_IRQ_TYPE_CPU, irq_id, !!level); #endif } diff --git a/target/arm/kvm.c b/target/arm/kvm.c index b2eaa50..b473c63 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -182,6 +182,7 @@ int kvm_arm_get_max_vm_ipa_size(MachineState *ms) int kvm_arch_init(MachineState *ms, KVMState *s) { + int ret = 0; /* For ARM interrupt delivery is always asynchronous, * whether we are using an in-kernel VGIC or not. */ @@ -195,7 +196,14 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); - return 0; + if (ms->smp.cpus > 256 && + !kvm_check_extension(s, KVM_CAP_ARM_IRQ_LINE_LAYOUT_2)) { + error_report("Using more than 256 vcpus requires a host kernel " + "with KVM_CAP_ARM_IRQ_LINE_LAYOUT_2"); + ret = -EINVAL; + } + + return ret; } unsigned long kvm_arch_vcpu_id(CPUState *cpu) @@ -744,6 +752,18 @@ int kvm_arm_vgic_probe(void) } } +int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level) +{ + int kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT) | irq; + int cpu_idx1 = cpu % 256; + int cpu_idx2 = cpu / 256; + + kvm_irq |= (cpu_idx1 << KVM_ARM_IRQ_VCPU_SHIFT) | + (cpu_idx2 << KVM_ARM_IRQ_VCPU2_SHIFT); + + return kvm_set_irq(kvm_state, kvm_irq, !!level); +} + int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data, PCIDevice *dev) { diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index b3106c8..b4e1945 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -253,6 +253,7 @@ int kvm_arm_vgic_probe(void); void kvm_arm_pmu_set_irq(CPUState *cs, int irq); void kvm_arm_pmu_init(CPUState *cs); +int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level); #else diff --git a/target/s390x/cc_helper.c b/target/s390x/cc_helper.c index cf68792..44731e4 100644 --- a/target/s390x/cc_helper.c +++ b/target/s390x/cc_helper.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" +#include "tcg_s390x.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" @@ -588,8 +589,7 @@ void HELPER(sacf)(CPUS390XState *env, uint64_t a1) break; default: HELPER_LOG("unknown sacf mode: %" PRIx64 "\n", a1); - s390_program_interrupt(env, PGM_SPECIFICATION, 2, GETPC()); - break; + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); } } #endif diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 163dae1..17460ed7 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -1,6 +1,10 @@ /* * S/390 virtual CPU header * + * For details on the s390x architecture and used definitions (e.g., + * PSW, PER and DAT (Dynamic Address Translation)), please refer to + * the "z/Architecture Principles of Operations" - a.k.a. PoP. + * * Copyright (c) 2009 Ulrich Hecht * Copyright IBM Corp. 2012, 2018 * @@ -30,7 +34,7 @@ /* The z/Architecture has a strong memory model with some store-after-load re-ordering */ #define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) -#define TARGET_INSN_START_EXTRA_WORDS 1 +#define TARGET_INSN_START_EXTRA_WORDS 2 #define MMU_MODE0_SUFFIX _primary #define MMU_MODE1_SUFFIX _secondary @@ -311,6 +315,7 @@ extern const VMStateDescription vmstate_s390_cpu; #define CR0_EDAT 0x0000000000800000ULL #define CR0_AFP 0x0000000000040000ULL #define CR0_VECTOR 0x0000000000020000ULL +#define CR0_IEP 0x0000000000100000ULL #define CR0_EMERGENCY_SIGNAL_SC 0x0000000000004000ULL #define CR0_EXTERNAL_CALL_SC 0x0000000000002000ULL #define CR0_CKC_SC 0x0000000000000800ULL @@ -558,26 +563,60 @@ QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096); #define ASCE_TYPE_SEGMENT 0x00 /* segment table type */ #define ASCE_TABLE_LENGTH 0x03 /* region table length */ -#define REGION_ENTRY_ORIGIN (~0xfffULL) /* region/segment table origin */ -#define REGION_ENTRY_RO 0x200 /* region/segment protection bit */ -#define REGION_ENTRY_TF 0xc0 /* region/segment table offset */ -#define REGION_ENTRY_INV 0x20 /* invalid region table entry */ -#define REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */ -#define REGION_ENTRY_TYPE_R1 0x0c /* region first table type */ -#define REGION_ENTRY_TYPE_R2 0x08 /* region second table type */ -#define REGION_ENTRY_TYPE_R3 0x04 /* region third table type */ -#define REGION_ENTRY_LENGTH 0x03 /* region third length */ - -#define SEGMENT_ENTRY_ORIGIN (~0x7ffULL) /* segment table origin */ -#define SEGMENT_ENTRY_FC 0x400 /* format control */ -#define SEGMENT_ENTRY_RO 0x200 /* page protection bit */ -#define SEGMENT_ENTRY_INV 0x20 /* invalid segment table entry */ - -#define VADDR_PX 0xff000 /* page index bits */ - -#define PAGE_RO 0x200 /* HW read-only bit */ -#define PAGE_INVALID 0x400 /* HW invalid bit */ -#define PAGE_RES0 0x800 /* bit must be zero */ +#define REGION_ENTRY_ORIGIN 0xfffffffffffff000ULL +#define REGION_ENTRY_P 0x0000000000000200ULL +#define REGION_ENTRY_TF 0x00000000000000c0ULL +#define REGION_ENTRY_I 0x0000000000000020ULL +#define REGION_ENTRY_TT 0x000000000000000cULL +#define REGION_ENTRY_TL 0x0000000000000003ULL + +#define REGION_ENTRY_TT_REGION1 0x000000000000000cULL +#define REGION_ENTRY_TT_REGION2 0x0000000000000008ULL +#define REGION_ENTRY_TT_REGION3 0x0000000000000004ULL + +#define REGION3_ENTRY_RFAA 0xffffffff80000000ULL +#define REGION3_ENTRY_AV 0x0000000000010000ULL +#define REGION3_ENTRY_ACC 0x000000000000f000ULL +#define REGION3_ENTRY_F 0x0000000000000800ULL +#define REGION3_ENTRY_FC 0x0000000000000400ULL +#define REGION3_ENTRY_IEP 0x0000000000000100ULL +#define REGION3_ENTRY_CR 0x0000000000000010ULL + +#define SEGMENT_ENTRY_ORIGIN 0xfffffffffffff800ULL +#define SEGMENT_ENTRY_SFAA 0xfffffffffff00000ULL +#define SEGMENT_ENTRY_AV 0x0000000000010000ULL +#define SEGMENT_ENTRY_ACC 0x000000000000f000ULL +#define SEGMENT_ENTRY_F 0x0000000000000800ULL +#define SEGMENT_ENTRY_FC 0x0000000000000400ULL +#define SEGMENT_ENTRY_P 0x0000000000000200ULL +#define SEGMENT_ENTRY_IEP 0x0000000000000100ULL +#define SEGMENT_ENTRY_I 0x0000000000000020ULL +#define SEGMENT_ENTRY_CS 0x0000000000000010ULL +#define SEGMENT_ENTRY_TT 0x000000000000000cULL + +#define SEGMENT_ENTRY_TT_SEGMENT 0x0000000000000000ULL + +#define PAGE_ENTRY_0 0x0000000000000800ULL +#define PAGE_ENTRY_I 0x0000000000000400ULL +#define PAGE_ENTRY_P 0x0000000000000200ULL +#define PAGE_ENTRY_IEP 0x0000000000000100ULL + +#define VADDR_REGION1_TX_MASK 0xffe0000000000000ULL +#define VADDR_REGION2_TX_MASK 0x001ffc0000000000ULL +#define VADDR_REGION3_TX_MASK 0x000003ff80000000ULL +#define VADDR_SEGMENT_TX_MASK 0x000000007ff00000ULL +#define VADDR_PAGE_TX_MASK 0x00000000000ff000ULL + +#define VADDR_REGION1_TX(vaddr) (((vaddr) & VADDR_REGION1_TX_MASK) >> 53) +#define VADDR_REGION2_TX(vaddr) (((vaddr) & VADDR_REGION2_TX_MASK) >> 42) +#define VADDR_REGION3_TX(vaddr) (((vaddr) & VADDR_REGION3_TX_MASK) >> 31) +#define VADDR_SEGMENT_TX(vaddr) (((vaddr) & VADDR_SEGMENT_TX_MASK) >> 20) +#define VADDR_PAGE_TX(vaddr) (((vaddr) & VADDR_PAGE_TX_MASK) >> 12) + +#define VADDR_REGION1_TL(vaddr) (((vaddr) & 0xc000000000000000ULL) >> 62) +#define VADDR_REGION2_TL(vaddr) (((vaddr) & 0x0018000000000000ULL) >> 51) +#define VADDR_REGION3_TL(vaddr) (((vaddr) & 0x0000030000000000ULL) >> 40) +#define VADDR_SEGMENT_TL(vaddr) (((vaddr) & 0x0000000060000000ULL) >> 29) #define SK_C (0x1 << 1) #define SK_R (0x1 << 2) @@ -765,11 +804,8 @@ int cpu_s390x_signal_handler(int host_signum, void *pinfo, void *puc); void s390_crw_mchk(void); void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, uint32_t io_int_parm, uint32_t io_int_word); -/* automatically detect the instruction length */ -#define ILEN_AUTO 0xff #define RA_IGNORED 0 -void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen, - uintptr_t ra); +void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra); /* service interrupts are floating therefore we must not pass an cpustate */ void s390_sclp_extint(uint32_t parm); diff --git a/target/s390x/crypto_helper.c b/target/s390x/crypto_helper.c index 5c79790..ff3fbc3 100644 --- a/target/s390x/crypto_helper.c +++ b/target/s390x/crypto_helper.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "internal.h" +#include "tcg_s390x.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" @@ -34,16 +35,14 @@ uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, case S390_FEAT_TYPE_PCKMO: case S390_FEAT_TYPE_PCC: if (mod) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); - return 0; + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } break; } s390_get_feat_block(type, subfunc); if (!test_be_bit(fc, subfunc)) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); - return 0; + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } switch (fc) { diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 65eabf0..53c2f81 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -61,12 +61,12 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) IplParameterBlock *iplb; if (env->psw.mask & PSW_MASK_PSTATE) { - s390_program_interrupt(env, PGM_PRIVILEGED, ILEN_AUTO, ra); + s390_program_interrupt(env, PGM_PRIVILEGED, ra); return; } if ((subcode & ~0x0ffffULL) || (subcode > 6)) { - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return; } @@ -82,13 +82,13 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) break; case 5: if ((r1 & 1) || (addr & 0x0fffULL)) { - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return; } if (!address_space_access_valid(&address_space_memory, addr, sizeof(IplParameterBlock), false, MEMTXATTRS_UNSPECIFIED)) { - s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); + s390_program_interrupt(env, PGM_ADDRESSING, ra); return; } iplb = g_new0(IplParameterBlock, 1); @@ -112,13 +112,13 @@ out: return; case 6: if ((r1 & 1) || (addr & 0x0fffULL)) { - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return; } if (!address_space_access_valid(&address_space_memory, addr, sizeof(IplParameterBlock), true, MEMTXATTRS_UNSPECIFIED)) { - s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); + s390_program_interrupt(env, PGM_ADDRESSING, ra); return; } iplb = s390_ipl_get_iplb(); @@ -130,7 +130,7 @@ out: } return; default: - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); break; } } diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index 892f659..e70c20d 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -34,15 +34,15 @@ #include "hw/boards.h" #endif -void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, - int ilen, uintptr_t ra) +void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, + uint32_t code, uintptr_t ra) { CPUState *cs = env_cpu(env); cpu_restore_state(cs, ra, true); qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", env->psw.addr); - trigger_pgm_exception(env, code, ilen); + trigger_pgm_exception(env, code); cpu_loop_exit(cs); } @@ -60,7 +60,7 @@ void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, if (env->cregs[0] & CR0_AFP) { env->fpc = deposit32(env->fpc, 8, 8, dxc); } - tcg_s390_program_interrupt(env, PGM_DATA, ILEN_AUTO, ra); + tcg_s390_program_interrupt(env, PGM_DATA, ra); } void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc, @@ -75,7 +75,7 @@ void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc, /* Always store the VXC into the FPC, without AFP it is undefined */ env->fpc = deposit32(env->fpc, 8, 8, vxc); - tcg_s390_program_interrupt(env, PGM_VECTOR_PROCESSING, ILEN_AUTO, ra); + tcg_s390_program_interrupt(env, PGM_VECTOR_PROCESSING, ra); } void HELPER(data_exception)(CPUS390XState *env, uint32_t dxc) @@ -96,7 +96,7 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, { S390CPU *cpu = S390_CPU(cs); - trigger_pgm_exception(&cpu->env, PGM_ADDRESSING, ILEN_AUTO); + trigger_pgm_exception(&cpu->env, PGM_ADDRESSING); /* On real machines this value is dropped into LowMem. Since this is userland, simply put this someplace that cpu_loop can find it. */ cpu->env.__excp_addr = address; @@ -126,8 +126,8 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, S390CPU *cpu = S390_CPU(cs); CPUS390XState *env = &cpu->env; target_ulong vaddr, raddr; - uint64_t asc; - int prot, fail; + uint64_t asc, tec; + int prot, excp; qemu_log_mask(CPU_LOG_MMU, "%s: addr 0x%" VADDR_PRIx " rw %d mmu_idx %d\n", __func__, address, access_type, mmu_idx); @@ -140,30 +140,30 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (!(env->psw.mask & PSW_MASK_64)) { vaddr &= 0x7fffffff; } - fail = mmu_translate(env, vaddr, access_type, asc, &raddr, &prot, true); + excp = mmu_translate(env, vaddr, access_type, asc, &raddr, &prot, &tec); } else if (mmu_idx == MMU_REAL_IDX) { /* 31-Bit mode */ if (!(env->psw.mask & PSW_MASK_64)) { vaddr &= 0x7fffffff; } - fail = mmu_translate_real(env, vaddr, access_type, &raddr, &prot); + excp = mmu_translate_real(env, vaddr, access_type, &raddr, &prot, &tec); } else { g_assert_not_reached(); } /* check out of RAM access */ - if (!fail && + if (!excp && !address_space_access_valid(&address_space_memory, raddr, TARGET_PAGE_SIZE, access_type, MEMTXATTRS_UNSPECIFIED)) { qemu_log_mask(CPU_LOG_MMU, "%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, (uint64_t)raddr, (uint64_t)ram_size); - trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO); - fail = 1; + excp = PGM_ADDRESSING; + tec = 0; /* unused */ } - if (!fail) { + if (!excp) { qemu_log_mask(CPU_LOG_MMU, "%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n", __func__, (uint64_t)vaddr, (uint64_t)raddr, prot); @@ -175,23 +175,20 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, return false; } - cpu_restore_state(cs, retaddr, true); + if (excp != PGM_ADDRESSING) { + stq_phys(env_cpu(env)->as, + env->psa + offsetof(LowCore, trans_exc_code), tec); + } /* - * The ILC value for code accesses is undefined. The important - * thing here is to *not* leave env->int_pgm_ilen set to ILEN_AUTO, - * which would cause do_program_interrupt to attempt to read from - * env->psw.addr again. C.f. the condition in trigger_page_fault, - * but is not universally applied. - * - * ??? If we remove ILEN_AUTO, by moving the computation of ILEN - * into cpu_restore_state, then we may remove this entirely. + * For data accesses, ILEN will be filled in from the unwind info, + * within cpu_loop_exit_restore. For code accesses, retaddr == 0, + * and so unwinding will not occur. However, ILEN is also undefined + * for that case -- we choose to set ILEN = 2. */ - if (access_type == MMU_INST_FETCH) { - env->int_pgm_ilen = 2; - } - - cpu_loop_exit(cs); + env->int_pgm_ilen = 2; + trigger_pgm_exception(env, excp); + cpu_loop_exit_restore(cs, retaddr); } static void do_program_interrupt(CPUS390XState *env) @@ -200,9 +197,6 @@ static void do_program_interrupt(CPUS390XState *env) LowCore *lowcore; int ilen = env->int_pgm_ilen; - if (ilen == ILEN_AUTO) { - ilen = get_ilen(cpu_ldub_code(env, env->psw.addr)); - } assert(ilen == 2 || ilen == 4 || ilen == 6); switch (env->int_pgm_code) { @@ -614,7 +608,7 @@ void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, S390CPU *cpu = S390_CPU(cs); CPUS390XState *env = &cpu->env; - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, retaddr); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, retaddr); } #endif /* CONFIG_USER_ONLY */ diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c index 5faf973..8bb9f54 100644 --- a/target/s390x/fpu_helper.c +++ b/target/s390x/fpu_helper.c @@ -825,7 +825,7 @@ void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc) { if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u || (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) { - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); } /* Install everything in the main FPC. */ @@ -843,7 +843,7 @@ void HELPER(sfas)(CPUS390XState *env, uint64_t fpc) if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u || (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) { - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); } /* @@ -880,7 +880,7 @@ void HELPER(sfas)(CPUS390XState *env, uint64_t fpc) void HELPER(srnm)(CPUS390XState *env, uint64_t rnd) { if (rnd > 0x7 || fpc_to_rnd[rnd & 0x7] == -1) { - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); } env->fpc = deposit32(env->fpc, 0, 3, rnd); diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 49a650a..6278845 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -698,15 +698,23 @@ static uint16_t qemu_V4_0[] = { S390_FEAT_ZPCI, }; -static uint16_t qemu_LATEST[] = { +static uint16_t qemu_V4_1[] = { S390_FEAT_STFLE_53, S390_FEAT_VECTOR, }; +static uint16_t qemu_LATEST[] = { + S390_FEAT_ACCESS_EXCEPTION_FS_INDICATION, + S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2, + S390_FEAT_ESOP, +}; + /* add all new definitions before this point */ static uint16_t qemu_MAX[] = { /* generates a dependency warning, leave it out for now */ S390_FEAT_MSA_EXT_5, + /* features introduced after the z13 */ + S390_FEAT_INSTRUCTION_EXEC_PROT, }; /****** END FEATURE DEFS ******/ @@ -824,6 +832,7 @@ static FeatGroupDefSpec QemuFeatDef[] = { QEMU_FEAT_INITIALIZER(V2_11), QEMU_FEAT_INITIALIZER(V3_1), QEMU_FEAT_INITIALIZER(V4_0), + QEMU_FEAT_INITIALIZER(V4_1), QEMU_FEAT_INITIALIZER(LATEST), QEMU_FEAT_INITIALIZER(MAX), }; diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 948c039..a3a4916 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -52,6 +52,7 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) target_ulong raddr; int prot; uint64_t asc = env->psw.mask & PSW_MASK_ASC; + uint64_t tec; /* 31-Bit mode */ if (!(env->psw.mask & PSW_MASK_64)) { @@ -63,7 +64,11 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) asc = PSW_ASC_PRIMARY; } - if (mmu_translate(env, vaddr, MMU_INST_FETCH, asc, &raddr, &prot, false)) { + /* + * We want to read code even if IEP is active. Use MMU_DATA_LOAD instead + * of MMU_INST_FETCH. + */ + if (mmu_translate(env, vaddr, MMU_DATA_LOAD, asc, &raddr, &prot, &tec)) { return -1; } return raddr; diff --git a/target/s390x/int_helper.c b/target/s390x/int_helper.c index d13cc49..658507d 100644 --- a/target/s390x/int_helper.c +++ b/target/s390x/int_helper.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" +#include "tcg_s390x.h" #include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" @@ -39,7 +40,7 @@ int64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64) int64_t q; if (b == 0) { - s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } ret = q = a / b; @@ -47,7 +48,7 @@ int64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64) /* Catch non-representable quotient. */ if (ret != q) { - s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } return ret; @@ -60,7 +61,7 @@ uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b64) uint64_t q; if (b == 0) { - s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } ret = q = a / b; @@ -68,7 +69,7 @@ uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b64) /* Catch non-representable quotient. */ if (ret != q) { - s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } return ret; @@ -79,7 +80,7 @@ int64_t HELPER(divs64)(CPUS390XState *env, int64_t a, int64_t b) { /* Catch divide by zero, and non-representable quotient (MIN / -1). */ if (b == 0 || (b == -1 && a == (1ll << 63))) { - s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } env->retxl = a % b; return a / b; @@ -92,7 +93,7 @@ uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t ret; /* Signal divide by zero. */ if (b == 0) { - s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } if (ah == 0) { /* 64 -> 64/64 case */ @@ -106,7 +107,7 @@ uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al, env->retxl = a % b; ret = q; if (ret != q) { - s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } #else /* 32-bit hosts would need special wrapper functionality - just abort if diff --git a/target/s390x/internal.h b/target/s390x/internal.h index c243fa7..d378161 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -317,7 +317,7 @@ void cpu_unmap_lowcore(LowCore *lowcore); /* interrupt.c */ -void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen); +void trigger_pgm_exception(CPUS390XState *env, uint32_t code); void cpu_inject_clock_comparator(S390CPU *cpu); void cpu_inject_cpu_timer(S390CPU *cpu); void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr); @@ -360,9 +360,9 @@ void probe_write_access(CPUS390XState *env, uint64_t addr, uint64_t len, /* mmu_helper.c */ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, - target_ulong *raddr, int *flags, bool exc); + target_ulong *raddr, int *flags, uint64_t *tec); int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw, - target_ulong *addr, int *flags); + target_ulong *addr, int *flags, uint64_t *tec); /* misc_helper.c */ diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c index a841f71..4cdbbc8 100644 --- a/target/s390x/interrupt.c +++ b/target/s390x/interrupt.c @@ -22,22 +22,21 @@ #endif /* Ensure to exit the TB after this call! */ -void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) +void trigger_pgm_exception(CPUS390XState *env, uint32_t code) { CPUState *cs = env_cpu(env); cs->exception_index = EXCP_PGM; env->int_pgm_code = code; - env->int_pgm_ilen = ilen; + /* env->int_pgm_ilen is already set, or will be set during unwinding */ } -void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen, - uintptr_t ra) +void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra) { if (kvm_enabled()) { kvm_s390_program_interrupt(env_archcpu(env), code); } else if (tcg_enabled()) { - tcg_s390_program_interrupt(env, code, ilen, ra); + tcg_s390_program_interrupt(env, code, ra); } else { g_assert_not_reached(); } diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index 83c164a..c437a1d 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -44,7 +44,7 @@ void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra) SubchDev *sch; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); + s390_program_interrupt(&cpu->env, PGM_OPERAND, ra); return; } trace_ioinst_sch_id("xsch", cssid, ssid, schid); @@ -62,7 +62,7 @@ void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1, uintptr_t ra) SubchDev *sch; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); + s390_program_interrupt(&cpu->env, PGM_OPERAND, ra); return; } trace_ioinst_sch_id("csch", cssid, ssid, schid); @@ -80,7 +80,7 @@ void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra) SubchDev *sch; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); + s390_program_interrupt(&cpu->env, PGM_OPERAND, ra); return; } trace_ioinst_sch_id("hsch", cssid, ssid, schid); @@ -116,7 +116,7 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra) addr = decode_basedisp_s(env, ipb, &ar); if (addr & 3) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return; } if (s390_cpu_virt_mem_read(cpu, addr, ar, &schib, sizeof(schib))) { @@ -125,7 +125,7 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra) } if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || !ioinst_schib_valid(&schib)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return; } trace_ioinst_sch_id("msch", cssid, ssid, schid); @@ -173,7 +173,7 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra) addr = decode_basedisp_s(env, ipb, &ar); if (addr & 3) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return; } if (s390_cpu_virt_mem_read(cpu, addr, ar, &orig_orb, sizeof(orb))) { @@ -183,7 +183,7 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra) copy_orb_from_guest(&orb, &orig_orb); if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || !ioinst_orb_valid(&orb)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return; } trace_ioinst_sch_id("ssch", cssid, ssid, schid); @@ -205,7 +205,7 @@ void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb, uintptr_t ra) addr = decode_basedisp_s(env, ipb, &ar); if (addr & 3) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return; } @@ -236,7 +236,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, addr = decode_basedisp_s(env, ipb, &ar); if (addr & 3) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return; } @@ -247,7 +247,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, * access execption if it is not) first. */ if (!s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib))) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); } else { s390_cpu_virt_mem_handle_exc(cpu, ra); } @@ -299,13 +299,13 @@ int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra) uint8_t ar; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return -EIO; } trace_ioinst_sch_id("tsch", cssid, ssid, schid); addr = decode_basedisp_s(env, ipb, &ar); if (addr & 3) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return -EIO; } @@ -613,7 +613,7 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra) addr = env->regs[reg]; /* Page boundary? */ if (addr & 0xfff) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + s390_program_interrupt(env, PGM_SPECIFICATION, ra); return; } /* @@ -629,7 +629,7 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra) len = be16_to_cpu(req->len); /* Length field valid? */ if ((len < 16) || (len > 4088) || (len & 7)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return; } memset((char *)req + len, 0, TARGET_PAGE_SIZE - len); @@ -678,7 +678,7 @@ void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, trace_ioinst("schm"); if (SCHM_REG1_RES(reg1)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return; } @@ -687,7 +687,7 @@ void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, dct = SCHM_REG1_DCT(reg1); if (update && (reg2 & 0x000000000000001f)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return; } @@ -700,7 +700,7 @@ void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra) SubchDev *sch; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); + s390_program_interrupt(&cpu->env, PGM_OPERAND, ra); return; } trace_ioinst_sch_id("rsch", cssid, ssid, schid); @@ -724,7 +724,7 @@ void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1, uintptr_t ra) CPUS390XState *env = &cpu->env; if (RCHP_REG1_RES(reg1)) { - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return; } @@ -747,7 +747,7 @@ void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1, uintptr_t ra) break; default: /* Invalid channel subsystem. */ - s390_program_interrupt(env, PGM_OPERAND, 4, ra); + s390_program_interrupt(env, PGM_OPERAND, ra); return; } setcc(cpu, cc); @@ -758,6 +758,6 @@ void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1, uintptr_t ra) { /* We do not provide address limit checking, so let's suppress it. */ if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) { - s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra); + s390_program_interrupt(&cpu->env, PGM_OPERAND, ra); } } diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index 44e5358..2325767 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" +#include "tcg_s390x.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" @@ -71,7 +72,7 @@ static inline void check_alignment(CPUS390XState *env, uint64_t v, int wordsize, uintptr_t ra) { if (v % wordsize) { - s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } } @@ -730,7 +731,7 @@ void HELPER(srst)(CPUS390XState *env, uint32_t r1, uint32_t r2) /* Bits 32-55 must contain all 0. */ if (env->regs[0] & 0xffffff00u) { - s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } str = get_address(env, r2); @@ -767,7 +768,7 @@ void HELPER(srstu)(CPUS390XState *env, uint32_t r1, uint32_t r2) /* Bits 32-47 of R0 must be zero. */ if (env->regs[0] & 0xffff0000u) { - s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } str = get_address(env, r2); @@ -846,7 +847,7 @@ uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2) S390Access srca, desta; if ((f && s) || extract64(r0, 12, 4)) { - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); } r1 = wrap_address(env, r1 & TARGET_PAGE_MASK); @@ -879,7 +880,7 @@ uint32_t HELPER(mvst)(CPUS390XState *env, uint32_t r1, uint32_t r2) int i; if (env->regs[0] & 0xffffff00ull) { - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } /* @@ -911,8 +912,7 @@ void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) int i; if (a2 & 0x3) { - /* we either came here by lam or lamy, which have different lengths */ - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } for (i = r1;; i = (i + 1) % 16) { @@ -932,7 +932,7 @@ void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) int i; if (a2 & 0x3) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } for (i = r1;; i = (i + 1) % 16) { @@ -1015,6 +1015,7 @@ uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2) uint64_t srclen = env->regs[r2 + 1] & 0xffffff; uint64_t src = get_address(env, r2); uint8_t pad = env->regs[r2 + 1] >> 24; + CPUState *cs = env_cpu(env); S390Access srca, desta; uint32_t cc, cur_len; @@ -1065,7 +1066,15 @@ uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2) env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen); set_address_zero(env, r1, dest); - /* TODO: Deliver interrupts. */ + /* + * MVCL is interruptible. Return to the main loop if requested after + * writing back all state to registers. If no interrupt will get + * injected, we'll end up back in this handler and continue processing + * the remaining parts. + */ + if (destlen && unlikely(cpu_loop_exit_requested(cs))) { + cpu_loop_exit_restore(cs, ra); + } } return cc; } @@ -1888,8 +1897,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, return cc; spec_exception: - s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); - g_assert_not_reached(); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2) @@ -1912,7 +1920,7 @@ void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint32_t i; if (src & 0x7) { - s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } for (i = r1;; i = (i + 1) % 16) { @@ -1945,7 +1953,7 @@ void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint32_t i; if (src & 0x3) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } for (i = r1;; i = (i + 1) % 16) { @@ -1976,7 +1984,7 @@ void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint32_t i; if (dest & 0x7) { - s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } for (i = r1;; i = (i + 1) % 16) { @@ -1996,7 +2004,7 @@ void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint32_t i; if (dest & 0x3) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } for (i = r1;; i = (i + 1) % 16) { @@ -2168,7 +2176,7 @@ uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) || psw_as == AS_HOME || psw_as == AS_ACCREG) { - s390_program_interrupt(env, PGM_SPECIAL_OP, ILEN_AUTO, ra); + s390_program_interrupt(env, PGM_SPECIAL_OP, ra); } l = wrap_length32(env, l); @@ -2199,7 +2207,7 @@ uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) || psw_as == AS_HOME || psw_as == AS_ACCREG) { - s390_program_interrupt(env, PGM_SPECIAL_OP, ILEN_AUTO, ra); + s390_program_interrupt(env, PGM_SPECIAL_OP, ra); } l = wrap_length32(env, l); @@ -2226,7 +2234,7 @@ void HELPER(idte)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint32_t m4) uint16_t entries, i, index = 0; if (r2 & 0xff000) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } if (!(r2 & 0x800)) { @@ -2252,9 +2260,9 @@ void HELPER(idte)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint32_t m4) /* addresses are not wrapped in 24/31bit mode but table index is */ raddr = table + ((index + i) & 0x7ff) * sizeof(entry); entry = cpu_ldq_real_ra(env, raddr, ra); - if (!(entry & REGION_ENTRY_INV)) { + if (!(entry & REGION_ENTRY_I)) { /* we are allowed to not store if already invalid */ - entry |= REGION_ENTRY_INV; + entry |= REGION_ENTRY_I; cpu_stq_real_ra(env, raddr, entry, ra); } } @@ -2279,17 +2287,17 @@ void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr, /* Compute the page table entry address */ pte_addr = (pto & SEGMENT_ENTRY_ORIGIN); - pte_addr += (vaddr & VADDR_PX) >> 9; + pte_addr += VADDR_PAGE_TX(vaddr) * 8; /* Mark the page table entry as invalid */ pte = cpu_ldq_real_ra(env, pte_addr, ra); - pte |= PAGE_INVALID; + pte |= PAGE_ENTRY_I; cpu_stq_real_ra(env, pte_addr, pte, ra); /* XXX we exploit the fact that Linux passes the exact virtual address here - it's not obliged to! */ if (m4 & 1) { - if (vaddr & ~VADDR_PX) { + if (vaddr & ~VADDR_PAGE_TX_MASK) { tlb_flush_page(cs, page); /* XXX 31-bit hack */ tlb_flush_page(cs, page ^ 0x80000000); @@ -2298,7 +2306,7 @@ void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr, tlb_flush(cs); } } else { - if (vaddr & ~VADDR_PX) { + if (vaddr & ~VADDR_PAGE_TX_MASK) { tlb_flush_page_all_cpus_synced(cs, page); /* XXX 31-bit hack */ tlb_flush_page_all_cpus_synced(cs, page ^ 0x80000000); @@ -2362,27 +2370,23 @@ void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1) /* load real address */ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) { - CPUState *cs = env_cpu(env); - uint32_t cc = 0; uint64_t asc = env->psw.mask & PSW_MASK_ASC; - uint64_t ret; - int old_exc, flags; + uint64_t ret, tec; + int flags, exc, cc; /* XXX incomplete - has more corner cases */ if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) { - s390_program_interrupt(env, PGM_SPECIAL_OP, 2, GETPC()); + tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, GETPC()); } - old_exc = cs->exception_index; - if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) { + exc = mmu_translate(env, addr, 0, asc, &ret, &flags, &tec); + if (exc) { cc = 3; - } - if (cs->exception_index == EXCP_PGM) { - ret = env->int_pgm_code | 0x80000000; + ret = exc | 0x80000000; } else { + cc = 0; ret |= addr & ~TARGET_PAGE_MASK; } - cs->exception_index = old_exc; env->cc_op = cc; return ret; @@ -2539,7 +2543,7 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src, __func__, dest, src, len); if (!(env->psw.mask & PSW_MASK_DAT)) { - s390_program_interrupt(env, PGM_SPECIAL_OP, 6, ra); + tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra); } /* OAC (operand access control) for the first operand -> dest */ @@ -2570,14 +2574,14 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src, } if (dest_a && dest_as == AS_HOME && (env->psw.mask & PSW_MASK_PSTATE)) { - s390_program_interrupt(env, PGM_SPECIAL_OP, 6, ra); + tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra); } if (!(env->cregs[0] & CR0_SECONDARY) && (dest_as == AS_SECONDARY || src_as == AS_SECONDARY)) { - s390_program_interrupt(env, PGM_SPECIAL_OP, 6, ra); + tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra); } if (!psw_key_valid(env, dest_key) || !psw_key_valid(env, src_key)) { - s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra); + tcg_s390_program_interrupt(env, PGM_PRIVILEGED, ra); } len = wrap_length32(env, len); @@ -2591,7 +2595,7 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src, (env->psw.mask & PSW_MASK_PSTATE)) { qemu_log_mask(LOG_UNIMP, "%s: AR-mode and PSTATE support missing\n", __func__); - s390_program_interrupt(env, PGM_ADDRESSING, 6, ra); + tcg_s390_program_interrupt(env, PGM_ADDRESSING, ra); } /* FIXME: Access using correct keys and AR-mode */ diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index 7530dcb..bfb457f 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -106,7 +106,7 @@ uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2) int r = sclp_service_call(env, r1, r2); qemu_mutex_unlock_iothread(); if (r < 0) { - s390_program_interrupt(env, -r, 4, GETPC()); + tcg_s390_program_interrupt(env, -r, GETPC()); } return r; } @@ -143,7 +143,7 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num) } if (r) { - s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); } } @@ -222,7 +222,7 @@ void HELPER(sckpf)(CPUS390XState *env, uint64_t r0) uint32_t val = r0; if (val & 0xffff0000) { - s390_program_interrupt(env, PGM_SPECIFICATION, 2, GETPC()); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); } env->todpr = val; } @@ -266,7 +266,7 @@ uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1) } if ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK)) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } if ((r0 & STSI_R0_FC_MASK) == STSI_R0_FC_CURRENT) { @@ -276,7 +276,7 @@ uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1) } if (a0 & ~TARGET_PAGE_MASK) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } /* count the cpus and split them into configured and reserved ones */ @@ -509,7 +509,7 @@ uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr) LowCore *lowcore; if (addr & 0x3) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } qemu_mutex_lock_iothread(); @@ -573,17 +573,8 @@ void HELPER(chsc)(CPUS390XState *env, uint64_t inst) #ifndef CONFIG_USER_ONLY void HELPER(per_check_exception)(CPUS390XState *env) { - uint32_t ilen; - if (env->per_perc_atmid) { - /* - * FIXME: ILEN_AUTO is most probably the right thing to use. ilen - * always has to match the instruction referenced in the PSW. E.g. - * if a PER interrupt is triggered via EXECUTE, we have to use ilen - * of EXECUTE, while per_address contains the target of EXECUTE. - */ - ilen = get_ilen(cpu_ldub_code(env, env->per_address)); - s390_program_interrupt(env, PGM_PER, ilen, GETPC()); + tcg_s390_program_interrupt(env, PGM_PER, GETPC()); } } @@ -673,7 +664,7 @@ uint32_t HELPER(stfle)(CPUS390XState *env, uint64_t addr) int i; if (addr & 0x7) { - s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } prepare_stfl(); @@ -746,7 +737,7 @@ void HELPER(sic)(CPUS390XState *env, uint64_t r1, uint64_t r3) qemu_mutex_unlock_iothread(); /* css_do_sic() may actually return a PGM_xxx value to inject */ if (r) { - s390_program_interrupt(env, -r, 4, GETPC()); + tcg_s390_program_interrupt(env, -r, GETPC()); } } diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 7e6b0d0..90b8133 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -28,37 +28,12 @@ #include "hw/hw.h" #include "hw/s390x/storage-keys.h" -/* #define DEBUG_S390 */ -/* #define DEBUG_S390_PTE */ -/* #define DEBUG_S390_STDOUT */ - -#ifdef DEBUG_S390 -#ifdef DEBUG_S390_STDOUT -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); \ - if (qemu_log_separate()) qemu_log(fmt, ##__VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { qemu_log(fmt, ## __VA_ARGS__); } while (0) -#endif -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -#ifdef DEBUG_S390_PTE -#define PTE_DPRINTF DPRINTF -#else -#define PTE_DPRINTF(fmt, ...) \ - do { } while (0) -#endif - /* Fetch/store bits in the translation exception code: */ #define FS_READ 0x800 #define FS_WRITE 0x400 static void trigger_access_exception(CPUS390XState *env, uint32_t type, - uint32_t ilen, uint64_t tec) + uint64_t tec) { S390CPU *cpu = env_archcpu(env); @@ -69,46 +44,8 @@ static void trigger_access_exception(CPUS390XState *env, uint32_t type, if (type != PGM_ADDRESSING) { stq_phys(cs->as, env->psa + offsetof(LowCore, trans_exc_code), tec); } - trigger_pgm_exception(env, type, ilen); - } -} - -static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr, - uint64_t asc, int rw, bool exc) -{ - uint64_t tec; - - tec = vaddr | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) | 4 | asc >> 46; - - DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec); - - if (!exc) { - return; - } - - trigger_access_exception(env, PGM_PROTECTION, ILEN_AUTO, tec); -} - -static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr, - uint32_t type, uint64_t asc, int rw, bool exc) -{ - int ilen = ILEN_AUTO; - uint64_t tec; - - tec = vaddr | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) | asc >> 46; - - DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec); - - if (!exc) { - return; - } - - /* Code accesses have an undefined ilc. */ - if (rw == MMU_INST_FETCH) { - ilen = 2; + trigger_pgm_exception(env, type); } - - trigger_access_exception(env, type, ilen, tec); } /* check whether the address would be proteted by Low-Address Protection */ @@ -156,122 +93,40 @@ target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr) return raddr; } -/* Decode page table entry (normal 4KB page) */ -static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr, - uint64_t asc, uint64_t pt_entry, - target_ulong *raddr, int *flags, int rw, bool exc) -{ - if (pt_entry & PAGE_INVALID) { - DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, pt_entry); - trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw, exc); - return -1; - } - if (pt_entry & PAGE_RES0) { - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc); - return -1; - } - if (pt_entry & PAGE_RO) { - *flags &= ~PAGE_WRITE; - } - - *raddr = pt_entry & ASCE_ORIGIN; - - PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, pt_entry); - - return 0; -} - -/* Decode segment table entry */ -static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr, - uint64_t asc, uint64_t st_entry, - target_ulong *raddr, int *flags, int rw, - bool exc) +static inline bool read_table_entry(CPUS390XState *env, hwaddr gaddr, + uint64_t *entry) { CPUState *cs = env_cpu(env); - uint64_t origin, offs, pt_entry; - if (st_entry & SEGMENT_ENTRY_RO) { - *flags &= ~PAGE_WRITE; - } - - if ((st_entry & SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) { - /* Decode EDAT1 segment frame absolute address (1MB page) */ - *raddr = (st_entry & 0xfffffffffff00000ULL) | (vaddr & 0xfffff); - PTE_DPRINTF("%s: SEG=0x%" PRIx64 "\n", __func__, st_entry); - return 0; - } - - /* Look up 4KB page entry */ - origin = st_entry & SEGMENT_ENTRY_ORIGIN; - offs = (vaddr & VADDR_PX) >> 9; - pt_entry = ldq_phys(cs->as, origin + offs); - PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n", - __func__, origin, offs, pt_entry); - return mmu_translate_pte(env, vaddr, asc, pt_entry, raddr, flags, rw, exc); -} - -/* Decode region table entries */ -static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr, - uint64_t asc, uint64_t entry, int level, - target_ulong *raddr, int *flags, int rw, - bool exc) -{ - CPUState *cs = env_cpu(env); - uint64_t origin, offs, new_entry; - const int pchks[4] = { - PGM_SEGMENT_TRANS, PGM_REG_THIRD_TRANS, - PGM_REG_SEC_TRANS, PGM_REG_FIRST_TRANS - }; - - PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, entry); - - origin = entry & REGION_ENTRY_ORIGIN; - offs = (vaddr >> (17 + 11 * level / 4)) & 0x3ff8; - - new_entry = ldq_phys(cs->as, origin + offs); - PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n", - __func__, origin, offs, new_entry); - - if ((new_entry & REGION_ENTRY_INV) != 0) { - DPRINTF("%s: invalid region\n", __func__); - trigger_page_fault(env, vaddr, pchks[level / 4], asc, rw, exc); - return -1; - } - - if ((new_entry & REGION_ENTRY_TYPE_MASK) != level) { - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc); - return -1; - } - - if (level == ASCE_TYPE_SEGMENT) { - return mmu_translate_segment(env, vaddr, asc, new_entry, raddr, flags, - rw, exc); - } - - /* Check region table offset and length */ - offs = (vaddr >> (28 + 11 * (level - 4) / 4)) & 3; - if (offs < ((new_entry & REGION_ENTRY_TF) >> 6) - || offs > (new_entry & REGION_ENTRY_LENGTH)) { - DPRINTF("%s: invalid offset or len (%lx)\n", __func__, new_entry); - trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw, exc); - return -1; - } - - if ((env->cregs[0] & CR0_EDAT) && (new_entry & REGION_ENTRY_RO)) { - *flags &= ~PAGE_WRITE; + /* + * According to the PoP, these table addresses are "unpredictably real + * or absolute". Also, "it is unpredictable whether the address wraps + * or an addressing exception is recognized". + * + * We treat them as absolute addresses and don't wrap them. + */ + if (unlikely(address_space_read(cs->as, gaddr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *)entry, sizeof(*entry)) != + MEMTX_OK)) { + return false; } - - /* yet another region */ - return mmu_translate_region(env, vaddr, asc, new_entry, level - 4, - raddr, flags, rw, exc); + *entry = be64_to_cpu(*entry); + return true; } static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, uint64_t asc, uint64_t asce, target_ulong *raddr, - int *flags, int rw, bool exc) + int *flags, int rw) { - int level; - int r; + const bool edat1 = (env->cregs[0] & CR0_EDAT) && + s390_has_feat(S390_FEAT_EDAT); + const bool edat2 = edat1 && s390_has_feat(S390_FEAT_EDAT_2); + const bool iep = (env->cregs[0] & CR0_IEP) && + s390_has_feat(S390_FEAT_INSTRUCTION_EXEC_PROT); + const int asce_tl = asce & ASCE_TABLE_LENGTH; + const int asce_p = asce & ASCE_PRIVATE_SPACE; + hwaddr gaddr = asce & ASCE_ORIGIN; + uint64_t entry; if (asce & ASCE_REAL_SPACE) { /* direct mapping */ @@ -279,60 +134,158 @@ static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, return 0; } - level = asce & ASCE_TYPE_MASK; - switch (level) { + switch (asce & ASCE_TYPE_MASK) { case ASCE_TYPE_REGION1: - if ((vaddr >> 62) > (asce & ASCE_TABLE_LENGTH)) { - trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw, exc); - return -1; + if (VADDR_REGION1_TL(vaddr) > asce_tl) { + return PGM_REG_FIRST_TRANS; } + gaddr += VADDR_REGION1_TX(vaddr) * 8; break; case ASCE_TYPE_REGION2: - if (vaddr & 0xffe0000000000000ULL) { - DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 - " 0xffe0000000000000ULL\n", __func__, vaddr); - trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc); - return -1; + if (VADDR_REGION1_TX(vaddr)) { + return PGM_ASCE_TYPE; } - if ((vaddr >> 51 & 3) > (asce & ASCE_TABLE_LENGTH)) { - trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw, exc); - return -1; + if (VADDR_REGION2_TL(vaddr) > asce_tl) { + return PGM_REG_SEC_TRANS; } + gaddr += VADDR_REGION2_TX(vaddr) * 8; break; case ASCE_TYPE_REGION3: - if (vaddr & 0xfffffc0000000000ULL) { - DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 - " 0xfffffc0000000000ULL\n", __func__, vaddr); - trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc); - return -1; + if (VADDR_REGION1_TX(vaddr) || VADDR_REGION2_TX(vaddr)) { + return PGM_ASCE_TYPE; } - if ((vaddr >> 40 & 3) > (asce & ASCE_TABLE_LENGTH)) { - trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw, exc); - return -1; + if (VADDR_REGION3_TL(vaddr) > asce_tl) { + return PGM_REG_THIRD_TRANS; } + gaddr += VADDR_REGION3_TX(vaddr) * 8; break; case ASCE_TYPE_SEGMENT: - if (vaddr & 0xffffffff80000000ULL) { - DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 - " 0xffffffff80000000ULL\n", __func__, vaddr); - trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc); - return -1; + if (VADDR_REGION1_TX(vaddr) || VADDR_REGION2_TX(vaddr) || + VADDR_REGION3_TX(vaddr)) { + return PGM_ASCE_TYPE; + } + if (VADDR_SEGMENT_TL(vaddr) > asce_tl) { + return PGM_SEGMENT_TRANS; + } + gaddr += VADDR_SEGMENT_TX(vaddr) * 8; + break; + } + + switch (asce & ASCE_TYPE_MASK) { + case ASCE_TYPE_REGION1: + if (!read_table_entry(env, gaddr, &entry)) { + return PGM_ADDRESSING; + } + if (entry & REGION_ENTRY_I) { + return PGM_REG_FIRST_TRANS; + } + if ((entry & REGION_ENTRY_TT) != REGION_ENTRY_TT_REGION1) { + return PGM_TRANS_SPEC; + } + if (VADDR_REGION2_TL(vaddr) < (entry & REGION_ENTRY_TF) >> 6 || + VADDR_REGION2_TL(vaddr) > (entry & REGION_ENTRY_TL)) { + return PGM_REG_SEC_TRANS; + } + if (edat1 && (entry & REGION_ENTRY_P)) { + *flags &= ~PAGE_WRITE; + } + gaddr = (entry & REGION_ENTRY_ORIGIN) + VADDR_REGION2_TX(vaddr) * 8; + /* fall through */ + case ASCE_TYPE_REGION2: + if (!read_table_entry(env, gaddr, &entry)) { + return PGM_ADDRESSING; + } + if (entry & REGION_ENTRY_I) { + return PGM_REG_SEC_TRANS; + } + if ((entry & REGION_ENTRY_TT) != REGION_ENTRY_TT_REGION2) { + return PGM_TRANS_SPEC; + } + if (VADDR_REGION3_TL(vaddr) < (entry & REGION_ENTRY_TF) >> 6 || + VADDR_REGION3_TL(vaddr) > (entry & REGION_ENTRY_TL)) { + return PGM_REG_THIRD_TRANS; + } + if (edat1 && (entry & REGION_ENTRY_P)) { + *flags &= ~PAGE_WRITE; + } + gaddr = (entry & REGION_ENTRY_ORIGIN) + VADDR_REGION3_TX(vaddr) * 8; + /* fall through */ + case ASCE_TYPE_REGION3: + if (!read_table_entry(env, gaddr, &entry)) { + return PGM_ADDRESSING; + } + if (entry & REGION_ENTRY_I) { + return PGM_REG_THIRD_TRANS; + } + if ((entry & REGION_ENTRY_TT) != REGION_ENTRY_TT_REGION3) { + return PGM_TRANS_SPEC; + } + if (edat2 && (entry & REGION3_ENTRY_CR) && asce_p) { + return PGM_TRANS_SPEC; + } + if (edat1 && (entry & REGION_ENTRY_P)) { + *flags &= ~PAGE_WRITE; + } + if (edat2 && (entry & REGION3_ENTRY_FC)) { + if (iep && (entry & REGION3_ENTRY_IEP)) { + *flags &= ~PAGE_EXEC; + } + *raddr = (entry & REGION3_ENTRY_RFAA) | + (vaddr & ~REGION3_ENTRY_RFAA); + return 0; + } + if (VADDR_SEGMENT_TL(vaddr) < (entry & REGION_ENTRY_TF) >> 6 || + VADDR_SEGMENT_TL(vaddr) > (entry & REGION_ENTRY_TL)) { + return PGM_SEGMENT_TRANS; + } + gaddr = (entry & REGION_ENTRY_ORIGIN) + VADDR_SEGMENT_TX(vaddr) * 8; + /* fall through */ + case ASCE_TYPE_SEGMENT: + if (!read_table_entry(env, gaddr, &entry)) { + return PGM_ADDRESSING; + } + if (entry & SEGMENT_ENTRY_I) { + return PGM_SEGMENT_TRANS; + } + if ((entry & SEGMENT_ENTRY_TT) != SEGMENT_ENTRY_TT_SEGMENT) { + return PGM_TRANS_SPEC; + } + if ((entry & SEGMENT_ENTRY_CS) && asce_p) { + return PGM_TRANS_SPEC; } - if ((vaddr >> 29 & 3) > (asce & ASCE_TABLE_LENGTH)) { - trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc); - return -1; + if (entry & SEGMENT_ENTRY_P) { + *flags &= ~PAGE_WRITE; + } + if (edat1 && (entry & SEGMENT_ENTRY_FC)) { + if (iep && (entry & SEGMENT_ENTRY_IEP)) { + *flags &= ~PAGE_EXEC; + } + *raddr = (entry & SEGMENT_ENTRY_SFAA) | + (vaddr & ~SEGMENT_ENTRY_SFAA); + return 0; } + gaddr = (entry & SEGMENT_ENTRY_ORIGIN) + VADDR_PAGE_TX(vaddr) * 8; break; } - r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw, - exc); - if (!r && rw == MMU_DATA_STORE && !(*flags & PAGE_WRITE)) { - trigger_prot_fault(env, vaddr, asc, rw, exc); - return -1; + if (!read_table_entry(env, gaddr, &entry)) { + return PGM_ADDRESSING; + } + if (entry & PAGE_ENTRY_I) { + return PGM_PAGE_TRANS; + } + if (entry & PAGE_ENTRY_0) { + return PGM_TRANS_SPEC; + } + if (entry & PAGE_ENTRY_P) { + *flags &= ~PAGE_WRITE; + } + if (iep && (entry & PAGE_ENTRY_IEP)) { + *flags &= ~PAGE_EXEC; } - return r; + *raddr = entry & TARGET_PAGE_MASK; + return 0; } static void mmu_handle_skey(target_ulong addr, int rw, int *flags) @@ -412,16 +365,18 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) * @param raddr the translated address is stored to this pointer * @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer * @param exc true = inject a program check if a fault occurred - * @return 0 if the translation was successful, -1 if a fault occurred + * @return 0 = success, != 0, the exception to raise */ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, - target_ulong *raddr, int *flags, bool exc) + target_ulong *raddr, int *flags, uint64_t *tec) { uint64_t asce; int r; - + *tec = (vaddr & TARGET_PAGE_MASK) | (asc >> 46) | + (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ); *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + if (is_low_address(vaddr & TARGET_PAGE_MASK) && lowprot_enabled(env, asc)) { /* * If any part of this page is currently protected, make sure the @@ -433,10 +388,9 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, */ *flags |= PAGE_WRITE_INV; if (is_low_address(vaddr) && rw == MMU_DATA_STORE) { - if (exc) { - trigger_access_exception(env, PGM_PROTECTION, ILEN_AUTO, 0); - } - return -EACCES; + /* LAP sets bit 56 */ + *tec |= 0x80; + return PGM_PROTECTION; } } @@ -449,15 +403,12 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, switch (asc) { case PSW_ASC_PRIMARY: - PTE_DPRINTF("%s: asc=primary\n", __func__); asce = env->cregs[1]; break; case PSW_ASC_HOME: - PTE_DPRINTF("%s: asc=home\n", __func__); asce = env->cregs[13]; break; case PSW_ASC_SECONDARY: - PTE_DPRINTF("%s: asc=secondary\n", __func__); asce = env->cregs[7]; break; case PSW_ASC_ACCREG: @@ -467,11 +418,25 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, } /* perform the DAT translation */ - r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags, rw, exc); - if (r) { + r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags, rw); + if (unlikely(r)) { return r; } + /* check for DAT protection */ + if (unlikely(rw == MMU_DATA_STORE && !(*flags & PAGE_WRITE))) { + /* DAT sets bit 61 only */ + *tec |= 0x4; + return PGM_PROTECTION; + } + + /* check for Instruction-Execution-Protection */ + if (unlikely(rw == MMU_INST_FETCH && !(*flags & PAGE_EXEC))) { + /* IEP sets bit 56 and 61 */ + *tec |= 0x84; + return PGM_PROTECTION; + } + nodat: /* Convert real address -> absolute address */ *raddr = mmu_real2abs(env, *raddr); @@ -486,22 +451,22 @@ nodat: * the MEMOP interface. */ static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages, - target_ulong *pages, bool is_write) + target_ulong *pages, bool is_write, uint64_t *tec) { uint64_t asc = cpu->env.psw.mask & PSW_MASK_ASC; CPUS390XState *env = &cpu->env; int ret, i, pflags; for (i = 0; i < nr_pages; i++) { - ret = mmu_translate(env, addr, is_write, asc, &pages[i], &pflags, true); + ret = mmu_translate(env, addr, is_write, asc, &pages[i], &pflags, tec); if (ret) { return ret; } if (!address_space_access_valid(&address_space_memory, pages[i], TARGET_PAGE_SIZE, is_write, MEMTXATTRS_UNSPECIFIED)) { - trigger_access_exception(env, PGM_ADDRESSING, ILEN_AUTO, 0); - return -EFAULT; + *tec = 0; /* unused */ + return PGM_ADDRESSING; } addr += TARGET_PAGE_SIZE; } @@ -529,6 +494,7 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, { int currlen, nr_pages, i; target_ulong *pages; + uint64_t tec; int ret; if (kvm_enabled()) { @@ -542,8 +508,10 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, + 1; pages = g_malloc(nr_pages * sizeof(*pages)); - ret = translate_pages(cpu, laddr, nr_pages, pages, is_write); - if (ret == 0 && hostbuf != NULL) { + ret = translate_pages(cpu, laddr, nr_pages, pages, is_write, &tec); + if (ret) { + trigger_access_exception(&cpu->env, ret, tec); + } else if (hostbuf != NULL) { /* Copy data by stepping through the area page by page */ for (i = 0; i < nr_pages; i++) { currlen = MIN(len, TARGET_PAGE_SIZE - (laddr % TARGET_PAGE_SIZE)); @@ -575,10 +543,10 @@ void s390_cpu_virt_mem_handle_exc(S390CPU *cpu, uintptr_t ra) * @param rw 0 = read, 1 = write, 2 = code fetch * @param addr the translated address is stored to this pointer * @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer - * @return 0 if the translation was successful, < 0 if a fault occurred + * @return 0 = success, != 0, the exception to raise */ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw, - target_ulong *addr, int *flags) + target_ulong *addr, int *flags, uint64_t *tec) { const bool lowprot_enabled = env->cregs[0] & CR0_LOWPROT; @@ -587,8 +555,11 @@ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw, /* see comment in mmu_translate() how this works */ *flags |= PAGE_WRITE_INV; if (is_low_address(raddr) && rw == MMU_DATA_STORE) { - trigger_access_exception(env, PGM_PROTECTION, ILEN_AUTO, 0); - return -EACCES; + /* LAP sets bit 56 */ + *tec = (raddr & TARGET_PAGE_MASK) + | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) + | 0x80; + return PGM_PROTECTION; } } diff --git a/target/s390x/tcg-stub.c b/target/s390x/tcg-stub.c index 32adb72..d22c898 100644 --- a/target/s390x/tcg-stub.c +++ b/target/s390x/tcg-stub.c @@ -18,8 +18,8 @@ void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) { } -void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, - int ilen, uintptr_t ra) +void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, + uint32_t code, uintptr_t ra) { g_assert_not_reached(); } diff --git a/target/s390x/tcg_s390x.h b/target/s390x/tcg_s390x.h index 2813f9d..2f54ccb 100644 --- a/target/s390x/tcg_s390x.h +++ b/target/s390x/tcg_s390x.h @@ -14,8 +14,8 @@ #define TCG_S390X_H void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque); -void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, - int ilen, uintptr_t ra); +void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, + uint32_t code, uintptr_t ra); void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, uintptr_t ra); void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc, diff --git a/target/s390x/translate.c b/target/s390x/translate.c index a3e43ff..151dfa9 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -6309,6 +6309,9 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) /* Search for the insn in the table. */ insn = extract_insn(env, s, &f); + /* Emit insn_start now that we know the ILEN. */ + tcg_gen_insn_start(s->base.pc_next, s->cc_op, s->ilen); + /* Not found means unimplemented/illegal opcode. */ if (insn == NULL) { qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%02x%02x\n", @@ -6463,9 +6466,6 @@ static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) { - DisasContext *dc = container_of(dcbase, DisasContext, base); - - tcg_gen_insn_start(dc->base.pc_next, dc->cc_op); } static bool s390x_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, @@ -6473,6 +6473,14 @@ static bool s390x_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, { DisasContext *dc = container_of(dcbase, DisasContext, base); + /* + * Emit an insn_start to accompany the breakpoint exception. + * The ILEN value is a dummy, since this does not result in + * an s390x exception, but an internal qemu exception which + * brings us back to interact with the gdbstub. + */ + tcg_gen_insn_start(dc->base.pc_next, dc->cc_op, 2); + dc->base.is_jmp = DISAS_PC_STALE; dc->do_debug = true; /* The address covered by the breakpoint must be included in @@ -6567,8 +6575,14 @@ void restore_state_to_opc(CPUS390XState *env, TranslationBlock *tb, target_ulong *data) { int cc_op = data[1]; + env->psw.addr = data[0]; + + /* Update the CC opcode if it is not already up-to-date. */ if ((cc_op != CC_OP_DYNAMIC) && (cc_op != CC_OP_STATIC)) { env->cc_op = cc_op; } + + /* Record ILEN. */ + env->int_pgm_ilen = data[2]; } diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 9b3d8b0..0b33fb2 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -10,6 +10,33 @@ * See the COPYING file in the top-level directory. */ +/* + * How to add or update the tests: + * Contributor: + * 1. add empty files for new tables, if any, under tests/data/acpi + * 2. list any changed files in tests/bios-tables-test-allowed-diff.h + * 3. commit the above *before* making changes that affect the tables + * Maintainer: + * After 1-3 above tests will pass but ignore differences with the expected files. + * You will also notice that tests/bios-tables-test-allowed-diff.h lists + * a bunch of files. This is your hint that you need to do the below: + * 4. Run + * make check V=1 + * this will produce a bunch of warnings about differences + * beween actual and expected ACPI tables. If you have IASL installed, + * they will also be disassembled so you can look at the disassembled + * output. If not - disassemble them yourself in any way you like. + * Look at the differences - make sure they make sense and match what the + * changes you are merging are supposed to do. + * + * 5. From build directory, run: + * $(SRC_PATH)/tests/data/acpi/rebuild-expected-aml.sh + * 6. Now commit any changes. + * 7. Before doing a pull request, make sure tests/bios-tables-test-allowed-diff.h + * is empty - this will ensure following changes to ACPI tables will + * be noticed. + */ + #include "qemu/osdep.h" #include <glib/gstdio.h> #include "qemu-common.h" @@ -334,7 +361,10 @@ try_again: g_assert(ret); g_assert_no_error(error); g_assert(exp_sdt.aml); - g_assert(exp_sdt.aml_len); + if (!exp_sdt.aml_len) { + fprintf(stderr, "Warning! zero length expected file '%s'\n", + aml_file); + } g_array_append_val(exp_tables, exp_sdt); } @@ -870,6 +900,53 @@ static void test_acpi_piix4_tcg_dimm_pxm(void) test_acpi_tcg_dimm_pxm(MACHINE_PC); } +static void test_acpi_virt_tcg_memhp(void) +{ + test_data data = { + .machine = "virt", + .accel = "tcg", + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 256ULL * 1024 * 1024, + }; + + data.variant = ".memhp"; + test_acpi_one(" -cpu cortex-a57" + " -m 256M,slots=3,maxmem=1G" + " -object memory-backend-ram,id=ram0,size=128M" + " -object memory-backend-ram,id=ram1,size=128M" + " -numa node,memdev=ram0 -numa node,memdev=ram1" + " -numa dist,src=0,dst=1,val=21", + &data); + + free_test_data(&data); + +} + +static void test_acpi_virt_tcg_numamem(void) +{ + test_data data = { + .machine = "virt", + .accel = "tcg", + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + data.variant = ".numamem"; + test_acpi_one(" -cpu cortex-a57" + " -object memory-backend-ram,id=ram0,size=128M" + " -numa node,memdev=ram0", + &data); + + free_test_data(&data); + +} + static void test_acpi_virt_tcg(void) { test_data data = { @@ -916,6 +993,8 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm); } else if (strcmp(arch, "aarch64") == 0) { qtest_add_func("acpi/virt", test_acpi_virt_tcg); + qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); + qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); } ret = g_test_run(); boot_sector_cleanup(disk); diff --git a/tests/data/acpi/virt/APIC.memhp b/tests/data/acpi/virt/APIC.memhp Binary files differnew file mode 100644 index 0000000..797dfde --- /dev/null +++ b/tests/data/acpi/virt/APIC.memhp diff --git a/tests/data/acpi/virt/APIC.numamem b/tests/data/acpi/virt/APIC.numamem Binary files differnew file mode 100644 index 0000000..797dfde --- /dev/null +++ b/tests/data/acpi/virt/APIC.numamem diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/virt/DSDT Binary files differindex 20e85c7..bce76e3 100644 --- a/tests/data/acpi/virt/DSDT +++ b/tests/data/acpi/virt/DSDT diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/virt/DSDT.memhp Binary files differnew file mode 100644 index 0000000..b4b153f --- /dev/null +++ b/tests/data/acpi/virt/DSDT.memhp diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.numamem Binary files differnew file mode 100644 index 0000000..bce76e3 --- /dev/null +++ b/tests/data/acpi/virt/DSDT.numamem diff --git a/tests/data/acpi/virt/FACP.memhp b/tests/data/acpi/virt/FACP.memhp Binary files differnew file mode 100644 index 0000000..27de99f --- /dev/null +++ b/tests/data/acpi/virt/FACP.memhp diff --git a/tests/data/acpi/virt/FACP.numamem b/tests/data/acpi/virt/FACP.numamem Binary files differnew file mode 100644 index 0000000..27de99f --- /dev/null +++ b/tests/data/acpi/virt/FACP.numamem diff --git a/tests/data/acpi/virt/GTDT.memhp b/tests/data/acpi/virt/GTDT.memhp Binary files differnew file mode 100644 index 0000000..10107a6 --- /dev/null +++ b/tests/data/acpi/virt/GTDT.memhp diff --git a/tests/data/acpi/virt/GTDT.numamem b/tests/data/acpi/virt/GTDT.numamem Binary files differnew file mode 100644 index 0000000..10107a6 --- /dev/null +++ b/tests/data/acpi/virt/GTDT.numamem diff --git a/tests/data/acpi/virt/MCFG.memhp b/tests/data/acpi/virt/MCFG.memhp Binary files differnew file mode 100644 index 0000000..e8987e1 --- /dev/null +++ b/tests/data/acpi/virt/MCFG.memhp diff --git a/tests/data/acpi/virt/MCFG.numamem b/tests/data/acpi/virt/MCFG.numamem Binary files differnew file mode 100644 index 0000000..e8987e1 --- /dev/null +++ b/tests/data/acpi/virt/MCFG.numamem diff --git a/tests/data/acpi/virt/SLIT.memhp b/tests/data/acpi/virt/SLIT.memhp Binary files differnew file mode 100644 index 0000000..74ec3b4 --- /dev/null +++ b/tests/data/acpi/virt/SLIT.memhp diff --git a/tests/data/acpi/virt/SPCR.memhp b/tests/data/acpi/virt/SPCR.memhp Binary files differnew file mode 100644 index 0000000..377271a --- /dev/null +++ b/tests/data/acpi/virt/SPCR.memhp diff --git a/tests/data/acpi/virt/SPCR.numamem b/tests/data/acpi/virt/SPCR.numamem Binary files differnew file mode 100644 index 0000000..377271a --- /dev/null +++ b/tests/data/acpi/virt/SPCR.numamem diff --git a/tests/data/acpi/virt/SRAT.memhp b/tests/data/acpi/virt/SRAT.memhp Binary files differnew file mode 100644 index 0000000..1b57db2 --- /dev/null +++ b/tests/data/acpi/virt/SRAT.memhp diff --git a/tests/data/acpi/virt/SRAT.numamem b/tests/data/acpi/virt/SRAT.numamem Binary files differnew file mode 100644 index 0000000..9526e5a --- /dev/null +++ b/tests/data/acpi/virt/SRAT.numamem diff --git a/tests/iothread.c b/tests/iothread.c index 777d9ee..13c9fdc 100644 --- a/tests/iothread.c +++ b/tests/iothread.c @@ -55,10 +55,16 @@ static void *iothread_run(void *opaque) return NULL; } -void iothread_join(IOThread *iothread) +static void iothread_stop_bh(void *opaque) { + IOThread *iothread = opaque; + iothread->stopping = true; - aio_notify(iothread->ctx); +} + +void iothread_join(IOThread *iothread) +{ + aio_bh_schedule_oneshot(iothread->ctx, iothread_stop_bh, iothread); qemu_thread_join(&iothread->thread); qemu_cond_destroy(&iothread->init_done_cond); qemu_mutex_destroy(&iothread->init_done_lock); diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c index 5b20e91..e16c30c 100644 --- a/tests/ptimer-test.c +++ b/tests/ptimer-test.c @@ -67,12 +67,13 @@ static void qemu_clock_step(uint64_t ns) static void check_set_count(gconstpointer arg) { const uint8_t *policy = arg; - QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); - ptimer_state *ptimer = ptimer_init(bh, *policy); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 1000); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 1000); g_assert_false(triggered); ptimer_free(ptimer); @@ -81,17 +82,20 @@ static void check_set_count(gconstpointer arg) static void check_set_limit(gconstpointer arg) { const uint8_t *policy = arg; - QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); - ptimer_state *ptimer = ptimer_init(bh, *policy); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_limit(ptimer, 1000, 0); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); g_assert_cmpuint(ptimer_get_limit(ptimer), ==, 1000); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_set_limit(ptimer, 2000, 1); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 2000); g_assert_cmpuint(ptimer_get_limit(ptimer), ==, 2000); g_assert_false(triggered); @@ -101,22 +105,25 @@ static void check_set_limit(gconstpointer arg) 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); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_period(ptimer, 2000000); ptimer_set_count(ptimer, 10); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 * 2 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); @@ -126,7 +133,9 @@ static void check_oneshot(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 * 7 + 1); @@ -157,28 +166,36 @@ static void check_oneshot(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 10); + ptimer_transaction_commit(ptimer); qemu_clock_step(20000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_set_limit(ptimer, 9, 1); + ptimer_transaction_commit(ptimer); qemu_clock_step(20000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 20); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 * 19 + 1); @@ -190,7 +207,9 @@ static void check_oneshot(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); g_assert_true(triggered); + ptimer_transaction_begin(ptimer); ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); triggered = false; @@ -204,8 +223,7 @@ static void check_oneshot(gconstpointer arg) 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); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *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); @@ -214,9 +232,11 @@ static void check_periodic(gconstpointer arg) triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_period(ptimer, 2000000); ptimer_set_limit(ptimer, 10, 1); ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); g_assert_false(triggered); @@ -245,7 +265,9 @@ static void check_periodic(gconstpointer arg) (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 20); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 20); g_assert_false(triggered); @@ -268,7 +290,9 @@ static void check_periodic(gconstpointer arg) triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 3); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); g_assert_false(triggered); @@ -284,7 +308,9 @@ static void check_periodic(gconstpointer arg) (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_true(triggered); + ptimer_transaction_begin(ptimer); ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); triggered = false; qemu_clock_step(2000000); @@ -293,8 +319,10 @@ static void check_periodic(gconstpointer arg) (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 3); ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 * 3 + 1); @@ -310,7 +338,9 @@ static void check_periodic(gconstpointer arg) (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 0); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_immediate_reload ? 0 : 10); @@ -348,7 +378,9 @@ static void check_periodic(gconstpointer arg) (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); g_assert_true(triggered); + ptimer_transaction_begin(ptimer); ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); triggered = false; @@ -358,8 +390,13 @@ static void check_periodic(gconstpointer arg) (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); + + ptimer_transaction_begin(ptimer); ptimer_set_period(ptimer, 0); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 + 1); @@ -372,23 +409,26 @@ static void check_periodic(gconstpointer arg) 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); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_period(ptimer, 2000000); ptimer_set_limit(ptimer, 10, 1); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 * 9 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); g_assert_false(triggered); @@ -403,7 +443,9 @@ static void check_on_the_fly_mode_change(gconstpointer arg) qemu_clock_step(2000000 * 9); + ptimer_transaction_begin(ptimer); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, (no_round_down ? 1 : 0) + (wrap_policy ? 1 : 0)); @@ -419,22 +461,25 @@ static void check_on_the_fly_mode_change(gconstpointer arg) 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); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_period(ptimer, 2000000); ptimer_set_limit(ptimer, 8, 1); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 * 4 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_set_period(ptimer, 4000000); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); qemu_clock_step(4000000 * 2 + 1); @@ -452,22 +497,25 @@ static void check_on_the_fly_period_change(gconstpointer arg) 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); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_freq(ptimer, 500); ptimer_set_limit(ptimer, 8, 1); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 * 4 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); g_assert_false(triggered); + ptimer_transaction_begin(ptimer); ptimer_set_freq(ptimer, 250); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); qemu_clock_step(2000000 * 4 + 1); @@ -485,13 +533,14 @@ static void check_on_the_fly_freq_change(gconstpointer arg) static void check_run_with_period_0(gconstpointer arg) { const uint8_t *policy = arg; - QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); - ptimer_state *ptimer = ptimer_init(bh, *policy); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 99); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); qemu_clock_step(10 * NANOSECONDS_PER_SECOND); @@ -503,8 +552,7 @@ static void check_run_with_period_0(gconstpointer arg) 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); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *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); @@ -513,9 +561,11 @@ static void check_run_with_delta_0(gconstpointer arg) triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_period(ptimer, 2000000); ptimer_set_limit(ptimer, 99, 0); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_immediate_reload ? 0 : 99); @@ -541,8 +591,10 @@ static void check_run_with_delta_0(gconstpointer arg) g_assert_false(triggered); } + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 99); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); } qemu_clock_step(2000000 + 1); @@ -562,8 +614,10 @@ static void check_run_with_delta_0(gconstpointer arg) triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 0); ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_immediate_reload ? 0 : 99); @@ -602,23 +656,26 @@ static void check_run_with_delta_0(gconstpointer arg) wrap_policy ? 0 : (no_round_down ? 99 : 98)); g_assert_true(triggered); + ptimer_transaction_begin(ptimer); ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); ptimer_free(ptimer); } 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); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER); bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_period(ptimer, 2000000); ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); @@ -642,8 +699,10 @@ static void check_periodic_with_load_0(gconstpointer arg) triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_count(ptimer, 10); ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); qemu_clock_step(2000000 * 10 + 1); @@ -662,22 +721,25 @@ static void check_periodic_with_load_0(gconstpointer arg) g_assert_false(triggered); } + ptimer_transaction_begin(ptimer); ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); ptimer_free(ptimer); } 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); + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; + ptimer_transaction_begin(ptimer); ptimer_set_period(ptimer, 2000000); ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index ffb18ab..3430029 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -107,7 +107,7 @@ if [ "$event" == "l2_load" ]; then $QEMU_IO -c "read $vmstate 0 128k " "$BLKDBG_TEST_IMG" | _filter_qemu_io fi -_check_test_img 2>&1 | grep -v "refcount=1 reference=0" +_check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0" done done @@ -152,7 +152,7 @@ echo echo "Event: $event; errno: $errno; imm: $imm; once: $once; write $vmstate" $QEMU_IO -c "write $vmstate 0 64M" "$BLKDBG_TEST_IMG" | _filter_qemu_io -_check_test_img 2>&1 | grep -v "refcount=1 reference=0" +_check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0" done done @@ -191,7 +191,7 @@ echo echo "Event: $event; errno: $errno; imm: $imm; once: $once" $QEMU_IO -c "write -b 0 64k" "$BLKDBG_TEST_IMG" | _filter_qemu_io -_check_test_img 2>&1 | grep -v "refcount=1 reference=0" +_check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0" done done diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out index fb89b84..ff0817b 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -17,18 +17,14 @@ Event: l1_update; errno: 5; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: on; write @@ -45,18 +41,14 @@ Event: l1_update; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: on; write @@ -137,18 +129,14 @@ Event: l2_update; errno: 5; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write @@ -165,18 +153,14 @@ Event: l2_update; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc_write; errno: 5; imm: off; once: on; write @@ -200,9 +184,7 @@ Event: l2_alloc_write; errno: 5; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc_write; errno: 28; imm: off; once: on; write @@ -226,9 +208,7 @@ Event: l2_alloc_write; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: on; write @@ -480,18 +460,14 @@ Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -55 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -251 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_write; errno: 28; imm: off; once: on; write @@ -532,18 +508,14 @@ Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -10 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -23 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write @@ -560,18 +532,14 @@ Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -10 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -23 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write @@ -588,18 +556,14 @@ Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -10 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -23 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. === L1 growth tests === @@ -658,9 +622,7 @@ Event: l1_grow_activate_table; errno: 5; imm: off; once: off qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -96 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow_activate_table; errno: 28; imm: off; once: on @@ -672,9 +634,7 @@ Event: l1_grow_activate_table; errno: 28; imm: off; once: off qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -96 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. === Avoid cluster leaks after temporary failure === diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache index 6dda95d..495d013 100644 --- a/tests/qemu-iotests/026.out.nocache +++ b/tests/qemu-iotests/026.out.nocache @@ -17,18 +17,14 @@ Event: l1_update; errno: 5; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: on; write @@ -45,18 +41,14 @@ Event: l1_update; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: on; write @@ -140,9 +132,7 @@ qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write -b @@ -150,9 +140,7 @@ qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write @@ -172,9 +160,7 @@ qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write -b @@ -182,9 +168,7 @@ qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc_write; errno: 5; imm: off; once: on; write @@ -208,9 +192,7 @@ Event: l2_alloc_write; errno: 5; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc_write; errno: 28; imm: off; once: on; write @@ -234,9 +216,7 @@ Event: l2_alloc_write; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -1 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: on; write @@ -488,18 +468,14 @@ Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -55 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -251 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_write; errno: 28; imm: off; once: on; write @@ -540,18 +516,14 @@ Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -10 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -23 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write @@ -568,18 +540,14 @@ Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -10 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -23 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write @@ -596,18 +564,14 @@ Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -10 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write -b qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -23 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. === L1 growth tests === @@ -666,9 +630,7 @@ Event: l1_grow_activate_table; errno: 5; imm: off; once: off qemu-io: Failed to flush the L2 table cache: Input/output error qemu-io: Failed to flush the refcount block cache: Input/output error write failed: Input/output error - -96 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow_activate_table; errno: 28; imm: off; once: on @@ -680,9 +642,7 @@ Event: l1_grow_activate_table; errno: 28; imm: off; once: off qemu-io: Failed to flush the L2 table cache: No space left on device qemu-io: Failed to flush the refcount block cache: No space left on device write failed: No space left on device - -96 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. === Avoid cluster leaks after temporary failure === diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 index 71301ec..bba1ee5 100755 --- a/tests/qemu-iotests/028 +++ b/tests/qemu-iotests/028 @@ -119,9 +119,14 @@ fi # Silence output since it contains the disk image path and QEMU's readline # character echoing makes it very hard to filter the output. Plus, there # is no telling how many times the command will repeat before succeeding. -_send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)" >/dev/null -_send_qemu_cmd $h "" "Formatting" | _filter_img_create -qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs" >/dev/null +# (Note that creating the image results in a "Formatting..." message over +# stdout, which is the same channel the monitor uses. We cannot reliably +# wait for it because the monitor output may interact with it in such a +# way that _timed_wait_for cannot read it. However, once the block job is +# done, we know that the "Formatting..." message must have appeared +# already, so the output is still deterministic.) +silent=y _send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)" +silent=y qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs" _send_qemu_cmd $h "info block-jobs" "No active jobs" _send_qemu_cmd $h 'quit' "" diff --git a/tests/qemu-iotests/028.out b/tests/qemu-iotests/028.out index 7d54aeb..37aed84 100644 --- a/tests/qemu-iotests/028.out +++ b/tests/qemu-iotests/028.out @@ -468,7 +468,6 @@ No errors were found on the image. block-backup -Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT (qemu) info block-jobs No active jobs === IO: pattern 195 diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056 index 98c55d8..f39287c 100755 --- a/tests/qemu-iotests/056 +++ b/tests/qemu-iotests/056 @@ -133,6 +133,7 @@ class BackupTest(iotests.QMPTestCase): self.vm = iotests.VM() self.test_img = img_create('test') self.dest_img = img_create('dest') + self.dest_img2 = img_create('dest2') self.ref_img = img_create('ref') self.vm.add_drive(self.test_img) self.vm.launch() @@ -141,6 +142,7 @@ class BackupTest(iotests.QMPTestCase): self.vm.shutdown() try_remove(self.test_img) try_remove(self.dest_img) + try_remove(self.dest_img2) try_remove(self.ref_img) def hmp_io_writes(self, drive, patterns): @@ -253,9 +255,9 @@ class BackupTest(iotests.QMPTestCase): res = self.vm.qmp('query-block-jobs') self.assert_qmp(res, 'return[0]/status', 'concluded') # Leave zombie job un-dismissed, observe a failure: - res = self.qmp_backup_and_wait(serror="Node 'drive0' is busy: block device is in use by block job: backup", + res = self.qmp_backup_and_wait(serror="Job ID 'drive0' already in use", device='drive0', format=iotests.imgfmt, - sync='full', target=self.dest_img, + sync='full', target=self.dest_img2, auto_dismiss=False) self.assertEqual(res, False) # OK, dismiss the zombie. @@ -265,7 +267,7 @@ class BackupTest(iotests.QMPTestCase): self.assert_qmp(res, 'return', []) # Ensure it's really gone. self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt, - sync='full', target=self.dest_img, + sync='full', target=self.dest_img2, auto_dismiss=False) def dismissal_failure(self, dismissal_opt): diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index ca40ba3..d3e851e 100755 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -105,7 +105,7 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): # Create a base image with a distinctive patterning drive0 = self.add_node('drive0') self.img_create(drive0['file'], drive0['fmt']) - self.vm.add_drive(drive0['file']) + self.vm.add_drive(drive0['file'], opts='node-name=node0') self.write_default_pattern(drive0['file']) self.vm.launch() @@ -348,12 +348,14 @@ class TestIncrementalBackup(TestIncrementalBackupBase): ('0xfe', '16M', '256k'), ('0x64', '32736k', '64k'))) # Check the dirty bitmap stats - result = self.vm.qmp('query-block') - self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/name', 'bitmap0') - self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/count', 458752) - self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/granularity', 65536) - self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/status', 'active') - self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/persistent', False) + self.assertTrue(self.vm.check_bitmap_status( + 'node0', bitmap0.name, { + 'name': 'bitmap0', + 'count': 458752, + 'granularity': 65536, + 'status': 'active', + 'persistent': False + })) # Prepare a cluster_size=128k backup target without a backing file. (target, _) = bitmap0.new_target() @@ -670,9 +672,8 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): """ drive0 = self.drives[0] - # NB: The blkdebug script here looks for a "flush, read, read" pattern. - # The flush occurs in hmp_io_writes, the first read in device_add, and - # the last read during the block job. + # NB: The blkdebug script here looks for a "flush, read" pattern. + # The flush occurs in hmp_io_writes, and the read during the block job. result = self.vm.qmp('blockdev-add', node_name=drive0['id'], driver=drive0['fmt'], @@ -686,15 +687,11 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): 'event': 'flush_to_disk', 'state': 1, 'new_state': 2 - },{ - 'event': 'read_aio', - 'state': 2, - 'new_state': 3 }], 'inject-error': [{ 'event': 'read_aio', 'errno': 5, - 'state': 3, + 'state': 2, 'immediately': False, 'once': True }], @@ -708,23 +705,15 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): ('0xfe', '16M', '256k'), ('0x64', '32736k', '64k'))) - # For the purposes of query-block visibility of bitmaps, add a drive - # frontend after we've written data; otherwise we can't use hmp-io - result = self.vm.qmp("device_add", - id="device0", - drive=drive0['id'], - driver="virtio-blk") - self.assert_qmp(result, 'return', {}) - # Bitmap Status Check - query = self.vm.qmp('query-block') - ret = [bmap for bmap in query['return'][0]['dirty-bitmaps'] - if bmap.get('name') == bitmap.name][0] - self.assert_qmp(ret, 'count', 458752) - self.assert_qmp(ret, 'granularity', 65536) - self.assert_qmp(ret, 'status', 'active') - self.assert_qmp(ret, 'busy', False) - self.assert_qmp(ret, 'recording', True) + self.assertTrue(self.vm.check_bitmap_status( + drive0['id'], bitmap.name, { + 'count': 458752, + 'granularity': 65536, + 'status': 'active', + 'busy': False, + 'recording': True + })) # Start backup parent, _ = bitmap.last_target() @@ -748,14 +737,14 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): 'operation': 'read'}) # Bitmap Status Check - query = self.vm.qmp('query-block') - ret = [bmap for bmap in query['return'][0]['dirty-bitmaps'] - if bmap.get('name') == bitmap.name][0] - self.assert_qmp(ret, 'count', 458752) - self.assert_qmp(ret, 'granularity', 65536) - self.assert_qmp(ret, 'status', 'frozen') - self.assert_qmp(ret, 'busy', True) - self.assert_qmp(ret, 'recording', True) + self.assertTrue(self.vm.check_bitmap_status( + drive0['id'], bitmap.name, { + 'count': 458752, + 'granularity': 65536, + 'status': 'frozen', + 'busy': True, + 'recording': True + })) # Resume and check incremental backup for consistency res = self.vm.qmp('block-job-resume', device=bitmap.drive['id']) @@ -763,14 +752,14 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): self.wait_qmp_backup(bitmap.drive['id']) # Bitmap Status Check - query = self.vm.qmp('query-block') - ret = [bmap for bmap in query['return'][0]['dirty-bitmaps'] - if bmap.get('name') == bitmap.name][0] - self.assert_qmp(ret, 'count', 0) - self.assert_qmp(ret, 'granularity', 65536) - self.assert_qmp(ret, 'status', 'active') - self.assert_qmp(ret, 'busy', False) - self.assert_qmp(ret, 'recording', True) + self.assertTrue(self.vm.check_bitmap_status( + drive0['id'], bitmap.name, { + 'count': 0, + 'granularity': 65536, + 'status': 'active', + 'busy': False, + 'recording': True + })) # Finalize / Cleanup self.make_reference_backup(bitmap) diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125 index dc4b8f5..4e31aa4 100755 --- a/tests/qemu-iotests/125 +++ b/tests/qemu-iotests/125 @@ -34,8 +34,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 get_image_size_on_host() { - $QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "disk size" \ - | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/' + echo $(($(stat -c '%b * %B' "$TEST_IMG_FILE"))) } # get standard environment and filters @@ -49,6 +48,46 @@ if [ -z "$TEST_IMG_FILE" ]; then TEST_IMG_FILE=$TEST_IMG fi +# Test whether we are running on a broken XFS version. There is this +# bug: + +# $ rm -f foo +# $ touch foo +# $ block_size=4096 # Your FS's block size +# $ fallocate -o $((block_size / 2)) -l $block_size foo +# $ LANG=C xfs_bmap foo | grep hole +# 1: [8..15]: hole +# +# The problem is that the XFS driver rounds down the offset and +# rounds up the length to the block size, but independently. As +# such, it only allocates the first block in the example above, +# even though it should allocate the first two blocks (because our +# request is to fallocate something that touches both the first +# two blocks). +# +# This means that when you then write to the beginning of the +# second block, the disk usage of the first two blocks grows. +# +# That is precisely what fallocate() promises, though: That when you +# write to an area that you have fallocated, no new blocks will have +# to be allocated. + +touch "$TEST_IMG_FILE" +# Assuming there is no FS with a block size greater than 64k +fallocate -o 65535 -l 2 "$TEST_IMG_FILE" +len0=$(get_image_size_on_host) + +# Write to something that in theory we have just fallocated +# (Thus, the on-disk size should not increase) +poke_file "$TEST_IMG_FILE" 65536 42 +len1=$(get_image_size_on_host) + +if [ $len1 -gt $len0 ]; then + _notrun "the test filesystem's fallocate() is broken" +fi + +rm -f "$TEST_IMG_FILE" + # Generally, we create some image with or without existing preallocation and # then resize it. Then we write some data into the image and verify that its # size does not change if we have used preallocation. @@ -111,7 +150,7 @@ for GROWTH_SIZE in 16 48 80; do if [ $file_length_2 -gt $file_length_1 ]; then echo "ERROR (grow): Image length has grown from $file_length_1 to $file_length_2" fi - if [ $create_mode != metadata ]; then + if [ $growth_mode != metadata ]; then # The host size should not have grown either if [ $host_size_2 -gt $host_size_1 ]; then echo "ERROR (grow): Host size has grown from $host_size_1 to $host_size_2" diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out index dbd3bde..e3b5782 100644 --- a/tests/qemu-iotests/141.out +++ b/tests/qemu-iotests/141.out @@ -10,7 +10,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m. Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} -{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} +{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}} diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index 4f363f2..8ab42e9 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149 @@ -153,7 +153,7 @@ def cryptsetup_format(config): (password, slot) = config.first_password() - args = ["luksFormat"] + args = ["luksFormat", "--type", "luks1"] cipher = config.cipher + "-" + config.mode + "-" + config.ivgen if config.ivgen_hash is not None: cipher = cipher + ":" + config.ivgen_hash diff --git a/tests/qemu-iotests/149.out b/tests/qemu-iotests/149.out index 1407ce6..6877ab6 100644 --- a/tests/qemu-iotests/149.out +++ b/tests/qemu-iotests/149.out @@ -2,7 +2,7 @@ # Create image truncate TEST_DIR/luks-aes-256-xts-plain64-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Write test pattern 0xa7 @@ -122,7 +122,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha1.img # Create image truncate TEST_DIR/luks-twofish-256-xts-plain64-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher twofish-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Write test pattern 0xa7 @@ -242,7 +242,7 @@ unlink TEST_DIR/luks-twofish-256-xts-plain64-sha1.img # Create image truncate TEST_DIR/luks-serpent-256-xts-plain64-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher serpent-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Write test pattern 0xa7 @@ -362,7 +362,7 @@ unlink TEST_DIR/luks-serpent-256-xts-plain64-sha1.img # Create image truncate TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher cast5-cbc-plain64 --key-size 128 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher cast5-cbc-plain64 --key-size 128 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Write test pattern 0xa7 @@ -483,7 +483,7 @@ Skipping cast6-256-xts-plain64-sha1 in blacklist # Create image truncate TEST_DIR/luks-aes-256-cbc-plain-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-plain --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Write test pattern 0xa7 @@ -603,7 +603,7 @@ unlink TEST_DIR/luks-aes-256-cbc-plain-sha1.img # Create image truncate TEST_DIR/luks-aes-256-cbc-plain64-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Write test pattern 0xa7 @@ -723,7 +723,7 @@ unlink TEST_DIR/luks-aes-256-cbc-plain64-sha1.img # Create image truncate TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Write test pattern 0xa7 @@ -843,7 +843,7 @@ unlink TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img # Create image truncate TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-essiv:sha256 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-essiv:sha256 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Write test pattern 0xa7 @@ -963,7 +963,7 @@ unlink TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img # Create image truncate TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Write test pattern 0xa7 @@ -1083,7 +1083,7 @@ unlink TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img # Create image truncate TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Write test pattern 0xa7 @@ -1203,7 +1203,7 @@ unlink TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img # Create image truncate TEST_DIR/luks-twofish-128-xts-plain64-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher twofish-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Write test pattern 0xa7 @@ -1324,7 +1324,7 @@ Skipping twofish-192-xts-plain64-sha1 in blacklist # Create image truncate TEST_DIR/luks-serpent-128-xts-plain64-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher serpent-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Write test pattern 0xa7 @@ -1444,7 +1444,7 @@ unlink TEST_DIR/luks-serpent-128-xts-plain64-sha1.img # Create image truncate TEST_DIR/luks-serpent-192-xts-plain64-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher serpent-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Write test pattern 0xa7 @@ -1566,7 +1566,7 @@ Skipping cast6-192-xts-plain64-sha1 in blacklist # Create image truncate TEST_DIR/luks-aes-256-xts-plain64-sha224.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha224 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha224.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha224 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha224.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Write test pattern 0xa7 @@ -1686,7 +1686,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha224.img # Create image truncate TEST_DIR/luks-aes-256-xts-plain64-sha256.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha256 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha256.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha256 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha256.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Write test pattern 0xa7 @@ -1806,7 +1806,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha256.img # Create image truncate TEST_DIR/luks-aes-256-xts-plain64-sha384.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha384 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha384.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha384 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha384.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Write test pattern 0xa7 @@ -1926,7 +1926,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha384.img # Create image truncate TEST_DIR/luks-aes-256-xts-plain64-sha512.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha512 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha512.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha512 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha512.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Write test pattern 0xa7 @@ -2046,7 +2046,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha512.img # Create image truncate TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash ripemd160 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash ripemd160 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Write test pattern 0xa7 @@ -2166,7 +2166,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img # Create image truncate TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 3 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 3 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img qiotest-145-aes-256-xts-plain-sha1-pwslot3 # Write test pattern 0xa7 @@ -2226,7 +2226,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img # Create image truncate TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img # Add password slot 1 sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 1 --key-file - --iter-time 10 TEST_DIR/passwd.txt # Add password slot 2 @@ -2360,7 +2360,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img # Create image truncate TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Write test pattern 0xa7 @@ -2480,7 +2480,7 @@ unlink TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img # Create image truncate TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img --size 4194304MB # Format image -sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-plain64:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Write test pattern 0xa7 diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162 index 2d719af..c0053ed9 100755 --- a/tests/qemu-iotests/162 +++ b/tests/qemu-iotests/162 @@ -46,7 +46,7 @@ echo '=== NBD ===' # NBD expects all of its arguments to be strings # So this should not crash -$QEMU_IMG info 'json:{"driver": "nbd", "host": 42}' +$QEMU_IMG info 'json:{"driver": "nbd", "host": -1}' # And this should not treat @port as if it had not been specified # (We need to set up a server here, because the error message for "Connection diff --git a/tests/qemu-iotests/162.out b/tests/qemu-iotests/162.out index 3c5be2c..5a00d36 100644 --- a/tests/qemu-iotests/162.out +++ b/tests/qemu-iotests/162.out @@ -1,7 +1,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": -1}': address resolution failed for -1:10809: Name or service not known image: nbd://localhost:PORT image: nbd+unix://?socket=42 diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out index 3dd3ca5..9c09ee3 100644 --- a/tests/qemu-iotests/227.out +++ b/tests/qemu-iotests/227.out @@ -15,6 +15,8 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio { "device": "virtio0", "stats": { + "unmap_operations": 0, + "unmap_merged": 0, "flush_total_time_ns": 0, "wr_highest_offset": 0, "wr_total_time_ns": 0, @@ -24,13 +26,17 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio "wr_bytes": 0, "timed_stats": [ ], + "failed_unmap_operations": 0, "failed_flush_operations": 0, "account_invalid": true, "rd_total_time_ns": 0, + "invalid_unmap_operations": 0, "flush_operations": 0, "wr_operations": 0, + "unmap_bytes": 0, "rd_merged": 0, "rd_bytes": 0, + "unmap_total_time_ns": 0, "invalid_flush_operations": 0, "account_failed": true, "rd_operations": 0, @@ -74,6 +80,8 @@ Testing: -drive driver=null-co,if=none { "device": "none0", "stats": { + "unmap_operations": 0, + "unmap_merged": 0, "flush_total_time_ns": 0, "wr_highest_offset": 0, "wr_total_time_ns": 0, @@ -83,13 +91,17 @@ Testing: -drive driver=null-co,if=none "wr_bytes": 0, "timed_stats": [ ], + "failed_unmap_operations": 0, "failed_flush_operations": 0, "account_invalid": true, "rd_total_time_ns": 0, + "invalid_unmap_operations": 0, "flush_operations": 0, "wr_operations": 0, + "unmap_bytes": 0, "rd_merged": 0, "rd_bytes": 0, + "unmap_total_time_ns": 0, "invalid_flush_operations": 0, "account_failed": true, "rd_operations": 0, @@ -163,6 +175,8 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b { "device": "", "stats": { + "unmap_operations": 0, + "unmap_merged": 0, "flush_total_time_ns": 0, "wr_highest_offset": 0, "wr_total_time_ns": 0, @@ -172,13 +186,17 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b "wr_bytes": 0, "timed_stats": [ ], + "failed_unmap_operations": 0, "failed_flush_operations": 0, "account_invalid": false, "rd_total_time_ns": 0, + "invalid_unmap_operations": 0, "flush_operations": 0, "wr_operations": 0, + "unmap_bytes": 0, "rd_merged": 0, "rd_bytes": 0, + "unmap_total_time_ns": 0, "invalid_flush_operations": 0, "account_failed": false, "rd_operations": 0, diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 index 4a636d8..a982825 100755 --- a/tests/qemu-iotests/257 +++ b/tests/qemu-iotests/257 @@ -148,11 +148,6 @@ class Drive: self.fmt = None self.size = None self.node = None - self.device = None - - @property - def name(self): - return self.node or self.device def img_create(self, fmt, size): self.fmt = fmt @@ -188,25 +183,6 @@ class Drive: self.size = size self.node = name -def query_bitmaps(vm): - res = vm.qmp("query-block") - return {"bitmaps": {device['device'] or device['qdev']: - device.get('dirty-bitmaps', []) for - device in res['return']}} - -def get_bitmap(bitmaps, drivename, name, recording=None): - """ - get a specific bitmap from the object returned by query_bitmaps. - :param recording: If specified, filter results by the specified value. - """ - for bitmap in bitmaps['bitmaps'][drivename]: - if bitmap.get('name', '') == name: - if recording is None: - return bitmap - elif bitmap.get('recording') == recording: - return bitmap - return None - def blockdev_backup(vm, device, target, sync, **kwargs): # Strip any arguments explicitly nulled by the caller: kwargs = {key: val for key, val in kwargs.items() if val is not None} @@ -214,13 +190,14 @@ def blockdev_backup(vm, device, target, sync, **kwargs): device=device, target=target, sync=sync, + filter_node_name='backup-top', **kwargs) return result def blockdev_backup_mktarget(drive, target_id, filepath, sync, **kwargs): target_drive = Drive(filepath, vm=drive.vm) target_drive.create_target(target_id, drive.fmt, drive.size) - blockdev_backup(drive.vm, drive.name, target_id, sync, **kwargs) + blockdev_backup(drive.vm, drive.node, target_id, sync, **kwargs) def reference_backup(drive, n, filepath): log("--- Reference Backup #{:d} ---\n".format(n)) @@ -240,7 +217,7 @@ def backup(drive, n, filepath, sync, **kwargs): job_id=job_id, **kwargs) return job_id -def perform_writes(drive, n): +def perform_writes(drive, n, filter_node_name=None): log("--- Write #{:d} ---\n".format(n)) for pattern in GROUPS[n].patterns: cmd = "write -P{:s} 0x{:07x} 0x{:x}".format( @@ -248,9 +225,9 @@ def perform_writes(drive, n): pattern.offset, pattern.size) log(cmd) - log(drive.vm.hmp_qemu_io(drive.name, cmd)) - bitmaps = query_bitmaps(drive.vm) - log(bitmaps, indent=2) + log(drive.vm.hmp_qemu_io(filter_node_name or drive.node, cmd)) + bitmaps = drive.vm.query_bitmaps() + log({'bitmaps': bitmaps}, indent=2) log('') return bitmaps @@ -343,26 +320,19 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): }] } + drive0.node = 'drive0' vm.qmp_log('blockdev-add', filters=[iotests.filter_qmp_testfiles], - node_name="drive0", + node_name=drive0.node, driver=drive0.fmt, file=file_config) - drive0.node = 'drive0' - drive0.device = 'device0' - # Use share-rw to allow writes directly to the node; - # The anonymous block-backend for this configuration prevents us - # from using HMP's qemu-io commands to address the device. - vm.qmp_log("device_add", id=drive0.device, - drive=drive0.name, driver="scsi-hd", - share_rw=True) log('') # 0 - Writes and Reference Backup perform_writes(drive0, 0) reference_backup(drive0, 0, fbackup0) log('--- Add Bitmap ---\n') - vm.qmp_log("block-dirty-bitmap-add", node=drive0.name, + vm.qmp_log("block-dirty-bitmap-add", node=drive0.node, name="bitmap0", granularity=GRANULARITY) log('') ebitmap = EmulatedBitmap() @@ -370,14 +340,14 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): # 1 - Writes and Reference Backup bitmaps = perform_writes(drive0, 1) ebitmap.dirty_group(1) - bitmap = get_bitmap(bitmaps, drive0.device, 'bitmap0') + bitmap = vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps) ebitmap.compare(bitmap) reference_backup(drive0, 1, fbackup1) # 1 - Test Backup (w/ Optional induced failure) if failure == 'intermediate': # Activate blkdebug induced failure for second-to-next read - log(vm.hmp_qemu_io(drive0.name, 'flush')) + log(vm.hmp_qemu_io(drive0.node, 'flush')) log('') job = backup(drive0, 1, bsync1, msync_mode, bitmap="bitmap0", bitmap_mode=bsync_mode) @@ -386,14 +356,15 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): """Issue writes while the job is open to test bitmap divergence.""" # Note: when `failure` is 'intermediate', this isn't called. log('') - bitmaps = perform_writes(drive0, 2) + bitmaps = perform_writes(drive0, 2, filter_node_name='backup-top') # Named bitmap (static, should be unchanged) - ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0')) + ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', + bitmaps=bitmaps)) # Anonymous bitmap (dynamic, shows new writes) anonymous = EmulatedBitmap() anonymous.dirty_group(2) - anonymous.compare(get_bitmap(bitmaps, drive0.device, '', - recording=True)) + anonymous.compare(vm.get_bitmap(drive0.node, '', recording=True, + bitmaps=bitmaps)) # Simulate the order in which this will happen: # group 1 gets cleared first, then group two gets written. @@ -405,8 +376,8 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): vm.run_job(job, auto_dismiss=True, auto_finalize=False, pre_finalize=_callback, cancel=(failure == 'simulated')) - bitmaps = query_bitmaps(vm) - log(bitmaps, indent=2) + bitmaps = vm.query_bitmaps() + log({'bitmaps': bitmaps}, indent=2) log('') if bsync_mode == 'always' and failure == 'intermediate': @@ -423,29 +394,30 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): ebitmap.clear() ebitmap.dirty_bits(range(fail_bit, SIZE // GRANULARITY)) - ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0')) + ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps)) # 2 - Writes and Reference Backup bitmaps = perform_writes(drive0, 3) ebitmap.dirty_group(3) - ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0')) + ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps)) reference_backup(drive0, 2, fbackup2) # 2 - Bitmap Backup (In failure modes, this is a recovery.) job = backup(drive0, 2, bsync2, "bitmap", bitmap="bitmap0", bitmap_mode=bsync_mode) vm.run_job(job, auto_dismiss=True, auto_finalize=False) - bitmaps = query_bitmaps(vm) - log(bitmaps, indent=2) + bitmaps = vm.query_bitmaps() + log({'bitmaps': bitmaps}, indent=2) log('') if bsync_mode != 'never': ebitmap.clear() - ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0')) + ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps)) log('--- Cleanup ---\n') vm.qmp_log("block-dirty-bitmap-remove", - node=drive0.name, name="bitmap0") - log(query_bitmaps(vm), indent=2) + node=drive0.node, name="bitmap0") + bitmaps = vm.query_bitmaps() + log({'bitmaps': bitmaps}, indent=2) vm.shutdown() log('') @@ -484,22 +456,19 @@ def test_backup_api(): 'filename': drive0.path } + drive0.node = 'drive0' vm.qmp_log('blockdev-add', filters=[iotests.filter_qmp_testfiles], - node_name="drive0", + node_name=drive0.node, driver=drive0.fmt, file=file_config) - drive0.node = 'drive0' - drive0.device = 'device0' - vm.qmp_log("device_add", id=drive0.device, - drive=drive0.name, driver="scsi-hd") log('') target0 = Drive(backup_path, vm=vm) target0.create_target("backup_target", drive0.fmt, drive0.size) log('') - vm.qmp_log("block-dirty-bitmap-add", node=drive0.name, + vm.qmp_log("block-dirty-bitmap-add", node=drive0.node, name="bitmap0", granularity=GRANULARITY) log('') @@ -538,7 +507,7 @@ def test_backup_api(): log("-- Sync mode {:s} tests --\n".format(sync_mode)) for bitmap in (None, 'bitmap404', 'bitmap0'): for policy in error_cases[sync_mode][bitmap]: - blockdev_backup(drive0.vm, drive0.name, "backup_target", + blockdev_backup(drive0.vm, drive0.node, "backup_target", sync_mode, job_id='api_job', bitmap=bitmap, bitmap_mode=policy) log('') diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out index 84b79d7..64dd460 100644 --- a/tests/qemu-iotests/257.out +++ b/tests/qemu-iotests/257.out @@ -5,8 +5,6 @@ {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -19,9 +17,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -34,7 +30,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -55,7 +51,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -82,7 +78,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -96,7 +92,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -111,7 +107,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -153,7 +149,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 655360, @@ -182,7 +178,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 983040, @@ -209,7 +205,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -223,7 +219,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -231,7 +227,7 @@ expecting 15 dirty sectors; have 15. OK! {"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 983040, @@ -253,9 +249,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -271,8 +265,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -285,9 +277,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -300,7 +290,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -321,7 +311,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -348,7 +338,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -364,13 +354,13 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} {"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -399,7 +389,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 917504, @@ -426,7 +416,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -440,7 +430,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -448,7 +438,7 @@ expecting 14 dirty sectors; have 14. OK! {"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 917504, @@ -470,9 +460,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -488,8 +476,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -502,9 +488,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -517,7 +501,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -538,7 +522,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -565,7 +549,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -579,7 +563,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -594,7 +578,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -636,7 +620,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 655360, @@ -665,7 +649,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 983040, @@ -692,7 +676,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -706,7 +690,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -714,7 +698,7 @@ expecting 15 dirty sectors; have 15. OK! {"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 983040, @@ -736,9 +720,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -754,8 +736,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -768,9 +748,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -783,7 +761,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -804,7 +782,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -831,7 +809,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -845,7 +823,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -860,7 +838,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -902,7 +880,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 655360, @@ -931,7 +909,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 983040, @@ -958,7 +936,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -972,7 +950,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -980,7 +958,7 @@ expecting 15 dirty sectors; have 15. OK! {"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -1002,9 +980,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -1020,8 +996,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -1034,9 +1008,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -1049,7 +1021,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1070,7 +1042,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -1097,7 +1069,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1113,13 +1085,13 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} {"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -1148,7 +1120,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 917504, @@ -1175,7 +1147,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1189,7 +1161,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -1197,7 +1169,7 @@ expecting 14 dirty sectors; have 14. OK! {"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -1219,9 +1191,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -1237,8 +1207,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -1251,9 +1219,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -1266,7 +1232,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1287,7 +1253,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -1314,7 +1280,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1328,7 +1294,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -1343,7 +1309,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -1385,7 +1351,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 458752, @@ -1414,7 +1380,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 786432, @@ -1441,7 +1407,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1455,7 +1421,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -1463,7 +1429,7 @@ expecting 12 dirty sectors; have 12. OK! {"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -1485,9 +1451,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -1503,8 +1467,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -1517,9 +1479,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -1532,7 +1492,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1553,7 +1513,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -1580,7 +1540,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1594,7 +1554,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -1609,7 +1569,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -1651,7 +1611,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 458752, @@ -1680,7 +1640,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 786432, @@ -1707,7 +1667,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1721,7 +1681,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -1729,7 +1689,7 @@ expecting 12 dirty sectors; have 12. OK! {"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -1751,9 +1711,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -1769,8 +1727,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -1783,9 +1739,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -1798,7 +1752,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1819,7 +1773,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -1846,7 +1800,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1862,13 +1816,13 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} {"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 327680, @@ -1897,7 +1851,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 851968, @@ -1924,7 +1878,7 @@ expecting 13 dirty sectors; have 13. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -1938,7 +1892,7 @@ expecting 13 dirty sectors; have 13. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -1946,7 +1900,7 @@ expecting 13 dirty sectors; have 13. OK! {"data": {"device": "backup_2", "len": 851968, "offset": 851968, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -1968,9 +1922,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -1986,8 +1938,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -2000,9 +1950,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -2015,7 +1963,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2036,7 +1984,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -2063,7 +2011,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2077,7 +2025,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -2092,7 +2040,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -2134,7 +2082,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 458752, @@ -2163,7 +2111,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 786432, @@ -2190,7 +2138,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2204,7 +2152,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -2212,7 +2160,7 @@ expecting 12 dirty sectors; have 12. OK! {"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -2234,9 +2182,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -2252,8 +2198,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -2266,9 +2210,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -2281,7 +2223,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2302,7 +2244,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -2329,7 +2271,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2343,7 +2285,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -2358,7 +2300,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -2400,7 +2342,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 655360, @@ -2429,7 +2371,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 983040, @@ -2456,7 +2398,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2470,7 +2412,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -2478,7 +2420,7 @@ expecting 15 dirty sectors; have 15. OK! {"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -2500,9 +2442,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -2518,8 +2458,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -2532,9 +2470,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -2547,7 +2483,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2568,7 +2504,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -2595,7 +2531,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2611,13 +2547,13 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} {"return": {}} {"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "backup_1", "error": "Input/output error", "len": 67108864, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -2646,7 +2582,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 917504, @@ -2673,7 +2609,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2687,7 +2623,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -2695,7 +2631,7 @@ expecting 14 dirty sectors; have 14. OK! {"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -2717,9 +2653,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -2735,8 +2669,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -2749,9 +2681,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -2764,7 +2694,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2785,7 +2715,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -2812,7 +2742,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2826,7 +2756,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -2841,7 +2771,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -2883,7 +2813,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 458752, @@ -2912,7 +2842,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 786432, @@ -2939,7 +2869,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -2953,7 +2883,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -2961,7 +2891,7 @@ expecting 12 dirty sectors; have 12. OK! {"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -2983,9 +2913,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -3001,8 +2929,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -3015,9 +2941,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -3030,7 +2954,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3051,7 +2975,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -3078,7 +3002,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3092,7 +3016,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -3107,7 +3031,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -3149,7 +3073,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 458752, @@ -3178,7 +3102,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 786432, @@ -3205,7 +3129,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3219,7 +3143,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -3227,7 +3151,7 @@ expecting 12 dirty sectors; have 12. OK! {"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -3249,9 +3173,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -3267,8 +3189,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -3281,9 +3201,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -3296,7 +3214,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3317,7 +3235,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -3344,7 +3262,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3360,13 +3278,13 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} {"return": {}} {"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "backup_1", "error": "Input/output error", "len": 67108864, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 66125824, @@ -3395,7 +3313,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 66453504, @@ -3422,7 +3340,7 @@ expecting 1014 dirty sectors; have 1014. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3436,7 +3354,7 @@ expecting 1014 dirty sectors; have 1014. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -3444,7 +3362,7 @@ expecting 1014 dirty sectors; have 1014. OK! {"data": {"device": "backup_2", "len": 66453504, "offset": 66453504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -3466,9 +3384,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -3484,8 +3400,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -3498,9 +3412,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -3513,7 +3425,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3534,7 +3446,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -3561,7 +3473,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3575,7 +3487,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -3590,7 +3502,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -3632,7 +3544,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 458752, @@ -3661,7 +3573,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 786432, @@ -3688,7 +3600,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3702,7 +3614,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -3710,7 +3622,7 @@ expecting 12 dirty sectors; have 12. OK! {"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -3732,9 +3644,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -3750,8 +3660,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -3764,9 +3672,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -3779,7 +3685,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3800,7 +3706,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -3827,7 +3733,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3841,7 +3747,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -3856,7 +3762,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -3898,7 +3804,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 655360, @@ -3927,7 +3833,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 983040, @@ -3954,7 +3860,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -3968,7 +3874,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -3976,7 +3882,7 @@ expecting 15 dirty sectors; have 15. OK! {"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -3998,9 +3904,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -4016,8 +3920,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -4030,9 +3932,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -4045,7 +3945,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4066,7 +3966,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -4093,7 +3993,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4109,13 +4009,13 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} {"return": {}} {"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "backup_1", "error": "Input/output error", "len": 458752, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -4144,7 +4044,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 917504, @@ -4171,7 +4071,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4185,7 +4085,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -4193,7 +4093,7 @@ expecting 14 dirty sectors; have 14. OK! {"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -4215,9 +4115,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -4233,8 +4131,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -4247,9 +4143,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -4262,7 +4156,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4283,7 +4177,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -4310,7 +4204,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4324,7 +4218,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -4339,7 +4233,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -4381,7 +4275,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 458752, @@ -4410,7 +4304,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 786432, @@ -4437,7 +4331,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4451,7 +4345,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -4459,7 +4353,7 @@ expecting 12 dirty sectors; have 12. OK! {"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -4481,9 +4375,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -4499,8 +4391,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -4513,9 +4403,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -4528,7 +4416,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4549,7 +4437,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -4576,7 +4464,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4590,7 +4478,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -4605,7 +4493,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -4647,7 +4535,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 458752, @@ -4676,7 +4564,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 786432, @@ -4703,7 +4591,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4717,7 +4605,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -4725,7 +4613,7 @@ expecting 12 dirty sectors; have 12. OK! {"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -4747,9 +4635,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -4765,8 +4651,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -4779,9 +4663,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -4794,7 +4676,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4815,7 +4697,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -4842,7 +4724,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4858,13 +4740,13 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} {"return": {}} {"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "backup_1", "error": "Input/output error", "len": 458752, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -4893,7 +4775,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 917504, @@ -4920,7 +4802,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -4934,7 +4816,7 @@ expecting 14 dirty sectors; have 14. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -4942,7 +4824,7 @@ expecting 14 dirty sectors; have 14. OK! {"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -4964,9 +4846,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -4982,8 +4862,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} -{"return": {}} --- Write #0 --- @@ -4996,9 +4874,7 @@ write -P0x6f 0x2000000 0x10000 write -P0x76 0x3ff0000 0x10000 {"return": ""} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Reference Backup #0 --- @@ -5011,7 +4887,7 @@ write -P0x76 0x3ff0000 0x10000 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}} {"return": {}} {"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -5032,7 +4908,7 @@ write -P0x69 0x3fe0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 393216, @@ -5059,7 +4935,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}} {"return": {}} {"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -5073,7 +4949,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}} {"return": {}} --- Write #2 --- @@ -5088,7 +4964,7 @@ write -P0x67 0x3fe0000 0x20000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -5130,7 +5006,7 @@ expecting 7 dirty sectors; have 7. OK! {"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 458752, @@ -5159,7 +5035,7 @@ write -P0xdd 0x3fc0000 0x10000 {"return": ""} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 786432, @@ -5186,7 +5062,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}} {"return": {}} {"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -5200,7 +5076,7 @@ expecting 12 dirty sectors; have 12. OK! {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} {"return": {}} {} -{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} +{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}} {"return": {}} {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} @@ -5208,7 +5084,7 @@ expecting 12 dirty sectors; have 12. OK! {"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { - "device0": [ + "drive0": [ { "busy": false, "count": 0, @@ -5230,9 +5106,7 @@ expecting 0 dirty sectors; have 0. OK! {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}} {"return": {}} { - "bitmaps": { - "device0": [] - } + "bitmaps": {} } --- Verification --- @@ -5248,8 +5122,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! {"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} {"return": {}} -{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0"}} -{"return": {}} {} {"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}} @@ -5267,155 +5139,155 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! -- Sync mode incremental tests -- -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}} -- Sync mode bitmap tests -- -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}} -{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}} -- Sync mode full tests -- -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode 'never' has no meaningful effect when combined with sync mode 'full'"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}} -- Sync mode top tests -- -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode 'never' has no meaningful effect when combined with sync mode 'top'"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}} -- Sync mode none tests -- -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}} -{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}} -{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}} +{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}} {"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}} diff --git a/tests/qemu-iotests/268 b/tests/qemu-iotests/268 new file mode 100755 index 0000000..78c3f4d --- /dev/null +++ b/tests/qemu-iotests/268 @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# +# Test write request with required alignment larger than the cluster size +# +# Copyright (C) 2019 Igalia, S.L. +# Author: Alberto Garcia <berto@igalia.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=berto@igalia.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file + +echo +echo "== Required alignment larger than cluster size ==" + +CLUSTER_SIZE=2k _make_test_img 1M +# Since commit c8bb23cbdb writing to an unallocated cluster fills the +# empty COW areas with bdrv_write_zeroes(flags=BDRV_REQ_NO_FALLBACK) +$QEMU_IO -c "open -o driver=$IMGFMT,file.align=4k blkdebug::$TEST_IMG" \ + -c "write 0 512" | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/268.out b/tests/qemu-iotests/268.out new file mode 100644 index 0000000..2ed6c68 --- /dev/null +++ b/tests/qemu-iotests/268.out @@ -0,0 +1,7 @@ +QA output created by 268 + +== Required alignment larger than cluster size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/270 b/tests/qemu-iotests/270 new file mode 100755 index 0000000..b9a12b9 --- /dev/null +++ b/tests/qemu-iotests/270 @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# +# Test large write to a qcow2 image +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +seq=$(basename "$0") +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This is a qcow2 regression test +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +# We use our own external data file and our own cluster size, and we +# require v3 images +_unsupported_imgopts data_file cluster_size 'compat=0.10' + + +# We need a backing file so that handle_alloc_space() will not do +# anything. (If it were to do anything, it would simply fail its +# write-zeroes request because the request range is too large.) +TEST_IMG="$TEST_IMG.base" _make_test_img 4G +$QEMU_IO -c 'write 0 512' "$TEST_IMG.base" | _filter_qemu_io + +# (Use .orig because _cleanup_test_img will remove that file) +# We need a large cluster size, see below for why (above the $QEMU_IO +# invocation) +_make_test_img -o cluster_size=2M,data_file="$TEST_IMG.orig" \ + -b "$TEST_IMG.base" 4G + +# We want a null-co as the data file, because it allows us to quickly +# "write" 2G of data without using any space. +# (qemu-img create does not like it, though, because null-co does not +# support image creation.) +$QEMU_IMG amend -o data_file="json:{'driver':'null-co',,'size':'4294967296'}" \ + "$TEST_IMG" + +# This gives us a range of: +# 2^31 - 512 + 768 - 1 = 2^31 + 255 > 2^31 +# until the beginning of the end COW block. (The total allocation +# size depends on the cluster size, but all that is important is that +# it exceeds INT_MAX.) +# +# 2^31 - 512 is the maximum request size. We want this to result in a +# single allocation, and because the qcow2 driver splits allocations +# on L2 boundaries, we need large L2 tables; hence the cluster size of +# 2 MB. (Anything from 256 kB should work, though, because then one L2 +# table covers 8 GB.) +$QEMU_IO -c "write 768 $((2 ** 31 - 512))" "$TEST_IMG" | _filter_qemu_io + +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/270.out b/tests/qemu-iotests/270.out new file mode 100644 index 0000000..c7be111 --- /dev/null +++ b/tests/qemu-iotests/270.out @@ -0,0 +1,9 @@ +QA output created by 270 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base data_file=TEST_DIR/t.IMGFMT.orig +wrote 2147483136/2147483136 bytes at offset 768 +2 GiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index e45cdfa..12b4751 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -405,6 +405,23 @@ _check_test_img() $QEMU_IMG check "$@" -f $IMGFMT "$TEST_IMG" 2>&1 fi ) | _filter_testdir | _filter_qemu_img_check + + # return real qemu_img check status, to analyze in + # _check_test_img_ignore_leaks + return ${PIPESTATUS[0]} +} + +_check_test_img_ignore_leaks() +{ + out=$(_check_test_img "$@") + status=$? + if [ $status = 3 ]; then + # This must correspond to success output in dump_human_image_check() + echo "No errors were found on the image." + return 0 + fi + echo "$out" + return $status } _img_info() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 5805a79..7dac79a 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -278,3 +278,5 @@ 265 rw auto quick 266 rw quick 267 rw auto quick snapshot +268 rw auto quick +270 rw backing quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 9fb5181..3a8f378 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -641,6 +641,33 @@ class VM(qtest.QEMUQtestMachine): return x return None + def query_bitmaps(self): + res = self.qmp("query-named-block-nodes") + return {device['node-name']: device['dirty-bitmaps'] + for device in res['return'] if 'dirty-bitmaps' in device} + + def get_bitmap(self, node_name, bitmap_name, recording=None, bitmaps=None): + """ + get a specific bitmap from the object returned by query_bitmaps. + :param recording: If specified, filter results by the specified value. + :param bitmaps: If specified, use it instead of call query_bitmaps() + """ + if bitmaps is None: + bitmaps = self.query_bitmaps() + + for bitmap in bitmaps[node_name]: + if bitmap.get('name', '') == bitmap_name: + if recording is None: + return bitmap + elif bitmap.get('recording') == recording: + return bitmap + return None + + def check_bitmap_status(self, node_name, bitmap_name, fields): + ret = self.get_bitmap(node_name, bitmap_name) + + return fields.items() <= ret.items() + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index e80c4c61..1e5be1d 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -812,6 +812,423 @@ static void test_load_q(void) qemu_fclose(fload); } +/* interval (key) */ +typedef struct TestGTreeInterval { + uint64_t low; + uint64_t high; +} TestGTreeInterval; + +#define VMSTATE_INTERVAL \ +{ \ + .name = "interval", \ + .version_id = 1, \ + .minimum_version_id = 1, \ + .fields = (VMStateField[]) { \ + VMSTATE_UINT64(low, TestGTreeInterval), \ + VMSTATE_UINT64(high, TestGTreeInterval), \ + VMSTATE_END_OF_LIST() \ + } \ +} + +/* mapping (value) */ +typedef struct TestGTreeMapping { + uint64_t phys_addr; + uint32_t flags; +} TestGTreeMapping; + +#define VMSTATE_MAPPING \ +{ \ + .name = "mapping", \ + .version_id = 1, \ + .minimum_version_id = 1, \ + .fields = (VMStateField[]) { \ + VMSTATE_UINT64(phys_addr, TestGTreeMapping), \ + VMSTATE_UINT32(flags, TestGTreeMapping), \ + VMSTATE_END_OF_LIST() \ + }, \ +} + +static const VMStateDescription vmstate_interval_mapping[2] = { + VMSTATE_MAPPING, /* value */ + VMSTATE_INTERVAL /* key */ +}; + +typedef struct TestGTreeDomain { + int32_t id; + GTree *mappings; +} TestGTreeDomain; + +typedef struct TestGTreeIOMMU { + int32_t id; + GTree *domains; +} TestGTreeIOMMU; + +/* Interval comparison function */ +static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + TestGTreeInterval *inta = (TestGTreeInterval *)a; + TestGTreeInterval *intb = (TestGTreeInterval *)b; + + if (inta->high < intb->low) { + return -1; + } else if (intb->high < inta->low) { + return 1; + } else { + return 0; + } +} + +/* ID comparison function */ +static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + uint ua = GPOINTER_TO_UINT(a); + uint ub = GPOINTER_TO_UINT(b); + return (ua > ub) - (ua < ub); +} + +static void destroy_domain(gpointer data) +{ + TestGTreeDomain *domain = (TestGTreeDomain *)data; + + g_tree_destroy(domain->mappings); + g_free(domain); +} + +static int domain_preload(void *opaque) +{ + TestGTreeDomain *domain = opaque; + + domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, + NULL, g_free, g_free); + return 0; +} + +static int iommu_preload(void *opaque) +{ + TestGTreeIOMMU *iommu = opaque; + + iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, + NULL, NULL, destroy_domain); + return 0; +} + +static const VMStateDescription vmstate_domain = { + .name = "domain", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = domain_preload, + .fields = (VMStateField[]) { + VMSTATE_INT32(id, TestGTreeDomain), + VMSTATE_GTREE_V(mappings, TestGTreeDomain, 1, + vmstate_interval_mapping, + TestGTreeInterval, TestGTreeMapping), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_iommu = { + .name = "iommu", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = iommu_preload, + .fields = (VMStateField[]) { + VMSTATE_INT32(id, TestGTreeIOMMU), + VMSTATE_GTREE_DIRECT_KEY_V(domains, TestGTreeIOMMU, 1, + &vmstate_domain, TestGTreeDomain), + VMSTATE_END_OF_LIST() + } +}; + +uint8_t first_domain_dump[] = { + /* id */ + 0x00, 0x0, 0x0, 0x6, + 0x00, 0x0, 0x0, 0x2, /* 2 mappings */ + 0x1, /* start of a */ + /* a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, + /* map_a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x1, /* start of b */ + /* b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF, + /* map_b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x0, /* end of gtree */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static TestGTreeDomain *create_first_domain(void) +{ + TestGTreeDomain *domain; + TestGTreeMapping *map_a, *map_b; + TestGTreeInterval *a, *b; + + domain = g_malloc0(sizeof(TestGTreeDomain)); + domain->id = 6; + + a = g_malloc0(sizeof(TestGTreeInterval)); + a->low = 0x1000; + a->high = 0x1FFF; + + b = g_malloc0(sizeof(TestGTreeInterval)); + b->low = 0x4000; + b->high = 0x4FFF; + + map_a = g_malloc0(sizeof(TestGTreeMapping)); + map_a->phys_addr = 0xa000; + map_a->flags = 1; + + map_b = g_malloc0(sizeof(TestGTreeMapping)); + map_b->phys_addr = 0xe0000; + map_b->flags = 2; + + domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, NULL, + (GDestroyNotify)g_free, + (GDestroyNotify)g_free); + g_tree_insert(domain->mappings, a, map_a); + g_tree_insert(domain->mappings, b, map_b); + return domain; +} + +static void test_gtree_save_domain(void) +{ + TestGTreeDomain *first_domain = create_first_domain(); + + save_vmstate(&vmstate_domain, first_domain); + compare_vmstate(first_domain_dump, sizeof(first_domain_dump)); + destroy_domain(first_domain); +} + +struct match_node_data { + GTree *tree; + gpointer key; + gpointer value; +}; + +struct tree_cmp_data { + GTree *tree1; + GTree *tree2; + GTraverseFunc match_node; +}; + +static gboolean match_interval_mapping_node(gpointer key, + gpointer value, gpointer data) +{ + TestGTreeMapping *map_a, *map_b; + TestGTreeInterval *a, *b; + struct match_node_data *d = (struct match_node_data *)data; + char *str = g_strdup_printf("dest"); + + g_free(str); + a = (TestGTreeInterval *)key; + b = (TestGTreeInterval *)d->key; + + map_a = (TestGTreeMapping *)value; + map_b = (TestGTreeMapping *)d->value; + + assert(a->low == b->low); + assert(a->high == b->high); + assert(map_a->phys_addr == map_b->phys_addr); + assert(map_a->flags == map_b->flags); + g_tree_remove(d->tree, key); + return true; +} + +static gboolean diff_tree(gpointer key, gpointer value, gpointer data) +{ + struct tree_cmp_data *tp = (struct tree_cmp_data *)data; + struct match_node_data d = {tp->tree2, key, value}; + + g_tree_foreach(tp->tree2, tp->match_node, &d); + g_tree_remove(tp->tree1, key); + return false; +} + +static void compare_trees(GTree *tree1, GTree *tree2, + GTraverseFunc function) +{ + struct tree_cmp_data tp = {tree1, tree2, function}; + + g_tree_foreach(tree1, diff_tree, &tp); + assert(g_tree_nnodes(tree1) == 0); + assert(g_tree_nnodes(tree2) == 0); +} + +static void diff_domain(TestGTreeDomain *d1, TestGTreeDomain *d2) +{ + assert(d1->id == d2->id); + compare_trees(d1->mappings, d2->mappings, match_interval_mapping_node); +} + +static gboolean match_domain_node(gpointer key, gpointer value, gpointer data) +{ + uint64_t id1, id2; + TestGTreeDomain *d1, *d2; + struct match_node_data *d = (struct match_node_data *)data; + + id1 = (uint64_t)(uintptr_t)key; + id2 = (uint64_t)(uintptr_t)d->key; + d1 = (TestGTreeDomain *)value; + d2 = (TestGTreeDomain *)d->value; + assert(id1 == id2); + diff_domain(d1, d2); + g_tree_remove(d->tree, key); + return true; +} + +static void diff_iommu(TestGTreeIOMMU *iommu1, TestGTreeIOMMU *iommu2) +{ + assert(iommu1->id == iommu2->id); + compare_trees(iommu1->domains, iommu2->domains, match_domain_node); +} + +static void test_gtree_load_domain(void) +{ + TestGTreeDomain *dest_domain = g_malloc0(sizeof(TestGTreeDomain)); + TestGTreeDomain *orig_domain = create_first_domain(); + QEMUFile *fload, *fsave; + char eof; + + fsave = open_test_file(true); + qemu_put_buffer(fsave, first_domain_dump, sizeof(first_domain_dump)); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + fload = open_test_file(false); + + vmstate_load_state(fload, &vmstate_domain, dest_domain, 1); + eof = qemu_get_byte(fload); + g_assert(!qemu_file_get_error(fload)); + g_assert_cmpint(orig_domain->id, ==, dest_domain->id); + g_assert_cmpint(eof, ==, QEMU_VM_EOF); + + diff_domain(orig_domain, dest_domain); + destroy_domain(orig_domain); + destroy_domain(dest_domain); + qemu_fclose(fload); +} + +uint8_t iommu_dump[] = { + /* iommu id */ + 0x00, 0x0, 0x0, 0x7, + 0x00, 0x0, 0x0, 0x2, /* 2 domains */ + 0x1,/* start of domain 5 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x5, /* key = 5 */ + 0x00, 0x0, 0x0, 0x5, /* domain1 id */ + 0x00, 0x0, 0x0, 0x1, /* 1 mapping */ + 0x1, /* start of mappings */ + /* c */ + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, + /* map_c */ + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x00, 0x0, 0x0, 0x3, + 0x0, /* end of domain1 mappings*/ + 0x1,/* start of domain 6 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x6, /* key = 6 */ + 0x00, 0x0, 0x0, 0x6, /* domain6 id */ + 0x00, 0x0, 0x0, 0x2, /* 2 mappings */ + 0x1, /* start of a */ + /* a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, + /* map_a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x1, /* start of b */ + /* b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF, + /* map_b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x0, /* end of domain6 mappings*/ + 0x0, /* end of domains */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static TestGTreeIOMMU *create_iommu(void) +{ + TestGTreeIOMMU *iommu = g_malloc0(sizeof(TestGTreeIOMMU)); + TestGTreeDomain *first_domain = create_first_domain(); + TestGTreeDomain *second_domain; + TestGTreeMapping *map_c; + TestGTreeInterval *c; + + iommu->id = 7; + iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, NULL, + NULL, + destroy_domain); + + second_domain = g_malloc0(sizeof(TestGTreeDomain)); + second_domain->id = 5; + second_domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, + NULL, + (GDestroyNotify)g_free, + (GDestroyNotify)g_free); + + g_tree_insert(iommu->domains, GUINT_TO_POINTER(6), first_domain); + g_tree_insert(iommu->domains, (gpointer)0x0000000000000005, second_domain); + + c = g_malloc0(sizeof(TestGTreeInterval)); + c->low = 0x1000000; + c->high = 0x1FFFFFF; + + map_c = g_malloc0(sizeof(TestGTreeMapping)); + map_c->phys_addr = 0xF000000; + map_c->flags = 0x3; + + g_tree_insert(second_domain->mappings, c, map_c); + return iommu; +} + +static void destroy_iommu(TestGTreeIOMMU *iommu) +{ + g_tree_destroy(iommu->domains); + g_free(iommu); +} + +static void test_gtree_save_iommu(void) +{ + TestGTreeIOMMU *iommu = create_iommu(); + + save_vmstate(&vmstate_iommu, iommu); + compare_vmstate(iommu_dump, sizeof(iommu_dump)); + destroy_iommu(iommu); +} + +static void test_gtree_load_iommu(void) +{ + TestGTreeIOMMU *dest_iommu = g_malloc0(sizeof(TestGTreeIOMMU)); + TestGTreeIOMMU *orig_iommu = create_iommu(); + QEMUFile *fsave, *fload; + char eof; + int ret; + + fsave = open_test_file(true); + qemu_put_buffer(fsave, iommu_dump, sizeof(iommu_dump)); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + fload = open_test_file(false); + vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1); + ret = qemu_file_get_error(fload); + eof = qemu_get_byte(fload); + ret = qemu_file_get_error(fload); + g_assert(!ret); + g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id); + g_assert_cmpint(eof, ==, QEMU_VM_EOF); + + diff_iommu(orig_iommu, dest_iommu); + destroy_iommu(orig_iommu); + destroy_iommu(dest_iommu); + qemu_fclose(fload); +} + typedef struct TmpTestStruct { TestStruct *parent; int64_t diff; @@ -932,6 +1349,10 @@ int main(int argc, char **argv) test_arr_ptr_prim_0_load); g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q); g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q); + g_test_add_func("/vmstate/gtree/save/savedomain", test_gtree_save_domain); + g_test_add_func("/vmstate/gtree/load/loaddomain", test_gtree_load_domain); + g_test_add_func("/vmstate/gtree/save/saveiommu", test_gtree_save_iommu); + g_test_add_func("/vmstate/gtree/load/loadiommu", test_gtree_load_iommu); g_test_add_func("/vmstate/tmp_struct", test_tmp_struct); g_test_run(); @@ -1203,7 +1203,7 @@ static void configure_blockdev(BlockdevOptionsQueue *bdo_queue, qapi_free_BlockdevOptions(bdo->bdo); g_free(bdo); } - if (snapshot || replay_mode != REPLAY_MODE_NONE) { + if (snapshot) { qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, NULL); } @@ -2649,57 +2649,7 @@ static int machine_set_property(void *opaque, */ static bool object_create_initial(const char *type, QemuOpts *opts) { - ObjectClass *klass; - - if (is_help_option(type)) { - GSList *l, *list; - - printf("List of user creatable objects:\n"); - list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false); - for (l = list; l != NULL; l = l->next) { - ObjectClass *oc = OBJECT_CLASS(l->data); - printf(" %s\n", object_class_get_name(oc)); - } - g_slist_free(list); - exit(0); - } - - klass = object_class_by_name(type); - if (klass && qemu_opt_has_help_opt(opts)) { - ObjectPropertyIterator iter; - ObjectProperty *prop; - GPtrArray *array = g_ptr_array_new(); - int i; - - object_class_property_iter_init(&iter, klass); - while ((prop = object_property_iter_next(&iter))) { - GString *str; - - if (!prop->set) { - continue; - } - - str = g_string_new(NULL); - g_string_append_printf(str, " %s=<%s>", prop->name, prop->type); - if (prop->description) { - if (str->len < 24) { - g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); - } - g_string_append_printf(str, " - %s", prop->description); - } - g_ptr_array_add(array, g_string_free(str, false)); - } - g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); - if (array->len > 0) { - printf("%s options:\n", type); - } else { - printf("There are no options for %s.\n", type); - } - for (i = 0; i < array->len; i++) { - printf("%s\n", (char *)array->pdata[i]); - } - g_ptr_array_set_free_func(array, g_free); - g_ptr_array_free(array, true); + if (user_creatable_print_help(type, opts)) { exit(0); } @@ -3066,7 +3016,13 @@ int main(int argc, char **argv, char **envp) drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS); break; case QEMU_OPTION_snapshot: - snapshot = 1; + { + Error *blocker = NULL; + snapshot = 1; + error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, + "-snapshot"); + replay_add_blocker(blocker); + } break; case QEMU_OPTION_numa: opts = qemu_opts_parse_noisily(qemu_find_opts("numa"), @@ -3335,7 +3291,8 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_virtfs: { QemuOpts *fsdev; QemuOpts *device; - const char *writeout, *sock_fd, *socket, *path, *security_model; + const char *writeout, *sock_fd, *socket, *path, *security_model, + *multidevs; olist = qemu_find_opts("virtfs"); if (!olist) { @@ -3395,6 +3352,10 @@ int main(int argc, char **argv, char **envp) qemu_opt_set_bool(fsdev, "readonly", qemu_opt_get_bool(opts, "readonly", 0), &error_abort); + multidevs = qemu_opt_get(opts, "multidevs"); + if (multidevs) { + qemu_opt_set(fsdev, "multidevs", multidevs, &error_abort); + } device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, &error_abort); qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort); @@ -4513,6 +4474,7 @@ int main(int argc, char **argv, char **envp) /* No more vcpu or device emulation activity beyond this point */ vm_shutdown(); + replay_finish(); job_cancel_sync_all(); bdrv_close_all(); |