diff options
164 files changed, 6783 insertions, 3410 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index e187b1f..bf482fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1314,6 +1314,27 @@ S: Maintained F: include/hw/misc/unimp.h F: hw/misc/unimp.c +Standard VGA +M: Gerd Hoffmann <kraxel@redhat.com> +S: Maintained +F: hw/display/vga* +F: hw/display/bochs-display.c +F: include/hw/display/vga.h +F: include/hw/display/bochs-vbe.h + +virtio-gpu +M: Gerd Hoffmann <kraxel@redhat.com> +S: Maintained +F: hw/display/virtio-gpu* +F: hw/display/virtio-vga.c +F: include/hw/virtio/virtio-gpu.h + +Cirrus VGA +M: Gerd Hoffmann <kraxel@redhat.com> +S: Odd Fixes +W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/ +F: hw/display/cirrus* + Subsystems ---------- Audio @@ -1369,10 +1390,14 @@ L: qemu-block@nongnu.org S: Supported F: blockjob.c F: include/block/blockjob.h +F: job.c +F: job-qmp.c +F: include/block/job.h F: block/backup.c F: block/commit.c F: block/stream.c F: block/mirror.c +F: qapi/job.json T: git git://github.com/codyprime/qemu-kvm-jtc.git block Block QAPI, monitor, command line @@ -1783,6 +1808,12 @@ F: include/sysemu/replay.h F: docs/replay.txt F: stubs/replay.c +IOVA Tree +M: Peter Xu <peterx@redhat.com> +S: Maintained +F: include/qemu/iova-tree.h +F: util/iova-tree.c + Usermode Emulation ------------------ Overall @@ -98,6 +98,7 @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c +GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c @@ -116,6 +117,7 @@ GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c +GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c @@ -133,6 +135,7 @@ GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c +GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c @@ -150,6 +153,7 @@ GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c +GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c @@ -582,6 +586,7 @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/char.json \ $(SRC_PATH)/qapi/crypto.json \ $(SRC_PATH)/qapi/introspect.json \ + $(SRC_PATH)/qapi/job.json \ $(SRC_PATH)/qapi/migration.json \ $(SRC_PATH)/qapi/misc.json \ $(SRC_PATH)/qapi/net.json \ @@ -601,6 +606,7 @@ qapi/qapi-types-char.c qapi/qapi-types-char.h \ qapi/qapi-types-common.c qapi/qapi-types-common.h \ qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ +qapi/qapi-types-job.c qapi/qapi-types-job.h \ qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ qapi/qapi-types-net.c qapi/qapi-types-net.h \ @@ -619,6 +625,7 @@ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ +qapi/qapi-visit-job.c qapi/qapi-visit-job.h \ qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ @@ -636,6 +643,7 @@ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ +qapi/qapi-commands-job.c qapi/qapi-commands-job.h \ qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ @@ -653,6 +661,7 @@ qapi/qapi-events-char.c qapi/qapi-events-char.h \ qapi/qapi-events-common.c qapi/qapi-events-common.h \ qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ +qapi/qapi-events-job.c qapi/qapi-events-job.h \ qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ qapi/qapi-events-net.c qapi/qapi-events-net.h \ diff --git a/Makefile.objs b/Makefile.objs index c6c9b8f..c6c3554 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -10,6 +10,7 @@ util-obj-y += qapi/qapi-types-char.o util-obj-y += qapi/qapi-types-common.o util-obj-y += qapi/qapi-types-crypto.o util-obj-y += qapi/qapi-types-introspect.o +util-obj-y += qapi/qapi-types-job.o util-obj-y += qapi/qapi-types-migration.o util-obj-y += qapi/qapi-types-misc.o util-obj-y += qapi/qapi-types-net.o @@ -28,6 +29,7 @@ util-obj-y += qapi/qapi-visit-char.o util-obj-y += qapi/qapi-visit-common.o util-obj-y += qapi/qapi-visit-crypto.o util-obj-y += qapi/qapi-visit-introspect.o +util-obj-y += qapi/qapi-visit-job.o util-obj-y += qapi/qapi-visit-migration.o util-obj-y += qapi/qapi-visit-misc.o util-obj-y += qapi/qapi-visit-net.o @@ -45,6 +47,7 @@ util-obj-y += qapi/qapi-events-char.o util-obj-y += qapi/qapi-events-common.o util-obj-y += qapi/qapi-events-crypto.o util-obj-y += qapi/qapi-events-introspect.o +util-obj-y += qapi/qapi-events-job.o util-obj-y += qapi/qapi-events-migration.o util-obj-y += qapi/qapi-events-misc.o util-obj-y += qapi/qapi-events-net.o @@ -63,7 +66,7 @@ chardev-obj-y = chardev/ # block-obj-y is code used by both qemu system emulation and qemu-img block-obj-y += nbd/ -block-obj-y += block.o blockjob.o +block-obj-y += block.o blockjob.o job.o block-obj-y += block/ scsi/ block-obj-y += qemu-io-cmds.o block-obj-$(CONFIG_REPLICATION) += replication.o @@ -94,6 +97,7 @@ io-obj-y = io/ ifeq ($(CONFIG_SOFTMMU),y) common-obj-y = blockdev.o blockdev-nbd.o block/ common-obj-y += bootdevice.o iothread.o +common-obj-y += job-qmp.o common-obj-y += net/ common-obj-y += qdev-monitor.o device-hotplug.o common-obj-$(CONFIG_WIN32) += os-win32.o @@ -140,6 +144,7 @@ common-obj-y += qapi/qapi-commands-char.o common-obj-y += qapi/qapi-commands-common.o common-obj-y += qapi/qapi-commands-crypto.o common-obj-y += qapi/qapi-commands-introspect.o +common-obj-y += qapi/qapi-commands-job.o common-obj-y += qapi/qapi-commands-migration.o common-obj-y += qapi/qapi-commands-misc.o common-obj-y += qapi/qapi-commands-net.o @@ -3362,7 +3362,7 @@ static void bdrv_close(BlockDriverState *bs) void bdrv_close_all(void) { - block_job_cancel_sync_all(); + assert(job_next(NULL) == NULL); nbd_export_close_all(); /* Drop references from requests still in flight, such as canceled block diff --git a/block/backup.c b/block/backup.c index e14d995..4e228e9 100644 --- a/block/backup.c +++ b/block/backup.c @@ -160,7 +160,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, * offset field is an opaque progress value, it is not a disk offset. */ job->bytes_read += n; - block_job_progress_update(&job->common, n); + job_progress_update(&job->common.job, n); } out: @@ -207,25 +207,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) } } -static void backup_commit(BlockJob *job) +static void backup_commit(Job *job) { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); if (s->sync_bitmap) { backup_cleanup_sync_bitmap(s, 0); } } -static void backup_abort(BlockJob *job) +static void backup_abort(Job *job) { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); if (s->sync_bitmap) { backup_cleanup_sync_bitmap(s, -1); } } -static void backup_clean(BlockJob *job) +static void backup_clean(Job *job) { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); assert(s->target); blk_unref(s->target); s->target = NULL; @@ -317,11 +317,11 @@ typedef struct { int ret; } BackupCompleteData; -static void backup_complete(BlockJob *job, void *opaque) +static void backup_complete(Job *job, void *opaque) { BackupCompleteData *data = opaque; - block_job_completed(job, data->ret); + job_completed(job, data->ret); g_free(data); } @@ -329,7 +329,7 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) { uint64_t delay_ns; - if (block_job_is_cancelled(&job->common)) { + if (job_is_cancelled(&job->common.job)) { return true; } @@ -337,9 +337,9 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) * 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; - block_job_sleep_ns(&job->common, delay_ns); + job_sleep_ns(&job->common.job, delay_ns); - if (block_job_is_cancelled(&job->common)) { + if (job_is_cancelled(&job->common.job)) { return true; } @@ -406,8 +406,8 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); } - /* TODO block_job_progress_set_remaining() would make more sense */ - block_job_progress_update(&job->common, + /* TODO job_progress_set_remaining() would make more sense */ + job_progress_update(&job->common.job, job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); bdrv_dirty_iter_free(dbi); @@ -425,7 +425,7 @@ static void coroutine_fn backup_run(void *opaque) qemu_co_rwlock_init(&job->flush_rwlock); nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); - block_job_progress_set_remaining(&job->common, job->len); + job_progress_set_remaining(&job->common.job, job->len); job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { @@ -441,10 +441,10 @@ static void coroutine_fn backup_run(void *opaque) if (job->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. */ - while (!block_job_is_cancelled(&job->common)) { + while (!job_is_cancelled(&job->common.job)) { /* Yield until the job is cancelled. We just let our before_write * notify callback service CoW requests. */ - block_job_yield(&job->common); + job_yield(&job->common.job); } } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { ret = backup_run_incremental(job); @@ -519,16 +519,21 @@ static void coroutine_fn backup_run(void *opaque) data = g_malloc(sizeof(*data)); data->ret = ret; - block_job_defer_to_main_loop(&job->common, backup_complete, data); + job_defer_to_main_loop(&job->common.job, backup_complete, data); } static const BlockJobDriver backup_job_driver = { - .instance_size = sizeof(BackupBlockJob), - .job_type = BLOCK_JOB_TYPE_BACKUP, - .start = backup_run, - .commit = backup_commit, - .abort = backup_abort, - .clean = backup_clean, + .job_driver = { + .instance_size = sizeof(BackupBlockJob), + .job_type = JOB_TYPE_BACKUP, + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = backup_run, + .commit = backup_commit, + .abort = backup_abort, + .clean = backup_clean, + }, .attached_aio_context = backup_attached_aio_context, .drain = backup_drain, }; @@ -541,7 +546,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, - BlockJobTxn *txn, Error **errp) + JobTxn *txn, Error **errp) { int64_t len; BlockDriverInfo bdi; @@ -673,8 +678,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); } if (job) { - backup_clean(&job->common); - block_job_early_fail(&job->common); + backup_clean(&job->common.job); + job_early_fail(&job->common.job); } return NULL; diff --git a/block/commit.c b/block/commit.c index ba5df6a..6206661 100644 --- a/block/commit.c +++ b/block/commit.c @@ -72,9 +72,10 @@ typedef struct { int ret; } CommitCompleteData; -static void commit_complete(BlockJob *job, void *opaque) +static void commit_complete(Job *job, void *opaque) { - CommitBlockJob *s = container_of(job, CommitBlockJob, common); + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); + BlockJob *bjob = &s->common; CommitCompleteData *data = opaque; BlockDriverState *top = blk_bs(s->top); BlockDriverState *base = blk_bs(s->base); @@ -90,7 +91,7 @@ static void commit_complete(BlockJob *job, void *opaque) * the normal backing chain can be restored. */ blk_unref(s->base); - if (!block_job_is_cancelled(&s->common) && ret == 0) { + if (!job_is_cancelled(job) && ret == 0) { /* success */ ret = bdrv_drop_intermediate(s->commit_top_bs, base, s->backing_file_str); @@ -111,12 +112,12 @@ static void commit_complete(BlockJob *job, void *opaque) blk_unref(s->top); /* If there is more than one reference to the job (e.g. if called from - * block_job_finish_sync()), block_job_completed() won't free it and - * therefore the blockers on the intermediate nodes remain. This would - * cause bdrv_set_backing_hd() to fail. */ - block_job_remove_all_bdrv(job); + * job_finish_sync()), job_completed() won't free it and therefore the + * blockers on the intermediate nodes remain. This would cause + * bdrv_set_backing_hd() to fail. */ + block_job_remove_all_bdrv(bjob); - block_job_completed(&s->common, ret); + job_completed(job, ret); g_free(data); /* If bdrv_drop_intermediate() didn't already do that, remove the commit @@ -149,7 +150,7 @@ static void coroutine_fn commit_run(void *opaque) if (len < 0) { goto out; } - block_job_progress_set_remaining(&s->common, len); + job_progress_set_remaining(&s->common.job, len); ret = base_len = blk_getlength(s->base); if (base_len < 0) { @@ -171,8 +172,8 @@ static void coroutine_fn commit_run(void *opaque) /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. */ - block_job_sleep_ns(&s->common, delay_ns); - if (block_job_is_cancelled(&s->common)) { + job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job)) { break; } /* Copy if allocated above the base */ @@ -195,7 +196,7 @@ static void coroutine_fn commit_run(void *opaque) } } /* Publish progress */ - block_job_progress_update(&s->common, n); + job_progress_update(&s->common.job, n); if (copy) { delay_ns = block_job_ratelimit_get_delay(&s->common, n); @@ -211,13 +212,18 @@ out: data = g_malloc(sizeof(*data)); data->ret = ret; - block_job_defer_to_main_loop(&s->common, commit_complete, data); + job_defer_to_main_loop(&s->common.job, commit_complete, data); } static const BlockJobDriver commit_job_driver = { - .instance_size = sizeof(CommitBlockJob), - .job_type = BLOCK_JOB_TYPE_COMMIT, - .start = commit_run, + .job_driver = { + .instance_size = sizeof(CommitBlockJob), + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = commit_run, + }, }; static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, @@ -277,7 +283,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, } s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, - speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); + speed, JOB_DEFAULT, NULL, NULL, errp); if (!s) { return; } @@ -367,7 +373,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, s->on_error = on_error; trace_commit_start(bs, base, top, s); - block_job_start(&s->common); + job_start(&s->common.job); return; fail: @@ -380,7 +386,7 @@ fail: if (commit_top_bs) { bdrv_replace_node(commit_top_bs, top, &error_abort); } - block_job_early_fail(&s->common); + job_early_fail(&s->common.job); } diff --git a/block/mirror.c b/block/mirror.c index a4197bb..dcb66ec 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -119,14 +119,14 @@ static void mirror_iteration_done(MirrorOp *op, int ret) bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); } if (!s->initial_zeroing_ongoing) { - block_job_progress_update(&s->common, op->bytes); + job_progress_update(&s->common.job, op->bytes); } } qemu_iovec_destroy(&op->qiov); g_free(op); if (s->waiting_for_io) { - qemu_coroutine_enter(s->common.co); + qemu_coroutine_enter(s->common.job.co); } } @@ -345,7 +345,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) mirror_wait_for_io(s); } - block_job_pause_point(&s->common); + job_pause_point(&s->common.job); /* Find the number of consective dirty chunks following the first dirty * one, and wait for in flight requests in them. */ @@ -484,9 +484,10 @@ typedef struct { int ret; } MirrorExitData; -static void mirror_exit(BlockJob *job, void *opaque) +static void mirror_exit(Job *job, void *opaque) { - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); + BlockJob *bjob = &s->common; MirrorExitData *data = opaque; AioContext *replace_aio_context = NULL; BlockDriverState *src = s->source; @@ -497,7 +498,7 @@ static void mirror_exit(BlockJob *job, void *opaque) bdrv_release_dirty_bitmap(src, s->dirty_bitmap); /* Make sure that the source BDS doesn't go away before we called - * block_job_completed(). */ + * job_completed(). */ bdrv_ref(src); bdrv_ref(mirror_top_bs); bdrv_ref(target_bs); @@ -568,7 +569,7 @@ static void mirror_exit(BlockJob *job, void *opaque) * the blockers on the intermediate nodes so that the resulting state is * valid. Also give up permissions on mirror_top_bs->backing, which might * block the removal. */ - block_job_remove_all_bdrv(job); + block_job_remove_all_bdrv(bjob); bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, &error_abort); bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort); @@ -576,11 +577,11 @@ static void mirror_exit(BlockJob *job, void *opaque) /* We just changed the BDS the job BB refers to (with either or both of the * bdrv_replace_node() calls), so switch the BB back so the cleanup does * the right thing. We don't need any permissions any more now. */ - blk_remove_bs(job->blk); - blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); - blk_insert_bs(job->blk, mirror_top_bs, &error_abort); + blk_remove_bs(bjob->blk); + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); + blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); - block_job_completed(&s->common, data->ret); + job_completed(job, data->ret); g_free(data); bdrv_drained_end(src); @@ -594,9 +595,9 @@ static void mirror_throttle(MirrorBlockJob *s) if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) { s->last_pause_ns = now; - block_job_sleep_ns(&s->common, 0); + job_sleep_ns(&s->common.job, 0); } else { - block_job_pause_point(&s->common); + job_pause_point(&s->common.job); } } @@ -622,7 +623,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) mirror_throttle(s); - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { s->initial_zeroing_ongoing = false; return 0; } @@ -650,7 +651,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) mirror_throttle(s); - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { return 0; } @@ -695,7 +696,7 @@ static void coroutine_fn mirror_run(void *opaque) checking for a NULL string */ int ret = 0; - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { goto immediate_exit; } @@ -726,13 +727,13 @@ static void coroutine_fn mirror_run(void *opaque) } if (s->bdev_length == 0) { - /* Report BLOCK_JOB_READY and wait for complete. */ - block_job_event_ready(&s->common); + /* Transition to the READY state and wait for complete. */ + job_transition_to_ready(&s->common.job); s->synced = true; - while (!block_job_is_cancelled(&s->common) && !s->should_complete) { - block_job_yield(&s->common); + while (!job_is_cancelled(&s->common.job) && !s->should_complete) { + job_yield(&s->common.job); } - s->common.cancelled = false; + s->common.job.cancelled = false; goto immediate_exit; } @@ -768,7 +769,7 @@ static void coroutine_fn mirror_run(void *opaque) s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); if (!s->is_none_mode) { ret = mirror_dirty_init(s); - if (ret < 0 || block_job_is_cancelled(&s->common)) { + if (ret < 0 || job_is_cancelled(&s->common.job)) { goto immediate_exit; } } @@ -785,13 +786,13 @@ static void coroutine_fn mirror_run(void *opaque) goto immediate_exit; } - block_job_pause_point(&s->common); + job_pause_point(&s->common.job); cnt = bdrv_get_dirty_count(s->dirty_bitmap); /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is * the number of bytes currently being processed; together those are * the current remaining operation length */ - block_job_progress_set_remaining(&s->common, s->bytes_in_flight + cnt); + job_progress_set_remaining(&s->common.job, s->bytes_in_flight + cnt); /* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that bdrv_drain_all() returns. @@ -823,12 +824,12 @@ static void coroutine_fn mirror_run(void *opaque) * report completion. This way, block-job-cancel will leave * the target in a consistent state. */ - block_job_event_ready(&s->common); + job_transition_to_ready(&s->common.job); s->synced = true; } should_complete = s->should_complete || - block_job_is_cancelled(&s->common); + job_is_cancelled(&s->common.job); cnt = bdrv_get_dirty_count(s->dirty_bitmap); } @@ -856,7 +857,7 @@ static void coroutine_fn mirror_run(void *opaque) * completion. */ assert(QLIST_EMPTY(&bs->tracked_requests)); - s->common.cancelled = false; + s->common.job.cancelled = false; need_drain = false; break; } @@ -868,9 +869,9 @@ static void coroutine_fn mirror_run(void *opaque) cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); } trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); - block_job_sleep_ns(&s->common, delay_ns); - if (block_job_is_cancelled(&s->common) && - (!s->synced || s->common.force)) + job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job) && + (!s->synced || s->common.job.force_cancel)) { break; } @@ -883,8 +884,8 @@ immediate_exit: * or it was cancelled prematurely so that we do not guarantee that * the target is a copy of the source. */ - assert(ret < 0 || ((s->common.force || !s->synced) && - block_job_is_cancelled(&s->common))); + assert(ret < 0 || ((s->common.job.force_cancel || !s->synced) && + job_is_cancelled(&s->common.job))); assert(need_drain); mirror_wait_for_all_io(s); } @@ -901,12 +902,12 @@ immediate_exit: if (need_drain) { bdrv_drained_begin(bs); } - block_job_defer_to_main_loop(&s->common, mirror_exit, data); + job_defer_to_main_loop(&s->common.job, mirror_exit, data); } -static void mirror_complete(BlockJob *job, Error **errp) +static void mirror_complete(Job *job, Error **errp) { - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockDriverState *target; target = blk_bs(s->target); @@ -953,12 +954,12 @@ static void mirror_complete(BlockJob *job, Error **errp) } s->should_complete = true; - block_job_enter(&s->common); + job_enter(job); } -static void mirror_pause(BlockJob *job) +static void mirror_pause(Job *job) { - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); mirror_wait_for_all_io(s); } @@ -986,21 +987,31 @@ static void mirror_drain(BlockJob *job) } static const BlockJobDriver mirror_job_driver = { - .instance_size = sizeof(MirrorBlockJob), - .job_type = BLOCK_JOB_TYPE_MIRROR, - .start = mirror_run, - .complete = mirror_complete, - .pause = mirror_pause, + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_MIRROR, + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = mirror_run, + .pause = mirror_pause, + .complete = mirror_complete, + }, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; static const BlockJobDriver commit_active_job_driver = { - .instance_size = sizeof(MirrorBlockJob), - .job_type = BLOCK_JOB_TYPE_COMMIT, - .start = mirror_run, - .complete = mirror_complete, - .pause = mirror_pause, + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = mirror_run, + .pause = mirror_pause, + .complete = mirror_complete, + }, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; @@ -1237,7 +1248,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, } trace_mirror_start(bs, s, opaque); - block_job_start(&s->common); + job_start(&s->common.job); return; fail: @@ -1248,7 +1259,7 @@ fail: g_free(s->replaces); blk_unref(s->target); - block_job_early_fail(&s->common); + job_early_fail(&s->common.job); } bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, @@ -1275,7 +1286,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, } is_none_mode = mode == MIRROR_SYNC_MODE_NONE; base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; - mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces, + mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces, speed, granularity, buf_size, backing_mode, on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, is_none_mode, base, false, diff --git a/block/replication.c b/block/replication.c index 48148b8..826db7b 100644 --- a/block/replication.c +++ b/block/replication.c @@ -145,7 +145,7 @@ static void replication_close(BlockDriverState *bs) replication_stop(s->rs, false, NULL); } if (s->stage == BLOCK_REPLICATION_FAILOVER) { - block_job_cancel_sync(s->active_disk->bs->job); + job_cancel_sync(&s->active_disk->bs->job->job); } if (s->mode == REPLICATION_MODE_SECONDARY) { @@ -568,7 +568,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs, 0, MIRROR_SYNC_MODE_NONE, NULL, false, BLOCKDEV_ON_ERROR_REPORT, - BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL, + BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, backup_job_completed, bs, NULL, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -576,7 +576,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, aio_context_release(aio_context); return; } - block_job_start(job); + job_start(&job->job); break; default: aio_context_release(aio_context); @@ -681,7 +681,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) * disk, secondary disk in backup_job_completed(). */ if (s->secondary_disk->bs->job) { - block_job_cancel_sync(s->secondary_disk->bs->job); + job_cancel_sync(&s->secondary_disk->bs->job->job); } if (!failover) { @@ -693,7 +693,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) s->stage = BLOCK_REPLICATION_FAILOVER; commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs, - BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, + JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, NULL, replication_done, bs, true, errp); break; default: diff --git a/block/sheepdog.c b/block/sheepdog.c index 4237132..2a5bc0a 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1859,9 +1859,7 @@ out: error_setg_errno(errp, -ret, "Can't pre-allocate"); } out_with_err_set: - if (blk) { - blk_unref(blk); - } + blk_unref(blk); g_free(buf); return ret; diff --git a/block/stream.c b/block/stream.c index df9660d..a5d6e0c 100644 --- a/block/stream.c +++ b/block/stream.c @@ -58,16 +58,16 @@ typedef struct { int ret; } StreamCompleteData; -static void stream_complete(BlockJob *job, void *opaque) +static void stream_complete(Job *job, void *opaque) { - StreamBlockJob *s = container_of(job, StreamBlockJob, common); + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); + BlockJob *bjob = &s->common; StreamCompleteData *data = opaque; - BlockDriverState *bs = blk_bs(job->blk); + BlockDriverState *bs = blk_bs(bjob->blk); BlockDriverState *base = s->base; Error *local_err = NULL; - if (!block_job_is_cancelled(&s->common) && bs->backing && - data->ret == 0) { + if (!job_is_cancelled(job) && bs->backing && data->ret == 0) { const char *base_id = NULL, *base_fmt = NULL; if (base) { base_id = s->backing_file_str; @@ -88,12 +88,12 @@ out: /* Reopen the image back in read-only mode if necessary */ if (s->bs_flags != bdrv_get_flags(bs)) { /* Give up write permissions before making it read-only */ - blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); bdrv_reopen(bs, s->bs_flags, NULL); } g_free(s->backing_file_str); - block_job_completed(&s->common, data->ret); + job_completed(job, data->ret); g_free(data); } @@ -121,7 +121,7 @@ static void coroutine_fn stream_run(void *opaque) ret = len; goto out; } - block_job_progress_set_remaining(&s->common, len); + job_progress_set_remaining(&s->common.job, len); buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); @@ -140,8 +140,8 @@ static void coroutine_fn stream_run(void *opaque) /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. */ - block_job_sleep_ns(&s->common, delay_ns); - if (block_job_is_cancelled(&s->common)) { + job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job)) { break; } @@ -184,7 +184,7 @@ static void coroutine_fn stream_run(void *opaque) ret = 0; /* Publish progress */ - block_job_progress_update(&s->common, n); + job_progress_update(&s->common.job, n); if (copy) { delay_ns = block_job_ratelimit_get_delay(&s->common, n); } else { @@ -205,13 +205,18 @@ out: /* Modify backing chain and close BDSes in main loop */ data = g_malloc(sizeof(*data)); data->ret = ret; - block_job_defer_to_main_loop(&s->common, stream_complete, data); + job_defer_to_main_loop(&s->common.job, stream_complete, data); } static const BlockJobDriver stream_job_driver = { - .instance_size = sizeof(StreamBlockJob), - .job_type = BLOCK_JOB_TYPE_STREAM, - .start = stream_run, + .job_driver = { + .instance_size = sizeof(StreamBlockJob), + .job_type = JOB_TYPE_STREAM, + .free = block_job_free, + .start = stream_run, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + }, }; void stream_start(const char *job_id, BlockDriverState *bs, @@ -238,7 +243,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, BLK_PERM_GRAPH_MOD, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE, - speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); + speed, JOB_DEFAULT, NULL, NULL, errp); if (!s) { goto fail; } @@ -259,7 +264,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, s->on_error = on_error; trace_stream_start(bs, base, s); - block_job_start(&s->common); + job_start(&s->common.job); return; fail: diff --git a/block/trace-events b/block/trace-events index f8c50b4..2d59b53 100644 --- a/block/trace-events +++ b/block/trace-events @@ -4,11 +4,6 @@ bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\"" bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" -# blockjob.c -block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" -block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" -block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" - # block/block-backend.c blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" @@ -150,7 +150,7 @@ void blockdev_mark_auto_del(BlockBackend *blk) aio_context_acquire(aio_context); if (bs->job) { - block_job_cancel(bs->job, false); + job_cancel(&bs->job->job, false); } aio_context_release(aio_context); @@ -1446,7 +1446,7 @@ typedef struct BlkActionOps { struct BlkActionState { TransactionAction *action; const BlkActionOps *ops; - BlockJobTxn *block_job_txn; + JobTxn *block_job_txn; TransactionProperties *txn_props; QSIMPLEQ_ENTRY(BlkActionState) entry; }; @@ -1864,7 +1864,7 @@ typedef struct DriveBackupState { BlockJob *job; } DriveBackupState; -static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, +static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, Error **errp); static void drive_backup_prepare(BlkActionState *common, Error **errp) @@ -1910,7 +1910,7 @@ static void drive_backup_commit(BlkActionState *common) aio_context_acquire(aio_context); assert(state->job); - block_job_start(state->job); + job_start(&state->job->job); aio_context_release(aio_context); } @@ -1925,7 +1925,7 @@ static void drive_backup_abort(BlkActionState *common) aio_context = bdrv_get_aio_context(state->bs); aio_context_acquire(aio_context); - block_job_cancel_sync(state->job); + job_cancel_sync(&state->job->job); aio_context_release(aio_context); } @@ -1954,7 +1954,7 @@ typedef struct BlockdevBackupState { BlockJob *job; } BlockdevBackupState; -static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, +static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, Error **errp); static void blockdev_backup_prepare(BlkActionState *common, Error **errp) @@ -2008,7 +2008,7 @@ static void blockdev_backup_commit(BlkActionState *common) aio_context_acquire(aio_context); assert(state->job); - block_job_start(state->job); + job_start(&state->job->job); aio_context_release(aio_context); } @@ -2023,7 +2023,7 @@ static void blockdev_backup_abort(BlkActionState *common) aio_context = bdrv_get_aio_context(state->bs); aio_context_acquire(aio_context); - block_job_cancel_sync(state->job); + job_cancel_sync(&state->job->job); aio_context_release(aio_context); } @@ -2243,7 +2243,7 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp) { TransactionActionList *dev_entry = dev_list; - BlockJobTxn *block_job_txn = NULL; + JobTxn *block_job_txn = NULL; BlkActionState *state, *next; Error *local_err = NULL; @@ -2251,11 +2251,11 @@ void qmp_transaction(TransactionActionList *dev_list, QSIMPLEQ_INIT(&snap_bdrv_states); /* Does this transaction get canceled as a group on failure? - * If not, we don't really need to make a BlockJobTxn. + * If not, we don't really need to make a JobTxn. */ props = get_transaction_properties(props); if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { - block_job_txn = block_job_txn_new(); + block_job_txn = job_txn_new(); } /* drain all i/o before any operations */ @@ -2314,7 +2314,7 @@ exit: if (!has_props) { qapi_free_TransactionProperties(props); } - block_job_txn_unref(block_job_txn); + job_txn_unref(block_job_txn); } void qmp_eject(bool has_device, const char *device, @@ -3244,7 +3244,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, goto out; } commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, - BLOCK_JOB_DEFAULT, speed, on_error, + JOB_DEFAULT, speed, on_error, filter_node_name, NULL, NULL, false, &local_err); } else { BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); @@ -3264,7 +3264,7 @@ out: aio_context_release(aio_context); } -static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, +static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, Error **errp) { BlockDriverState *bs; @@ -3275,7 +3275,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, AioContext *aio_context; QDict *options = NULL; Error *local_err = NULL; - int flags, job_flags = BLOCK_JOB_DEFAULT; + int flags, job_flags = JOB_DEFAULT; int64_t size; bool set_backing_hd = false; @@ -3398,10 +3398,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, } } if (!backup->auto_finalize) { - job_flags |= BLOCK_JOB_MANUAL_FINALIZE; + job_flags |= JOB_MANUAL_FINALIZE; } if (!backup->auto_dismiss) { - job_flags |= BLOCK_JOB_MANUAL_DISMISS; + job_flags |= JOB_MANUAL_DISMISS; } job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, @@ -3425,7 +3425,7 @@ void qmp_drive_backup(DriveBackup *arg, Error **errp) BlockJob *job; job = do_drive_backup(arg, NULL, errp); if (job) { - block_job_start(job); + job_start(&job->job); } } @@ -3434,7 +3434,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) return bdrv_named_nodes_list(errp); } -BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, +BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, Error **errp) { BlockDriverState *bs; @@ -3442,7 +3442,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error *local_err = NULL; AioContext *aio_context; BlockJob *job = NULL; - int job_flags = BLOCK_JOB_DEFAULT; + int job_flags = JOB_DEFAULT; if (!backup->has_speed) { backup->speed = 0; @@ -3491,10 +3491,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, } } if (!backup->auto_finalize) { - job_flags |= BLOCK_JOB_MANUAL_FINALIZE; + job_flags |= JOB_MANUAL_FINALIZE; } if (!backup->auto_dismiss) { - job_flags |= BLOCK_JOB_MANUAL_DISMISS; + job_flags |= JOB_MANUAL_DISMISS; } job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, NULL, backup->compress, @@ -3513,7 +3513,7 @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp) BlockJob *job; job = do_blockdev_backup(arg, NULL, errp); if (job) { - block_job_start(job); + job_start(&job->job); } } @@ -3844,14 +3844,14 @@ void qmp_block_job_cancel(const char *device, force = false; } - if (block_job_user_paused(job) && !force) { + if (job_user_paused(&job->job) && !force) { error_setg(errp, "The block job for device '%s' is currently paused", device); goto out; } trace_qmp_block_job_cancel(job); - block_job_user_cancel(job, force, errp); + job_user_cancel(&job->job, force, errp); out: aio_context_release(aio_context); } @@ -3866,7 +3866,7 @@ void qmp_block_job_pause(const char *device, Error **errp) } trace_qmp_block_job_pause(job); - block_job_user_pause(job, errp); + job_user_pause(&job->job, errp); aio_context_release(aio_context); } @@ -3880,7 +3880,7 @@ void qmp_block_job_resume(const char *device, Error **errp) } trace_qmp_block_job_resume(job); - block_job_user_resume(job, errp); + job_user_resume(&job->job, errp); aio_context_release(aio_context); } @@ -3894,7 +3894,7 @@ void qmp_block_job_complete(const char *device, Error **errp) } trace_qmp_block_job_complete(job); - block_job_complete(job, errp); + job_complete(&job->job, errp); aio_context_release(aio_context); } @@ -3908,21 +3908,23 @@ void qmp_block_job_finalize(const char *id, Error **errp) } trace_qmp_block_job_finalize(job); - block_job_finalize(job, errp); + job_finalize(&job->job, errp); aio_context_release(aio_context); } void qmp_block_job_dismiss(const char *id, Error **errp) { AioContext *aio_context; - BlockJob *job = find_block_job(id, &aio_context, errp); + BlockJob *bjob = find_block_job(id, &aio_context, errp); + Job *job; - if (!job) { + if (!bjob) { return; } - trace_qmp_block_job_dismiss(job); - block_job_dismiss(&job, errp); + trace_qmp_block_job_dismiss(bjob); + job = &bjob->job; + job_dismiss(&job, errp); aio_context_release(aio_context); } @@ -34,103 +34,8 @@ #include "qapi/qapi-events-block-core.h" #include "qapi/qmp/qerror.h" #include "qemu/coroutine.h" -#include "qemu/id.h" #include "qemu/timer.h" -/* Right now, this mutex is only needed to synchronize accesses to job->busy - * and job->sleep_timer, such as concurrent calls to block_job_do_yield and - * block_job_enter. */ -static QemuMutex block_job_mutex; - -/* BlockJob State Transition Table */ -bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = { - /* U, C, R, P, Y, S, W, D, X, E, N */ - /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, - /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, - /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, - /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, - /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, - /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, - /* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, - /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, - /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -}; - -bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = { - /* U, C, R, P, Y, S, W, D, X, E, N */ - [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, - [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, - [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, -}; - -static void block_job_state_transition(BlockJob *job, BlockJobStatus s1) -{ - BlockJobStatus s0 = job->status; - assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX); - trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ? - "allowed" : "disallowed", - BlockJobStatus_str(s0), - BlockJobStatus_str(s1)); - assert(BlockJobSTT[s0][s1]); - job->status = s1; -} - -static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp) -{ - assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX); - trace_block_job_apply_verb(job, BlockJobStatus_str(job->status), - BlockJobVerb_str(bv), - BlockJobVerbTable[bv][job->status] ? - "allowed" : "prohibited"); - if (BlockJobVerbTable[bv][job->status]) { - return 0; - } - error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", - job->id, BlockJobStatus_str(job->status), BlockJobVerb_str(bv)); - return -EPERM; -} - -static void block_job_lock(void) -{ - qemu_mutex_lock(&block_job_mutex); -} - -static void block_job_unlock(void) -{ - qemu_mutex_unlock(&block_job_mutex); -} - -static void __attribute__((__constructor__)) block_job_init(void) -{ - qemu_mutex_init(&block_job_mutex); -} - -static void block_job_event_cancelled(BlockJob *job); -static void block_job_event_completed(BlockJob *job, const char *msg); -static int block_job_event_pending(BlockJob *job); -static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)); - -/* Transactional group of block jobs */ -struct BlockJobTxn { - - /* Is this txn being cancelled? */ - bool aborting; - - /* List of jobs */ - QLIST_HEAD(, BlockJob) jobs; - - /* Reference count */ - int refcnt; -}; - -static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs); - /* * The block job API is composed of two categories of functions. * @@ -146,147 +51,78 @@ static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs); * blockjob_int.h. */ -BlockJob *block_job_next(BlockJob *job) +static bool is_block_job(Job *job) { - if (!job) { - return QLIST_FIRST(&block_jobs); - } - return QLIST_NEXT(job, job_list); -} - -BlockJob *block_job_get(const char *id) -{ - BlockJob *job; - - QLIST_FOREACH(job, &block_jobs, job_list) { - if (job->id && !strcmp(id, job->id)) { - return job; - } - } - - return NULL; + return job_type(job) == JOB_TYPE_BACKUP || + job_type(job) == JOB_TYPE_COMMIT || + job_type(job) == JOB_TYPE_MIRROR || + job_type(job) == JOB_TYPE_STREAM; } -BlockJobTxn *block_job_txn_new(void) +BlockJob *block_job_next(BlockJob *bjob) { - BlockJobTxn *txn = g_new0(BlockJobTxn, 1); - QLIST_INIT(&txn->jobs); - txn->refcnt = 1; - return txn; -} - -static void block_job_txn_ref(BlockJobTxn *txn) -{ - txn->refcnt++; -} - -void block_job_txn_unref(BlockJobTxn *txn) -{ - if (txn && --txn->refcnt == 0) { - g_free(txn); - } -} - -void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job) -{ - if (!txn) { - return; - } + Job *job = bjob ? &bjob->job : NULL; - assert(!job->txn); - job->txn = txn; + do { + job = job_next(job); + } while (job && !is_block_job(job)); - QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); - block_job_txn_ref(txn); + return job ? container_of(job, BlockJob, job) : NULL; } -static void block_job_txn_del_job(BlockJob *job) -{ - if (job->txn) { - QLIST_REMOVE(job, txn_list); - block_job_txn_unref(job->txn); - job->txn = NULL; - } -} - -/* Assumes the block_job_mutex is held */ -static bool block_job_timer_pending(BlockJob *job) -{ - return timer_pending(&job->sleep_timer); -} - -/* Assumes the block_job_mutex is held */ -static bool block_job_timer_not_pending(BlockJob *job) -{ - return !block_job_timer_pending(job); -} - -static void block_job_pause(BlockJob *job) +BlockJob *block_job_get(const char *id) { - job->pause_count++; -} + Job *job = job_get(id); -static void block_job_resume(BlockJob *job) -{ - assert(job->pause_count > 0); - job->pause_count--; - if (job->pause_count) { - return; + if (job && is_block_job(job)) { + return container_of(job, BlockJob, job); + } else { + return NULL; } - - /* kick only if no timer is pending */ - block_job_enter_cond(job, block_job_timer_not_pending); -} - -void block_job_ref(BlockJob *job) -{ - ++job->refcnt; } static void block_job_attached_aio_context(AioContext *new_context, void *opaque); static void block_job_detach_aio_context(void *opaque); -void block_job_unref(BlockJob *job) +void block_job_free(Job *job) { - if (--job->refcnt == 0) { - assert(job->status == BLOCK_JOB_STATUS_NULL); - assert(!job->txn); - BlockDriverState *bs = blk_bs(job->blk); - QLIST_REMOVE(job, job_list); - bs->job = NULL; - block_job_remove_all_bdrv(job); - blk_remove_aio_context_notifier(job->blk, - block_job_attached_aio_context, - block_job_detach_aio_context, job); - blk_unref(job->blk); - error_free(job->blocker); - g_free(job->id); - assert(!timer_pending(&job->sleep_timer)); - g_free(job); - } + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); + + bs->job = NULL; + block_job_remove_all_bdrv(bjob); + blk_remove_aio_context_notifier(bjob->blk, + block_job_attached_aio_context, + block_job_detach_aio_context, bjob); + blk_unref(bjob->blk); + error_free(bjob->blocker); } static void block_job_attached_aio_context(AioContext *new_context, void *opaque) { BlockJob *job = opaque; + const JobDriver *drv = job->job.driver; + BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver); - if (job->driver->attached_aio_context) { - job->driver->attached_aio_context(job, new_context); + job->job.aio_context = new_context; + if (bjdrv->attached_aio_context) { + bjdrv->attached_aio_context(job, new_context); } - block_job_resume(job); + job_resume(&job->job); } -static void block_job_drain(BlockJob *job) +void block_job_drain(Job *job) { - /* If job is !job->busy this kicks it into the next pause point. */ - block_job_enter(job); + BlockJob *bjob = container_of(job, BlockJob, job); + const JobDriver *drv = job->driver; + BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver); - blk_drain(job->blk); - if (job->driver->drain) { - job->driver->drain(job); + blk_drain(bjob->blk); + if (bjdrv->drain) { + bjdrv->drain(bjob); } } @@ -295,35 +131,34 @@ static void block_job_detach_aio_context(void *opaque) BlockJob *job = opaque; /* In case the job terminates during aio_poll()... */ - block_job_ref(job); + job_ref(&job->job); - block_job_pause(job); + job_pause(&job->job); - while (!job->paused && !job->completed) { - block_job_drain(job); + while (!job->job.paused && !job_is_completed(&job->job)) { + job_drain(&job->job); } - block_job_unref(job); + job->job.aio_context = NULL; + job_unref(&job->job); } static char *child_job_get_parent_desc(BdrvChild *c) { BlockJob *job = c->opaque; - return g_strdup_printf("%s job '%s'", - BlockJobType_str(job->driver->job_type), - job->id); + return g_strdup_printf("%s job '%s'", job_type_str(&job->job), job->job.id); } static void child_job_drained_begin(BdrvChild *c) { BlockJob *job = c->opaque; - block_job_pause(job); + job_pause(&job->job); } static void child_job_drained_end(BdrvChild *c) { BlockJob *job = c->opaque; - block_job_resume(job); + job_resume(&job->job); } static const BdrvChildRole child_job = { @@ -365,316 +200,25 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, bool block_job_is_internal(BlockJob *job) { - return (job->id == NULL); -} - -static bool block_job_started(BlockJob *job) -{ - return job->co; + return (job->job.id == NULL); } const BlockJobDriver *block_job_driver(BlockJob *job) { - return job->driver; -} - -/** - * All jobs must allow a pause point before entering their job proper. This - * ensures that jobs can be paused prior to being started, then resumed later. - */ -static void coroutine_fn block_job_co_entry(void *opaque) -{ - BlockJob *job = opaque; - - assert(job && job->driver && job->driver->start); - block_job_pause_point(job); - job->driver->start(job); -} - -static void block_job_sleep_timer_cb(void *opaque) -{ - BlockJob *job = opaque; - - block_job_enter(job); -} - -void block_job_start(BlockJob *job) -{ - assert(job && !block_job_started(job) && job->paused && - job->driver && job->driver->start); - job->co = qemu_coroutine_create(block_job_co_entry, job); - job->pause_count--; - job->busy = true; - job->paused = false; - block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING); - bdrv_coroutine_enter(blk_bs(job->blk), job->co); -} - -static void block_job_decommission(BlockJob *job) -{ - assert(job); - job->completed = true; - job->busy = false; - job->paused = false; - job->deferred_to_main_loop = true; - block_job_txn_del_job(job); - block_job_state_transition(job, BLOCK_JOB_STATUS_NULL); - block_job_unref(job); + return container_of(job->job.driver, BlockJobDriver, job_driver); } -static void block_job_do_dismiss(BlockJob *job) +/* Assumes the job_mutex is held */ +static bool job_timer_pending(Job *job) { - block_job_decommission(job); -} - -static void block_job_conclude(BlockJob *job) -{ - block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED); - if (job->auto_dismiss || !block_job_started(job)) { - block_job_do_dismiss(job); - } -} - -static void block_job_update_rc(BlockJob *job) -{ - if (!job->ret && block_job_is_cancelled(job)) { - job->ret = -ECANCELED; - } - if (job->ret) { - block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING); - } -} - -static int block_job_prepare(BlockJob *job) -{ - if (job->ret == 0 && job->driver->prepare) { - job->ret = job->driver->prepare(job); - } - return job->ret; -} - -static void block_job_commit(BlockJob *job) -{ - assert(!job->ret); - if (job->driver->commit) { - job->driver->commit(job); - } -} - -static void block_job_abort(BlockJob *job) -{ - assert(job->ret); - if (job->driver->abort) { - job->driver->abort(job); - } -} - -static void block_job_clean(BlockJob *job) -{ - if (job->driver->clean) { - job->driver->clean(job); - } -} - -static int block_job_finalize_single(BlockJob *job) -{ - assert(job->completed); - - /* Ensure abort is called for late-transactional failures */ - block_job_update_rc(job); - - if (!job->ret) { - block_job_commit(job); - } else { - block_job_abort(job); - } - block_job_clean(job); - - if (job->cb) { - job->cb(job->opaque, job->ret); - } - - /* Emit events only if we actually started */ - if (block_job_started(job)) { - if (block_job_is_cancelled(job)) { - block_job_event_cancelled(job); - } else { - const char *msg = NULL; - if (job->ret < 0) { - msg = strerror(-job->ret); - } - block_job_event_completed(job, msg); - } - } - - block_job_txn_del_job(job); - block_job_conclude(job); - return 0; -} - -static void block_job_cancel_async(BlockJob *job, bool force) -{ - if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) { - block_job_iostatus_reset(job); - } - if (job->user_paused) { - /* Do not call block_job_enter here, the caller will handle it. */ - job->user_paused = false; - job->pause_count--; - } - job->cancelled = true; - /* To prevent 'force == false' overriding a previous 'force == true' */ - job->force |= force; -} - -static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) -{ - AioContext *ctx; - BlockJob *job, *next; - int rc = 0; - - QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { - if (lock) { - ctx = blk_get_aio_context(job->blk); - aio_context_acquire(ctx); - } - rc = fn(job); - if (lock) { - aio_context_release(ctx); - } - if (rc) { - break; - } - } - return rc; -} - -static int block_job_finish_sync(BlockJob *job, - void (*finish)(BlockJob *, Error **errp), - Error **errp) -{ - Error *local_err = NULL; - int ret; - - assert(blk_bs(job->blk)->job == job); - - block_job_ref(job); - - if (finish) { - finish(job, &local_err); - } - if (local_err) { - error_propagate(errp, local_err); - block_job_unref(job); - return -EBUSY; - } - /* block_job_drain calls block_job_enter, and it should be enough to - * induce progress until the job completes or moves to the main thread. - */ - while (!job->deferred_to_main_loop && !job->completed) { - block_job_drain(job); - } - while (!job->completed) { - aio_poll(qemu_get_aio_context(), true); - } - ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret; - block_job_unref(job); - return ret; -} - -static void block_job_completed_txn_abort(BlockJob *job) -{ - AioContext *ctx; - BlockJobTxn *txn = job->txn; - BlockJob *other_job; - - if (txn->aborting) { - /* - * We are cancelled by another job, which will handle everything. - */ - return; - } - txn->aborting = true; - block_job_txn_ref(txn); - - /* We are the first failed job. Cancel other jobs. */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - ctx = blk_get_aio_context(other_job->blk); - aio_context_acquire(ctx); - } - - /* Other jobs are effectively cancelled by us, set the status for - * them; this job, however, may or may not be cancelled, depending - * on the caller, so leave it. */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (other_job != job) { - block_job_cancel_async(other_job, false); - } - } - while (!QLIST_EMPTY(&txn->jobs)) { - other_job = QLIST_FIRST(&txn->jobs); - ctx = blk_get_aio_context(other_job->blk); - if (!other_job->completed) { - assert(other_job->cancelled); - block_job_finish_sync(other_job, NULL, NULL); - } - block_job_finalize_single(other_job); - aio_context_release(ctx); - } - - block_job_txn_unref(txn); -} - -static int block_job_needs_finalize(BlockJob *job) -{ - return !job->auto_finalize; -} - -static void block_job_do_finalize(BlockJob *job) -{ - int rc; - assert(job && job->txn); - - /* prepare the transaction to complete */ - rc = block_job_txn_apply(job->txn, block_job_prepare, true); - if (rc) { - block_job_completed_txn_abort(job); - } else { - block_job_txn_apply(job->txn, block_job_finalize_single, true); - } -} - -static void block_job_completed_txn_success(BlockJob *job) -{ - BlockJobTxn *txn = job->txn; - BlockJob *other_job; - - block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING); - - /* - * Successful completion, see if there are other running jobs in this - * txn. - */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (!other_job->completed) { - return; - } - assert(other_job->ret == 0); - } - - block_job_txn_apply(txn, block_job_event_pending, false); - - /* If no jobs need manual finalization, automatically do so */ - if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) { - block_job_do_finalize(job); - } + return timer_pending(&job->sleep_timer); } void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { int64_t old_speed = job->speed; - if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) { + if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) { return; } if (speed < 0) { @@ -690,7 +234,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) } /* kick only if a timer is pending */ - block_job_enter_cond(job, block_job_timer_pending); + job_enter_cond(&job->job, job_timer_pending); } int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) @@ -702,142 +246,6 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) return ratelimit_calculate_delay(&job->limit, n); } -void block_job_complete(BlockJob *job, Error **errp) -{ - /* Should not be reachable via external interface for internal jobs */ - assert(job->id); - if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) { - return; - } - if (job->pause_count || job->cancelled || !job->driver->complete) { - error_setg(errp, "The active block job '%s' cannot be completed", - job->id); - return; - } - - job->driver->complete(job, errp); -} - -void block_job_finalize(BlockJob *job, Error **errp) -{ - assert(job && job->id); - if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) { - return; - } - block_job_do_finalize(job); -} - -void block_job_dismiss(BlockJob **jobptr, Error **errp) -{ - BlockJob *job = *jobptr; - /* similarly to _complete, this is QMP-interface only. */ - assert(job->id); - if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) { - return; - } - - block_job_do_dismiss(job); - *jobptr = NULL; -} - -void block_job_user_pause(BlockJob *job, Error **errp) -{ - if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) { - return; - } - if (job->user_paused) { - error_setg(errp, "Job is already paused"); - return; - } - job->user_paused = true; - block_job_pause(job); -} - -bool block_job_user_paused(BlockJob *job) -{ - return job->user_paused; -} - -void block_job_user_resume(BlockJob *job, Error **errp) -{ - assert(job); - if (!job->user_paused || job->pause_count <= 0) { - error_setg(errp, "Can't resume a job that was not paused"); - return; - } - if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) { - return; - } - block_job_iostatus_reset(job); - job->user_paused = false; - block_job_resume(job); -} - -void block_job_cancel(BlockJob *job, bool force) -{ - if (job->status == BLOCK_JOB_STATUS_CONCLUDED) { - block_job_do_dismiss(job); - return; - } - block_job_cancel_async(job, force); - if (!block_job_started(job)) { - block_job_completed(job, -ECANCELED); - } else if (job->deferred_to_main_loop) { - block_job_completed_txn_abort(job); - } else { - block_job_enter(job); - } -} - -void block_job_user_cancel(BlockJob *job, bool force, Error **errp) -{ - if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) { - return; - } - block_job_cancel(job, force); -} - -/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be - * used with block_job_finish_sync() without the need for (rather nasty) - * function pointer casts there. */ -static void block_job_cancel_err(BlockJob *job, Error **errp) -{ - block_job_cancel(job, false); -} - -int block_job_cancel_sync(BlockJob *job) -{ - return block_job_finish_sync(job, &block_job_cancel_err, NULL); -} - -void block_job_cancel_sync_all(void) -{ - BlockJob *job; - AioContext *aio_context; - - while ((job = QLIST_FIRST(&block_jobs))) { - aio_context = blk_get_aio_context(job->blk); - aio_context_acquire(aio_context); - block_job_cancel_sync(job); - aio_context_release(aio_context); - } -} - -int block_job_complete_sync(BlockJob *job, Error **errp) -{ - return block_job_finish_sync(job, &block_job_complete, errp); -} - -void block_job_progress_update(BlockJob *job, uint64_t done) -{ - job->offset += done; -} - -void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining) -{ - job->len = job->offset + remaining; -} - BlockJobInfo *block_job_query(BlockJob *job, Error **errp) { BlockJobInfo *info; @@ -847,20 +255,20 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) return NULL; } info = g_new0(BlockJobInfo, 1); - info->type = g_strdup(BlockJobType_str(job->driver->job_type)); - info->device = g_strdup(job->id); - info->len = job->len; - info->busy = atomic_read(&job->busy); - info->paused = job->pause_count > 0; - info->offset = job->offset; + info->type = g_strdup(job_type_str(&job->job)); + info->device = g_strdup(job->job.id); + info->busy = atomic_read(&job->job.busy); + info->paused = job->job.pause_count > 0; + info->offset = job->job.progress_current; + info->len = job->job.progress_total; info->speed = job->speed; info->io_status = job->iostatus; - info->ready = job->ready; - info->status = job->status; - info->auto_finalize = job->auto_finalize; - info->auto_dismiss = job->auto_dismiss; - info->has_error = job->ret != 0; - info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL; + info->ready = job_is_ready(&job->job), + info->status = job->job.status; + info->auto_finalize = job->job.auto_finalize; + info->auto_dismiss = job->job.auto_dismiss; + info->has_error = job->job.ret != 0; + info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL; return info; } @@ -872,54 +280,81 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) } } -static void block_job_event_cancelled(BlockJob *job) +static void block_job_event_cancelled(Notifier *n, void *opaque) { + BlockJob *job = opaque; + if (block_job_is_internal(job)) { return; } - qapi_event_send_block_job_cancelled(job->driver->job_type, - job->id, - job->len, - job->offset, + qapi_event_send_block_job_cancelled(job_type(&job->job), + job->job.id, + job->job.progress_total, + job->job.progress_current, job->speed, &error_abort); } -static void block_job_event_completed(BlockJob *job, const char *msg) +static void block_job_event_completed(Notifier *n, void *opaque) { + BlockJob *job = opaque; + const char *msg = NULL; + if (block_job_is_internal(job)) { return; } - qapi_event_send_block_job_completed(job->driver->job_type, - job->id, - job->len, - job->offset, + if (job->job.ret < 0) { + msg = strerror(-job->job.ret); + } + + qapi_event_send_block_job_completed(job_type(&job->job), + job->job.id, + job->job.progress_total, + job->job.progress_current, job->speed, !!msg, msg, &error_abort); } -static int block_job_event_pending(BlockJob *job) +static void block_job_event_pending(Notifier *n, void *opaque) { - block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING); - if (!job->auto_finalize && !block_job_is_internal(job)) { - qapi_event_send_block_job_pending(job->driver->job_type, - job->id, - &error_abort); + BlockJob *job = opaque; + + if (block_job_is_internal(job)) { + return; } - return 0; + + qapi_event_send_block_job_pending(job_type(&job->job), + job->job.id, + &error_abort); } +static void block_job_event_ready(Notifier *n, void *opaque) +{ + BlockJob *job = opaque; + + if (block_job_is_internal(job)) { + return; + } + + qapi_event_send_block_job_ready(job_type(&job->job), + job->job.id, + job->job.progress_total, + job->job.progress_current, + job->speed, &error_abort); +} + + /* * API for block job drivers and the block layer. These functions are * declared in blockjob_int.h. */ void *block_job_create(const char *job_id, const BlockJobDriver *driver, - BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm, + JobTxn *txn, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, int64_t speed, int flags, BlockCompletionFunc *cb, void *opaque, Error **errp) { @@ -932,29 +367,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } - if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) { + if (job_id == NULL && !(flags & JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); - if (!*job_id) { - error_setg(errp, "An explicit job ID is required for this node"); - return NULL; - } - } - - if (job_id) { - if (flags & BLOCK_JOB_INTERNAL) { - error_setg(errp, "Cannot specify job ID for internal block job"); - return NULL; - } - - if (!id_wellformed(job_id)) { - error_setg(errp, "Invalid job ID '%s'", job_id); - return NULL; - } - - if (block_job_get(job_id)) { - error_setg(errp, "Job ID '%s' already in use", job_id); - return NULL; - } } blk = blk_new(perm, shared_perm); @@ -964,32 +378,39 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } - job = g_malloc0(driver->instance_size); - job->driver = driver; - job->id = g_strdup(job_id); - job->blk = blk; - job->cb = cb; - job->opaque = opaque; - job->busy = false; - job->paused = true; - job->pause_count = 1; - job->refcnt = 1; - job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); - job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); - block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED); - aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, - QEMU_CLOCK_REALTIME, SCALE_NS, - block_job_sleep_timer_cb, job); + job = job_create(job_id, &driver->job_driver, txn, blk_get_aio_context(blk), + flags, cb, opaque, errp); + if (job == NULL) { + blk_unref(blk); + return NULL; + } + + assert(is_block_job(&job->job)); + assert(job->job.driver->free == &block_job_free); + assert(job->job.driver->user_resume == &block_job_user_resume); + assert(job->job.driver->drain == &block_job_drain); + + job->blk = blk; + + job->finalize_cancelled_notifier.notify = block_job_event_cancelled; + job->finalize_completed_notifier.notify = block_job_event_completed; + job->pending_notifier.notify = block_job_event_pending; + job->ready_notifier.notify = block_job_event_ready; + + notifier_list_add(&job->job.on_finalize_cancelled, + &job->finalize_cancelled_notifier); + notifier_list_add(&job->job.on_finalize_completed, + &job->finalize_completed_notifier); + notifier_list_add(&job->job.on_pending, &job->pending_notifier); + notifier_list_add(&job->job.on_ready, &job->ready_notifier); error_setg(&job->blocker, "block device is in use by block job: %s", - BlockJobType_str(driver->job_type)); + job_type_str(&job->job)); block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); bs->job = job; bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); - QLIST_INSERT_HEAD(&block_jobs, job, job_list); - blk_add_aio_context_notifier(blk, block_job_attached_aio_context, block_job_detach_aio_context, job); @@ -999,198 +420,28 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, block_job_set_speed(job, speed, &local_err); if (local_err) { - block_job_early_fail(job); + job_early_fail(&job->job); error_propagate(errp, local_err); return NULL; } } - /* Single jobs are modeled as single-job transactions for sake of - * consolidating the job management logic */ - if (!txn) { - txn = block_job_txn_new(); - block_job_txn_add_job(txn, job); - block_job_txn_unref(txn); - } else { - block_job_txn_add_job(txn, job); - } - return job; } -void block_job_early_fail(BlockJob *job) -{ - assert(job->status == BLOCK_JOB_STATUS_CREATED); - block_job_decommission(job); -} - -void block_job_completed(BlockJob *job, int ret) -{ - assert(job && job->txn && !job->completed); - assert(blk_bs(job->blk)->job == job); - job->completed = true; - job->ret = ret; - block_job_update_rc(job); - trace_block_job_completed(job, ret, job->ret); - if (job->ret) { - block_job_completed_txn_abort(job); - } else { - block_job_completed_txn_success(job); - } -} - -static bool block_job_should_pause(BlockJob *job) -{ - return job->pause_count > 0; -} - -/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. - * Reentering the job coroutine with block_job_enter() before the timer has - * expired is allowed and cancels the timer. - * - * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be - * called explicitly. */ -static void block_job_do_yield(BlockJob *job, uint64_t ns) -{ - block_job_lock(); - if (ns != -1) { - timer_mod(&job->sleep_timer, ns); - } - job->busy = false; - block_job_unlock(); - qemu_coroutine_yield(); - - /* Set by block_job_enter before re-entering the coroutine. */ - assert(job->busy); -} - -void coroutine_fn block_job_pause_point(BlockJob *job) -{ - assert(job && block_job_started(job)); - - if (!block_job_should_pause(job)) { - return; - } - if (block_job_is_cancelled(job)) { - return; - } - - if (job->driver->pause) { - job->driver->pause(job); - } - - if (block_job_should_pause(job) && !block_job_is_cancelled(job)) { - BlockJobStatus status = job->status; - block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \ - BLOCK_JOB_STATUS_STANDBY : \ - BLOCK_JOB_STATUS_PAUSED); - job->paused = true; - block_job_do_yield(job, -1); - job->paused = false; - block_job_state_transition(job, status); - } - - if (job->driver->resume) { - job->driver->resume(job); - } -} - -/* - * Conditionally enter a block_job pending a call to fn() while - * under the block_job_lock critical section. - */ -static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)) -{ - if (!block_job_started(job)) { - return; - } - if (job->deferred_to_main_loop) { - return; - } - - block_job_lock(); - if (job->busy) { - block_job_unlock(); - return; - } - - if (fn && !fn(job)) { - block_job_unlock(); - return; - } - - assert(!job->deferred_to_main_loop); - timer_del(&job->sleep_timer); - job->busy = true; - block_job_unlock(); - aio_co_wake(job->co); -} - -void block_job_enter(BlockJob *job) -{ - block_job_enter_cond(job, NULL); -} - -bool block_job_is_cancelled(BlockJob *job) -{ - return job->cancelled; -} - -void block_job_sleep_ns(BlockJob *job, int64_t ns) -{ - assert(job->busy); - - /* Check cancellation *before* setting busy = false, too! */ - if (block_job_is_cancelled(job)) { - return; - } - - if (!block_job_should_pause(job)) { - block_job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); - } - - block_job_pause_point(job); -} - -void block_job_yield(BlockJob *job) -{ - assert(job->busy); - - /* Check cancellation *before* setting busy = false, too! */ - if (block_job_is_cancelled(job)) { - return; - } - - if (!block_job_should_pause(job)) { - block_job_do_yield(job, -1); - } - - block_job_pause_point(job); -} - void block_job_iostatus_reset(BlockJob *job) { if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { return; } - assert(job->user_paused && job->pause_count > 0); + assert(job->job.user_paused && job->job.pause_count > 0); job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; } -void block_job_event_ready(BlockJob *job) +void block_job_user_resume(Job *job) { - block_job_state_transition(job, BLOCK_JOB_STATUS_READY); - job->ready = true; - - if (block_job_is_internal(job)) { - return; - } - - qapi_event_send_block_job_ready(job->driver->job_type, - job->id, - job->len, - job->offset, - job->speed, &error_abort); + BlockJob *bjob = container_of(job, BlockJob, job); + block_job_iostatus_reset(bjob); } BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, @@ -1217,63 +468,16 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, abort(); } if (!block_job_is_internal(job)) { - qapi_event_send_block_job_error(job->id, + qapi_event_send_block_job_error(job->job.id, is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE, action, &error_abort); } if (action == BLOCK_ERROR_ACTION_STOP) { - block_job_pause(job); + job_pause(&job->job); /* make the pause user visible, which will be resumed from QMP. */ - job->user_paused = true; + job->job.user_paused = true; block_job_iostatus_set_err(job, error); } return action; } - -typedef struct { - BlockJob *job; - AioContext *aio_context; - BlockJobDeferToMainLoopFn *fn; - void *opaque; -} BlockJobDeferToMainLoopData; - -static void block_job_defer_to_main_loop_bh(void *opaque) -{ - BlockJobDeferToMainLoopData *data = opaque; - AioContext *aio_context; - - /* Prevent race with block_job_defer_to_main_loop() */ - aio_context_acquire(data->aio_context); - - /* Fetch BDS AioContext again, in case it has changed */ - aio_context = blk_get_aio_context(data->job->blk); - if (aio_context != data->aio_context) { - aio_context_acquire(aio_context); - } - - data->fn(data->job, data->opaque); - - if (aio_context != data->aio_context) { - aio_context_release(aio_context); - } - - aio_context_release(data->aio_context); - - g_free(data); -} - -void block_job_defer_to_main_loop(BlockJob *job, - BlockJobDeferToMainLoopFn *fn, - void *opaque) -{ - BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data)); - data->job = job; - data->aio_context = blk_get_aio_context(job->blk); - data->fn = fn; - data->opaque = opaque; - job->deferred_to_main_loop = true; - - aio_bh_schedule_oneshot(qemu_get_aio_context(), - block_job_defer_to_main_loop_bh, data); -} diff --git a/bsd-user/main.c b/bsd-user/main.c index 283dc6f..da3b833 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -898,9 +898,10 @@ int main(int argc, char **argv) cpu_model = "any"; #endif } + + /* init tcg before creating CPUs and to get qemu_host_page_size */ tcg_exec_init(0); - /* NOTE: we need to init the CPU at this stage to get - qemu_host_page_size */ + cpu_type = parse_cpu_model(cpu_model); cpu = cpu_create(cpu_type); env = cpu->env_ptr; @@ -917,7 +918,7 @@ int main(int argc, char **argv) envlist_free(envlist); /* - * Now that page sizes are configured in cpu_init() we can do + * Now that page sizes are configured in tcg_exec_init() we can do * proper page alignment for guest_base. */ guest_base = HOST_PAGE_ALIGN(guest_base); @@ -1588,7 +1588,7 @@ disabled with --disable-FEATURE, default is enabled if available: virtfs VirtFS mpath Multipath persistent reservation passthrough xen xen backend driver support - xen-pci-passthrough + xen-pci-passthrough PCI passthrough support for Xen brlapi BrlAPI (Braile) curl curl connectivity membarrier membarrier system call (for Linux 4.14+ or Windows) diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c index beeed0c..54e643d 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -323,13 +323,15 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) rc = sendmsg(conn_fd, &msg, 0); } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); - do { - if (vmsg->data) { - rc = write(conn_fd, vmsg->data, vmsg->size); - } else { - rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size); - } - } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (vmsg->size) { + do { + if (vmsg->data) { + rc = write(conn_fd, vmsg->data, vmsg->size); + } else { + rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size); + } + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + } if (rc <= 0) { vu_panic(dev, "Error while writing: %s", strerror(errno)); diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index 67dac81..a6a132a 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -311,6 +311,12 @@ vub_get_features(VuDev *dev) 1ull << VHOST_USER_F_PROTOCOL_FEATURES; } +static uint64_t +vub_get_protocol_features(VuDev *dev) +{ + return 1ull << VHOST_USER_PROTOCOL_F_CONFIG; +} + static int vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len) { @@ -373,6 +379,7 @@ vub_set_config(VuDev *vu_dev, const uint8_t *data, static const VuDevIface vub_iface = { .get_features = vub_get_features, .queue_set_started = vub_queue_set_started, + .get_protocol_features = vub_get_protocol_features, .get_config = vub_get_config, .set_config = vub_set_config, }; diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt index bb99a02..4d53e5c 100644 --- a/docs/specs/pci-ids.txt +++ b/docs/specs/pci-ids.txt @@ -62,6 +62,7 @@ PCI devices (other than virtio): 1b36:000a PCI-PCI bridge (multiseat) 1b36:000b PCIe Expander Bridge (-device pxb-pcie) 1b36:000d PCI xhci usb host adapter +1b36:000f mdpy (mdev sample device), linux/samples/vfio-mdev/mdpy.c All these devices are documented in docs/specs. diff --git a/docs/virtio-balloon-stats.txt b/docs/virtio-balloon-stats.txt index 7a66d25..9985e1d 100644 --- a/docs/virtio-balloon-stats.txt +++ b/docs/virtio-balloon-stats.txt @@ -34,6 +34,8 @@ which will return a dictionary containing: - stat-total-memory - stat-available-memory - stat-disk-caches + - stat-htlb-pgalloc + - stat-htlb-pgfail o A key named last-update, which contains the last stats update timestamp in seconds. Since this timestamp is generated by the host, @@ -1558,6 +1558,12 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) *p = 0; #ifdef CONFIG_USER_ONLY put_packet(s, s->syscall_buf); + /* Return control to gdb for it to process the syscall request. + * Since the protocol requires that gdb hands control back to us + * using a "here are the results" F packet, we don't need to check + * gdb_handlesig's return value (which is the signal to deliver if + * execution was resumed via a continue packet). + */ gdb_handlesig(s->c_cpu, 0); #else /* In this case wait to send the syscall packet until notification that diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 95e50c4..6026780 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -331,14 +331,14 @@ static int xen_9pfs_free(struct XenDevice *xendev) for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].data != NULL) { - xengnttab_unmap(xen_9pdev->xendev.gnttabdev, - xen_9pdev->rings[i].data, - (1 << xen_9pdev->rings[i].ring_order)); + xen_be_unmap_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].data, + (1 << xen_9pdev->rings[i].ring_order)); } if (xen_9pdev->rings[i].intf != NULL) { - xengnttab_unmap(xen_9pdev->xendev.gnttabdev, - xen_9pdev->rings[i].intf, - 1); + xen_be_unmap_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].intf, + 1); } if (xen_9pdev->rings[i].bh != NULL) { qemu_bh_delete(xen_9pdev->rings[i].bh); @@ -390,11 +390,10 @@ static int xen_9pfs_connect(struct XenDevice *xendev) } g_free(str); - xen_9pdev->rings[i].intf = xengnttab_map_grant_ref( - xen_9pdev->xendev.gnttabdev, - xen_9pdev->xendev.dom, - xen_9pdev->rings[i].ref, - PROT_READ | PROT_WRITE); + xen_9pdev->rings[i].intf = + xen_be_map_grant_ref(&xen_9pdev->xendev, + xen_9pdev->rings[i].ref, + PROT_READ | PROT_WRITE); if (!xen_9pdev->rings[i].intf) { goto out; } @@ -403,12 +402,11 @@ static int xen_9pfs_connect(struct XenDevice *xendev) goto out; } xen_9pdev->rings[i].ring_order = ring_order; - xen_9pdev->rings[i].data = xengnttab_map_domain_grant_refs( - xen_9pdev->xendev.gnttabdev, - (1 << ring_order), - xen_9pdev->xendev.dom, - xen_9pdev->rings[i].intf->ref, - PROT_READ | PROT_WRITE); + xen_9pdev->rings[i].data = + xen_be_map_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].intf->ref, + (1 << ring_order), + PROT_READ | PROT_WRITE); if (!xen_9pdev->rings[i].data) { goto out; } diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index f74fcd4..9fbc0cd 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -36,27 +36,9 @@ /* ------------------------------------------------------------- */ -static int batch_maps = 0; - -/* ------------------------------------------------------------- */ - #define BLOCK_SIZE 512 #define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) -struct PersistentGrant { - void *page; - struct XenBlkDev *blkdev; -}; - -typedef struct PersistentGrant PersistentGrant; - -struct PersistentRegion { - void *addr; - int num; -}; - -typedef struct PersistentRegion PersistentRegion; - struct ioreq { blkif_request_t req; int16_t status; @@ -64,16 +46,9 @@ struct ioreq { /* parsed request */ off_t start; QEMUIOVector v; + void *buf; + size_t size; int presync; - uint8_t mapped; - - /* grant mapping */ - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int prot; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *pages; - int num_unmap; /* aio status */ int aio_inflight; @@ -104,7 +79,6 @@ struct XenBlkDev { int protocol; blkif_back_rings_t rings; int more_work; - int cnt_map; /* request lists */ QLIST_HEAD(inflight_head, ioreq) inflight; @@ -115,13 +89,7 @@ struct XenBlkDev { int requests_finished; unsigned int max_requests; - /* Persistent grants extension */ gboolean feature_discard; - gboolean feature_persistent; - GTree *persistent_gnts; - GSList *persistent_regions; - unsigned int persistent_gnt_count; - unsigned int max_grants; /* qemu block driver */ DriveInfo *dinfo; @@ -139,14 +107,9 @@ static void ioreq_reset(struct ioreq *ioreq) memset(&ioreq->req, 0, sizeof(ioreq->req)); ioreq->status = 0; ioreq->start = 0; + ioreq->buf = NULL; + ioreq->size = 0; ioreq->presync = 0; - ioreq->mapped = 0; - - memset(ioreq->domids, 0, sizeof(ioreq->domids)); - memset(ioreq->refs, 0, sizeof(ioreq->refs)); - ioreq->prot = 0; - memset(ioreq->page, 0, sizeof(ioreq->page)); - ioreq->pages = NULL; ioreq->aio_inflight = 0; ioreq->aio_errors = 0; @@ -158,46 +121,6 @@ static void ioreq_reset(struct ioreq *ioreq) qemu_iovec_reset(&ioreq->v); } -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_grant(gpointer pgnt) -{ - PersistentGrant *grant = pgnt; - xengnttab_handle *gnt = grant->blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, grant->page, 1) != 0) { - xen_pv_printf(&grant->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - grant->blkdev->persistent_gnt_count--; - xen_pv_printf(&grant->blkdev->xendev, 3, - "unmapped grant %p\n", grant->page); - g_free(grant); -} - -static void remove_persistent_region(gpointer data, gpointer dev) -{ - PersistentRegion *region = data; - struct XenBlkDev *blkdev = dev; - xengnttab_handle *gnt = blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, region->addr, region->num) != 0) { - xen_pv_printf(&blkdev->xendev, 0, - "xengnttab_unmap region %p failed: %s\n", - region->addr, strerror(errno)); - } - xen_pv_printf(&blkdev->xendev, 3, - "unmapped grant region %p with %d pages\n", - region->addr, region->num); - g_free(region); -} - static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) { struct ioreq *ioreq = NULL; @@ -210,7 +133,7 @@ static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) ioreq = g_malloc0(sizeof(*ioreq)); ioreq->blkdev = blkdev; blkdev->requests_total++; - qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); + qemu_iovec_init(&ioreq->v, 1); } else { /* get one from freelist */ ioreq = QLIST_FIRST(&blkdev->freelist); @@ -255,17 +178,16 @@ static void ioreq_release(struct ioreq *ioreq, bool finish) static int ioreq_parse(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - uintptr_t mem; + struct XenDevice *xendev = &blkdev->xendev; size_t len; int i; - xen_pv_printf(&blkdev->xendev, 3, + xen_pv_printf(xendev, 3, "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", ioreq->req.operation, ioreq->req.nr_segments, ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); switch (ioreq->req.operation) { case BLKIF_OP_READ: - ioreq->prot = PROT_WRITE; /* to memory */ break; case BLKIF_OP_FLUSH_DISKCACHE: ioreq->presync = 1; @@ -274,45 +196,40 @@ static int ioreq_parse(struct ioreq *ioreq) } /* fall through */ case BLKIF_OP_WRITE: - ioreq->prot = PROT_READ; /* from memory */ break; case BLKIF_OP_DISCARD: return 0; default: - xen_pv_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + xen_pv_printf(xendev, 0, "error: unknown operation (%d)\n", ioreq->req.operation); goto err; }; if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { - xen_pv_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + xen_pv_printf(xendev, 0, "error: write req for ro device\n"); goto err; } ioreq->start = ioreq->req.sector_number * blkdev->file_blk; for (i = 0; i < ioreq->req.nr_segments; i++) { if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_pv_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + xen_pv_printf(xendev, 0, "error: nr_segments too big\n"); goto err; } if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_pv_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + xen_pv_printf(xendev, 0, "error: first > last sector\n"); goto err; } if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_pv_printf(&blkdev->xendev, 0, "error: page crossing\n"); + xen_pv_printf(xendev, 0, "error: page crossing\n"); goto err; } - ioreq->domids[i] = blkdev->xendev.dom; - ioreq->refs[i] = ioreq->req.seg[i].gref; - - mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; - qemu_iovec_add(&ioreq->v, (void*)mem, len); + ioreq->size += len; } - if (ioreq->start + ioreq->v.size > blkdev->file_size) { - xen_pv_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + if (ioreq->start + ioreq->size > blkdev->file_size) { + xen_pv_printf(xendev, 0, "error: access beyond end of file\n"); goto err; } return 0; @@ -322,279 +239,48 @@ err: return -1; } -static void ioreq_unmap(struct ioreq *ioreq) -{ - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - int i; - - if (ioreq->num_unmap == 0 || ioreq->mapped == 0) { - return; - } - if (batch_maps) { - if (!ioreq->pages) { - return; - } - if (xengnttab_unmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - ioreq->blkdev->cnt_map -= ioreq->num_unmap; - ioreq->pages = NULL; - } else { - for (i = 0; i < ioreq->num_unmap; i++) { - if (!ioreq->page[i]) { - continue; - } - if (xengnttab_unmap(gnt, ioreq->page[i], 1) != 0) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - ioreq->blkdev->cnt_map--; - ioreq->page[i] = NULL; - } - } - ioreq->mapped = 0; -} - -static int ioreq_map(struct ioreq *ioreq) -{ - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int i, j, new_maps = 0; - PersistentGrant *grant; - PersistentRegion *region; - /* domids and refs variables will contain the information necessary - * to map the grants that are needed to fulfill this request. - * - * After mapping the needed grants, the page array will contain the - * memory address of each granted page in the order specified in ioreq - * (disregarding if it's a persistent grant or not). - */ - - if (ioreq->v.niov == 0 || ioreq->mapped == 1) { - return 0; - } - if (ioreq->blkdev->feature_persistent) { - for (i = 0; i < ioreq->v.niov; i++) { - grant = g_tree_lookup(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(ioreq->refs[i])); - - if (grant != NULL) { - page[i] = grant->page; - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "using persistent-grant %" PRIu32 "\n", - ioreq->refs[i]); - } else { - /* Add the grant to the list of grants that - * should be mapped - */ - domids[new_maps] = ioreq->domids[i]; - refs[new_maps] = ioreq->refs[i]; - page[i] = NULL; - new_maps++; - } - } - /* Set the protection to RW, since grants may be reused later - * with a different protection than the one needed for this request - */ - ioreq->prot = PROT_WRITE | PROT_READ; - } else { - /* All grants in the request should be mapped */ - memcpy(refs, ioreq->refs, sizeof(refs)); - memcpy(domids, ioreq->domids, sizeof(domids)); - memset(page, 0, sizeof(page)); - new_maps = ioreq->v.niov; - } - - if (batch_maps && new_maps) { - ioreq->pages = xengnttab_map_grant_refs - (gnt, new_maps, domids, refs, ioreq->prot); - if (ioreq->pages == NULL) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "can't map %d grant refs (%s, %d maps)\n", - new_maps, strerror(errno), ioreq->blkdev->cnt_map); - return -1; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->pages + (j++) * XC_PAGE_SIZE; - } - } - ioreq->blkdev->cnt_map += new_maps; - } else if (new_maps) { - for (i = 0; i < new_maps; i++) { - ioreq->page[i] = xengnttab_map_grant_ref - (gnt, domids[i], refs[i], ioreq->prot); - if (ioreq->page[i] == NULL) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "can't map grant ref %d (%s, %d maps)\n", - refs[i], strerror(errno), ioreq->blkdev->cnt_map); - ioreq->mapped = 1; - ioreq_unmap(ioreq); - return -1; - } - ioreq->blkdev->cnt_map++; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->page[j++]; - } - } - } - if (ioreq->blkdev->feature_persistent && new_maps != 0 && - (!batch_maps || (ioreq->blkdev->persistent_gnt_count + new_maps <= - ioreq->blkdev->max_grants))) { - /* - * If we are using persistent grants and batch mappings only - * add the new maps to the list of persistent grants if the whole - * area can be persistently mapped. - */ - if (batch_maps) { - region = g_malloc0(sizeof(*region)); - region->addr = ioreq->pages; - region->num = new_maps; - ioreq->blkdev->persistent_regions = g_slist_append( - ioreq->blkdev->persistent_regions, - region); - } - while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants) - && new_maps) { - /* Go through the list of newly mapped grants and add as many - * as possible to the list of persistently mapped grants. - * - * Since we start at the end of ioreq->page(s), we only need - * to decrease new_maps to prevent this granted pages from - * being unmapped in ioreq_unmap. - */ - grant = g_malloc0(sizeof(*grant)); - new_maps--; - if (batch_maps) { - grant->page = ioreq->pages + (new_maps) * XC_PAGE_SIZE; - } else { - grant->page = ioreq->page[new_maps]; - } - grant->blkdev = ioreq->blkdev; - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "adding grant %" PRIu32 " page: %p\n", - refs[new_maps], grant->page); - g_tree_insert(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(refs[new_maps]), - grant); - ioreq->blkdev->persistent_gnt_count++; - } - assert(!batch_maps || new_maps == 0); - } - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->v.iov[i].iov_base += (uintptr_t)page[i]; - } - ioreq->mapped = 1; - ioreq->num_unmap = new_maps; - return 0; -} - -#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 - -static void ioreq_free_copy_buffers(struct ioreq *ioreq) -{ - int i; - - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = NULL; - } - - qemu_vfree(ioreq->pages); -} - -static int ioreq_init_copy_buffers(struct ioreq *ioreq) -{ - int i; - - if (ioreq->v.niov == 0) { - return 0; - } - - ioreq->pages = qemu_memalign(XC_PAGE_SIZE, ioreq->v.niov * XC_PAGE_SIZE); - - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = ioreq->pages + i * XC_PAGE_SIZE; - ioreq->v.iov[i].iov_base = ioreq->page[i]; - } - - return 0; -} - static int ioreq_grant_copy(struct ioreq *ioreq) { - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - xengnttab_grant_copy_segment_t segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; + XenGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; int i, count, rc; - int64_t file_blk = ioreq->blkdev->file_blk; + int64_t file_blk = blkdev->file_blk; + bool to_domain = (ioreq->req.operation == BLKIF_OP_READ); + void *virt = ioreq->buf; - if (ioreq->v.niov == 0) { + if (ioreq->req.nr_segments == 0) { return 0; } - count = ioreq->v.niov; + count = ioreq->req.nr_segments; for (i = 0; i < count; i++) { - if (ioreq->req.operation == BLKIF_OP_READ) { - segs[i].flags = GNTCOPY_dest_gref; - segs[i].dest.foreign.ref = ioreq->refs[i]; - segs[i].dest.foreign.domid = ioreq->domids[i]; + if (to_domain) { + segs[i].dest.foreign.ref = ioreq->req.seg[i].gref; segs[i].dest.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].source.virt = ioreq->v.iov[i].iov_base; + segs[i].source.virt = virt; } else { - segs[i].flags = GNTCOPY_source_gref; - segs[i].source.foreign.ref = ioreq->refs[i]; - segs[i].source.foreign.domid = ioreq->domids[i]; + segs[i].source.foreign.ref = ioreq->req.seg[i].gref; segs[i].source.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].dest.virt = ioreq->v.iov[i].iov_base; + segs[i].dest.virt = virt; } segs[i].len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * file_blk; + virt += segs[i].len; } - rc = xengnttab_grant_copy(gnt, count, segs); + rc = xen_be_copy_grant_refs(xendev, to_domain, segs, count); if (rc) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, + xen_pv_printf(xendev, 0, "failed to copy data %d\n", rc); ioreq->aio_errors++; return -1; } - for (i = 0; i < count; i++) { - if (segs[i].status != GNTST_okay) { - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "failed to copy data %d for gref %d, domid %d\n", - segs[i].status, ioreq->refs[i], ioreq->domids[i]); - ioreq->aio_errors++; - rc = -1; - } - } - return rc; } -#else -static void ioreq_free_copy_buffers(struct ioreq *ioreq) -{ - abort(); -} - -static int ioreq_init_copy_buffers(struct ioreq *ioreq) -{ - abort(); -} - -static int ioreq_grant_copy(struct ioreq *ioreq) -{ - abort(); -} -#endif static int ioreq_runio_qemu_aio(struct ioreq *ioreq); @@ -602,11 +288,12 @@ static void qemu_aio_complete(void *opaque, int ret) { struct ioreq *ioreq = opaque; struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; aio_context_acquire(blkdev->ctx); if (ret != 0) { - xen_pv_printf(&blkdev->xendev, 0, "%s I/O error\n", + xen_pv_printf(xendev, 0, "%s I/O error\n", ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); ioreq->aio_errors++; } @@ -621,32 +308,28 @@ static void qemu_aio_complete(void *opaque, int ret) goto done; } - if (xen_feature_grant_copy) { - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - /* in case of failure ioreq->aio_errors is increased */ - if (ret == 0) { - ioreq_grant_copy(ioreq); - } - ioreq_free_copy_buffers(ioreq); - break; - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - ioreq_free_copy_buffers(ioreq); - break; - default: + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + /* in case of failure ioreq->aio_errors is increased */ + if (ret == 0) { + ioreq_grant_copy(ioreq); + } + qemu_vfree(ioreq->buf); + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_FLUSH_DISKCACHE: + if (!ioreq->req.nr_segments) { break; } + qemu_vfree(ioreq->buf); + break; + default: + break; } ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; - if (!xen_feature_grant_copy) { - ioreq_unmap(ioreq); - } ioreq_finish(ioreq); + switch (ioreq->req.operation) { case BLKIF_OP_WRITE: case BLKIF_OP_FLUSH_DISKCACHE: @@ -706,18 +389,13 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - if (xen_feature_grant_copy) { - ioreq_init_copy_buffers(ioreq); - if (ioreq->req.nr_segments && (ioreq->req.operation == BLKIF_OP_WRITE || - ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && - ioreq_grant_copy(ioreq)) { - ioreq_free_copy_buffers(ioreq); - goto err; - } - } else { - if (ioreq->req.nr_segments && ioreq_map(ioreq)) { - goto err; - } + ioreq->buf = qemu_memalign(XC_PAGE_SIZE, ioreq->size); + if (ioreq->req.nr_segments && + (ioreq->req.operation == BLKIF_OP_WRITE || + ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && + ioreq_grant_copy(ioreq)) { + qemu_vfree(ioreq->buf); + goto err; } ioreq->aio_inflight++; @@ -728,6 +406,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) switch (ioreq->req.operation) { case BLKIF_OP_READ: + qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, ioreq->v.size, BLOCK_ACCT_READ); ioreq->aio_inflight++; @@ -740,6 +419,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) break; } + qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, ioreq->v.size, ioreq->req.operation == BLKIF_OP_WRITE ? @@ -758,9 +438,6 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) } default: /* unknown operation (shouldn't happen -- parse catches this) */ - if (!xen_feature_grant_copy) { - ioreq_unmap(ioreq); - } goto err; } @@ -946,24 +623,21 @@ static void blk_alloc(struct XenDevice *xendev) blkdev->ctx = iothread_get_aio_context(blkdev->iothread); blkdev->bh = aio_bh_new(blkdev->ctx, blk_bh, blkdev); - - if (xen_mode != XEN_EMULATE) { - batch_maps = 1; - } } static void blk_parse_discard(struct XenBlkDev *blkdev) { + struct XenDevice *xendev = &blkdev->xendev; int enable; blkdev->feature_discard = true; - if (xenstore_read_be_int(&blkdev->xendev, "discard-enable", &enable) == 0) { + if (xenstore_read_be_int(xendev, "discard-enable", &enable) == 0) { blkdev->feature_discard = !!enable; } if (blkdev->feature_discard) { - xenstore_write_be_int(&blkdev->xendev, "feature-discard", 1); + xenstore_write_be_int(xendev, "feature-discard", 1); } } @@ -978,7 +652,7 @@ static int blk_init(struct XenDevice *xendev) /* read xenstore entries */ if (blkdev->params == NULL) { char *h = NULL; - blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + blkdev->params = xenstore_read_be_str(xendev, "params"); if (blkdev->params != NULL) { h = strchr(blkdev->params, ':'); } @@ -998,18 +672,18 @@ static int blk_init(struct XenDevice *xendev) blkdev->fileproto = "vpc"; } if (blkdev->mode == NULL) { - blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + blkdev->mode = xenstore_read_be_str(xendev, "mode"); } if (blkdev->type == NULL) { - blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + blkdev->type = xenstore_read_be_str(xendev, "type"); } if (blkdev->dev == NULL) { - blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + blkdev->dev = xenstore_read_be_str(xendev, "dev"); } if (blkdev->devtype == NULL) { - blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + blkdev->devtype = xenstore_read_be_str(xendev, "device-type"); } - directiosafe = xenstore_read_be_str(&blkdev->xendev, "direct-io-safe"); + directiosafe = xenstore_read_be_str(xendev, "direct-io-safe"); blkdev->directiosafe = (directiosafe && atoi(directiosafe)); /* do we have all we need? */ @@ -1032,18 +706,13 @@ static int blk_init(struct XenDevice *xendev) blkdev->file_blk = BLOCK_SIZE; - xen_pv_printf(&blkdev->xendev, 3, "grant copy operation %s\n", - xen_feature_grant_copy ? "enabled" : "disabled"); - /* fill info * blk_connect supplies sector-size and sectors */ - xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); - xenstore_write_be_int(&blkdev->xendev, "feature-persistent", - !xen_feature_grant_copy); - xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(xendev, "feature-flush-cache", 1); + xenstore_write_be_int(xendev, "info", info); - xenstore_write_be_int(&blkdev->xendev, "max-ring-page-order", + xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_PAGE_ORDER); blk_parse_discard(blkdev); @@ -1067,25 +736,15 @@ out_error: return -1; } -/* - * We need to account for the grant allocations requiring contiguous - * chunks; the worst case number would be - * max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1, - * but in order to keep things simple just use - * 2 * max_req * max_seg. - */ -#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg)) - static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int pers, index, qflags; + int index, qflags; bool readonly = true; bool writethrough = true; int order, ring_ref; unsigned int ring_size, max_grants; unsigned int i; - uint32_t *domids; trace_xen_disk_connect(xendev->name); @@ -1105,7 +764,7 @@ static int blk_connect(struct XenDevice *xendev) } /* init qemu block driver */ - index = (blkdev->xendev.dev - 202 * 256) / 16; + index = (xendev->dev - 202 * 256) / 16; blkdev->dinfo = drive_get(IF_XEN, 0, index); if (!blkdev->dinfo) { Error *local_err = NULL; @@ -1117,11 +776,11 @@ static int blk_connect(struct XenDevice *xendev) } /* setup via xenbus -> create new block driver instance */ - xen_pv_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); + xen_pv_printf(xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->blk = blk_new_open(blkdev->filename, NULL, options, qflags, &local_err); if (!blkdev->blk) { - xen_pv_printf(&blkdev->xendev, 0, "error: %s\n", + xen_pv_printf(xendev, 0, "error: %s\n", error_get_pretty(local_err)); error_free(local_err); return -1; @@ -1129,11 +788,11 @@ static int blk_connect(struct XenDevice *xendev) blk_set_enable_write_cache(blkdev->blk, !writethrough); } else { /* setup via qemu cmdline -> already setup for us */ - xen_pv_printf(&blkdev->xendev, 2, + xen_pv_printf(xendev, 2, "get configured bdrv (cmdline setup)\n"); blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo); if (blk_is_read_only(blkdev->blk) && !readonly) { - xen_pv_printf(&blkdev->xendev, 0, "Unexpected read-only drive"); + xen_pv_printf(xendev, 0, "Unexpected read-only drive"); blkdev->blk = NULL; return -1; } @@ -1146,7 +805,7 @@ static int blk_connect(struct XenDevice *xendev) if (blkdev->file_size < 0) { BlockDriverState *bs = blk_bs(blkdev->blk); const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; - xen_pv_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", + xen_pv_printf(xendev, 1, "blk_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), drv_name ?: "-"); blkdev->file_size = 0; @@ -1158,15 +817,15 @@ static int blk_connect(struct XenDevice *xendev) blkdev->file_size, blkdev->file_size >> 20); /* Fill in number of sector size and number of sectors */ - xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); - xenstore_write_be_int64(&blkdev->xendev, "sectors", + xenstore_write_be_int(xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int64(xendev, "sectors", blkdev->file_size / blkdev->file_blk); - if (xenstore_read_fe_int(&blkdev->xendev, "ring-page-order", + if (xenstore_read_fe_int(xendev, "ring-page-order", &order) == -1) { blkdev->nr_ring_ref = 1; - if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", + if (xenstore_read_fe_int(xendev, "ring-ref", &ring_ref) == -1) { return -1; } @@ -1183,7 +842,7 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, key, + if (xenstore_read_fe_int(xendev, key, &ring_ref) == -1) { g_free(key); return -1; @@ -1198,23 +857,18 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", - &blkdev->xendev.remote_port) == -1) { + if (xenstore_read_fe_int(xendev, "event-channel", + &xendev->remote_port) == -1) { return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, "feature-persistent", &pers)) { - blkdev->feature_persistent = FALSE; - } else { - blkdev->feature_persistent = !!pers; - } - if (!blkdev->xendev.protocol) { + if (!xendev->protocol) { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_32; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_64; } else { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; @@ -1241,43 +895,17 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - /* Calculate the maximum number of grants needed by ioreqs */ - max_grants = MAX_GRANTS(blkdev->max_requests, - BLKIF_MAX_SEGMENTS_PER_REQUEST); /* Add on the number needed for the ring pages */ - max_grants += blkdev->nr_ring_ref; - - blkdev->xendev.gnttabdev = xengnttab_open(NULL, 0); - if (blkdev->xendev.gnttabdev == NULL) { - xen_pv_printf(xendev, 0, "xengnttab_open failed: %s\n", - strerror(errno)); - return -1; - } - if (xengnttab_set_max_grants(blkdev->xendev.gnttabdev, max_grants)) { - xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - return -1; - } - - domids = g_new0(uint32_t, blkdev->nr_ring_ref); - for (i = 0; i < blkdev->nr_ring_ref; i++) { - domids[i] = blkdev->xendev.dom; - } - - blkdev->sring = xengnttab_map_grant_refs(blkdev->xendev.gnttabdev, - blkdev->nr_ring_ref, - domids, - blkdev->ring_ref, - PROT_READ | PROT_WRITE); - - g_free(domids); + max_grants = blkdev->nr_ring_ref; + xen_be_set_max_grant_refs(xendev, max_grants); + blkdev->sring = xen_be_map_grant_refs(xendev, blkdev->ring_ref, + blkdev->nr_ring_ref, + PROT_READ | PROT_WRITE); if (!blkdev->sring) { return -1; } - blkdev->cnt_map++; - switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: { @@ -1301,27 +929,14 @@ static int blk_connect(struct XenDevice *xendev) } } - if (blkdev->feature_persistent) { - /* Init persistent grants */ - blkdev->max_grants = blkdev->max_requests * - BLKIF_MAX_SEGMENTS_PER_REQUEST; - blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp, - NULL, NULL, - batch_maps ? - (GDestroyNotify)g_free : - (GDestroyNotify)destroy_grant); - blkdev->persistent_regions = NULL; - blkdev->persistent_gnt_count = 0; - } - blk_set_aio_context(blkdev->blk, blkdev->ctx); - xen_be_bind_evtchn(&blkdev->xendev); + xen_be_bind_evtchn(xendev); - xen_pv_printf(&blkdev->xendev, 1, "ok: proto %s, nr-ring-ref %u, " + xen_pv_printf(xendev, 1, "ok: proto %s, nr-ring-ref %u, " "remote port %d, local port %d\n", - blkdev->xendev.protocol, blkdev->nr_ring_ref, - blkdev->xendev.remote_port, blkdev->xendev.local_port); + xendev->protocol, blkdev->nr_ring_ref, + xendev->remote_port, xendev->local_port); return 0; } @@ -1339,41 +954,15 @@ static void blk_disconnect(struct XenDevice *xendev) blk_unref(blkdev->blk); blkdev->blk = NULL; } - xen_pv_unbind_evtchn(&blkdev->xendev); + xen_pv_unbind_evtchn(xendev); aio_context_release(blkdev->ctx); if (blkdev->sring) { - xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, - blkdev->nr_ring_ref); - blkdev->cnt_map--; + xen_be_unmap_grant_refs(xendev, blkdev->sring, + blkdev->nr_ring_ref); blkdev->sring = NULL; } - - /* - * Unmap persistent grants before switching to the closed state - * so the frontend can free them. - * - * In the !batch_maps case g_tree_destroy will take care of unmapping - * the grant, but in the batch_maps case we need to iterate over every - * region in persistent_regions and unmap it. - */ - if (blkdev->feature_persistent) { - g_tree_destroy(blkdev->persistent_gnts); - assert(batch_maps || blkdev->persistent_gnt_count == 0); - if (batch_maps) { - blkdev->persistent_gnt_count = 0; - g_slist_foreach(blkdev->persistent_regions, - (GFunc)remove_persistent_region, blkdev); - g_slist_free(blkdev->persistent_regions); - } - blkdev->feature_persistent = false; - } - - if (blkdev->xendev.gnttabdev) { - xengnttab_close(blkdev->xendev.gnttabdev); - blkdev->xendev.gnttabdev = NULL; - } } static int blk_free(struct XenDevice *xendev) @@ -1410,10 +999,11 @@ static void blk_event(struct XenDevice *xendev) } struct XenDevOps xen_blkdev_ops = { + .flags = DEVOPS_FLAG_NEED_GNTDEV, .size = sizeof(struct XenBlkDev), .alloc = blk_alloc, .init = blk_init, - .initialise = blk_connect, + .initialise = blk_connect, .disconnect = blk_disconnect, .event = blk_event, .free = blk_free, diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index bdfaa40..8b4b4bf 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -233,12 +233,11 @@ static int con_initialise(struct XenDevice *xendev) if (!xendev->dev) { xen_pfn_t mfn = con->ring_ref; con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom, - PROT_READ|PROT_WRITE, + PROT_READ | PROT_WRITE, 1, &mfn, NULL); } else { - con->sring = xengnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom, - con->ring_ref, - PROT_READ|PROT_WRITE); + con->sring = xen_be_map_grant_ref(xendev, con->ring_ref, + PROT_READ | PROT_WRITE); } if (!con->sring) return -1; @@ -267,7 +266,7 @@ static void con_disconnect(struct XenDevice *xendev) if (!xendev->dev) { xenforeignmemory_unmap(xen_fmem, con->sring, 1); } else { - xengnttab_unmap(xendev->gnttabdev, con->sring, 1); + xen_be_unmap_grant_ref(xendev, con->sring); } con->sring = NULL; } diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 11321e4..d907b38 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -9,6 +9,7 @@ common-obj-$(CONFIG_SSD0323) += ssd0323.o common-obj-$(CONFIG_XEN) += xenfb.o common-obj-$(CONFIG_VGA_PCI) += vga-pci.o +common-obj-$(CONFIG_VGA_PCI) += bochs-display.o common-obj-$(CONFIG_VGA_ISA) += vga-isa.o common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c new file mode 100644 index 0000000..c33524b --- /dev/null +++ b/hw/display/bochs-display.c @@ -0,0 +1,363 @@ +/* + * QEMU PCI bochs display adapter. + * + * 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 "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/display/bochs-vbe.h" + +#include "qapi/error.h" + +#include "ui/console.h" +#include "ui/qemu-pixman.h" + +typedef struct BochsDisplayMode { + pixman_format_code_t format; + uint32_t bytepp; + uint32_t width; + uint32_t height; + uint32_t stride; + uint64_t offset; + uint64_t size; +} BochsDisplayMode; + +typedef struct BochsDisplayState { + /* parent */ + PCIDevice pci; + + /* device elements */ + QemuConsole *con; + MemoryRegion vram; + MemoryRegion mmio; + MemoryRegion vbe; + MemoryRegion qext; + + /* device config */ + uint64_t vgamem; + + /* device registers */ + uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; + bool big_endian_fb; + + /* device state */ + BochsDisplayMode mode; +} BochsDisplayState; + +#define TYPE_BOCHS_DISPLAY "bochs-display" +#define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \ + TYPE_BOCHS_DISPLAY) + +static const VMStateDescription vmstate_bochs_display = { + .name = "bochs-display", + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pci, BochsDisplayState), + VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB), + VMSTATE_BOOL(big_endian_fb, BochsDisplayState), + VMSTATE_END_OF_LIST() + } +}; + +static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr, + unsigned size) +{ + BochsDisplayState *s = ptr; + unsigned int index = addr >> 1; + + switch (index) { + case VBE_DISPI_INDEX_ID: + return VBE_DISPI_ID5; + case VBE_DISPI_INDEX_VIDEO_MEMORY_64K: + return s->vgamem / (64 * 1024); + } + + if (index >= ARRAY_SIZE(s->vbe_regs)) { + return -1; + } + return s->vbe_regs[index]; +} + +static void bochs_display_vbe_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + BochsDisplayState *s = ptr; + unsigned int index = addr >> 1; + + if (index >= ARRAY_SIZE(s->vbe_regs)) { + return; + } + s->vbe_regs[index] = val; +} + +static const MemoryRegionOps bochs_display_vbe_ops = { + .read = bochs_display_vbe_read, + .write = bochs_display_vbe_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 2, + .impl.max_access_size = 2, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr, + unsigned size) +{ + BochsDisplayState *s = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_SIZE: + return PCI_VGA_QEXT_SIZE; + case PCI_VGA_QEXT_REG_BYTEORDER: + return s->big_endian_fb ? + PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN; + default: + return 0; + } +} + +static void bochs_display_qext_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + BochsDisplayState *s = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_BYTEORDER: + if (val == PCI_VGA_QEXT_BIG_ENDIAN) { + s->big_endian_fb = true; + } + if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) { + s->big_endian_fb = false; + } + break; + } +} + +static const MemoryRegionOps bochs_display_qext_ops = { + .read = bochs_display_qext_read, + .write = bochs_display_qext_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int bochs_display_get_mode(BochsDisplayState *s, + BochsDisplayMode *mode) +{ + uint16_t *vbe = s->vbe_regs; + uint32_t virt_width; + + if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { + return -1; + } + + memset(mode, 0, sizeof(*mode)); + switch (vbe[VBE_DISPI_INDEX_BPP]) { + case 16: + /* best effort: support native endianess only */ + mode->format = PIXMAN_r5g6b5; + mode->bytepp = 2; + case 32: + mode->format = s->big_endian_fb + ? PIXMAN_BE_x8r8g8b8 + : PIXMAN_LE_x8r8g8b8; + mode->bytepp = 4; + break; + default: + return -1; + } + + mode->width = vbe[VBE_DISPI_INDEX_XRES]; + mode->height = vbe[VBE_DISPI_INDEX_YRES]; + virt_width = vbe[VBE_DISPI_INDEX_VIRT_WIDTH]; + if (virt_width < mode->width) { + virt_width = mode->width; + } + mode->stride = virt_width * mode->bytepp; + mode->size = (uint64_t)mode->stride * mode->height; + mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp + + (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride); + + if (mode->width < 64 || mode->height < 64) { + return -1; + } + if (mode->offset + mode->size > s->vgamem) { + return -1; + } + return 0; +} + +static void bochs_display_update(void *opaque) +{ + BochsDisplayState *s = opaque; + DirtyBitmapSnapshot *snap = NULL; + bool full_update = false; + BochsDisplayMode mode; + DisplaySurface *ds; + uint8_t *ptr; + bool dirty; + int y, ys, ret; + + ret = bochs_display_get_mode(s, &mode); + if (ret < 0) { + /* no (valid) video mode */ + return; + } + + if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) { + /* video mode switch */ + s->mode = mode; + ptr = memory_region_get_ram_ptr(&s->vram); + ds = qemu_create_displaysurface_from(mode.width, + mode.height, + mode.format, + mode.stride, + ptr + mode.offset); + dpy_gfx_replace_surface(s->con, ds); + full_update = true; + } + + if (full_update) { + dpy_gfx_update_full(s->con); + } else { + snap = memory_region_snapshot_and_clear_dirty(&s->vram, + mode.offset, mode.size, + DIRTY_MEMORY_VGA); + ys = -1; + for (y = 0; y < mode.height; y++) { + dirty = memory_region_snapshot_get_dirty(&s->vram, snap, + mode.offset + mode.stride * y, + mode.stride); + if (dirty && ys < 0) { + ys = y; + } + if (!dirty && ys >= 0) { + dpy_gfx_update(s->con, 0, ys, + mode.width, y - ys); + ys = -1; + } + } + if (ys >= 0) { + dpy_gfx_update(s->con, 0, ys, + mode.width, y - ys); + } + } +} + +static const GraphicHwOps bochs_display_gfx_ops = { + .gfx_update = bochs_display_update, +}; + +static void bochs_display_realize(PCIDevice *dev, Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(dev); + Object *obj = OBJECT(dev); + int ret; + + s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); + + if (s->vgamem < (4 * 1024 * 1024)) { + error_setg(errp, "bochs-display: video memory too small"); + } + if (s->vgamem > (256 * 1024 * 1024)) { + error_setg(errp, "bochs-display: video memory too big"); + } + s->vgamem = pow2ceil(s->vgamem); + + memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem, + &error_fatal); + memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s, + "bochs dispi interface", PCI_VGA_BOCHS_SIZE); + memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s, + "qemu extended regs", PCI_VGA_QEXT_SIZE); + + memory_region_init(&s->mmio, obj, "bochs-display-mmio", + PCI_VGA_MMIO_SIZE); + memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe); + memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext); + + pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2); + pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); + pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + + if (pci_bus_is_express(pci_get_bus(dev))) { + dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + ret = pcie_endpoint_cap_init(dev, 0x80); + assert(ret > 0); + } + + memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); +} + +static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(obj); + + return s->big_endian_fb; +} + +static void bochs_display_set_big_endian_fb(Object *obj, bool value, + Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(obj); + + s->big_endian_fb = value; +} + +static void bochs_display_init(Object *obj) +{ + /* Expose framebuffer byteorder via QOM */ + object_property_add_bool(obj, "big-endian-framebuffer", + bochs_display_get_big_endian_fb, + bochs_display_set_big_endian_fb, + NULL); +} + +static void bochs_display_exit(PCIDevice *dev) +{ + BochsDisplayState *s = BOCHS_DISPLAY(dev); + + graphic_console_close(s->con); +} + +static Property bochs_display_properties[] = { + DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * 1024 * 1024), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bochs_display_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->class_id = PCI_CLASS_DISPLAY_OTHER; + k->vendor_id = PCI_VENDOR_ID_QEMU; + k->device_id = PCI_DEVICE_ID_QEMU_VGA; + + k->realize = bochs_display_realize; + k->exit = bochs_display_exit; + dc->vmsd = &vmstate_bochs_display; + dc->props = bochs_display_properties; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); +} + +static const TypeInfo bochs_display_type_info = { + .name = TYPE_BOCHS_DISPLAY, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(BochsDisplayState), + .instance_init = bochs_display_init, + .class_init = bochs_display_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void bochs_display_register_types(void) +{ + type_register_static(&bochs_display_type_info); +} + +type_init(bochs_display_register_types) diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index f312930..700ac58 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -31,19 +31,6 @@ #include "qemu/timer.h" #include "hw/loader.h" -#define PCI_VGA_IOPORT_OFFSET 0x400 -#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) -#define PCI_VGA_BOCHS_OFFSET 0x500 -#define PCI_VGA_BOCHS_SIZE (0x0b * 2) -#define PCI_VGA_QEXT_OFFSET 0x600 -#define PCI_VGA_QEXT_SIZE (2 * 4) -#define PCI_VGA_MMIO_SIZE 0x1000 - -#define PCI_VGA_QEXT_REG_SIZE (0 * 4) -#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) -#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e -#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe - enum vga_pci_flags { PCI_VGA_FLAG_ENABLE_MMIO = 1, PCI_VGA_FLAG_ENABLE_QEXT = 2, @@ -245,7 +232,8 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) /* mmio bar for vga register access */ if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) { - memory_region_init(&d->mmio, NULL, "vga.mmio", 4096); + memory_region_init(&d->mmio, NULL, "vga.mmio", + PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; @@ -280,7 +268,8 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* mmio bar */ - memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", 4096); + memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", + PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; diff --git a/hw/display/vga.c b/hw/display/vga.c index 7218133..a7794f6 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1480,13 +1480,28 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->get_resolution(s, &width, &height); disp_width = width; + depth = s->get_bpp(s); region_start = (s->start_addr * 4); region_end = region_start + (ram_addr_t)s->line_offset * height; - region_end += width * s->get_bpp(s) / 8; /* scanline length */ + region_end += width * depth / 8; /* scanline length */ region_end -= s->line_offset; - if (region_end > s->vbe_size) { - /* wraps around (can happen with cirrus vbe modes) */ + if (region_end > s->vbe_size || depth == 0 || depth == 15) { + /* + * We land here on: + * - wraps around (can happen with cirrus vbe modes) + * - depth == 0 (256 color palette video mode) + * - depth == 15 + * + * Take the safe and slow route: + * - create a dirty bitmap snapshot for all vga memory. + * - force shadowing (so all vga memory access goes + * through vga_read_*() helpers). + * + * Given this affects only vga features which are pretty much + * unused by modern guests there should be no performance + * impact. + */ region_start = 0; region_end = s->vbe_size; force_shadow = true; @@ -1520,8 +1535,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } } - depth = s->get_bpp(s); - /* * Check whether we can share the surface with the backend * or whether we need a shadow surface. We share native diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index fe23b81..313cff8 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -29,42 +29,11 @@ #include "exec/memory.h" #include "ui/console.h" +#include "hw/display/bochs-vbe.h" + #define ST01_V_RETRACE 0x08 #define ST01_DISP_ENABLE 0x01 -#define VBE_DISPI_MAX_XRES 16000 -#define VBE_DISPI_MAX_YRES 12000 -#define VBE_DISPI_MAX_BPP 32 - -#define VBE_DISPI_INDEX_ID 0x0 -#define VBE_DISPI_INDEX_XRES 0x1 -#define VBE_DISPI_INDEX_YRES 0x2 -#define VBE_DISPI_INDEX_BPP 0x3 -#define VBE_DISPI_INDEX_ENABLE 0x4 -#define VBE_DISPI_INDEX_BANK 0x5 -#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 -#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 -#define VBE_DISPI_INDEX_X_OFFSET 0x8 -#define VBE_DISPI_INDEX_Y_OFFSET 0x9 -#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ -#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ - -#define VBE_DISPI_ID0 0xB0C0 -#define VBE_DISPI_ID1 0xB0C1 -#define VBE_DISPI_ID2 0xB0C2 -#define VBE_DISPI_ID3 0xB0C3 -#define VBE_DISPI_ID4 0xB0C4 -#define VBE_DISPI_ID5 0xB0C5 - -#define VBE_DISPI_DISABLED 0x00 -#define VBE_DISPI_ENABLED 0x01 -#define VBE_DISPI_GETCAPS 0x02 -#define VBE_DISPI_8BIT_DAC 0x20 -#define VBE_DISPI_LFB_ENABLED 0x40 -#define VBE_DISPI_NOCLEARMEM 0x80 - -#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 - #define CH_ATTR_SIZE (160 * 100) #define VGA_MAX_HEIGHT 2048 diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index fb31de9..b5a09b7 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -128,6 +128,22 @@ static uint64_t vtd_set_clear_mask_quad(IntelIOMMUState *s, hwaddr addr, return new_val; } +static inline void vtd_iommu_lock(IntelIOMMUState *s) +{ + qemu_mutex_lock(&s->iommu_lock); +} + +static inline void vtd_iommu_unlock(IntelIOMMUState *s) +{ + qemu_mutex_unlock(&s->iommu_lock); +} + +/* Whether the address space needs to notify new mappings */ +static inline gboolean vtd_as_has_map_notifier(VTDAddressSpace *as) +{ + return as->notifier_flags & IOMMU_NOTIFIER_MAP; +} + /* GHashTable functions */ static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2) { @@ -172,9 +188,9 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, } /* Reset all the gen of VTDAddressSpace to zero and set the gen of - * IntelIOMMUState to 1. + * IntelIOMMUState to 1. Must be called with IOMMU lock held. */ -static void vtd_reset_context_cache(IntelIOMMUState *s) +static void vtd_reset_context_cache_locked(IntelIOMMUState *s) { VTDAddressSpace *vtd_as; VTDBus *vtd_bus; @@ -197,12 +213,20 @@ static void vtd_reset_context_cache(IntelIOMMUState *s) s->context_cache_gen = 1; } -static void vtd_reset_iotlb(IntelIOMMUState *s) +/* Must be called with IOMMU lock held. */ +static void vtd_reset_iotlb_locked(IntelIOMMUState *s) { assert(s->iotlb); g_hash_table_remove_all(s->iotlb); } +static void vtd_reset_iotlb(IntelIOMMUState *s) +{ + vtd_iommu_lock(s); + vtd_reset_iotlb_locked(s); + vtd_iommu_unlock(s); +} + static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id, uint32_t level) { @@ -215,6 +239,7 @@ static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; } +/* Must be called with IOMMU lock held */ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, hwaddr addr) { @@ -235,6 +260,7 @@ out: return entry; } +/* Must be with IOMMU lock held */ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, uint16_t domain_id, hwaddr addr, uint64_t slpte, uint8_t access_flags, uint32_t level) @@ -246,7 +272,7 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id); if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) { trace_vtd_iotlb_reset("iotlb exceeds size limit"); - vtd_reset_iotlb(s); + vtd_reset_iotlb_locked(s); } entry->gfn = gfn; @@ -723,22 +749,116 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); /** + * Constant information used during page walking + * + * @hook_fn: hook func to be called when detected page + * @private: private data to be passed into hook func + * @notify_unmap: whether we should notify invalid entries + * @as: VT-d address space of the device + * @aw: maximum address width + * @domain: domain ID of the page walk + */ +typedef struct { + VTDAddressSpace *as; + vtd_page_walk_hook hook_fn; + void *private; + bool notify_unmap; + uint8_t aw; + uint16_t domain_id; +} vtd_page_walk_info; + +static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) +{ + VTDAddressSpace *as = info->as; + vtd_page_walk_hook hook_fn = info->hook_fn; + void *private = info->private; + DMAMap target = { + .iova = entry->iova, + .size = entry->addr_mask, + .translated_addr = entry->translated_addr, + .perm = entry->perm, + }; + DMAMap *mapped = iova_tree_find(as->iova_tree, &target); + + if (entry->perm == IOMMU_NONE && !info->notify_unmap) { + trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); + return 0; + } + + assert(hook_fn); + + /* Update local IOVA mapped ranges */ + if (entry->perm) { + if (mapped) { + /* If it's exactly the same translation, skip */ + if (!memcmp(mapped, &target, sizeof(target))) { + trace_vtd_page_walk_one_skip_map(entry->iova, entry->addr_mask, + entry->translated_addr); + return 0; + } else { + /* + * Translation changed. Normally this should not + * happen, but it can happen when with buggy guest + * OSes. Note that there will be a small window that + * we don't have map at all. But that's the best + * effort we can do. The ideal way to emulate this is + * atomically modify the PTE to follow what has + * changed, but we can't. One example is that vfio + * driver only has VFIO_IOMMU_[UN]MAP_DMA but no + * interface to modify a mapping (meanwhile it seems + * meaningless to even provide one). Anyway, let's + * mark this as a TODO in case one day we'll have + * a better solution. + */ + IOMMUAccessFlags cache_perm = entry->perm; + int ret; + + /* Emulate an UNMAP */ + entry->perm = IOMMU_NONE; + trace_vtd_page_walk_one(info->domain_id, + entry->iova, + entry->translated_addr, + entry->addr_mask, + entry->perm); + ret = hook_fn(entry, private); + if (ret) { + return ret; + } + /* Drop any existing mapping */ + iova_tree_remove(as->iova_tree, &target); + /* Recover the correct permission */ + entry->perm = cache_perm; + } + } + iova_tree_insert(as->iova_tree, &target); + } else { + if (!mapped) { + /* Skip since we didn't map this range at all */ + trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); + return 0; + } + iova_tree_remove(as->iova_tree, &target); + } + + trace_vtd_page_walk_one(info->domain_id, entry->iova, + entry->translated_addr, entry->addr_mask, + entry->perm); + return hook_fn(entry, private); +} + +/** * vtd_page_walk_level - walk over specific level for IOVA range * * @addr: base GPA addr to start the walk * @start: IOVA range start address * @end: IOVA range end address (start <= addr < end) - * @hook_fn: hook func to be called when detected page - * @private: private data to be passed into hook func * @read: whether parent level has read permission * @write: whether parent level has write permission - * @notify_unmap: whether we should notify invalid entries - * @aw: maximum address width + * @info: constant information for the page walk */ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, - uint64_t end, vtd_page_walk_hook hook_fn, - void *private, uint32_t level, bool read, - bool write, bool notify_unmap, uint8_t aw) + uint64_t end, uint32_t level, bool read, + bool write, vtd_page_walk_info *info) { bool read_cur, write_cur, entry_valid; uint32_t offset; @@ -781,37 +901,34 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, */ entry_valid = read_cur | write_cur; - if (vtd_is_last_slpte(slpte, level)) { + if (!vtd_is_last_slpte(slpte, level) && entry_valid) { + /* + * This is a valid PDE (or even bigger than PDE). We need + * to walk one further level. + */ + ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw), + iova, MIN(iova_next, end), level - 1, + read_cur, write_cur, info); + } else { + /* + * This means we are either: + * + * (1) the real page entry (either 4K page, or huge page) + * (2) the whole range is invalid + * + * In either case, we send an IOTLB notification down. + */ entry.target_as = &address_space_memory; entry.iova = iova & subpage_mask; - /* NOTE: this is only meaningful if entry_valid == true */ - entry.translated_addr = vtd_get_slpte_addr(slpte, aw); - entry.addr_mask = ~subpage_mask; entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); - if (!entry_valid && !notify_unmap) { - trace_vtd_page_walk_skip_perm(iova, iova_next); - goto next; - } - trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr, - entry.addr_mask, entry.perm); - if (hook_fn) { - ret = hook_fn(&entry, private); - if (ret < 0) { - return ret; - } - } - } else { - if (!entry_valid) { - trace_vtd_page_walk_skip_perm(iova, iova_next); - goto next; - } - ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, aw), iova, - MIN(iova_next, end), hook_fn, private, - level - 1, read_cur, write_cur, - notify_unmap, aw); - if (ret < 0) { - return ret; - } + entry.addr_mask = ~subpage_mask; + /* NOTE: this is only meaningful if entry_valid == true */ + entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); + ret = vtd_page_walk_one(&entry, info); + } + + if (ret < 0) { + return ret; } next: @@ -827,28 +944,24 @@ next: * @ce: context entry to walk upon * @start: IOVA address to start the walk * @end: IOVA range end address (start <= addr < end) - * @hook_fn: the hook that to be called for each detected area - * @private: private data for the hook function - * @aw: maximum address width + * @info: page walking information struct */ static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end, - vtd_page_walk_hook hook_fn, void *private, - bool notify_unmap, uint8_t aw) + vtd_page_walk_info *info) { dma_addr_t addr = vtd_ce_get_slpt_base(ce); uint32_t level = vtd_ce_get_level(ce); - if (!vtd_iova_range_check(start, ce, aw)) { + if (!vtd_iova_range_check(start, ce, info->aw)) { return -VTD_FR_ADDR_BEYOND_MGAW; } - if (!vtd_iova_range_check(end, ce, aw)) { + if (!vtd_iova_range_check(end, ce, info->aw)) { /* Fix end so that it reaches the maximum */ - end = vtd_iova_limit(ce, aw); + end = vtd_iova_limit(ce, info->aw); } - return vtd_page_walk_level(addr, start, end, hook_fn, private, - level, true, true, notify_unmap, aw); + return vtd_page_walk_level(addr, start, end, level, true, true, info); } /* Map a device to its corresponding domain (context-entry) */ @@ -907,6 +1020,58 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return 0; } +static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry, + void *private) +{ + memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); + return 0; +} + +/* If context entry is NULL, we'll try to fetch it on our own. */ +static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, + VTDContextEntry *ce, + hwaddr addr, hwaddr size) +{ + IntelIOMMUState *s = vtd_as->iommu_state; + vtd_page_walk_info info = { + .hook_fn = vtd_sync_shadow_page_hook, + .private = (void *)&vtd_as->iommu, + .notify_unmap = true, + .aw = s->aw_bits, + .as = vtd_as, + }; + VTDContextEntry ce_cache; + int ret; + + if (ce) { + /* If the caller provided context entry, use it */ + ce_cache = *ce; + } else { + /* If the caller didn't provide ce, try to fetch */ + ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce_cache); + if (ret) { + /* + * This should not really happen, but in case it happens, + * we just skip the sync for this time. After all we even + * don't have the root table pointer! + */ + trace_vtd_err("Detected invalid context entry when " + "trying to sync shadow page table"); + return 0; + } + } + + info.domain_id = VTD_CONTEXT_ENTRY_DID(ce_cache.hi); + + return vtd_page_walk(&ce_cache, addr, addr + size, &info); +} + +static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) +{ + return vtd_sync_shadow_page_table_range(vtd_as, NULL, 0, UINT64_MAX); +} + /* * Fetch translation type for specific device. Returns <0 if error * happens, otherwise return the shifted type to check against @@ -1088,7 +1253,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, IntelIOMMUState *s = vtd_as->iommu_state; VTDContextEntry ce; uint8_t bus_num = pci_bus_num(bus); - VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry; + VTDContextCacheEntry *cc_entry; uint64_t slpte, page_mask; uint32_t level; uint16_t source_id = vtd_make_source_id(bus_num, devfn); @@ -1105,6 +1270,10 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, */ assert(!vtd_is_interrupt_addr(addr)); + vtd_iommu_lock(s); + + cc_entry = &vtd_as->context_cache_entry; + /* Try to fetch slpte form IOTLB */ iotlb_entry = vtd_lookup_iotlb(s, source_id, addr); if (iotlb_entry) { @@ -1164,7 +1333,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, * IOMMU region can be swapped back. */ vtd_pt_enable_fast_path(s, source_id); - + vtd_iommu_unlock(s); return true; } @@ -1185,6 +1354,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte, access_flags, level); out: + vtd_iommu_unlock(s); entry->iova = addr & page_mask; entry->translated_addr = vtd_get_slpte_addr(slpte, s->aw_bits) & page_mask; entry->addr_mask = ~page_mask; @@ -1192,6 +1362,7 @@ out: return true; error: + vtd_iommu_unlock(s); entry->iova = 0; entry->translated_addr = 0; entry->addr_mask = 0; @@ -1230,20 +1401,23 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) static void vtd_iommu_replay_all(IntelIOMMUState *s) { - IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; - QLIST_FOREACH(node, &s->notifiers_list, next) { - memory_region_iommu_replay_all(&node->vtd_as->iommu); + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { + vtd_sync_shadow_page_table(vtd_as); } } static void vtd_context_global_invalidate(IntelIOMMUState *s) { trace_vtd_inv_desc_cc_global(); + /* Protects context cache */ + vtd_iommu_lock(s); s->context_cache_gen++; if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) { - vtd_reset_context_cache(s); + vtd_reset_context_cache_locked(s); } + vtd_iommu_unlock(s); vtd_switch_address_space_all(s); /* * From VT-d spec 6.5.2.1, a global context entry invalidation @@ -1295,7 +1469,9 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, if (vtd_as && ((devfn_it & mask) == (devfn & mask))) { trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it), VTD_PCI_FUNC(devfn_it)); + vtd_iommu_lock(s); vtd_as->context_cache_entry.context_cache_gen = 0; + vtd_iommu_unlock(s); /* * Do switch address space when needed, in case if the * device passthrough bit is switched. @@ -1303,14 +1479,13 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, vtd_switch_address_space(vtd_as); /* * So a device is moving out of (or moving into) a - * domain, a replay() suites here to notify all the - * IOMMU_NOTIFIER_MAP registers about this change. + * domain, resync the shadow page table. * This won't bring bad even if we have no such * notifier registered - the IOMMU notification * framework will skip MAP notifications if that * happened. */ - memory_region_iommu_replay_all(&vtd_as->iommu); + vtd_sync_shadow_page_table(vtd_as); } } } @@ -1354,48 +1529,60 @@ static void vtd_iotlb_global_invalidate(IntelIOMMUState *s) static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) { - IntelIOMMUNotifierNode *node; VTDContextEntry ce; VTDAddressSpace *vtd_as; trace_vtd_inv_desc_iotlb_domain(domain_id); + vtd_iommu_lock(s); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, &domain_id); + vtd_iommu_unlock(s); - QLIST_FOREACH(node, &s->notifiers_list, next) { - vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce) && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { - memory_region_iommu_replay_all(&vtd_as->iommu); + vtd_sync_shadow_page_table(vtd_as); } } } -static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, - void *private) -{ - memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); - return 0; -} - static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, uint16_t domain_id, hwaddr addr, uint8_t am) { - IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; VTDContextEntry ce; int ret; + hwaddr size = (1 << am) * VTD_PAGE_SIZE; - QLIST_FOREACH(node, &(s->notifiers_list), next) { - VTDAddressSpace *vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { - vtd_page_walk(&ce, addr, addr + (1 << am) * VTD_PAGE_SIZE, - vtd_page_invalidate_notify_hook, - (void *)&vtd_as->iommu, true, s->aw_bits); + if (vtd_as_has_map_notifier(vtd_as)) { + /* + * As long as we have MAP notifications registered in + * any of our IOMMU notifiers, we need to sync the + * shadow page table. + */ + vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size); + } else { + /* + * For UNMAP-only notifiers, we don't need to walk the + * page tables. We just deliver the PSI down to + * invalidate caches. + */ + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = 0, + .addr_mask = size - 1, + .perm = IOMMU_NONE, + }; + memory_region_notify_iommu(&vtd_as->iommu, entry); + } } } } @@ -1411,7 +1598,9 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, info.domain_id = domain_id; info.addr = addr; info.mask = ~((1 << am) - 1); + vtd_iommu_lock(s); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); + vtd_iommu_unlock(s); vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am); } @@ -2326,8 +2515,6 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; - IntelIOMMUNotifierNode *node = NULL; - IntelIOMMUNotifierNode *next_node = NULL; if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) { error_report("We need to set caching-mode=1 for intel-iommu to enable " @@ -2335,22 +2522,13 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, exit(1); } - if (old == IOMMU_NOTIFIER_NONE) { - node = g_malloc0(sizeof(*node)); - node->vtd_as = vtd_as; - QLIST_INSERT_HEAD(&s->notifiers_list, node, next); - return; - } + /* Update per-address-space notifier flags */ + vtd_as->notifier_flags = new; - /* update notifier node with new flags */ - QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { - if (node->vtd_as == vtd_as) { - if (new == IOMMU_NOTIFIER_NONE) { - QLIST_REMOVE(node, next); - g_free(node); - } - return; - } + if (old == IOMMU_NOTIFIER_NONE) { + QLIST_INSERT_HEAD(&s->vtd_as_with_notifiers, vtd_as, next); + } else if (new == IOMMU_NOTIFIER_NONE) { + QLIST_REMOVE(vtd_as, next); } } @@ -2719,6 +2897,7 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) vtd_dev_as->devfn = (uint8_t)devfn; vtd_dev_as->iommu_state = s; vtd_dev_as->context_cache_entry.context_cache_gen = 0; + vtd_dev_as->iova_tree = iova_tree_new(); /* * Memory region relationships looks like (Address range shows @@ -2771,6 +2950,7 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) hwaddr start = n->start; hwaddr end = n->end; IntelIOMMUState *s = as->iommu_state; + DMAMap map; /* * Note: all the codes in this function has a assumption that IOVA @@ -2815,17 +2995,19 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) VTD_PCI_FUNC(as->devfn), entry.iova, size); + map.iova = entry.iova; + map.size = entry.addr_mask; + iova_tree_remove(as->iova_tree, &map); + memory_region_notify_one(n, &entry); } static void vtd_address_space_unmap_all(IntelIOMMUState *s) { - IntelIOMMUNotifierNode *node; VTDAddressSpace *vtd_as; IOMMUNotifier *n; - QLIST_FOREACH(node, &s->notifiers_list, next) { - vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { vtd_address_space_unmap(vtd_as, n); } @@ -2857,8 +3039,19 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) PCI_FUNC(vtd_as->devfn), VTD_CONTEXT_ENTRY_DID(ce.hi), ce.hi, ce.lo); - vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false, - s->aw_bits); + if (vtd_as_has_map_notifier(vtd_as)) { + /* This is required only for MAP typed notifiers */ + vtd_page_walk_info info = { + .hook_fn = vtd_replay_hook, + .private = (void *)n, + .notify_unmap = false, + .aw = s->aw_bits, + .as = vtd_as, + .domain_id = VTD_CONTEXT_ENTRY_DID(ce.hi), + }; + + vtd_page_walk(&ce, 0, ~0ULL, &info); + } } else { trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), PCI_FUNC(vtd_as->devfn)); @@ -2930,8 +3123,10 @@ static void vtd_init(IntelIOMMUState *s) s->cap |= VTD_CAP_CM; } - vtd_reset_context_cache(s); - vtd_reset_iotlb(s); + vtd_iommu_lock(s); + vtd_reset_context_cache_locked(s); + vtd_reset_iotlb_locked(s); + vtd_iommu_unlock(s); /* Define registers with default values and bit semantics */ vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0); @@ -3070,7 +3265,8 @@ static void vtd_realize(DeviceState *dev, Error **errp) return; } - QLIST_INIT(&s->notifiers_list); + QLIST_INIT(&s->vtd_as_with_notifiers); + qemu_mutex_init(&s->iommu_lock); memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, "intel_iommu", DMAR_REG_SIZE); diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 7dac319..0bf1c60 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include <linux/kvm.h> -#include <linux/kvm_para.h> +#include "standard-headers/asm-x86/kvm_para.h" #define TYPE_KVM_CLOCK "kvmclock" #define KVM_CLOCK(obj) OBJECT_CHECK(KVMClockState, (obj), TYPE_KVM_CLOCK) diff --git a/hw/i386/trace-events b/hw/i386/trace-events index 22d4464..e14d06e 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -39,9 +39,10 @@ vtd_fault_disabled(void) "Fault processing disabled for context entry" vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 -vtd_page_walk_one(uint32_t level, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "detected page level 0x%"PRIx32" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_one_skip_map(uint64_t iova, uint64_t mask, uint64_t translated) "iova 0x%"PRIx64" mask 0x%"PRIx64" translated 0x%"PRIx64 +vtd_page_walk_one_skip_unmap(uint64_t iova, uint64_t mask) "iova 0x%"PRIx64" mask 0x%"PRIx64 vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" -vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty" vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64 diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index caa563b..6ffa3c2 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -95,7 +95,8 @@ typedef struct XenIOState { CPUState **cpu_by_vcpu_id; /* the evtchn port for polling the notification, */ evtchn_port_t *ioreq_local_port; - /* evtchn local port for buffered io */ + /* evtchn remote and local ports for buffered io */ + evtchn_port_t bufioreq_remote_port; evtchn_port_t bufioreq_local_port; /* the evtchn fd for polling */ xenevtchn_handle *xce_handle; @@ -1236,12 +1237,52 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } -void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) +static int xen_map_ioreq_server(XenIOState *state) { - int i, rc; xen_pfn_t ioreq_pfn; xen_pfn_t bufioreq_pfn; evtchn_port_t bufioreq_evtchn; + int rc; + + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, + &ioreq_pfn, &bufioreq_pfn, + &bufioreq_evtchn); + if (rc < 0) { + error_report("failed to get ioreq server info: error %d handle=%p", + errno, xen_xc); + return rc; + } + + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); + DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); + + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &ioreq_pfn, NULL); + if (state->shared_page == NULL) { + error_report("map shared IO page returned error %d handle=%p", + errno, xen_xc); + return -1; + } + + state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &bufioreq_pfn, NULL); + if (state->buffered_io_page == NULL) { + error_report("map buffered IO page returned error %d", errno); + return -1; + } + + state->bufioreq_remote_port = bufioreq_evtchn; + + return 0; +} + +void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) +{ + int i, rc; + xen_pfn_t ioreq_pfn; XenIOState *state; state = g_malloc0(sizeof (XenIOState)); @@ -1269,25 +1310,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) state->wakeup.notify = xen_wakeup_notifier; qemu_register_wakeup_notifier(&state->wakeup); - rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, - &ioreq_pfn, &bufioreq_pfn, - &bufioreq_evtchn); + rc = xen_map_ioreq_server(state); if (rc < 0) { - error_report("failed to get ioreq server info: error %d handle=%p", - errno, xen_xc); - goto err; - } - - DPRINTF("shared page at pfn %lx\n", ioreq_pfn); - DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); - DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - - state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ|PROT_WRITE, - 1, &ioreq_pfn, NULL); - if (state->shared_page == NULL) { - error_report("map shared IO page returned error %d handle=%p", - errno, xen_xc); goto err; } @@ -1308,14 +1332,6 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ|PROT_WRITE, - 1, &bufioreq_pfn, NULL); - if (state->buffered_io_page == NULL) { - error_report("map buffered IO page returned error %d", errno); - goto err; - } - /* Note: cpus is empty at this point in init */ state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *)); @@ -1340,7 +1356,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) } rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid, - bufioreq_evtchn); + state->bufioreq_remote_port); if (rc == -1) { error_report("buffered evtchn bind error %d", errno); goto err; diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index f748823..a146f18 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -71,6 +71,16 @@ static const MemoryRegionOps xen_pv_mmio_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static const VMStateDescription vmstate_xen_pvdevice = { + .name = "xen-pvdevice", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, XenPVDevice), + VMSTATE_END_OF_LIST() + } +}; + static void xen_pv_realize(PCIDevice *pci_dev, Error **errp) { XenPVDevice *d = XEN_PV_DEVICE(pci_dev); @@ -120,6 +130,7 @@ static void xen_pv_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SYSTEM_OTHER; dc->desc = "Xen PV Device"; dc->props = xen_pv_props; + dc->vmsd = &vmstate_xen_pvdevice; } static const TypeInfo xen_pv_type_info = { diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index acb656b..4087aca 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -89,7 +89,7 @@ static void nvdimm_set_unarmed(Object *obj, bool value, Error **errp) static void nvdimm_init(Object *obj) { - object_property_add(obj, NVDIMM_LABLE_SIZE_PROP, "int", + object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int", nvdimm_get_label_size, nvdimm_set_label_size, NULL, NULL, NULL); object_property_add_bool(obj, NVDIMM_UNARMED_PROP, diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 20c43a6..46a8dbf 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -160,9 +160,8 @@ static void net_tx_packets(struct XenNetDev *netdev) (txreq.flags & NETTXF_more_data) ? " more_data" : "", (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - txreq.gref, PROT_READ); + page = xen_be_map_grant_ref(&netdev->xendev, txreq.gref, + PROT_READ); if (page == NULL) { xen_pv_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", @@ -183,7 +182,7 @@ static void net_tx_packets(struct XenNetDev *netdev) qemu_send_packet(qemu_get_queue(netdev->nic), page + txreq.offset, txreq.size); } - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); + xen_be_unmap_grant_ref(&netdev->xendev, page); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); } if (!netdev->tx_work) { @@ -254,9 +253,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - rxreq.gref, PROT_WRITE); + page = xen_be_map_grant_ref(&netdev->xendev, rxreq.gref, PROT_WRITE); if (page == NULL) { xen_pv_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", @@ -265,7 +262,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size return -1; } memcpy(page + NET_IP_ALIGN, buf, size); - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); + xen_be_unmap_grant_ref(&netdev->xendev, page); net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); return size; @@ -338,19 +335,17 @@ static int net_connect(struct XenDevice *xendev) return -1; } - netdev->txs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->tx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->txs = xen_be_map_grant_ref(&netdev->xendev, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); if (!netdev->txs) { return -1; } - netdev->rxs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->rx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->rxs = xen_be_map_grant_ref(&netdev->xendev, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); if (!netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); netdev->txs = NULL; return -1; } @@ -375,11 +370,11 @@ static void net_disconnect(struct XenDevice *xendev) xen_pv_unbind_evtchn(&netdev->xendev); if (netdev->txs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); netdev->txs = NULL; } if (netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->rxs); netdev->rxs = NULL; } } diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index a36a119..02f9576 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -535,13 +535,15 @@ static void mch_realize(PCIDevice *d, Error **errp) /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", - mch->pci_address_space, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(mch->system_memory, 0xa0000, + mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); + memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, &mch->smram_region, 1); memory_region_set_enabled(&mch->smram_region, true); memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_add_subregion_overlap(mch->system_memory, 0xfeda0000, &mch->open_high_smram, 1); memory_region_set_enabled(&mch->open_high_smram, false); @@ -550,11 +552,14 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_init(&mch->smram, OBJECT(mch), "smram", 1ull << 32); memory_region_set_enabled(&mch->smram, true); memory_region_init_alias(&mch->low_smram, OBJECT(mch), "smram-low", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_set_enabled(&mch->low_smram, true); - memory_region_add_subregion(&mch->smram, 0xa0000, &mch->low_smram); + memory_region_add_subregion(&mch->smram, MCH_HOST_BRIDGE_SMRAM_C_BASE, + &mch->low_smram); memory_region_init_alias(&mch->high_smram, OBJECT(mch), "smram-high", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_set_enabled(&mch->high_smram, true); memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram); diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index b3a90c0..5b2e21e 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -173,8 +173,9 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_buffer_segs; i++) { ref[i] = usbback_req->req.seg[i].gref; } - usbback_req->buffer = xengnttab_map_domain_grant_refs(xendev->gnttabdev, - usbback_req->nr_buffer_segs, xendev->dom, ref, prot); + usbback_req->buffer = + xen_be_map_grant_refs(xendev, ref, usbback_req->nr_buffer_segs, + prot); if (!usbback_req->buffer) { return -ENOMEM; @@ -206,8 +207,9 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_extra_segs; i++) { ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref; } - usbback_req->isoc_buffer = xengnttab_map_domain_grant_refs( - xendev->gnttabdev, usbback_req->nr_extra_segs, xendev->dom, ref, prot); + usbback_req->isoc_buffer = + xen_be_map_grant_refs(xendev, ref, usbback_req->nr_extra_segs, + prot); if (!usbback_req->isoc_buffer) { return -ENOMEM; @@ -291,14 +293,14 @@ static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, } if (usbback_req->buffer) { - xengnttab_unmap(xendev->gnttabdev, usbback_req->buffer, - usbback_req->nr_buffer_segs); + xen_be_unmap_grant_refs(xendev, usbback_req->buffer, + usbback_req->nr_buffer_segs); usbback_req->buffer = NULL; } if (usbback_req->isoc_buffer) { - xengnttab_unmap(xendev->gnttabdev, usbback_req->isoc_buffer, - usbback_req->nr_extra_segs); + xen_be_unmap_grant_refs(xendev, usbback_req->isoc_buffer, + usbback_req->nr_extra_segs); usbback_req->isoc_buffer = NULL; } @@ -834,11 +836,11 @@ static void usbback_disconnect(struct XenDevice *xendev) xen_pv_unbind_evtchn(xendev); if (usbif->urb_sring) { - xengnttab_unmap(xendev->gnttabdev, usbif->urb_sring, 1); + xen_be_unmap_grant_ref(xendev, usbif->urb_sring); usbif->urb_sring = NULL; } if (usbif->conn_sring) { - xengnttab_unmap(xendev->gnttabdev, usbif->conn_sring, 1); + xen_be_unmap_grant_ref(xendev, usbif->conn_sring); usbif->conn_sring = NULL; } @@ -877,12 +879,10 @@ static int usbback_connect(struct XenDevice *xendev) return -1; } - usbif->urb_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, - urb_ring_ref, - PROT_READ | PROT_WRITE); - usbif->conn_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, - conn_ring_ref, - PROT_READ | PROT_WRITE); + usbif->urb_sring = xen_be_map_grant_ref(xendev, urb_ring_ref, + PROT_READ | PROT_WRITE); + usbif->conn_sring = xen_be_map_grant_ref(xendev, conn_ring_ref, + PROT_READ | PROT_WRITE); if (!usbif->urb_sring || !usbif->conn_sring) { xen_pv_printf(xendev, 0, "error mapping rings\n"); usbback_disconnect(xendev); @@ -1024,10 +1024,7 @@ static void usbback_alloc(struct XenDevice *xendev) /* max_grants: for each request and for the rings (request and connect). */ max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2; - if (xengnttab_set_max_grants(xendev->gnttabdev, max_grants) < 0) { - xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - } + xen_be_set_max_grant_refs(xendev, max_grants); } static int usbback_free(struct XenDevice *xendev) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 1422ff0..07bcbe9 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -6,6 +6,7 @@ vhost_region_add_section(const char *name, uint64_t gpa, uint64_t size, uint64_t vhost_region_add_section_merge(const char *name, uint64_t new_size, uint64_t gpa, uint64_t owr) "%s: size: 0x%"PRIx64 " gpa: 0x%"PRIx64 " owr: 0x%"PRIx64 vhost_region_add_section_aligned(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64 vhost_section(const char *name, int r) "%s:%d" +vhost_iotlb_miss(void *dev, int step) "%p step %d" # hw/virtio/vhost-user.c vhost_user_postcopy_end_entry(void) "" diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 38da869..ca554d4 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -852,14 +852,44 @@ static void slave_read(void *opaque) VhostUserHeader hdr = { 0, }; VhostUserPayload payload = { 0, }; int size, ret = 0; + struct iovec iov; + struct msghdr msgh; + int fd = -1; + char control[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; + size_t fdsize; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = control; + msgh.msg_controllen = sizeof(control); /* Read header */ - size = read(u->slave_fd, &hdr, VHOST_USER_HDR_SIZE); + iov.iov_base = &hdr; + iov.iov_len = VHOST_USER_HDR_SIZE; + + size = recvmsg(u->slave_fd, &msgh, 0); if (size != VHOST_USER_HDR_SIZE) { error_report("Failed to read from slave."); goto err; } + if (msgh.msg_flags & MSG_CTRUNC) { + error_report("Truncated message."); + goto err; + } + + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fdsize = cmsg->cmsg_len - CMSG_LEN(0); + memcpy(&fd, CMSG_DATA(cmsg), fdsize); + break; + } + } + if (hdr.size > VHOST_USER_PAYLOAD_SIZE) { error_report("Failed to read msg header." " Size %d exceeds the maximum %zu.", hdr.size, @@ -883,9 +913,15 @@ static void slave_read(void *opaque) break; default: error_report("Received unexpected msg type."); + if (fd != -1) { + close(fd); + } ret = -EINVAL; } + /* Message handlers need to make sure that fd will be consumed. */ + fd = -1; + /* * REPLY_ACK feature handling. Other reply types has to be managed * directly in their request handlers. @@ -918,6 +954,9 @@ err: qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); close(u->slave_fd); u->slave_fd = -1; + if (fd != -1) { + close(fd); + } return; } @@ -1076,7 +1115,7 @@ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) error_setg(errp, "%s: Failed to get ufd", __func__); return -1; } - fcntl(ufd, F_SETFL, O_NONBLOCK); + qemu_set_nonblock(ufd); /* register ufd with userfault thread */ u->postcopy_fd.fd = ufd; @@ -1316,7 +1355,7 @@ static bool vhost_user_requires_shm_log(struct vhost_dev *dev) static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) { - VhostUserMsg msg = { 0 }; + VhostUserMsg msg = { }; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 9d5850a..b082900 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -894,12 +894,15 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) rcu_read_lock(); + trace_vhost_iotlb_miss(dev, 1); + iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, iova, write); if (iotlb.target_as != NULL) { ret = vhost_memory_region_lookup(dev, iotlb.translated_addr, &uaddr, &len); if (ret) { + trace_vhost_iotlb_miss(dev, 3); error_report("Fail to lookup the translated address " "%"PRIx64, iotlb.translated_addr); goto out; @@ -911,10 +914,14 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) ret = vhost_backend_update_device_iotlb(dev, iova, uaddr, len, iotlb.perm); if (ret) { + trace_vhost_iotlb_miss(dev, 4); error_report("Fail to update device iotlb"); goto out; } } + + trace_vhost_iotlb_miss(dev, 2); + out: rcu_read_unlock(); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index f456cea..1f7a87f 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -52,6 +52,8 @@ static const char *balloon_stat_names[] = { [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", [VIRTIO_BALLOON_S_AVAIL] = "stat-available-memory", [VIRTIO_BALLOON_S_CACHES] = "stat-disk-caches", + [VIRTIO_BALLOON_S_HTLB_PGALLOC] = "stat-htlb-pgalloc", + [VIRTIO_BALLOON_S_HTLB_PGFAIL] = "stat-htlb-pgfail", [VIRTIO_BALLOON_S_NR] = NULL }; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 1e8ab7b..5eb0c32 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1037,6 +1037,27 @@ assign_error: return r; } +static int virtio_pci_set_host_notifier_mr(DeviceState *d, int n, + MemoryRegion *mr, bool assign) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + int offset; + + if (n >= VIRTIO_QUEUE_MAX || !virtio_pci_modern(proxy) || + virtio_pci_queue_mem_mult(proxy) != memory_region_size(mr)) { + return -1; + } + + if (assign) { + offset = virtio_pci_queue_mem_mult(proxy) * n; + memory_region_add_subregion_overlap(&proxy->notify.mr, offset, mr, 1); + } else { + memory_region_del_subregion(&proxy->notify.mr, mr); + } + + return 0; +} + static void virtio_pci_vmstate_change(DeviceState *d, bool running) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); @@ -2652,6 +2673,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->has_extra_state = virtio_pci_has_extra_state; k->query_guest_notifiers = virtio_pci_query_guest_notifiers; k->set_guest_notifiers = virtio_pci_set_guest_notifiers; + k->set_host_notifier_mr = virtio_pci_set_host_notifier_mr; k->vmstate_change = virtio_pci_vmstate_change; k->pre_plugged = virtio_pci_pre_plugged; k->device_plugged = virtio_pci_device_plugged; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 006d3d1..1debb01 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2454,6 +2454,19 @@ EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) return &vq->host_notifier; } +int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, + MemoryRegion *mr, bool assign) +{ + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + + if (k->set_host_notifier_mr) { + return k->set_host_notifier_mr(qbus->parent, n, mr, assign); + } + + return -1; +} + void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name) { g_free(vdev->bus_name); diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 7445b50..9a8e877 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -44,9 +44,9 @@ BusState *xen_sysbus; /* public */ struct xs_handle *xenstore = NULL; const char *xen_protocol; -bool xen_feature_grant_copy; /* private */ +static bool xen_feature_grant_copy; static int debug; int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) @@ -106,6 +106,156 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) return 0; } +void xen_be_set_max_grant_refs(struct XenDevice *xendev, + unsigned int nr_refs) +{ + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (xengnttab_set_max_grants(xendev->gnttabdev, nr_refs)) { + xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", + strerror(errno)); + } +} + +void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, + unsigned int nr_refs, int prot) +{ + void *ptr; + + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + ptr = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_refs, + xen_domid, refs, prot); + if (!ptr) { + xen_pv_printf(xendev, 0, + "xengnttab_map_domain_grant_refs failed: %s\n", + strerror(errno)); + } + + return ptr; +} + +void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, + unsigned int nr_refs) +{ + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (xengnttab_unmap(xendev->gnttabdev, ptr, nr_refs)) { + xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", + strerror(errno)); + } +} + +static int compat_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, + XenGrantCopySegment segs[], + unsigned int nr_segs) +{ + uint32_t *refs = g_new(uint32_t, nr_segs); + int prot = to_domain ? PROT_WRITE : PROT_READ; + void *pages; + unsigned int i; + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + + refs[i] = to_domain ? + seg->dest.foreign.ref : seg->source.foreign.ref; + } + + pages = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_segs, + xen_domid, refs, prot); + if (!pages) { + xen_pv_printf(xendev, 0, + "xengnttab_map_domain_grant_refs failed: %s\n", + strerror(errno)); + g_free(refs); + return -1; + } + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + void *page = pages + (i * XC_PAGE_SIZE); + + if (to_domain) { + memcpy(page + seg->dest.foreign.offset, seg->source.virt, + seg->len); + } else { + memcpy(seg->dest.virt, page + seg->source.foreign.offset, + seg->len); + } + } + + if (xengnttab_unmap(xendev->gnttabdev, pages, nr_segs)) { + xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", + strerror(errno)); + } + + g_free(refs); + return 0; +} + +int xen_be_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, + XenGrantCopySegment segs[], + unsigned int nr_segs) +{ + xengnttab_grant_copy_segment_t *xengnttab_segs; + unsigned int i; + int rc; + + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (!xen_feature_grant_copy) { + return compat_copy_grant_refs(xendev, to_domain, segs, nr_segs); + } + + xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; + + if (to_domain) { + xengnttab_seg->flags = GNTCOPY_dest_gref; + xengnttab_seg->dest.foreign.domid = xen_domid; + xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; + xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; + xengnttab_seg->source.virt = seg->source.virt; + } else { + xengnttab_seg->flags = GNTCOPY_source_gref; + xengnttab_seg->source.foreign.domid = xen_domid; + xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; + xengnttab_seg->source.foreign.offset = + seg->source.foreign.offset; + xengnttab_seg->dest.virt = seg->dest.virt; + } + + xengnttab_seg->len = seg->len; + } + + rc = xengnttab_grant_copy(xendev->gnttabdev, nr_segs, xengnttab_segs); + + if (rc) { + xen_pv_printf(xendev, 0, "xengnttab_copy failed: %s\n", + strerror(errno)); + } + + for (i = 0; i < nr_segs; i++) { + xengnttab_grant_copy_segment_t *xengnttab_seg = + &xengnttab_segs[i]; + + if (xengnttab_seg->status != GNTST_okay) { + xen_pv_printf(xendev, 0, "segment[%u] status: %d\n", i, + xengnttab_seg->status); + rc = -1; + } + } + + g_free(xengnttab_segs); + return rc; +} + /* * get xen backend device, allocate a new one if it doesn't exist. */ @@ -149,18 +299,6 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, } qemu_set_cloexec(xenevtchn_fd(xendev->evtchndev)); - if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { - xendev->gnttabdev = xengnttab_open(NULL, 0); - if (xendev->gnttabdev == NULL) { - xen_pv_printf(NULL, 0, "can't open gnttab device\n"); - xenevtchn_close(xendev->evtchndev); - qdev_unplug(DEVICE(xendev), NULL); - return NULL; - } - } else { - xendev->gnttabdev = NULL; - } - xen_pv_insert_xendev(xendev); if (xendev->ops->alloc) { @@ -322,6 +460,16 @@ static int xen_be_try_initialise(struct XenDevice *xendev) } } + if (xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xengnttab_open(NULL, 0); + if (xendev->gnttabdev == NULL) { + xen_pv_printf(NULL, 0, "can't open gnttab device\n"); + return -1; + } + } else { + xendev->gnttabdev = NULL; + } + if (xendev->ops->initialise) { rc = xendev->ops->initialise(xendev); } @@ -369,6 +517,10 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) xendev->ops->disconnect) { xendev->ops->disconnect(xendev); } + if (xendev->gnttabdev) { + xengnttab_close(xendev->gnttabdev); + xendev->gnttabdev = NULL; + } if (xendev->be_state != state) { xen_be_set_state(xendev, state); } diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 9b7a960..e5a6eff 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -907,7 +907,7 @@ out: } } - memory_listener_register(&s->memory_listener, &s->dev.bus_master_as); + memory_listener_register(&s->memory_listener, &address_space_memory); memory_listener_register(&s->io_listener, &address_space_io); s->listener_set = true; XEN_PT_LOG(d, diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index a3ce33e..aee31c6 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -504,6 +504,8 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, bar_ro_mask = XEN_PT_BAR_IO_RO_MASK | (r_size - 1); break; case XEN_PT_BAR_FLAG_UPPER: + assert(index > 0); + r_size = d->io_regions[index - 1].size >> 32; bar_emu_mask = XEN_PT_BAR_ALLF; bar_ro_mask = r_size ? r_size - 1 : 0; break; diff --git a/include/block/block_int.h b/include/block/block_int.h index 76b589d..6c0927b 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -1029,7 +1029,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, - BlockJobTxn *txn, Error **errp); + JobTxn *txn, Error **errp); void hmp_drive_add_node(Monitor *mon, const char *optstr); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 0f56f72..32c00b7 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -26,13 +26,13 @@ #ifndef BLOCKJOB_H #define BLOCKJOB_H +#include "qemu/job.h" #include "block/block.h" #include "qemu/ratelimit.h" #define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ typedef struct BlockJobDriver BlockJobDriver; -typedef struct BlockJobTxn BlockJobTxn; /** * BlockJob: @@ -40,141 +40,40 @@ typedef struct BlockJobTxn BlockJobTxn; * Long-running operation on a BlockDriverState. */ typedef struct BlockJob { - /** The job type, including the job vtable. */ - const BlockJobDriver *driver; + /** Data belonging to the generic Job infrastructure */ + Job job; /** The block device on which the job is operating. */ BlockBackend *blk; - /** - * The ID of the block job. May be NULL for internal jobs. - */ - char *id; - - /** - * The coroutine that executes the job. If not NULL, it is - * reentered when busy is false and the job is cancelled. - */ - Coroutine *co; - - /** - * Set to true if the job should cancel itself. The flag must - * always be tested just before toggling the busy flag from false - * to true. After a job has been cancelled, it should only yield - * if #aio_poll will ("sooner or later") reenter the coroutine. - */ - bool cancelled; - - /** - * Set to true if the job should abort immediately without waiting - * for data to be in sync. - */ - bool force; - - /** - * Counter for pause request. If non-zero, the block job is either paused, - * or if busy == true will pause itself as soon as possible. - */ - int pause_count; - - /** - * Set to true if the job is paused by user. Can be unpaused with the - * block-job-resume QMP command. - */ - bool user_paused; - - /** - * Set to false by the job while the coroutine has yielded and may be - * re-entered by block_job_enter(). There may still be I/O or event loop - * activity pending. Accessed under block_job_mutex (in blockjob.c). - */ - bool busy; - - /** - * Set to true by the job while it is in a quiescent state, where - * no I/O or event loop activity is pending. - */ - bool paused; - - /** - * Set to true when the job is ready to be completed. - */ - bool ready; - - /** - * Set to true when the job has deferred work to the main loop. - */ - bool deferred_to_main_loop; - - /** Element of the list of block jobs */ - QLIST_ENTRY(BlockJob) job_list; - /** Status that is published by the query-block-jobs QMP API */ BlockDeviceIoStatus iostatus; - /** Offset that is published by the query-block-jobs QMP API */ - int64_t offset; - - /** Length that is published by the query-block-jobs QMP API */ - int64_t len; - /** Speed that was set with @block_job_set_speed. */ int64_t speed; /** Rate limiting data structure for implementing @speed. */ RateLimit limit; - /** The completion function that will be called when the job completes. */ - BlockCompletionFunc *cb; - /** Block other operations when block job is running */ Error *blocker; - /** BlockDriverStates that are involved in this block job */ - GSList *nodes; - - /** The opaque value that is passed to the completion function. */ - void *opaque; - - /** Reference count of the block job */ - int refcnt; - - /** True when job has reported completion by calling block_job_completed. */ - bool completed; - - /** ret code passed to block_job_completed. */ - int ret; - - /** - * Timer that is used by @block_job_sleep_ns. Accessed under - * block_job_mutex (in blockjob.c). - */ - QEMUTimer sleep_timer; + /** Called when a cancelled job is finalised. */ + Notifier finalize_cancelled_notifier; - /** Current state; See @BlockJobStatus for details. */ - BlockJobStatus status; + /** Called when a successfully completed job is finalised. */ + Notifier finalize_completed_notifier; - /** True if this job should automatically finalize itself */ - bool auto_finalize; + /** Called when the job transitions to PENDING */ + Notifier pending_notifier; - /** True if this job should automatically dismiss itself */ - bool auto_dismiss; + /** Called when the job transitions to READY */ + Notifier ready_notifier; - BlockJobTxn *txn; - QLIST_ENTRY(BlockJob) txn_list; + /** BlockDriverStates that are involved in this block job */ + GSList *nodes; } BlockJob; -typedef enum BlockJobCreateFlags { - /* Default behavior */ - BLOCK_JOB_DEFAULT = 0x00, - /* BlockJob is not QMP-created and should not send QMP events */ - BLOCK_JOB_INTERNAL = 0x01, - /* BlockJob requires manual finalize step */ - BLOCK_JOB_MANUAL_FINALIZE = 0x02, - /* BlockJob requires manual dismiss step */ - BLOCK_JOB_MANUAL_DISMISS = 0x04, -} BlockJobCreateFlags; - /** * block_job_next: * @job: A block job, or %NULL. @@ -231,78 +130,6 @@ void block_job_remove_all_bdrv(BlockJob *job); void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); /** - * block_job_start: - * @job: A job that has not yet been started. - * - * Begins execution of a block job. - * Takes ownership of one reference to the job object. - */ -void block_job_start(BlockJob *job); - -/** - * block_job_cancel: - * @job: The job to be canceled. - * @force: Quit a job without waiting for data to be in sync. - * - * Asynchronously cancel the specified job. - */ -void block_job_cancel(BlockJob *job, bool force); - -/** - * block_job_complete: - * @job: The job to be completed. - * @errp: Error object. - * - * Asynchronously complete the specified job. - */ -void block_job_complete(BlockJob *job, Error **errp); - - -/** - * block_job_finalize: - * @job: The job to fully commit and finish. - * @errp: Error object. - * - * For jobs that have finished their work and are pending - * awaiting explicit acknowledgement to commit their work, - * This will commit that work. - * - * FIXME: Make the below statement universally true: - * For jobs that support the manual workflow mode, all graph - * changes that occur as a result will occur after this command - * and before a successful reply. - */ -void block_job_finalize(BlockJob *job, Error **errp); - -/** - * block_job_dismiss: - * @job: The job to be dismissed. - * @errp: Error object. - * - * Remove a concluded job from the query list. - */ -void block_job_dismiss(BlockJob **job, Error **errp); - -/** - * block_job_progress_update: - * @job: The job that has made progress - * @done: How much progress the job made - * - * Updates the progress counter of the job. - */ -void block_job_progress_update(BlockJob *job, uint64_t done); - -/** - * block_job_progress_set_remaining: - * @job: The job whose expected progress end value is set - * @remaining: Expected end value of the progress counter of the job - * - * Sets the expected end value of the progress counter of a job so that a - * completion percentage can be calculated when the progress is updated. - */ -void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); - -/** * block_job_query: * @job: The job to get information about. * @@ -311,78 +138,6 @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); BlockJobInfo *block_job_query(BlockJob *job, Error **errp); /** - * block_job_user_pause: - * @job: The job to be paused. - * - * Asynchronously pause the specified job. - * Do not allow a resume until a matching call to block_job_user_resume. - */ -void block_job_user_pause(BlockJob *job, Error **errp); - -/** - * block_job_paused: - * @job: The job to query. - * - * Returns true if the job is user-paused. - */ -bool block_job_user_paused(BlockJob *job); - -/** - * block_job_user_resume: - * @job: The job to be resumed. - * - * Resume the specified job. - * Must be paired with a preceding block_job_user_pause. - */ -void block_job_user_resume(BlockJob *job, Error **errp); - -/** - * block_job_user_cancel: - * @job: The job to be cancelled. - * @force: Quit a job without waiting for data to be in sync. - * - * Cancels the specified job, but may refuse to do so if the - * operation isn't currently meaningful. - */ -void block_job_user_cancel(BlockJob *job, bool force, Error **errp); - -/** - * block_job_cancel_sync: - * @job: The job to be canceled. - * - * Synchronously cancel the job. The completion callback is called - * before the function returns. The job may actually complete - * instead of canceling itself; the circumstances under which this - * happens depend on the kind of job that is active. - * - * Returns the return value from the job if the job actually completed - * during the call, or -ECANCELED if it was canceled. - */ -int block_job_cancel_sync(BlockJob *job); - -/** - * block_job_cancel_sync_all: - * - * Synchronously cancels all jobs using block_job_cancel_sync(). - */ -void block_job_cancel_sync_all(void); - -/** - * block_job_complete_sync: - * @job: The job to be completed. - * @errp: Error object which may be set by block_job_complete(); this is not - * necessarily set on every error, the job return value has to be - * checked as well. - * - * Synchronously complete the job. The completion callback is called before the - * function returns, unless it is NULL (which is permissible when using this - * function). - * - * Returns the return value from the job. - */ -int block_job_complete_sync(BlockJob *job, Error **errp); - -/** * block_job_iostatus_reset: * @job: The job whose I/O status should be reset. * @@ -392,59 +147,6 @@ int block_job_complete_sync(BlockJob *job, Error **errp); void block_job_iostatus_reset(BlockJob *job); /** - * block_job_txn_new: - * - * Allocate and return a new block job transaction. Jobs can be added to the - * transaction using block_job_txn_add_job(). - * - * The transaction is automatically freed when the last job completes or is - * cancelled. - * - * All jobs in the transaction either complete successfully or fail/cancel as a - * group. Jobs wait for each other before completing. Cancelling one job - * cancels all jobs in the transaction. - */ -BlockJobTxn *block_job_txn_new(void); - -/** - * block_job_ref: - * - * Add a reference to BlockJob refcnt, it will be decreased with - * block_job_unref, and then be freed if it comes to be the last - * reference. - */ -void block_job_ref(BlockJob *job); - -/** - * block_job_unref: - * - * Release a reference that was previously acquired with block_job_ref - * or block_job_create. If it's the last reference to the object, it will be - * freed. - */ -void block_job_unref(BlockJob *job); - -/** - * block_job_txn_unref: - * - * Release a reference that was previously acquired with block_job_txn_add_job - * or block_job_txn_new. If it's the last reference to the object, it will be - * freed. - */ -void block_job_txn_unref(BlockJobTxn *txn); - -/** - * block_job_txn_add_job: - * @txn: The transaction (may be NULL) - * @job: Job to add to the transaction - * - * Add @job to the transaction. The @job must not already be in a transaction. - * The caller must call either block_job_txn_unref() or block_job_completed() - * to release the reference that is automatically grabbed here. - */ -void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job); - -/** * block_job_is_internal: * @job: The job to determine if it is user-visible or not. * diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 62ec964..5cd50c6 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -35,72 +35,8 @@ * A class type for block job driver. */ struct BlockJobDriver { - /** Derived BlockJob struct size */ - size_t instance_size; - - /** String describing the operation, part of query-block-jobs QMP API */ - BlockJobType job_type; - - /** Mandatory: Entrypoint for the Coroutine. */ - CoroutineEntry *start; - - /** - * Optional callback for job types whose completion must be triggered - * manually. - */ - void (*complete)(BlockJob *job, Error **errp); - - /** - * If the callback is not NULL, prepare will be invoked when all the jobs - * belonging to the same transaction complete; or upon this job's completion - * if it is not in a transaction. - * - * This callback will not be invoked if the job has already failed. - * If it fails, abort and then clean will be called. - */ - int (*prepare)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when all the jobs - * belonging to the same transaction complete; or upon this job's - * completion if it is not in a transaction. Skipped if NULL. - * - * All jobs will complete with a call to either .commit() or .abort() but - * never both. - */ - void (*commit)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when any job in the - * same transaction fails; or upon this job's failure (due to error or - * cancellation) if it is not in a transaction. Skipped if NULL. - * - * All jobs will complete with a call to either .commit() or .abort() but - * never both. - */ - void (*abort)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked after a call to either - * .commit() or .abort(). Regardless of which callback is invoked after - * completion, .clean() will always be called, even if the job does not - * belong to a transaction group. - */ - void (*clean)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when the job transitions - * into the paused state. Paused jobs must not perform any asynchronous - * I/O or event loop activity. This callback is used to quiesce jobs. - */ - void coroutine_fn (*pause)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when the job transitions - * out of the paused state. Any asynchronous I/O or event loop activity - * should be restarted from this callback. - */ - void coroutine_fn (*resume)(BlockJob *job); + /** Generic JobDriver callbacks and settings */ + JobDriver job_driver; /* * If the callback is not NULL, it will be invoked before the job is @@ -113,6 +49,10 @@ struct BlockJobDriver { * If the callback is not NULL, it will be invoked when the job has to be * synchronously cancelled or completed; it should drain BlockDriverStates * as required to ensure progress. + * + * Block jobs must use the default implementation for job_driver.drain, + * which will in turn call this callback after doing generic block job + * stuff. */ void (*drain)(BlockJob *job); }; @@ -126,8 +66,7 @@ struct BlockJobDriver { * @bs: The block * @perm, @shared_perm: Permissions to request for @bs * @speed: The maximum speed, in bytes per second, or 0 for unlimited. - * @flags: Creation flags for the Block Job. - * See @BlockJobCreateFlags + * @flags: Creation flags for the Block Job. See @JobCreateFlags. * @cb: Completion function for the job. * @opaque: Opaque pointer value passed to @cb. * @errp: Error object. @@ -142,28 +81,31 @@ struct BlockJobDriver { * called from a wrapper that is specific to the job type. */ void *block_job_create(const char *job_id, const BlockJobDriver *driver, - BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm, + JobTxn *txn, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, int64_t speed, int flags, BlockCompletionFunc *cb, void *opaque, Error **errp); /** - * block_job_sleep_ns: - * @job: The job that calls the function. - * @ns: How many nanoseconds to stop for. - * - * Put the job to sleep (assuming that it wasn't canceled) for @ns - * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately - * interrupt the wait. + * block_job_free: + * Callback to be used for JobDriver.free in all block jobs. Frees block job + * specific resources in @job. */ -void block_job_sleep_ns(BlockJob *job, int64_t ns); +void block_job_free(Job *job); /** - * block_job_yield: - * @job: The job that calls the function. - * - * Yield the block job coroutine. + * block_job_user_resume: + * Callback to be used for JobDriver.user_resume in all block jobs. Resets the + * iostatus when the user resumes @job. + */ +void block_job_user_resume(Job *job); + +/** + * block_job_drain: + * Callback to be used for JobDriver.drain in all block jobs. Drains the main + * block node associated with the block jobs and calls BlockJobDriver.drain for + * job-specific actions. */ -void block_job_yield(BlockJob *job); +void block_job_drain(Job *job); /** * block_job_ratelimit_get_delay: @@ -174,57 +116,6 @@ void block_job_yield(BlockJob *job); int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); /** - * block_job_early_fail: - * @bs: The block device. - * - * The block job could not be started, free it. - */ -void block_job_early_fail(BlockJob *job); - -/** - * block_job_completed: - * @job: The job being completed. - * @ret: The status code. - * - * Call the completion function that was registered at creation time, and - * free @job. - */ -void block_job_completed(BlockJob *job, int ret); - -/** - * block_job_is_cancelled: - * @job: The job being queried. - * - * Returns whether the job is scheduled for cancellation. - */ -bool block_job_is_cancelled(BlockJob *job); - -/** - * block_job_pause_point: - * @job: The job that is ready to pause. - * - * Pause now if block_job_pause() has been called. Block jobs that perform - * lots of I/O must call this between requests so that the job can be paused. - */ -void coroutine_fn block_job_pause_point(BlockJob *job); - -/** - * block_job_enter: - * @job: The job to enter. - * - * Continue the specified job by entering the coroutine. - */ -void block_job_enter(BlockJob *job); - -/** - * block_job_event_ready: - * @job: The job which is now ready to be completed. - * - * Send a BLOCK_JOB_READY event for the specified job. - */ -void block_job_event_ready(BlockJob *job); - -/** * block_job_error_action: * @job: The job to signal an error for. * @on_err: The error action setting. @@ -237,23 +128,4 @@ void block_job_event_ready(BlockJob *job); BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, int is_read, int error); -typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque); - -/** - * block_job_defer_to_main_loop: - * @job: The job - * @fn: The function to run in the main loop - * @opaque: The opaque value that is passed to @fn - * - * This function must be called by the main job coroutine just before it - * returns. @fn is executed in the main loop with the BlockDriverState - * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and - * anything that uses bdrv_drain_all() in the main loop. - * - * The @job AioContext is held while @fn executes. - */ -void block_job_defer_to_main_loop(BlockJob *job, - BlockJobDeferToMainLoopFn *fn, - void *opaque); - #endif diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 2e8a4b8..0836396 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -48,6 +48,21 @@ int use_gdb_syscalls(void); void gdb_set_stop_cpu(CPUState *cpu); void gdb_exit(CPUArchState *, int); #ifdef CONFIG_USER_ONLY +/** + * gdb_handlesig: yield control to gdb + * @cpu: CPU + * @sig: if non-zero, the signal number which caused us to stop + * + * This function yields control to gdb, when a user-mode-only target + * needs to stop execution. If @sig is non-zero, then we will send a + * stop packet to tell gdb that we have stopped because of this signal. + * + * This function will block (handling protocol requests from gdb) + * until gdb tells us to continue target execution. When it does + * return, the return value is a signal to deliver to the target, + * or 0 if no signal should be delivered, ie the signal that caused + * us to stop should be ignored. + */ int gdb_handlesig(CPUState *, int); void gdb_signalled(CPUArchState *, int); void gdbserver_fork(CPUState *); diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h index 8f55b23..8d3af5a 100644 --- a/include/exec/user/thunk.h +++ b/include/exec/user/thunk.h @@ -135,7 +135,7 @@ static inline int thunk_type_size(const argtype *type_ptr, int is_host) se = struct_entries + type_ptr[1]; return se->size[is_host]; default: - return -1; + g_assert_not_reached(); } } @@ -185,7 +185,7 @@ static inline int thunk_type_align(const argtype *type_ptr, int is_host) se = struct_entries + type_ptr[1]; return se->align[is_host]; default: - return -1; + g_assert_not_reached(); } } diff --git a/include/hw/display/bochs-vbe.h b/include/hw/display/bochs-vbe.h new file mode 100644 index 0000000..bc2f046 --- /dev/null +++ b/include/hw/display/bochs-vbe.h @@ -0,0 +1,69 @@ +#ifndef HW_DISPLAY_BOCHS_VBE_H +#define HW_DISPLAY_BOCHS_VBE_H + +/* + * bochs vesa bios extension interface + */ + +#define VBE_DISPI_MAX_XRES 16000 +#define VBE_DISPI_MAX_YRES 12000 +#define VBE_DISPI_MAX_BPP 32 + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ + +/* VBE_DISPI_INDEX_ID */ +#define VBE_DISPI_ID0 0xB0C0 +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 +#define VBE_DISPI_ID3 0xB0C3 +#define VBE_DISPI_ID4 0xB0C4 +#define VBE_DISPI_ID5 0xB0C5 + +/* VBE_DISPI_INDEX_ENABLE */ +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +/* only used by isa-vga, pci vga devices use a memory bar */ +#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + + +/* + * qemu extension: mmio bar (region 2) + */ + +#define PCI_VGA_MMIO_SIZE 0x1000 + +/* vga register region */ +#define PCI_VGA_IOPORT_OFFSET 0x400 +#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) + +/* bochs vbe register region */ +#define PCI_VGA_BOCHS_OFFSET 0x500 +#define PCI_VGA_BOCHS_SIZE (0x0b * 2) + +/* qemu extension register region */ +#define PCI_VGA_QEXT_OFFSET 0x600 +#define PCI_VGA_QEXT_SIZE (2 * 4) + +/* qemu extension registers */ +#define PCI_VGA_QEXT_REG_SIZE (0 * 4) +#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) +#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e +#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe + +#endif /* HW_DISPLAY_BOCHS_VBE_H */ diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 45ec891..fbfedcb 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -27,6 +27,7 @@ #include "hw/i386/ioapic.h" #include "hw/pci/msi.h" #include "hw/sysbus.h" +#include "qemu/iova-tree.h" #define TYPE_INTEL_IOMMU_DEVICE "intel-iommu" #define INTEL_IOMMU_DEVICE(obj) \ @@ -67,7 +68,6 @@ typedef union VTD_IR_TableEntry VTD_IR_TableEntry; typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress; typedef struct VTDIrq VTDIrq; typedef struct VTD_MSIMessage VTD_MSIMessage; -typedef struct IntelIOMMUNotifierNode IntelIOMMUNotifierNode; /* Context-Entry */ struct VTDContextEntry { @@ -93,6 +93,10 @@ struct VTDAddressSpace { MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */ IntelIOMMUState *iommu_state; VTDContextCacheEntry context_cache_entry; + QLIST_ENTRY(VTDAddressSpace) next; + /* Superset of notifier flags that this address space has */ + IOMMUNotifierFlag notifier_flags; + IOVATree *iova_tree; /* Traces mapped IOVA ranges */ }; struct VTDBus { @@ -253,11 +257,6 @@ struct VTD_MSIMessage { /* When IR is enabled, all MSI/MSI-X data bits should be zero */ #define VTD_IR_MSI_DATA (0) -struct IntelIOMMUNotifierNode { - VTDAddressSpace *vtd_as; - QLIST_ENTRY(IntelIOMMUNotifierNode) next; -}; - /* The iommu (DMAR) device state struct */ struct IntelIOMMUState { X86IOMMUState x86_iommu; @@ -295,7 +294,7 @@ struct IntelIOMMUState { GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */ VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */ /* list of registered notifiers */ - QLIST_HEAD(, IntelIOMMUNotifierNode) notifiers_list; + QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers; /* interrupt remapping */ bool intr_enabled; /* Whether guest enabled IR */ @@ -305,6 +304,12 @@ struct IntelIOMMUState { OnOffAuto intr_eim; /* Toggle for EIM cabability */ bool buggy_eim; /* Force buggy EIM unless eim=off */ uint8_t aw_bits; /* Host/IOVA address width (in bits) */ + + /* + * Protects IOMMU states in general. Currently it protects the + * per-IOMMU IOTLB cache, and context entry cache in VTDAddressSpace. + */ + QemuMutex iommu_lock; }; /* Find the VTD Address space associated with the given bus pointer, diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index 7fd87c4..74c6033 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -48,7 +48,7 @@ #define NVDIMM_GET_CLASS(obj) OBJECT_GET_CLASS(NVDIMMClass, (obj), \ TYPE_NVDIMM) -#define NVDIMM_LABLE_SIZE_PROP "label-size" +#define NVDIMM_LABEL_SIZE_PROP "label-size" #define NVDIMM_UNARMED_PROP "unarmed" struct NVDIMMDevice { diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index a9c3ee5..990d6fc 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -101,6 +101,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_REDHAT_PCIE_RP 0x000c #define PCI_DEVICE_ID_REDHAT_XHCI 0x000d #define PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE 0x000e +#define PCI_DEVICE_ID_REDHAT_MDPY 0x000f #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index ced3d2d..7fec9dc 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -52,6 +52,8 @@ typedef struct VirtioBusClass { bool (*has_extra_state)(DeviceState *d); bool (*query_guest_notifiers)(DeviceState *d); int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assign); + int (*set_host_notifier_mr)(DeviceState *d, int n, + MemoryRegion *mr, bool assign); void (*vmstate_change)(DeviceState *d, bool running); /* * Expose the features the transport layer supports before diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 098bdaa..9c1fa07 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -239,6 +239,8 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align); void virtio_queue_notify(VirtIODevice *vdev, int n); uint16_t virtio_queue_vector(VirtIODevice *vdev, int n); void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector); +int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, + MemoryRegion *mr, bool assign); int virtio_set_status(VirtIODevice *vdev, uint8_t val); void virtio_reset(void *opaque); void virtio_update_irq(VirtIODevice *vdev); diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen_backend.h index 3a27692..9c17fdd 100644 --- a/include/hw/xen/xen_backend.h +++ b/include/hw/xen/xen_backend.h @@ -16,7 +16,6 @@ /* variables */ extern struct xs_handle *xenstore; extern const char *xen_protocol; -extern bool xen_feature_grant_copy; extern DeviceState *xen_sysdev; extern BusState *xen_sysbus; @@ -42,6 +41,39 @@ void xen_be_register_common(void); int xen_be_register(const char *type, struct XenDevOps *ops); int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_set_max_grant_refs(struct XenDevice *xendev, + unsigned int nr_refs); +void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, + unsigned int nr_refs, int prot); +void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, + unsigned int nr_refs); + +typedef struct XenGrantCopySegment { + union { + void *virt; + struct { + uint32_t ref; + off_t offset; + } foreign; + } source, dest; + size_t len; +} XenGrantCopySegment; + +int xen_be_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, XenGrantCopySegment segs[], + unsigned int nr_segs); + +static inline void *xen_be_map_grant_ref(struct XenDevice *xendev, + uint32_t ref, int prot) +{ + return xen_be_map_grant_refs(xendev, &ref, 1, prot); +} + +static inline void xen_be_unmap_grant_ref(struct XenDevice *xendev, + void *ptr) +{ + return xen_be_unmap_grant_refs(xendev, ptr, 1); +} /* actual backend drivers */ extern struct XenDevOps xen_console_ops; /* xen_console.c */ diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 5f1402b..bbf207d 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -667,8 +667,21 @@ static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40800 - -typedef void *xengnttab_grant_copy_segment_t; +struct xengnttab_grant_copy_segment { + union xengnttab_copy_ptr { + void *virt; + struct { + uint32_t ref; + uint16_t offset; + uint16_t domid; + } foreign; + } source, dest; + uint16_t len; + uint16_t flags; + int16_t status; +}; + +typedef struct xengnttab_grant_copy_segment xengnttab_grant_copy_segment_t; static inline int xengnttab_grant_copy(xengnttab_handle *xgt, uint32_t count, xengnttab_grant_copy_segment_t *segs) diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h new file mode 100644 index 0000000..b061932 --- /dev/null +++ b/include/qemu/iova-tree.h @@ -0,0 +1,134 @@ +/* + * An very simplified iova tree implementation based on GTree. + * + * Copyright 2018 Red Hat, Inc. + * + * Authors: + * Peter Xu <peterx@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + */ +#ifndef IOVA_TREE_H +#define IOVA_TREE_H + +/* + * Currently the iova tree will only allow to keep ranges + * information, and no extra user data is allowed for each element. A + * benefit is that we can merge adjacent ranges internally within the + * tree. It can save a lot of memory when the ranges are splitted but + * mostly continuous. + * + * Note that current implementation does not provide any thread + * protections. Callers of the iova tree should be responsible + * for the thread safety issue. + */ + +#include "qemu/osdep.h" +#include "exec/memory.h" +#include "exec/hwaddr.h" + +#define IOVA_OK (0) +#define IOVA_ERR_INVALID (-1) /* Invalid parameters */ +#define IOVA_ERR_OVERLAP (-2) /* IOVA range overlapped */ + +typedef struct IOVATree IOVATree; +typedef struct DMAMap { + hwaddr iova; + hwaddr translated_addr; + hwaddr size; /* Inclusive */ + IOMMUAccessFlags perm; +} QEMU_PACKED DMAMap; +typedef gboolean (*iova_tree_iterator)(DMAMap *map); + +/** + * iova_tree_new: + * + * Create a new iova tree. + * + * Returns: the tree pointer when succeeded, or NULL if error. + */ +IOVATree *iova_tree_new(void); + +/** + * iova_tree_insert: + * + * @tree: the iova tree to insert + * @map: the mapping to insert + * + * Insert an iova range to the tree. If there is overlapped + * ranges, IOVA_ERR_OVERLAP will be returned. + * + * Return: 0 if succeeded, or <0 if error. + */ +int iova_tree_insert(IOVATree *tree, DMAMap *map); + +/** + * iova_tree_remove: + * + * @tree: the iova tree to remove range from + * @map: the map range to remove + * + * Remove mappings from the tree that are covered by the map range + * provided. The range does not need to be exactly what has inserted, + * all the mappings that are included in the provided range will be + * removed from the tree. Here map->translated_addr is meaningless. + * + * Return: 0 if succeeded, or <0 if error. + */ +int iova_tree_remove(IOVATree *tree, DMAMap *map); + +/** + * iova_tree_find: + * + * @tree: the iova tree to search from + * @map: the mapping to search + * + * Search for a mapping in the iova tree that overlaps with the + * mapping range specified. Only the first found mapping will be + * returned. + * + * Return: DMAMap pointer if found, or NULL if not found. Note that + * the returned DMAMap pointer is maintained internally. User should + * only read the content but never modify or free the content. Also, + * user is responsible to make sure the pointer is valid (say, no + * concurrent deletion in progress). + */ +DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map); + +/** + * iova_tree_find_address: + * + * @tree: the iova tree to search from + * @iova: the iova address to find + * + * Similar to iova_tree_find(), but it tries to find mapping with + * range iova=iova & size=0. + * + * Return: same as iova_tree_find(). + */ +DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova); + +/** + * iova_tree_foreach: + * + * @tree: the iova tree to iterate on + * @iterator: the interator for the mappings, return true to stop + * + * Iterate over the iova tree. + * + * Return: 1 if found any overlap, 0 if not, <0 if error. + */ +void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator); + +/** + * iova_tree_destroy: + * + * @tree: the iova tree to destroy + * + * Destroy an existing iova tree. + * + * Return: None. + */ +void iova_tree_destroy(IOVATree *tree); + +#endif diff --git a/include/qemu/job.h b/include/qemu/job.h new file mode 100644 index 0000000..8c8badf --- /dev/null +++ b/include/qemu/job.h @@ -0,0 +1,562 @@ +/* + * Declarations for background jobs + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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 JOB_H +#define JOB_H + +#include "qapi/qapi-types-block-core.h" +#include "qemu/queue.h" +#include "qemu/coroutine.h" +#include "block/aio.h" + +typedef struct JobDriver JobDriver; +typedef struct JobTxn JobTxn; + + +/** + * Long-running operation. + */ +typedef struct Job { + /** The ID of the job. May be NULL for internal jobs. */ + char *id; + + /** The type of this job. */ + const JobDriver *driver; + + /** Reference count of the block job */ + int refcnt; + + /** Current state; See @JobStatus for details. */ + JobStatus status; + + /** AioContext to run the job coroutine in */ + AioContext *aio_context; + + /** + * The coroutine that executes the job. If not NULL, it is reentered when + * busy is false and the job is cancelled. + */ + Coroutine *co; + + /** + * Timer that is used by @job_sleep_ns. Accessed under job_mutex (in + * job.c). + */ + QEMUTimer sleep_timer; + + /** + * Counter for pause request. If non-zero, the block job is either paused, + * or if busy == true will pause itself as soon as possible. + */ + int pause_count; + + /** + * Set to false by the job while the coroutine has yielded and may be + * re-entered by job_enter(). There may still be I/O or event loop activity + * pending. Accessed under block_job_mutex (in blockjob.c). + */ + bool busy; + + /** + * Set to true by the job while it is in a quiescent state, where + * no I/O or event loop activity is pending. + */ + bool paused; + + /** + * Set to true if the job is paused by user. Can be unpaused with the + * block-job-resume QMP command. + */ + bool user_paused; + + /** + * Set to true if the job should cancel itself. The flag must + * always be tested just before toggling the busy flag from false + * to true. After a job has been cancelled, it should only yield + * if #aio_poll will ("sooner or later") reenter the coroutine. + */ + bool cancelled; + + /** + * Set to true if the job should abort immediately without waiting + * for data to be in sync. + */ + bool force_cancel; + + /** Set to true when the job has deferred work to the main loop. */ + bool deferred_to_main_loop; + + /** True if this job should automatically finalize itself */ + bool auto_finalize; + + /** True if this job should automatically dismiss itself */ + bool auto_dismiss; + + /** + * Current progress. The unit is arbitrary as long as the ratio between + * progress_current and progress_total represents the estimated percentage + * of work already done. + */ + int64_t progress_current; + + /** Estimated progress_current value at the completion of the job */ + int64_t progress_total; + + /** ret code passed to job_completed. */ + int ret; + + /** The completion function that will be called when the job completes. */ + BlockCompletionFunc *cb; + + /** The opaque value that is passed to the completion function. */ + void *opaque; + + /** Notifiers called when a cancelled job is finalised */ + NotifierList on_finalize_cancelled; + + /** Notifiers called when a successfully completed job is finalised */ + NotifierList on_finalize_completed; + + /** Notifiers called when the job transitions to PENDING */ + NotifierList on_pending; + + /** Notifiers called when the job transitions to READY */ + NotifierList on_ready; + + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; + + /** Transaction this job is part of */ + JobTxn *txn; + + /** Element of the list of jobs in a job transaction */ + QLIST_ENTRY(Job) txn_list; +} Job; + +/** + * Callbacks and other information about a Job driver. + */ +struct JobDriver { + /** Derived Job struct size */ + size_t instance_size; + + /** Enum describing the operation */ + JobType job_type; + + /** Mandatory: Entrypoint for the Coroutine. */ + CoroutineEntry *start; + + /** + * If the callback is not NULL, it will be invoked when the job transitions + * into the paused state. Paused jobs must not perform any asynchronous + * I/O or event loop activity. This callback is used to quiesce jobs. + */ + void coroutine_fn (*pause)(Job *job); + + /** + * If the callback is not NULL, it will be invoked when the job transitions + * out of the paused state. Any asynchronous I/O or event loop activity + * should be restarted from this callback. + */ + void coroutine_fn (*resume)(Job *job); + + /** + * Called when the job is resumed by the user (i.e. user_paused becomes + * false). .user_resume is called before .resume. + */ + void (*user_resume)(Job *job); + + /** + * Optional callback for job types whose completion must be triggered + * manually. + */ + void (*complete)(Job *job, Error **errp); + + /* + * If the callback is not NULL, it will be invoked when the job has to be + * synchronously cancelled or completed; it should drain any activities + * as required to ensure progress. + */ + void (*drain)(Job *job); + + /** + * If the callback is not NULL, prepare will be invoked when all the jobs + * belonging to the same transaction complete; or upon this job's completion + * if it is not in a transaction. + * + * This callback will not be invoked if the job has already failed. + * If it fails, abort and then clean will be called. + */ + int (*prepare)(Job *job); + + /** + * If the callback is not NULL, it will be invoked when all the jobs + * belonging to the same transaction complete; or upon this job's + * completion if it is not in a transaction. Skipped if NULL. + * + * All jobs will complete with a call to either .commit() or .abort() but + * never both. + */ + void (*commit)(Job *job); + + /** + * If the callback is not NULL, it will be invoked when any job in the + * same transaction fails; or upon this job's failure (due to error or + * cancellation) if it is not in a transaction. Skipped if NULL. + * + * All jobs will complete with a call to either .commit() or .abort() but + * never both. + */ + void (*abort)(Job *job); + + /** + * If the callback is not NULL, it will be invoked after a call to either + * .commit() or .abort(). Regardless of which callback is invoked after + * completion, .clean() will always be called, even if the job does not + * belong to a transaction group. + */ + void (*clean)(Job *job); + + + /** Called when the job is freed */ + void (*free)(Job *job); +}; + +typedef enum JobCreateFlags { + /* Default behavior */ + JOB_DEFAULT = 0x00, + /* Job is not QMP-created and should not send QMP events */ + JOB_INTERNAL = 0x01, + /* Job requires manual finalize step */ + JOB_MANUAL_FINALIZE = 0x02, + /* Job requires manual dismiss step */ + JOB_MANUAL_DISMISS = 0x04, +} JobCreateFlags; + +/** + * Allocate and return a new job transaction. Jobs can be added to the + * transaction using job_txn_add_job(). + * + * The transaction is automatically freed when the last job completes or is + * cancelled. + * + * All jobs in the transaction either complete successfully or fail/cancel as a + * group. Jobs wait for each other before completing. Cancelling one job + * cancels all jobs in the transaction. + */ +JobTxn *job_txn_new(void); + +/** + * Release a reference that was previously acquired with job_txn_add_job or + * job_txn_new. If it's the last reference to the object, it will be freed. + */ +void job_txn_unref(JobTxn *txn); + +/** + * @txn: The transaction (may be NULL) + * @job: Job to add to the transaction + * + * Add @job to the transaction. The @job must not already be in a transaction. + * The caller must call either job_txn_unref() or job_completed() to release + * the reference that is automatically grabbed here. + * + * If @txn is NULL, the function does nothing. + */ +void job_txn_add_job(JobTxn *txn, Job *job); + +/** + * Create a new long-running job and return it. + * + * @job_id: The id of the newly-created job, or %NULL for internal jobs + * @driver: The class object for the newly-created job. + * @txn: The transaction this job belongs to, if any. %NULL otherwise. + * @ctx: The AioContext to run the job coroutine in. + * @flags: Creation flags for the job. See @JobCreateFlags. + * @cb: Completion function for the job. + * @opaque: Opaque pointer value passed to @cb. + * @errp: Error object. + */ +void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, + AioContext *ctx, int flags, BlockCompletionFunc *cb, + void *opaque, Error **errp); + +/** + * Add a reference to Job refcnt, it will be decreased with job_unref, and then + * be freed if it comes to be the last reference. + */ +void job_ref(Job *job); + +/** + * Release a reference that was previously acquired with job_ref() or + * job_create(). If it's the last reference to the object, it will be freed. + */ +void job_unref(Job *job); + +/** + * @job: The job that has made progress + * @done: How much progress the job made since the last call + * + * Updates the progress counter of the job. + */ +void job_progress_update(Job *job, uint64_t done); + +/** + * @job: The job whose expected progress end value is set + * @remaining: Missing progress (on top of the current progress counter value) + * until the new expected end value is reached + * + * Sets the expected end value of the progress counter of a job so that a + * completion percentage can be calculated when the progress is updated. + */ +void job_progress_set_remaining(Job *job, uint64_t remaining); + +/** To be called when a cancelled job is finalised. */ +void job_event_cancelled(Job *job); + +/** To be called when a successfully completed job is finalised. */ +void job_event_completed(Job *job); + +/** + * Conditionally enter the job coroutine if the job is ready to run, not + * already busy and fn() returns true. fn() is called while under the job_lock + * critical section. + */ +void job_enter_cond(Job *job, bool(*fn)(Job *job)); + +/** + * @job: A job that has not yet been started. + * + * Begins execution of a job. + * Takes ownership of one reference to the job object. + */ +void job_start(Job *job); + +/** + * @job: The job to enter. + * + * Continue the specified job by entering the coroutine. + */ +void job_enter(Job *job); + +/** + * @job: The job that is ready to pause. + * + * Pause now if job_pause() has been called. Jobs that perform lots of I/O + * must call this between requests so that the job can be paused. + */ +void coroutine_fn job_pause_point(Job *job); + +/** + * @job: The job that calls the function. + * + * Yield the job coroutine. + */ +void job_yield(Job *job); + +/** + * @job: The job that calls the function. + * @ns: How many nanoseconds to stop for. + * + * Put the job to sleep (assuming that it wasn't canceled) for @ns + * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately + * interrupt the wait. + */ +void coroutine_fn job_sleep_ns(Job *job, int64_t ns); + + +/** Returns the JobType of a given Job. */ +JobType job_type(const Job *job); + +/** Returns the enum string for the JobType of a given Job. */ +const char *job_type_str(const Job *job); + +/** Returns true if the job should not be visible to the management layer. */ +bool job_is_internal(Job *job); + +/** Returns whether the job is scheduled for cancellation. */ +bool job_is_cancelled(Job *job); + +/** Returns whether the job is in a completed state. */ +bool job_is_completed(Job *job); + +/** Returns whether the job is ready to be completed. */ +bool job_is_ready(Job *job); + +/** + * Request @job to pause at the next pause point. Must be paired with + * job_resume(). If the job is supposed to be resumed by user action, call + * job_user_pause() instead. + */ +void job_pause(Job *job); + +/** Resumes a @job paused with job_pause. */ +void job_resume(Job *job); + +/** + * Asynchronously pause the specified @job. + * Do not allow a resume until a matching call to job_user_resume. + */ +void job_user_pause(Job *job, Error **errp); + +/** Returns true if the job is user-paused. */ +bool job_user_paused(Job *job); + +/** + * Resume the specified @job. + * Must be paired with a preceding job_user_pause. + */ +void job_user_resume(Job *job, Error **errp); + +/* + * Drain any activities as required to ensure progress. This can be called in a + * loop to synchronously complete a job. + */ +void job_drain(Job *job); + +/** + * Get the next element from the list of block jobs after @job, or the + * first one if @job is %NULL. + * + * Returns the requested job, or %NULL if there are no more jobs left. + */ +Job *job_next(Job *job); + +/** + * Get the job identified by @id (which must not be %NULL). + * + * Returns the requested job, or %NULL if it doesn't exist. + */ +Job *job_get(const char *id); + +/** + * Check whether the verb @verb can be applied to @job in its current state. + * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM + * returned. + */ +int job_apply_verb(Job *job, JobVerb verb, Error **errp); + +/** The @job could not be started, free it. */ +void job_early_fail(Job *job); + +/** Moves the @job from RUNNING to READY */ +void job_transition_to_ready(Job *job); + +/** + * @job: The job being completed. + * @ret: The status code. + * + * Marks @job as completed. If @ret is non-zero, the job transaction it is part + * of is aborted. If @ret is zero, the job moves into the WAITING state. If it + * is the last job to complete in its transaction, all jobs in the transaction + * move from WAITING to PENDING. + */ +void job_completed(Job *job, int ret); + +/** Asynchronously complete the specified @job. */ +void job_complete(Job *job, Error **errp); + +/** + * Asynchronously cancel the specified @job. If @force is true, the job should + * be cancelled immediately without waiting for a consistent state. + */ +void job_cancel(Job *job, bool force); + +/** + * Cancels the specified job like job_cancel(), but may refuse to do so if the + * operation isn't meaningful in the current state of the job. + */ +void job_user_cancel(Job *job, bool force, Error **errp); + +/** + * Synchronously cancel the @job. The completion callback is called + * before the function returns. The job may actually complete + * instead of canceling itself; the circumstances under which this + * happens depend on the kind of job that is active. + * + * Returns the return value from the job if the job actually completed + * during the call, or -ECANCELED if it was canceled. + */ +int job_cancel_sync(Job *job); + +/** Synchronously cancels all jobs using job_cancel_sync(). */ +void job_cancel_sync_all(void); + +/** + * @job: The job to be completed. + * @errp: Error object which may be set by job_complete(); this is not + * necessarily set on every error, the job return value has to be + * checked as well. + * + * Synchronously complete the job. The completion callback is called before the + * function returns, unless it is NULL (which is permissible when using this + * function). + * + * Returns the return value from the job. + */ +int job_complete_sync(Job *job, Error **errp); + +/** + * For a @job that has finished its work and is pending awaiting explicit + * acknowledgement to commit its work, this will commit that work. + * + * FIXME: Make the below statement universally true: + * For jobs that support the manual workflow mode, all graph changes that occur + * as a result will occur after this command and before a successful reply. + */ +void job_finalize(Job *job, Error **errp); + +/** + * Remove the concluded @job from the query list and resets the passed pointer + * to %NULL. Returns an error if the job is not actually concluded. + */ +void job_dismiss(Job **job, Error **errp); + +typedef void JobDeferToMainLoopFn(Job *job, void *opaque); + +/** + * @job: The job + * @fn: The function to run in the main loop + * @opaque: The opaque value that is passed to @fn + * + * This function must be called by the main job coroutine just before it + * returns. @fn is executed in the main loop with the job AioContext acquired. + * + * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses + * bdrv_drain_all() in the main loop. + * + * The @job AioContext is held while @fn executes. + */ +void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); + +/** + * Synchronously finishes the given @job. If @finish is given, it is called to + * trigger completion or cancellation of the job. + * + * Returns 0 if the job is successfully completed, -ECANCELED if the job was + * cancelled before completing, and -errno in other error cases. + */ +int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp); + +#endif diff --git a/linux-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h index 4c58184..53a85ae 100644 --- a/linux-headers/asm-x86/kvm_para.h +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -2,16 +2,17 @@ #ifndef _ASM_X86_KVM_PARA_H #define _ASM_X86_KVM_PARA_H -#include <linux/types.h> -#include <asm/hyperv.h> +#include "standard-headers/linux/types.h" /* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It * should be used to determine that a VM is running under KVM. */ #define KVM_CPUID_SIGNATURE 0x40000000 -/* This CPUID returns a feature bitmap in eax. Before enabling a particular - * paravirtualization, the appropriate feature bit should be checked. +/* This CPUID returns two feature bitmaps in eax, edx. Before enabling + * a particular paravirtualization, the appropriate feature bit should + * be checked in eax. The performance hint feature bit should be checked + * in edx. */ #define KVM_CPUID_FEATURES 0x40000001 #define KVM_FEATURE_CLOCKSOURCE 0 @@ -28,6 +29,8 @@ #define KVM_FEATURE_PV_TLB_FLUSH 9 #define KVM_FEATURE_ASYNC_PF_VMEXIT 10 +#define KVM_HINTS_DEDICATED 0 + /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. */ @@ -45,12 +48,12 @@ #define MSR_KVM_PV_EOI_EN 0x4b564d04 struct kvm_steal_time { - __u64 steal; - __u32 version; - __u32 flags; - __u8 preempted; - __u8 u8_pad[3]; - __u32 pad[11]; + uint64_t steal; + uint32_t version; + uint32_t flags; + uint8_t preempted; + uint8_t uint8_t_pad[3]; + uint32_t pad[11]; }; #define KVM_VCPU_PREEMPTED (1 << 0) @@ -58,11 +61,11 @@ struct kvm_steal_time { #define KVM_CLOCK_PAIRING_WALLCLOCK 0 struct kvm_clock_pairing { - __s64 sec; - __s64 nsec; - __u64 tsc; - __u32 flags; - __u32 pad[9]; + int64_t sec; + int64_t nsec; + uint64_t tsc; + uint32_t flags; + uint32_t pad[9]; }; #define KVM_STEAL_ALIGNMENT_BITS 5 @@ -82,14 +85,14 @@ struct kvm_clock_pairing { /* Payload for KVM_HC_MMU_OP */ struct kvm_mmu_op_header { - __u32 op; - __u32 pad; + uint32_t op; + uint32_t pad; }; struct kvm_mmu_op_write_pte { struct kvm_mmu_op_header header; - __u64 pte_phys; - __u64 pte_val; + uint64_t pte_phys; + uint64_t pte_val; }; struct kvm_mmu_op_flush_tlb { @@ -98,16 +101,16 @@ struct kvm_mmu_op_flush_tlb { struct kvm_mmu_op_release_pt { struct kvm_mmu_op_header header; - __u64 pt_phys; + uint64_t pt_phys; }; #define KVM_PV_REASON_PAGE_NOT_PRESENT 1 #define KVM_PV_REASON_PAGE_READY 2 struct kvm_vcpu_pv_apf_data { - __u32 reason; - __u8 pad[60]; - __u32 enabled; + uint32_t reason; + uint8_t pad[60]; + uint32_t enabled; }; #define KVM_PV_EOI_BIT 0 diff --git a/include/standard-headers/linux/virtio_balloon.h b/include/standard-headers/linux/virtio_balloon.h index 7b0a41b..e446805 100644 --- a/include/standard-headers/linux/virtio_balloon.h +++ b/include/standard-headers/linux/virtio_balloon.h @@ -53,7 +53,9 @@ struct virtio_balloon_config { #define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ #define VIRTIO_BALLOON_S_AVAIL 6 /* Available memory as in /proc */ #define VIRTIO_BALLOON_S_CACHES 7 /* Disk caches */ -#define VIRTIO_BALLOON_S_NR 8 +#define VIRTIO_BALLOON_S_HTLB_PGALLOC 8 /* Hugetlb page allocations */ +#define VIRTIO_BALLOON_S_HTLB_PGFAIL 9 /* Hugetlb page allocation failures */ +#define VIRTIO_BALLOON_S_NR 10 /* * Memory statistics structure. diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 23669c4..0b64b8e 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -22,7 +22,6 @@ #ifdef NEED_CPU_H # ifdef CONFIG_KVM # include <linux/kvm.h> -# include <linux/kvm_para.h> # define CONFIG_KVM_IS_POSSIBLE # endif #else diff --git a/job-qmp.c b/job-qmp.c new file mode 100644 index 0000000..7f38f63 --- /dev/null +++ b/job-qmp.c @@ -0,0 +1,188 @@ +/* + * QMP interface for background jobs + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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 "qemu-common.h" +#include "qemu/job.h" +#include "qapi/qapi-commands-job.h" +#include "qapi/error.h" +#include "trace-root.h" + +/* Get a job using its ID and acquire its AioContext */ +static Job *find_job(const char *id, AioContext **aio_context, Error **errp) +{ + Job *job; + + *aio_context = NULL; + + job = job_get(id); + if (!job) { + error_setg(errp, "Job not found"); + return NULL; + } + + *aio_context = job->aio_context; + aio_context_acquire(*aio_context); + + return job; +} + +void qmp_job_cancel(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_cancel(job); + job_user_cancel(job, true, errp); + aio_context_release(aio_context); +} + +void qmp_job_pause(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_pause(job); + job_user_pause(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_resume(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_resume(job); + job_user_resume(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_complete(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_complete(job); + job_complete(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_finalize(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_finalize(job); + job_finalize(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_dismiss(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_dismiss(job); + job_dismiss(&job, errp); + aio_context_release(aio_context); +} + +static JobInfo *job_query_single(Job *job, Error **errp) +{ + JobInfo *info; + const char *errmsg = NULL; + + assert(!job_is_internal(job)); + + if (job->ret < 0) { + errmsg = strerror(-job->ret); + } + + info = g_new(JobInfo, 1); + *info = (JobInfo) { + .id = g_strdup(job->id), + .type = job_type(job), + .status = job->status, + .current_progress = job->progress_current, + .total_progress = job->progress_total, + .has_error = !!errmsg, + .error = g_strdup(errmsg), + }; + + return info; +} + +JobInfoList *qmp_query_jobs(Error **errp) +{ + JobInfoList *head = NULL, **p_next = &head; + Job *job; + + for (job = job_next(NULL); job; job = job_next(job)) { + JobInfoList *elem; + AioContext *aio_context; + + if (job_is_internal(job)) { + continue; + } + elem = g_new0(JobInfoList, 1); + aio_context = job->aio_context; + aio_context_acquire(aio_context); + elem->value = job_query_single(job, errp); + aio_context_release(aio_context); + if (!elem->value) { + g_free(elem); + qapi_free_JobInfoList(head); + return NULL; + } + *p_next = elem; + p_next = &elem->next; + } + + return head; +} @@ -0,0 +1,1000 @@ +/* + * Background jobs (long-running operations) + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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 "qemu-common.h" +#include "qapi/error.h" +#include "qemu/job.h" +#include "qemu/id.h" +#include "qemu/main-loop.h" +#include "trace-root.h" +#include "qapi/qapi-events-job.h" + +static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); + +/* Job State Transition Table */ +bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = { + /* U, C, R, P, Y, S, W, D, X, E, N */ + /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, + /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, + /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, + /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, + /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, + /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, + /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, + /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { + /* U, C, R, P, Y, S, W, D, X, E, N */ + [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, + [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, + [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, + [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, +}; + +/* Transactional group of jobs */ +struct JobTxn { + + /* Is this txn being cancelled? */ + bool aborting; + + /* List of jobs */ + QLIST_HEAD(, Job) jobs; + + /* Reference count */ + int refcnt; +}; + +/* Right now, this mutex is only needed to synchronize accesses to job->busy + * and job->sleep_timer, such as concurrent calls to job_do_yield and + * job_enter. */ +static QemuMutex job_mutex; + +static void job_lock(void) +{ + qemu_mutex_lock(&job_mutex); +} + +static void job_unlock(void) +{ + qemu_mutex_unlock(&job_mutex); +} + +static void __attribute__((__constructor__)) job_init(void) +{ + qemu_mutex_init(&job_mutex); +} + +JobTxn *job_txn_new(void) +{ + JobTxn *txn = g_new0(JobTxn, 1); + QLIST_INIT(&txn->jobs); + txn->refcnt = 1; + return txn; +} + +static void job_txn_ref(JobTxn *txn) +{ + txn->refcnt++; +} + +void job_txn_unref(JobTxn *txn) +{ + if (txn && --txn->refcnt == 0) { + g_free(txn); + } +} + +void job_txn_add_job(JobTxn *txn, Job *job) +{ + if (!txn) { + return; + } + + assert(!job->txn); + job->txn = txn; + + QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); + job_txn_ref(txn); +} + +static void job_txn_del_job(Job *job) +{ + if (job->txn) { + QLIST_REMOVE(job, txn_list); + job_txn_unref(job->txn); + job->txn = NULL; + } +} + +static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) +{ + AioContext *ctx; + Job *job, *next; + int rc = 0; + + QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { + if (lock) { + ctx = job->aio_context; + aio_context_acquire(ctx); + } + rc = fn(job); + if (lock) { + aio_context_release(ctx); + } + if (rc) { + break; + } + } + return rc; +} + +bool job_is_internal(Job *job) +{ + return (job->id == NULL); +} + +static void job_state_transition(Job *job, JobStatus s1) +{ + JobStatus s0 = job->status; + assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); + trace_job_state_transition(job, job->ret, + JobSTT[s0][s1] ? "allowed" : "disallowed", + JobStatus_str(s0), JobStatus_str(s1)); + assert(JobSTT[s0][s1]); + job->status = s1; + + if (!job_is_internal(job) && s1 != s0) { + qapi_event_send_job_status_change(job->id, job->status, &error_abort); + } +} + +int job_apply_verb(Job *job, JobVerb verb, Error **errp) +{ + JobStatus s0 = job->status; + assert(verb >= 0 && verb <= JOB_VERB__MAX); + trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), + JobVerbTable[verb][s0] ? "allowed" : "prohibited"); + if (JobVerbTable[verb][s0]) { + return 0; + } + error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", + job->id, JobStatus_str(s0), JobVerb_str(verb)); + return -EPERM; +} + +JobType job_type(const Job *job) +{ + return job->driver->job_type; +} + +const char *job_type_str(const Job *job) +{ + return JobType_str(job_type(job)); +} + +bool job_is_cancelled(Job *job) +{ + return job->cancelled; +} + +bool job_is_ready(Job *job) +{ + switch (job->status) { + case JOB_STATUS_UNDEFINED: + case JOB_STATUS_CREATED: + case JOB_STATUS_RUNNING: + case JOB_STATUS_PAUSED: + case JOB_STATUS_WAITING: + case JOB_STATUS_PENDING: + case JOB_STATUS_ABORTING: + case JOB_STATUS_CONCLUDED: + case JOB_STATUS_NULL: + return false; + case JOB_STATUS_READY: + case JOB_STATUS_STANDBY: + return true; + default: + g_assert_not_reached(); + } + return false; +} + +bool job_is_completed(Job *job) +{ + switch (job->status) { + case JOB_STATUS_UNDEFINED: + case JOB_STATUS_CREATED: + case JOB_STATUS_RUNNING: + case JOB_STATUS_PAUSED: + case JOB_STATUS_READY: + case JOB_STATUS_STANDBY: + return false; + case JOB_STATUS_WAITING: + case JOB_STATUS_PENDING: + case JOB_STATUS_ABORTING: + case JOB_STATUS_CONCLUDED: + case JOB_STATUS_NULL: + return true; + default: + g_assert_not_reached(); + } + return false; +} + +static bool job_started(Job *job) +{ + return job->co; +} + +static bool job_should_pause(Job *job) +{ + return job->pause_count > 0; +} + +Job *job_next(Job *job) +{ + if (!job) { + return QLIST_FIRST(&jobs); + } + return QLIST_NEXT(job, job_list); +} + +Job *job_get(const char *id) +{ + Job *job; + + QLIST_FOREACH(job, &jobs, job_list) { + if (job->id && !strcmp(id, job->id)) { + return job; + } + } + + return NULL; +} + +static void job_sleep_timer_cb(void *opaque) +{ + Job *job = opaque; + + job_enter(job); +} + +void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, + AioContext *ctx, int flags, BlockCompletionFunc *cb, + void *opaque, Error **errp) +{ + Job *job; + + if (job_id) { + if (flags & JOB_INTERNAL) { + error_setg(errp, "Cannot specify job ID for internal job"); + return NULL; + } + if (!id_wellformed(job_id)) { + error_setg(errp, "Invalid job ID '%s'", job_id); + return NULL; + } + if (job_get(job_id)) { + error_setg(errp, "Job ID '%s' already in use", job_id); + return NULL; + } + } else if (!(flags & JOB_INTERNAL)) { + error_setg(errp, "An explicit job ID is required"); + return NULL; + } + + job = g_malloc0(driver->instance_size); + job->driver = driver; + job->id = g_strdup(job_id); + job->refcnt = 1; + job->aio_context = ctx; + job->busy = false; + job->paused = true; + job->pause_count = 1; + job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); + job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); + job->cb = cb; + job->opaque = opaque; + + notifier_list_init(&job->on_finalize_cancelled); + notifier_list_init(&job->on_finalize_completed); + notifier_list_init(&job->on_pending); + notifier_list_init(&job->on_ready); + + job_state_transition(job, JOB_STATUS_CREATED); + aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, + QEMU_CLOCK_REALTIME, SCALE_NS, + job_sleep_timer_cb, job); + + QLIST_INSERT_HEAD(&jobs, job, job_list); + + /* Single jobs are modeled as single-job transactions for sake of + * consolidating the job management logic */ + if (!txn) { + txn = job_txn_new(); + job_txn_add_job(txn, job); + job_txn_unref(txn); + } else { + job_txn_add_job(txn, job); + } + + return job; +} + +void job_ref(Job *job) +{ + ++job->refcnt; +} + +void job_unref(Job *job) +{ + if (--job->refcnt == 0) { + assert(job->status == JOB_STATUS_NULL); + assert(!timer_pending(&job->sleep_timer)); + assert(!job->txn); + + if (job->driver->free) { + job->driver->free(job); + } + + QLIST_REMOVE(job, job_list); + + g_free(job->id); + g_free(job); + } +} + +void job_progress_update(Job *job, uint64_t done) +{ + job->progress_current += done; +} + +void job_progress_set_remaining(Job *job, uint64_t remaining) +{ + job->progress_total = job->progress_current + remaining; +} + +void job_event_cancelled(Job *job) +{ + notifier_list_notify(&job->on_finalize_cancelled, job); +} + +void job_event_completed(Job *job) +{ + notifier_list_notify(&job->on_finalize_completed, job); +} + +static void job_event_pending(Job *job) +{ + notifier_list_notify(&job->on_pending, job); +} + +static void job_event_ready(Job *job) +{ + notifier_list_notify(&job->on_ready, job); +} + +void job_enter_cond(Job *job, bool(*fn)(Job *job)) +{ + if (!job_started(job)) { + return; + } + if (job->deferred_to_main_loop) { + return; + } + + job_lock(); + if (job->busy) { + job_unlock(); + return; + } + + if (fn && !fn(job)) { + job_unlock(); + return; + } + + assert(!job->deferred_to_main_loop); + timer_del(&job->sleep_timer); + job->busy = true; + job_unlock(); + aio_co_wake(job->co); +} + +void job_enter(Job *job) +{ + job_enter_cond(job, NULL); +} + +/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. + * Reentering the job coroutine with job_enter() before the timer has expired + * is allowed and cancels the timer. + * + * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be + * called explicitly. */ +static void coroutine_fn job_do_yield(Job *job, uint64_t ns) +{ + job_lock(); + if (ns != -1) { + timer_mod(&job->sleep_timer, ns); + } + job->busy = false; + job_unlock(); + qemu_coroutine_yield(); + + /* Set by job_enter_cond() before re-entering the coroutine. */ + assert(job->busy); +} + +void coroutine_fn job_pause_point(Job *job) +{ + assert(job && job_started(job)); + + if (!job_should_pause(job)) { + return; + } + if (job_is_cancelled(job)) { + return; + } + + if (job->driver->pause) { + job->driver->pause(job); + } + + if (job_should_pause(job) && !job_is_cancelled(job)) { + JobStatus status = job->status; + job_state_transition(job, status == JOB_STATUS_READY + ? JOB_STATUS_STANDBY + : JOB_STATUS_PAUSED); + job->paused = true; + job_do_yield(job, -1); + job->paused = false; + job_state_transition(job, status); + } + + if (job->driver->resume) { + job->driver->resume(job); + } +} + +void job_yield(Job *job) +{ + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (job_is_cancelled(job)) { + return; + } + + if (!job_should_pause(job)) { + job_do_yield(job, -1); + } + + job_pause_point(job); +} + +void coroutine_fn job_sleep_ns(Job *job, int64_t ns) +{ + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (job_is_cancelled(job)) { + return; + } + + if (!job_should_pause(job)) { + job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); + } + + job_pause_point(job); +} + +void job_drain(Job *job) +{ + /* If job is !busy this kicks it into the next pause point. */ + job_enter(job); + + if (job->driver->drain) { + job->driver->drain(job); + } +} + + +/** + * All jobs must allow a pause point before entering their job proper. This + * ensures that jobs can be paused prior to being started, then resumed later. + */ +static void coroutine_fn job_co_entry(void *opaque) +{ + Job *job = opaque; + + assert(job && job->driver && job->driver->start); + job_pause_point(job); + job->driver->start(job); +} + + +void job_start(Job *job) +{ + assert(job && !job_started(job) && job->paused && + job->driver && job->driver->start); + job->co = qemu_coroutine_create(job_co_entry, job); + job->pause_count--; + job->busy = true; + job->paused = false; + job_state_transition(job, JOB_STATUS_RUNNING); + aio_co_enter(job->aio_context, job->co); +} + +/* Assumes the block_job_mutex is held */ +static bool job_timer_not_pending(Job *job) +{ + return !timer_pending(&job->sleep_timer); +} + +void job_pause(Job *job) +{ + job->pause_count++; +} + +void job_resume(Job *job) +{ + assert(job->pause_count > 0); + job->pause_count--; + if (job->pause_count) { + return; + } + + /* kick only if no timer is pending */ + job_enter_cond(job, job_timer_not_pending); +} + +void job_user_pause(Job *job, Error **errp) +{ + if (job_apply_verb(job, JOB_VERB_PAUSE, errp)) { + return; + } + if (job->user_paused) { + error_setg(errp, "Job is already paused"); + return; + } + job->user_paused = true; + job_pause(job); +} + +bool job_user_paused(Job *job) +{ + return job->user_paused; +} + +void job_user_resume(Job *job, Error **errp) +{ + assert(job); + if (!job->user_paused || job->pause_count <= 0) { + error_setg(errp, "Can't resume a job that was not paused"); + return; + } + if (job_apply_verb(job, JOB_VERB_RESUME, errp)) { + return; + } + if (job->driver->user_resume) { + job->driver->user_resume(job); + } + job->user_paused = false; + job_resume(job); +} + +static void job_do_dismiss(Job *job) +{ + assert(job); + job->busy = false; + job->paused = false; + job->deferred_to_main_loop = true; + + job_txn_del_job(job); + + job_state_transition(job, JOB_STATUS_NULL); + job_unref(job); +} + +void job_dismiss(Job **jobptr, Error **errp) +{ + Job *job = *jobptr; + /* similarly to _complete, this is QMP-interface only. */ + assert(job->id); + if (job_apply_verb(job, JOB_VERB_DISMISS, errp)) { + return; + } + + job_do_dismiss(job); + *jobptr = NULL; +} + +void job_early_fail(Job *job) +{ + assert(job->status == JOB_STATUS_CREATED); + job_do_dismiss(job); +} + +static void job_conclude(Job *job) +{ + job_state_transition(job, JOB_STATUS_CONCLUDED); + if (job->auto_dismiss || !job_started(job)) { + job_do_dismiss(job); + } +} + +static void job_update_rc(Job *job) +{ + if (!job->ret && job_is_cancelled(job)) { + job->ret = -ECANCELED; + } + if (job->ret) { + job_state_transition(job, JOB_STATUS_ABORTING); + } +} + +static void job_commit(Job *job) +{ + assert(!job->ret); + if (job->driver->commit) { + job->driver->commit(job); + } +} + +static void job_abort(Job *job) +{ + assert(job->ret); + if (job->driver->abort) { + job->driver->abort(job); + } +} + +static void job_clean(Job *job) +{ + if (job->driver->clean) { + job->driver->clean(job); + } +} + +static int job_finalize_single(Job *job) +{ + assert(job_is_completed(job)); + + /* Ensure abort is called for late-transactional failures */ + job_update_rc(job); + + if (!job->ret) { + job_commit(job); + } else { + job_abort(job); + } + job_clean(job); + + if (job->cb) { + job->cb(job->opaque, job->ret); + } + + /* Emit events only if we actually started */ + if (job_started(job)) { + if (job_is_cancelled(job)) { + job_event_cancelled(job); + } else { + job_event_completed(job); + } + } + + job_txn_del_job(job); + job_conclude(job); + return 0; +} + +static void job_cancel_async(Job *job, bool force) +{ + if (job->user_paused) { + /* Do not call job_enter here, the caller will handle it. */ + job->user_paused = false; + if (job->driver->user_resume) { + job->driver->user_resume(job); + } + assert(job->pause_count > 0); + job->pause_count--; + } + job->cancelled = true; + /* To prevent 'force == false' overriding a previous 'force == true' */ + job->force_cancel |= force; +} + +static void job_completed_txn_abort(Job *job) +{ + AioContext *ctx; + JobTxn *txn = job->txn; + Job *other_job; + + if (txn->aborting) { + /* + * We are cancelled by another job, which will handle everything. + */ + return; + } + txn->aborting = true; + job_txn_ref(txn); + + /* We are the first failed job. Cancel other jobs. */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + ctx = other_job->aio_context; + aio_context_acquire(ctx); + } + + /* Other jobs are effectively cancelled by us, set the status for + * them; this job, however, may or may not be cancelled, depending + * on the caller, so leave it. */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + if (other_job != job) { + job_cancel_async(other_job, false); + } + } + while (!QLIST_EMPTY(&txn->jobs)) { + other_job = QLIST_FIRST(&txn->jobs); + ctx = other_job->aio_context; + if (!job_is_completed(other_job)) { + assert(job_is_cancelled(other_job)); + job_finish_sync(other_job, NULL, NULL); + } + job_finalize_single(other_job); + aio_context_release(ctx); + } + + job_txn_unref(txn); +} + +static int job_prepare(Job *job) +{ + if (job->ret == 0 && job->driver->prepare) { + job->ret = job->driver->prepare(job); + } + return job->ret; +} + +static int job_needs_finalize(Job *job) +{ + return !job->auto_finalize; +} + +static void job_do_finalize(Job *job) +{ + int rc; + assert(job && job->txn); + + /* prepare the transaction to complete */ + rc = job_txn_apply(job->txn, job_prepare, true); + if (rc) { + job_completed_txn_abort(job); + } else { + job_txn_apply(job->txn, job_finalize_single, true); + } +} + +void job_finalize(Job *job, Error **errp) +{ + assert(job && job->id); + if (job_apply_verb(job, JOB_VERB_FINALIZE, errp)) { + return; + } + job_do_finalize(job); +} + +static int job_transition_to_pending(Job *job) +{ + job_state_transition(job, JOB_STATUS_PENDING); + if (!job->auto_finalize) { + job_event_pending(job); + } + return 0; +} + +void job_transition_to_ready(Job *job) +{ + job_state_transition(job, JOB_STATUS_READY); + job_event_ready(job); +} + +static void job_completed_txn_success(Job *job) +{ + JobTxn *txn = job->txn; + Job *other_job; + + job_state_transition(job, JOB_STATUS_WAITING); + + /* + * Successful completion, see if there are other running jobs in this + * txn. + */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + if (!job_is_completed(other_job)) { + return; + } + assert(other_job->ret == 0); + } + + job_txn_apply(txn, job_transition_to_pending, false); + + /* If no jobs need manual finalization, automatically do so */ + if (job_txn_apply(txn, job_needs_finalize, false) == 0) { + job_do_finalize(job); + } +} + +void job_completed(Job *job, int ret) +{ + assert(job && job->txn && !job_is_completed(job)); + job->ret = ret; + job_update_rc(job); + trace_job_completed(job, ret, job->ret); + if (job->ret) { + job_completed_txn_abort(job); + } else { + job_completed_txn_success(job); + } +} + +void job_cancel(Job *job, bool force) +{ + if (job->status == JOB_STATUS_CONCLUDED) { + job_do_dismiss(job); + return; + } + job_cancel_async(job, force); + if (!job_started(job)) { + job_completed(job, -ECANCELED); + } else if (job->deferred_to_main_loop) { + job_completed_txn_abort(job); + } else { + job_enter(job); + } +} + +void job_user_cancel(Job *job, bool force, Error **errp) +{ + if (job_apply_verb(job, JOB_VERB_CANCEL, errp)) { + return; + } + job_cancel(job, force); +} + +/* A wrapper around job_cancel() taking an Error ** parameter so it may be + * used with job_finish_sync() without the need for (rather nasty) function + * pointer casts there. */ +static void job_cancel_err(Job *job, Error **errp) +{ + job_cancel(job, false); +} + +int job_cancel_sync(Job *job) +{ + return job_finish_sync(job, &job_cancel_err, NULL); +} + +void job_cancel_sync_all(void) +{ + Job *job; + AioContext *aio_context; + + while ((job = job_next(NULL))) { + aio_context = job->aio_context; + aio_context_acquire(aio_context); + job_cancel_sync(job); + aio_context_release(aio_context); + } +} + +int job_complete_sync(Job *job, Error **errp) +{ + return job_finish_sync(job, job_complete, errp); +} + +void job_complete(Job *job, Error **errp) +{ + /* Should not be reachable via external interface for internal jobs */ + assert(job->id); + if (job_apply_verb(job, JOB_VERB_COMPLETE, errp)) { + return; + } + if (job->pause_count || job_is_cancelled(job) || !job->driver->complete) { + error_setg(errp, "The active block job '%s' cannot be completed", + job->id); + return; + } + + job->driver->complete(job, errp); +} + + +typedef struct { + Job *job; + JobDeferToMainLoopFn *fn; + void *opaque; +} JobDeferToMainLoopData; + +static void job_defer_to_main_loop_bh(void *opaque) +{ + JobDeferToMainLoopData *data = opaque; + Job *job = data->job; + AioContext *aio_context = job->aio_context; + + aio_context_acquire(aio_context); + data->fn(data->job, data->opaque); + aio_context_release(aio_context); + + g_free(data); +} + +void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque) +{ + JobDeferToMainLoopData *data = g_malloc(sizeof(*data)); + data->job = job; + data->fn = fn; + data->opaque = opaque; + job->deferred_to_main_loop = true; + + aio_bh_schedule_oneshot(qemu_get_aio_context(), + job_defer_to_main_loop_bh, data); +} + +int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) +{ + Error *local_err = NULL; + int ret; + + job_ref(job); + + if (finish) { + finish(job, &local_err); + } + if (local_err) { + error_propagate(errp, local_err); + job_unref(job); + return -EBUSY; + } + /* job_drain calls job_enter, and it should be enough to induce progress + * until the job completes or moves to the main thread. */ + while (!job->deferred_to_main_loop && !job_is_completed(job)) { + job_drain(job); + } + while (!job_is_completed(job)) { + aio_poll(qemu_get_aio_context(), true); + } + ret = (job_is_cancelled(job) && job->ret == 0) ? -ECANCELED : job->ret; + job_unref(job); + return ret; +} diff --git a/linux-headers/asm-arm/bitsperlong.h b/linux-headers/asm-arm/bitsperlong.h new file mode 100644 index 0000000..6dc0bb0 --- /dev/null +++ b/linux-headers/asm-arm/bitsperlong.h @@ -0,0 +1 @@ +#include <asm-generic/bitsperlong.h> diff --git a/linux-headers/asm-arm/kvm_para.h b/linux-headers/asm-arm/kvm_para.h deleted file mode 100644 index baacc49..0000000 --- a/linux-headers/asm-arm/kvm_para.h +++ /dev/null @@ -1,2 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#include <asm-generic/kvm_para.h> diff --git a/linux-headers/asm-arm64/bitsperlong.h b/linux-headers/asm-arm64/bitsperlong.h new file mode 100644 index 0000000..485d60b --- /dev/null +++ b/linux-headers/asm-arm64/bitsperlong.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2012 ARM Ltd. + * + * 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. + * + * 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 __ASM_BITSPERLONG_H +#define __ASM_BITSPERLONG_H + +#define __BITS_PER_LONG 64 + +#include <asm-generic/bitsperlong.h> + +#endif /* __ASM_BITSPERLONG_H */ diff --git a/linux-headers/asm-arm64/kvm_para.h b/linux-headers/asm-arm64/kvm_para.h deleted file mode 100644 index 14fab8f..0000000 --- a/linux-headers/asm-arm64/kvm_para.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/kvm_para.h> diff --git a/linux-headers/asm-generic/bitsperlong.h b/linux-headers/asm-generic/bitsperlong.h new file mode 100644 index 0000000..0aac245 --- /dev/null +++ b/linux-headers/asm-generic/bitsperlong.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_GENERIC_BITS_PER_LONG +#define __ASM_GENERIC_BITS_PER_LONG + +/* + * There seems to be no way of detecting this automatically from user + * space, so 64 bit architectures should override this in their + * bitsperlong.h. In particular, an architecture that supports + * both 32 and 64 bit user space must not rely on CONFIG_64BIT + * to decide it, but rather check a compiler provided macro. + */ +#ifndef __BITS_PER_LONG +#define __BITS_PER_LONG 32 +#endif + +#endif /* __ASM_GENERIC_BITS_PER_LONG */ diff --git a/linux-headers/asm-generic/kvm_para.h b/linux-headers/asm-generic/kvm_para.h deleted file mode 100644 index 486f0af..0000000 --- a/linux-headers/asm-generic/kvm_para.h +++ /dev/null @@ -1,4 +0,0 @@ -/* - * There isn't anything here, but the file must not be empty or patch - * will delete it. - */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h new file mode 100644 index 0000000..8bcb186 --- /dev/null +++ b/linux-headers/asm-generic/unistd.h @@ -0,0 +1,781 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#include <asm/bitsperlong.h> + +/* + * This file contains the system call numbers, based on the + * layout of the x86-64 architecture, which embeds the + * pointer to the syscall in the table. + * + * As a basic principle, no duplication of functionality + * should be added, e.g. we don't use lseek when llseek + * is present. New architectures should use this file + * and implement the less feature-full calls in user space. + */ + +#ifndef __SYSCALL +#define __SYSCALL(x, y) +#endif + +#if __BITS_PER_LONG == 32 || defined(__SYSCALL_COMPAT) +#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _32) +#else +#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _64) +#endif + +#ifdef __SYSCALL_COMPAT +#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _comp) +#define __SC_COMP_3264(_nr, _32, _64, _comp) __SYSCALL(_nr, _comp) +#else +#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _sys) +#define __SC_COMP_3264(_nr, _32, _64, _comp) __SC_3264(_nr, _32, _64) +#endif + +#define __NR_io_setup 0 +__SC_COMP(__NR_io_setup, sys_io_setup, compat_sys_io_setup) +#define __NR_io_destroy 1 +__SYSCALL(__NR_io_destroy, sys_io_destroy) +#define __NR_io_submit 2 +__SC_COMP(__NR_io_submit, sys_io_submit, compat_sys_io_submit) +#define __NR_io_cancel 3 +__SYSCALL(__NR_io_cancel, sys_io_cancel) +#define __NR_io_getevents 4 +__SC_COMP(__NR_io_getevents, sys_io_getevents, compat_sys_io_getevents) + +/* fs/xattr.c */ +#define __NR_setxattr 5 +__SYSCALL(__NR_setxattr, sys_setxattr) +#define __NR_lsetxattr 6 +__SYSCALL(__NR_lsetxattr, sys_lsetxattr) +#define __NR_fsetxattr 7 +__SYSCALL(__NR_fsetxattr, sys_fsetxattr) +#define __NR_getxattr 8 +__SYSCALL(__NR_getxattr, sys_getxattr) +#define __NR_lgetxattr 9 +__SYSCALL(__NR_lgetxattr, sys_lgetxattr) +#define __NR_fgetxattr 10 +__SYSCALL(__NR_fgetxattr, sys_fgetxattr) +#define __NR_listxattr 11 +__SYSCALL(__NR_listxattr, sys_listxattr) +#define __NR_llistxattr 12 +__SYSCALL(__NR_llistxattr, sys_llistxattr) +#define __NR_flistxattr 13 +__SYSCALL(__NR_flistxattr, sys_flistxattr) +#define __NR_removexattr 14 +__SYSCALL(__NR_removexattr, sys_removexattr) +#define __NR_lremovexattr 15 +__SYSCALL(__NR_lremovexattr, sys_lremovexattr) +#define __NR_fremovexattr 16 +__SYSCALL(__NR_fremovexattr, sys_fremovexattr) + +/* fs/dcache.c */ +#define __NR_getcwd 17 +__SYSCALL(__NR_getcwd, sys_getcwd) + +/* fs/cookies.c */ +#define __NR_lookup_dcookie 18 +__SC_COMP(__NR_lookup_dcookie, sys_lookup_dcookie, compat_sys_lookup_dcookie) + +/* fs/eventfd.c */ +#define __NR_eventfd2 19 +__SYSCALL(__NR_eventfd2, sys_eventfd2) + +/* fs/eventpoll.c */ +#define __NR_epoll_create1 20 +__SYSCALL(__NR_epoll_create1, sys_epoll_create1) +#define __NR_epoll_ctl 21 +__SYSCALL(__NR_epoll_ctl, sys_epoll_ctl) +#define __NR_epoll_pwait 22 +__SC_COMP(__NR_epoll_pwait, sys_epoll_pwait, compat_sys_epoll_pwait) + +/* fs/fcntl.c */ +#define __NR_dup 23 +__SYSCALL(__NR_dup, sys_dup) +#define __NR_dup3 24 +__SYSCALL(__NR_dup3, sys_dup3) +#define __NR3264_fcntl 25 +__SC_COMP_3264(__NR3264_fcntl, sys_fcntl64, sys_fcntl, compat_sys_fcntl64) + +/* fs/inotify_user.c */ +#define __NR_inotify_init1 26 +__SYSCALL(__NR_inotify_init1, sys_inotify_init1) +#define __NR_inotify_add_watch 27 +__SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch) +#define __NR_inotify_rm_watch 28 +__SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch) + +/* fs/ioctl.c */ +#define __NR_ioctl 29 +__SC_COMP(__NR_ioctl, sys_ioctl, compat_sys_ioctl) + +/* fs/ioprio.c */ +#define __NR_ioprio_set 30 +__SYSCALL(__NR_ioprio_set, sys_ioprio_set) +#define __NR_ioprio_get 31 +__SYSCALL(__NR_ioprio_get, sys_ioprio_get) + +/* fs/locks.c */ +#define __NR_flock 32 +__SYSCALL(__NR_flock, sys_flock) + +/* fs/namei.c */ +#define __NR_mknodat 33 +__SYSCALL(__NR_mknodat, sys_mknodat) +#define __NR_mkdirat 34 +__SYSCALL(__NR_mkdirat, sys_mkdirat) +#define __NR_unlinkat 35 +__SYSCALL(__NR_unlinkat, sys_unlinkat) +#define __NR_symlinkat 36 +__SYSCALL(__NR_symlinkat, sys_symlinkat) +#define __NR_linkat 37 +__SYSCALL(__NR_linkat, sys_linkat) +#ifdef __ARCH_WANT_RENAMEAT +/* renameat is superseded with flags by renameat2 */ +#define __NR_renameat 38 +__SYSCALL(__NR_renameat, sys_renameat) +#endif /* __ARCH_WANT_RENAMEAT */ + +/* fs/namespace.c */ +#define __NR_umount2 39 +__SYSCALL(__NR_umount2, sys_umount) +#define __NR_mount 40 +__SC_COMP(__NR_mount, sys_mount, compat_sys_mount) +#define __NR_pivot_root 41 +__SYSCALL(__NR_pivot_root, sys_pivot_root) + +/* fs/nfsctl.c */ +#define __NR_nfsservctl 42 +__SYSCALL(__NR_nfsservctl, sys_ni_syscall) + +/* fs/open.c */ +#define __NR3264_statfs 43 +__SC_COMP_3264(__NR3264_statfs, sys_statfs64, sys_statfs, \ + compat_sys_statfs64) +#define __NR3264_fstatfs 44 +__SC_COMP_3264(__NR3264_fstatfs, sys_fstatfs64, sys_fstatfs, \ + compat_sys_fstatfs64) +#define __NR3264_truncate 45 +__SC_COMP_3264(__NR3264_truncate, sys_truncate64, sys_truncate, \ + compat_sys_truncate64) +#define __NR3264_ftruncate 46 +__SC_COMP_3264(__NR3264_ftruncate, sys_ftruncate64, sys_ftruncate, \ + compat_sys_ftruncate64) + +#define __NR_fallocate 47 +__SC_COMP(__NR_fallocate, sys_fallocate, compat_sys_fallocate) +#define __NR_faccessat 48 +__SYSCALL(__NR_faccessat, sys_faccessat) +#define __NR_chdir 49 +__SYSCALL(__NR_chdir, sys_chdir) +#define __NR_fchdir 50 +__SYSCALL(__NR_fchdir, sys_fchdir) +#define __NR_chroot 51 +__SYSCALL(__NR_chroot, sys_chroot) +#define __NR_fchmod 52 +__SYSCALL(__NR_fchmod, sys_fchmod) +#define __NR_fchmodat 53 +__SYSCALL(__NR_fchmodat, sys_fchmodat) +#define __NR_fchownat 54 +__SYSCALL(__NR_fchownat, sys_fchownat) +#define __NR_fchown 55 +__SYSCALL(__NR_fchown, sys_fchown) +#define __NR_openat 56 +__SC_COMP(__NR_openat, sys_openat, compat_sys_openat) +#define __NR_close 57 +__SYSCALL(__NR_close, sys_close) +#define __NR_vhangup 58 +__SYSCALL(__NR_vhangup, sys_vhangup) + +/* fs/pipe.c */ +#define __NR_pipe2 59 +__SYSCALL(__NR_pipe2, sys_pipe2) + +/* fs/quota.c */ +#define __NR_quotactl 60 +__SYSCALL(__NR_quotactl, sys_quotactl) + +/* fs/readdir.c */ +#define __NR_getdents64 61 +__SYSCALL(__NR_getdents64, sys_getdents64) + +/* fs/read_write.c */ +#define __NR3264_lseek 62 +__SC_3264(__NR3264_lseek, sys_llseek, sys_lseek) +#define __NR_read 63 +__SYSCALL(__NR_read, sys_read) +#define __NR_write 64 +__SYSCALL(__NR_write, sys_write) +#define __NR_readv 65 +__SC_COMP(__NR_readv, sys_readv, compat_sys_readv) +#define __NR_writev 66 +__SC_COMP(__NR_writev, sys_writev, compat_sys_writev) +#define __NR_pread64 67 +__SC_COMP(__NR_pread64, sys_pread64, compat_sys_pread64) +#define __NR_pwrite64 68 +__SC_COMP(__NR_pwrite64, sys_pwrite64, compat_sys_pwrite64) +#define __NR_preadv 69 +__SC_COMP(__NR_preadv, sys_preadv, compat_sys_preadv) +#define __NR_pwritev 70 +__SC_COMP(__NR_pwritev, sys_pwritev, compat_sys_pwritev) + +/* fs/sendfile.c */ +#define __NR3264_sendfile 71 +__SYSCALL(__NR3264_sendfile, sys_sendfile64) + +/* fs/select.c */ +#define __NR_pselect6 72 +__SC_COMP(__NR_pselect6, sys_pselect6, compat_sys_pselect6) +#define __NR_ppoll 73 +__SC_COMP(__NR_ppoll, sys_ppoll, compat_sys_ppoll) + +/* fs/signalfd.c */ +#define __NR_signalfd4 74 +__SC_COMP(__NR_signalfd4, sys_signalfd4, compat_sys_signalfd4) + +/* fs/splice.c */ +#define __NR_vmsplice 75 +__SC_COMP(__NR_vmsplice, sys_vmsplice, compat_sys_vmsplice) +#define __NR_splice 76 +__SYSCALL(__NR_splice, sys_splice) +#define __NR_tee 77 +__SYSCALL(__NR_tee, sys_tee) + +/* fs/stat.c */ +#define __NR_readlinkat 78 +__SYSCALL(__NR_readlinkat, sys_readlinkat) +#define __NR3264_fstatat 79 +__SC_3264(__NR3264_fstatat, sys_fstatat64, sys_newfstatat) +#define __NR3264_fstat 80 +__SC_3264(__NR3264_fstat, sys_fstat64, sys_newfstat) + +/* fs/sync.c */ +#define __NR_sync 81 +__SYSCALL(__NR_sync, sys_sync) +#define __NR_fsync 82 +__SYSCALL(__NR_fsync, sys_fsync) +#define __NR_fdatasync 83 +__SYSCALL(__NR_fdatasync, sys_fdatasync) +#ifdef __ARCH_WANT_SYNC_FILE_RANGE2 +#define __NR_sync_file_range2 84 +__SC_COMP(__NR_sync_file_range2, sys_sync_file_range2, \ + compat_sys_sync_file_range2) +#else +#define __NR_sync_file_range 84 +__SC_COMP(__NR_sync_file_range, sys_sync_file_range, \ + compat_sys_sync_file_range) +#endif + +/* fs/timerfd.c */ +#define __NR_timerfd_create 85 +__SYSCALL(__NR_timerfd_create, sys_timerfd_create) +#define __NR_timerfd_settime 86 +__SC_COMP(__NR_timerfd_settime, sys_timerfd_settime, \ + compat_sys_timerfd_settime) +#define __NR_timerfd_gettime 87 +__SC_COMP(__NR_timerfd_gettime, sys_timerfd_gettime, \ + compat_sys_timerfd_gettime) + +/* fs/utimes.c */ +#define __NR_utimensat 88 +__SC_COMP(__NR_utimensat, sys_utimensat, compat_sys_utimensat) + +/* kernel/acct.c */ +#define __NR_acct 89 +__SYSCALL(__NR_acct, sys_acct) + +/* kernel/capability.c */ +#define __NR_capget 90 +__SYSCALL(__NR_capget, sys_capget) +#define __NR_capset 91 +__SYSCALL(__NR_capset, sys_capset) + +/* kernel/exec_domain.c */ +#define __NR_personality 92 +__SYSCALL(__NR_personality, sys_personality) + +/* kernel/exit.c */ +#define __NR_exit 93 +__SYSCALL(__NR_exit, sys_exit) +#define __NR_exit_group 94 +__SYSCALL(__NR_exit_group, sys_exit_group) +#define __NR_waitid 95 +__SC_COMP(__NR_waitid, sys_waitid, compat_sys_waitid) + +/* kernel/fork.c */ +#define __NR_set_tid_address 96 +__SYSCALL(__NR_set_tid_address, sys_set_tid_address) +#define __NR_unshare 97 +__SYSCALL(__NR_unshare, sys_unshare) + +/* kernel/futex.c */ +#define __NR_futex 98 +__SC_COMP(__NR_futex, sys_futex, compat_sys_futex) +#define __NR_set_robust_list 99 +__SC_COMP(__NR_set_robust_list, sys_set_robust_list, \ + compat_sys_set_robust_list) +#define __NR_get_robust_list 100 +__SC_COMP(__NR_get_robust_list, sys_get_robust_list, \ + compat_sys_get_robust_list) + +/* kernel/hrtimer.c */ +#define __NR_nanosleep 101 +__SC_COMP(__NR_nanosleep, sys_nanosleep, compat_sys_nanosleep) + +/* kernel/itimer.c */ +#define __NR_getitimer 102 +__SC_COMP(__NR_getitimer, sys_getitimer, compat_sys_getitimer) +#define __NR_setitimer 103 +__SC_COMP(__NR_setitimer, sys_setitimer, compat_sys_setitimer) + +/* kernel/kexec.c */ +#define __NR_kexec_load 104 +__SC_COMP(__NR_kexec_load, sys_kexec_load, compat_sys_kexec_load) + +/* kernel/module.c */ +#define __NR_init_module 105 +__SYSCALL(__NR_init_module, sys_init_module) +#define __NR_delete_module 106 +__SYSCALL(__NR_delete_module, sys_delete_module) + +/* kernel/posix-timers.c */ +#define __NR_timer_create 107 +__SC_COMP(__NR_timer_create, sys_timer_create, compat_sys_timer_create) +#define __NR_timer_gettime 108 +__SC_COMP(__NR_timer_gettime, sys_timer_gettime, compat_sys_timer_gettime) +#define __NR_timer_getoverrun 109 +__SYSCALL(__NR_timer_getoverrun, sys_timer_getoverrun) +#define __NR_timer_settime 110 +__SC_COMP(__NR_timer_settime, sys_timer_settime, compat_sys_timer_settime) +#define __NR_timer_delete 111 +__SYSCALL(__NR_timer_delete, sys_timer_delete) +#define __NR_clock_settime 112 +__SC_COMP(__NR_clock_settime, sys_clock_settime, compat_sys_clock_settime) +#define __NR_clock_gettime 113 +__SC_COMP(__NR_clock_gettime, sys_clock_gettime, compat_sys_clock_gettime) +#define __NR_clock_getres 114 +__SC_COMP(__NR_clock_getres, sys_clock_getres, compat_sys_clock_getres) +#define __NR_clock_nanosleep 115 +__SC_COMP(__NR_clock_nanosleep, sys_clock_nanosleep, \ + compat_sys_clock_nanosleep) + +/* kernel/printk.c */ +#define __NR_syslog 116 +__SYSCALL(__NR_syslog, sys_syslog) + +/* kernel/ptrace.c */ +#define __NR_ptrace 117 +__SYSCALL(__NR_ptrace, sys_ptrace) + +/* kernel/sched/core.c */ +#define __NR_sched_setparam 118 +__SYSCALL(__NR_sched_setparam, sys_sched_setparam) +#define __NR_sched_setscheduler 119 +__SYSCALL(__NR_sched_setscheduler, sys_sched_setscheduler) +#define __NR_sched_getscheduler 120 +__SYSCALL(__NR_sched_getscheduler, sys_sched_getscheduler) +#define __NR_sched_getparam 121 +__SYSCALL(__NR_sched_getparam, sys_sched_getparam) +#define __NR_sched_setaffinity 122 +__SC_COMP(__NR_sched_setaffinity, sys_sched_setaffinity, \ + compat_sys_sched_setaffinity) +#define __NR_sched_getaffinity 123 +__SC_COMP(__NR_sched_getaffinity, sys_sched_getaffinity, \ + compat_sys_sched_getaffinity) +#define __NR_sched_yield 124 +__SYSCALL(__NR_sched_yield, sys_sched_yield) +#define __NR_sched_get_priority_max 125 +__SYSCALL(__NR_sched_get_priority_max, sys_sched_get_priority_max) +#define __NR_sched_get_priority_min 126 +__SYSCALL(__NR_sched_get_priority_min, sys_sched_get_priority_min) +#define __NR_sched_rr_get_interval 127 +__SC_COMP(__NR_sched_rr_get_interval, sys_sched_rr_get_interval, \ + compat_sys_sched_rr_get_interval) + +/* kernel/signal.c */ +#define __NR_restart_syscall 128 +__SYSCALL(__NR_restart_syscall, sys_restart_syscall) +#define __NR_kill 129 +__SYSCALL(__NR_kill, sys_kill) +#define __NR_tkill 130 +__SYSCALL(__NR_tkill, sys_tkill) +#define __NR_tgkill 131 +__SYSCALL(__NR_tgkill, sys_tgkill) +#define __NR_sigaltstack 132 +__SC_COMP(__NR_sigaltstack, sys_sigaltstack, compat_sys_sigaltstack) +#define __NR_rt_sigsuspend 133 +__SC_COMP(__NR_rt_sigsuspend, sys_rt_sigsuspend, compat_sys_rt_sigsuspend) +#define __NR_rt_sigaction 134 +__SC_COMP(__NR_rt_sigaction, sys_rt_sigaction, compat_sys_rt_sigaction) +#define __NR_rt_sigprocmask 135 +__SC_COMP(__NR_rt_sigprocmask, sys_rt_sigprocmask, compat_sys_rt_sigprocmask) +#define __NR_rt_sigpending 136 +__SC_COMP(__NR_rt_sigpending, sys_rt_sigpending, compat_sys_rt_sigpending) +#define __NR_rt_sigtimedwait 137 +__SC_COMP(__NR_rt_sigtimedwait, sys_rt_sigtimedwait, \ + compat_sys_rt_sigtimedwait) +#define __NR_rt_sigqueueinfo 138 +__SC_COMP(__NR_rt_sigqueueinfo, sys_rt_sigqueueinfo, \ + compat_sys_rt_sigqueueinfo) +#define __NR_rt_sigreturn 139 +__SC_COMP(__NR_rt_sigreturn, sys_rt_sigreturn, compat_sys_rt_sigreturn) + +/* kernel/sys.c */ +#define __NR_setpriority 140 +__SYSCALL(__NR_setpriority, sys_setpriority) +#define __NR_getpriority 141 +__SYSCALL(__NR_getpriority, sys_getpriority) +#define __NR_reboot 142 +__SYSCALL(__NR_reboot, sys_reboot) +#define __NR_setregid 143 +__SYSCALL(__NR_setregid, sys_setregid) +#define __NR_setgid 144 +__SYSCALL(__NR_setgid, sys_setgid) +#define __NR_setreuid 145 +__SYSCALL(__NR_setreuid, sys_setreuid) +#define __NR_setuid 146 +__SYSCALL(__NR_setuid, sys_setuid) +#define __NR_setresuid 147 +__SYSCALL(__NR_setresuid, sys_setresuid) +#define __NR_getresuid 148 +__SYSCALL(__NR_getresuid, sys_getresuid) +#define __NR_setresgid 149 +__SYSCALL(__NR_setresgid, sys_setresgid) +#define __NR_getresgid 150 +__SYSCALL(__NR_getresgid, sys_getresgid) +#define __NR_setfsuid 151 +__SYSCALL(__NR_setfsuid, sys_setfsuid) +#define __NR_setfsgid 152 +__SYSCALL(__NR_setfsgid, sys_setfsgid) +#define __NR_times 153 +__SC_COMP(__NR_times, sys_times, compat_sys_times) +#define __NR_setpgid 154 +__SYSCALL(__NR_setpgid, sys_setpgid) +#define __NR_getpgid 155 +__SYSCALL(__NR_getpgid, sys_getpgid) +#define __NR_getsid 156 +__SYSCALL(__NR_getsid, sys_getsid) +#define __NR_setsid 157 +__SYSCALL(__NR_setsid, sys_setsid) +#define __NR_getgroups 158 +__SYSCALL(__NR_getgroups, sys_getgroups) +#define __NR_setgroups 159 +__SYSCALL(__NR_setgroups, sys_setgroups) +#define __NR_uname 160 +__SYSCALL(__NR_uname, sys_newuname) +#define __NR_sethostname 161 +__SYSCALL(__NR_sethostname, sys_sethostname) +#define __NR_setdomainname 162 +__SYSCALL(__NR_setdomainname, sys_setdomainname) +#define __NR_getrlimit 163 +__SC_COMP(__NR_getrlimit, sys_getrlimit, compat_sys_getrlimit) +#define __NR_setrlimit 164 +__SC_COMP(__NR_setrlimit, sys_setrlimit, compat_sys_setrlimit) +#define __NR_getrusage 165 +__SC_COMP(__NR_getrusage, sys_getrusage, compat_sys_getrusage) +#define __NR_umask 166 +__SYSCALL(__NR_umask, sys_umask) +#define __NR_prctl 167 +__SYSCALL(__NR_prctl, sys_prctl) +#define __NR_getcpu 168 +__SYSCALL(__NR_getcpu, sys_getcpu) + +/* kernel/time.c */ +#define __NR_gettimeofday 169 +__SC_COMP(__NR_gettimeofday, sys_gettimeofday, compat_sys_gettimeofday) +#define __NR_settimeofday 170 +__SC_COMP(__NR_settimeofday, sys_settimeofday, compat_sys_settimeofday) +#define __NR_adjtimex 171 +__SC_COMP(__NR_adjtimex, sys_adjtimex, compat_sys_adjtimex) + +/* kernel/timer.c */ +#define __NR_getpid 172 +__SYSCALL(__NR_getpid, sys_getpid) +#define __NR_getppid 173 +__SYSCALL(__NR_getppid, sys_getppid) +#define __NR_getuid 174 +__SYSCALL(__NR_getuid, sys_getuid) +#define __NR_geteuid 175 +__SYSCALL(__NR_geteuid, sys_geteuid) +#define __NR_getgid 176 +__SYSCALL(__NR_getgid, sys_getgid) +#define __NR_getegid 177 +__SYSCALL(__NR_getegid, sys_getegid) +#define __NR_gettid 178 +__SYSCALL(__NR_gettid, sys_gettid) +#define __NR_sysinfo 179 +__SC_COMP(__NR_sysinfo, sys_sysinfo, compat_sys_sysinfo) + +/* ipc/mqueue.c */ +#define __NR_mq_open 180 +__SC_COMP(__NR_mq_open, sys_mq_open, compat_sys_mq_open) +#define __NR_mq_unlink 181 +__SYSCALL(__NR_mq_unlink, sys_mq_unlink) +#define __NR_mq_timedsend 182 +__SC_COMP(__NR_mq_timedsend, sys_mq_timedsend, compat_sys_mq_timedsend) +#define __NR_mq_timedreceive 183 +__SC_COMP(__NR_mq_timedreceive, sys_mq_timedreceive, \ + compat_sys_mq_timedreceive) +#define __NR_mq_notify 184 +__SC_COMP(__NR_mq_notify, sys_mq_notify, compat_sys_mq_notify) +#define __NR_mq_getsetattr 185 +__SC_COMP(__NR_mq_getsetattr, sys_mq_getsetattr, compat_sys_mq_getsetattr) + +/* ipc/msg.c */ +#define __NR_msgget 186 +__SYSCALL(__NR_msgget, sys_msgget) +#define __NR_msgctl 187 +__SC_COMP(__NR_msgctl, sys_msgctl, compat_sys_msgctl) +#define __NR_msgrcv 188 +__SC_COMP(__NR_msgrcv, sys_msgrcv, compat_sys_msgrcv) +#define __NR_msgsnd 189 +__SC_COMP(__NR_msgsnd, sys_msgsnd, compat_sys_msgsnd) + +/* ipc/sem.c */ +#define __NR_semget 190 +__SYSCALL(__NR_semget, sys_semget) +#define __NR_semctl 191 +__SC_COMP(__NR_semctl, sys_semctl, compat_sys_semctl) +#define __NR_semtimedop 192 +__SC_COMP(__NR_semtimedop, sys_semtimedop, compat_sys_semtimedop) +#define __NR_semop 193 +__SYSCALL(__NR_semop, sys_semop) + +/* ipc/shm.c */ +#define __NR_shmget 194 +__SYSCALL(__NR_shmget, sys_shmget) +#define __NR_shmctl 195 +__SC_COMP(__NR_shmctl, sys_shmctl, compat_sys_shmctl) +#define __NR_shmat 196 +__SC_COMP(__NR_shmat, sys_shmat, compat_sys_shmat) +#define __NR_shmdt 197 +__SYSCALL(__NR_shmdt, sys_shmdt) + +/* net/socket.c */ +#define __NR_socket 198 +__SYSCALL(__NR_socket, sys_socket) +#define __NR_socketpair 199 +__SYSCALL(__NR_socketpair, sys_socketpair) +#define __NR_bind 200 +__SYSCALL(__NR_bind, sys_bind) +#define __NR_listen 201 +__SYSCALL(__NR_listen, sys_listen) +#define __NR_accept 202 +__SYSCALL(__NR_accept, sys_accept) +#define __NR_connect 203 +__SYSCALL(__NR_connect, sys_connect) +#define __NR_getsockname 204 +__SYSCALL(__NR_getsockname, sys_getsockname) +#define __NR_getpeername 205 +__SYSCALL(__NR_getpeername, sys_getpeername) +#define __NR_sendto 206 +__SYSCALL(__NR_sendto, sys_sendto) +#define __NR_recvfrom 207 +__SC_COMP(__NR_recvfrom, sys_recvfrom, compat_sys_recvfrom) +#define __NR_setsockopt 208 +__SC_COMP(__NR_setsockopt, sys_setsockopt, compat_sys_setsockopt) +#define __NR_getsockopt 209 +__SC_COMP(__NR_getsockopt, sys_getsockopt, compat_sys_getsockopt) +#define __NR_shutdown 210 +__SYSCALL(__NR_shutdown, sys_shutdown) +#define __NR_sendmsg 211 +__SC_COMP(__NR_sendmsg, sys_sendmsg, compat_sys_sendmsg) +#define __NR_recvmsg 212 +__SC_COMP(__NR_recvmsg, sys_recvmsg, compat_sys_recvmsg) + +/* mm/filemap.c */ +#define __NR_readahead 213 +__SC_COMP(__NR_readahead, sys_readahead, compat_sys_readahead) + +/* mm/nommu.c, also with MMU */ +#define __NR_brk 214 +__SYSCALL(__NR_brk, sys_brk) +#define __NR_munmap 215 +__SYSCALL(__NR_munmap, sys_munmap) +#define __NR_mremap 216 +__SYSCALL(__NR_mremap, sys_mremap) + +/* security/keys/keyctl.c */ +#define __NR_add_key 217 +__SYSCALL(__NR_add_key, sys_add_key) +#define __NR_request_key 218 +__SYSCALL(__NR_request_key, sys_request_key) +#define __NR_keyctl 219 +__SC_COMP(__NR_keyctl, sys_keyctl, compat_sys_keyctl) + +/* arch/example/kernel/sys_example.c */ +#define __NR_clone 220 +__SYSCALL(__NR_clone, sys_clone) +#define __NR_execve 221 +__SC_COMP(__NR_execve, sys_execve, compat_sys_execve) + +#define __NR3264_mmap 222 +__SC_3264(__NR3264_mmap, sys_mmap2, sys_mmap) +/* mm/fadvise.c */ +#define __NR3264_fadvise64 223 +__SC_COMP(__NR3264_fadvise64, sys_fadvise64_64, compat_sys_fadvise64_64) + +/* mm/, CONFIG_MMU only */ +#ifndef __ARCH_NOMMU +#define __NR_swapon 224 +__SYSCALL(__NR_swapon, sys_swapon) +#define __NR_swapoff 225 +__SYSCALL(__NR_swapoff, sys_swapoff) +#define __NR_mprotect 226 +__SYSCALL(__NR_mprotect, sys_mprotect) +#define __NR_msync 227 +__SYSCALL(__NR_msync, sys_msync) +#define __NR_mlock 228 +__SYSCALL(__NR_mlock, sys_mlock) +#define __NR_munlock 229 +__SYSCALL(__NR_munlock, sys_munlock) +#define __NR_mlockall 230 +__SYSCALL(__NR_mlockall, sys_mlockall) +#define __NR_munlockall 231 +__SYSCALL(__NR_munlockall, sys_munlockall) +#define __NR_mincore 232 +__SYSCALL(__NR_mincore, sys_mincore) +#define __NR_madvise 233 +__SYSCALL(__NR_madvise, sys_madvise) +#define __NR_remap_file_pages 234 +__SYSCALL(__NR_remap_file_pages, sys_remap_file_pages) +#define __NR_mbind 235 +__SC_COMP(__NR_mbind, sys_mbind, compat_sys_mbind) +#define __NR_get_mempolicy 236 +__SC_COMP(__NR_get_mempolicy, sys_get_mempolicy, compat_sys_get_mempolicy) +#define __NR_set_mempolicy 237 +__SC_COMP(__NR_set_mempolicy, sys_set_mempolicy, compat_sys_set_mempolicy) +#define __NR_migrate_pages 238 +__SC_COMP(__NR_migrate_pages, sys_migrate_pages, compat_sys_migrate_pages) +#define __NR_move_pages 239 +__SC_COMP(__NR_move_pages, sys_move_pages, compat_sys_move_pages) +#endif + +#define __NR_rt_tgsigqueueinfo 240 +__SC_COMP(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo, \ + compat_sys_rt_tgsigqueueinfo) +#define __NR_perf_event_open 241 +__SYSCALL(__NR_perf_event_open, sys_perf_event_open) +#define __NR_accept4 242 +__SYSCALL(__NR_accept4, sys_accept4) +#define __NR_recvmmsg 243 +__SC_COMP(__NR_recvmmsg, sys_recvmmsg, compat_sys_recvmmsg) + +/* + * Architectures may provide up to 16 syscalls of their own + * starting with this value. + */ +#define __NR_arch_specific_syscall 244 + +#define __NR_wait4 260 +__SC_COMP(__NR_wait4, sys_wait4, compat_sys_wait4) +#define __NR_prlimit64 261 +__SYSCALL(__NR_prlimit64, sys_prlimit64) +#define __NR_fanotify_init 262 +__SYSCALL(__NR_fanotify_init, sys_fanotify_init) +#define __NR_fanotify_mark 263 +__SYSCALL(__NR_fanotify_mark, sys_fanotify_mark) +#define __NR_name_to_handle_at 264 +__SYSCALL(__NR_name_to_handle_at, sys_name_to_handle_at) +#define __NR_open_by_handle_at 265 +__SC_COMP(__NR_open_by_handle_at, sys_open_by_handle_at, \ + compat_sys_open_by_handle_at) +#define __NR_clock_adjtime 266 +__SC_COMP(__NR_clock_adjtime, sys_clock_adjtime, compat_sys_clock_adjtime) +#define __NR_syncfs 267 +__SYSCALL(__NR_syncfs, sys_syncfs) +#define __NR_setns 268 +__SYSCALL(__NR_setns, sys_setns) +#define __NR_sendmmsg 269 +__SC_COMP(__NR_sendmmsg, sys_sendmmsg, compat_sys_sendmmsg) +#define __NR_process_vm_readv 270 +__SC_COMP(__NR_process_vm_readv, sys_process_vm_readv, \ + compat_sys_process_vm_readv) +#define __NR_process_vm_writev 271 +__SC_COMP(__NR_process_vm_writev, sys_process_vm_writev, \ + compat_sys_process_vm_writev) +#define __NR_kcmp 272 +__SYSCALL(__NR_kcmp, sys_kcmp) +#define __NR_finit_module 273 +__SYSCALL(__NR_finit_module, sys_finit_module) +#define __NR_sched_setattr 274 +__SYSCALL(__NR_sched_setattr, sys_sched_setattr) +#define __NR_sched_getattr 275 +__SYSCALL(__NR_sched_getattr, sys_sched_getattr) +#define __NR_renameat2 276 +__SYSCALL(__NR_renameat2, sys_renameat2) +#define __NR_seccomp 277 +__SYSCALL(__NR_seccomp, sys_seccomp) +#define __NR_getrandom 278 +__SYSCALL(__NR_getrandom, sys_getrandom) +#define __NR_memfd_create 279 +__SYSCALL(__NR_memfd_create, sys_memfd_create) +#define __NR_bpf 280 +__SYSCALL(__NR_bpf, sys_bpf) +#define __NR_execveat 281 +__SC_COMP(__NR_execveat, sys_execveat, compat_sys_execveat) +#define __NR_userfaultfd 282 +__SYSCALL(__NR_userfaultfd, sys_userfaultfd) +#define __NR_membarrier 283 +__SYSCALL(__NR_membarrier, sys_membarrier) +#define __NR_mlock2 284 +__SYSCALL(__NR_mlock2, sys_mlock2) +#define __NR_copy_file_range 285 +__SYSCALL(__NR_copy_file_range, sys_copy_file_range) +#define __NR_preadv2 286 +__SC_COMP(__NR_preadv2, sys_preadv2, compat_sys_preadv2) +#define __NR_pwritev2 287 +__SC_COMP(__NR_pwritev2, sys_pwritev2, compat_sys_pwritev2) +#define __NR_pkey_mprotect 288 +__SYSCALL(__NR_pkey_mprotect, sys_pkey_mprotect) +#define __NR_pkey_alloc 289 +__SYSCALL(__NR_pkey_alloc, sys_pkey_alloc) +#define __NR_pkey_free 290 +__SYSCALL(__NR_pkey_free, sys_pkey_free) +#define __NR_statx 291 +__SYSCALL(__NR_statx, sys_statx) + +#undef __NR_syscalls +#define __NR_syscalls 292 + +/* + * 32 bit systems traditionally used different + * syscalls for off_t and loff_t arguments, while + * 64 bit systems only need the off_t version. + * For new 32 bit platforms, there is no need to + * implement the old 32 bit off_t syscalls, so + * they take different names. + * Here we map the numbers so that both versions + * use the same syscall table layout. + */ +#if __BITS_PER_LONG == 64 && !defined(__SYSCALL_COMPAT) +#define __NR_fcntl __NR3264_fcntl +#define __NR_statfs __NR3264_statfs +#define __NR_fstatfs __NR3264_fstatfs +#define __NR_truncate __NR3264_truncate +#define __NR_ftruncate __NR3264_ftruncate +#define __NR_lseek __NR3264_lseek +#define __NR_sendfile __NR3264_sendfile +#define __NR_newfstatat __NR3264_fstatat +#define __NR_fstat __NR3264_fstat +#define __NR_mmap __NR3264_mmap +#define __NR_fadvise64 __NR3264_fadvise64 +#ifdef __NR3264_stat +#define __NR_stat __NR3264_stat +#define __NR_lstat __NR3264_lstat +#endif +#else +#define __NR_fcntl64 __NR3264_fcntl +#define __NR_statfs64 __NR3264_statfs +#define __NR_fstatfs64 __NR3264_fstatfs +#define __NR_truncate64 __NR3264_truncate +#define __NR_ftruncate64 __NR3264_ftruncate +#define __NR_llseek __NR3264_lseek +#define __NR_sendfile64 __NR3264_sendfile +#define __NR_fstatat64 __NR3264_fstatat +#define __NR_fstat64 __NR3264_fstat +#define __NR_mmap2 __NR3264_mmap +#define __NR_fadvise64_64 __NR3264_fadvise64 +#ifdef __NR3264_stat +#define __NR_stat64 __NR3264_stat +#define __NR_lstat64 __NR3264_lstat +#endif +#endif diff --git a/linux-headers/asm-mips/bitsperlong.h b/linux-headers/asm-mips/bitsperlong.h new file mode 100644 index 0000000..7268380 --- /dev/null +++ b/linux-headers/asm-mips/bitsperlong.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_MIPS_BITSPERLONG_H +#define __ASM_MIPS_BITSPERLONG_H + +#define __BITS_PER_LONG _MIPS_SZLONG + +#include <asm-generic/bitsperlong.h> + +#endif /* __ASM_MIPS_BITSPERLONG_H */ diff --git a/linux-headers/asm-mips/kvm.h b/linux-headers/asm-mips/kvm.h index 6985eb5..edcf717 100644 --- a/linux-headers/asm-mips/kvm.h +++ b/linux-headers/asm-mips/kvm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -19,6 +20,10 @@ * Some parts derived from the x86 version of this file. */ +#define __KVM_HAVE_READONLY_MEM + +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + /* * for KVM_GET_REGS and KVM_SET_REGS * @@ -52,9 +57,14 @@ struct kvm_fpu { * Register set = 0: GP registers from kvm_regs (see definitions below). * * Register set = 1: CP0 registers. - * bits[15..8] - Must be zero. - * bits[7..3] - Register 'rd' index. - * bits[2..0] - Register 'sel' index. + * bits[15..8] - COP0 register set. + * + * COP0 register set = 0: Main CP0 registers. + * bits[7..3] - Register 'rd' index. + * bits[2..0] - Register 'sel' index. + * + * COP0 register set = 1: MAARs. + * bits[7..0] - MAAR index. * * Register set = 2: KVM specific registers (see definitions below). * @@ -113,6 +123,15 @@ struct kvm_fpu { /* + * KVM_REG_MIPS_CP0 - Coprocessor 0 registers. + */ + +#define KVM_REG_MIPS_MAAR (KVM_REG_MIPS_CP0 | (1 << 8)) +#define KVM_REG_MIPS_CP0_MAAR(n) (KVM_REG_MIPS_MAAR | \ + KVM_REG_SIZE_U64 | (n)) + + +/* * KVM_REG_MIPS_KVM - KVM specific control registers. */ diff --git a/linux-headers/asm-mips/kvm_para.h b/linux-headers/asm-mips/kvm_para.h deleted file mode 100644 index dbb2464..0000000 --- a/linux-headers/asm-mips/kvm_para.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef _ASM_MIPS_KVM_PARA_H -#define _ASM_MIPS_KVM_PARA_H - - -#endif /* _ASM_MIPS_KVM_PARA_H */ diff --git a/linux-headers/asm-mips/sgidefs.h b/linux-headers/asm-mips/sgidefs.h new file mode 100644 index 0000000..26143e3 --- /dev/null +++ b/linux-headers/asm-mips/sgidefs.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996, 1999, 2001 Ralf Baechle + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#ifndef __ASM_SGIDEFS_H +#define __ASM_SGIDEFS_H + +/* + * Using a Linux compiler for building Linux seems logic but not to + * everybody. + */ +#ifndef __linux__ +#error Use a Linux compiler or give up. +#endif + +/* + * Definitions for the ISA levels + * + * With the introduction of MIPS32 / MIPS64 instruction sets definitions + * MIPS ISAs are no longer subsets of each other. Therefore comparisons + * on these symbols except with == may result in unexpected results and + * are forbidden! + */ +#define _MIPS_ISA_MIPS1 1 +#define _MIPS_ISA_MIPS2 2 +#define _MIPS_ISA_MIPS3 3 +#define _MIPS_ISA_MIPS4 4 +#define _MIPS_ISA_MIPS5 5 +#define _MIPS_ISA_MIPS32 6 +#define _MIPS_ISA_MIPS64 7 + +/* + * Subprogram calling convention + */ +#define _MIPS_SIM_ABI32 1 +#define _MIPS_SIM_NABI32 2 +#define _MIPS_SIM_ABI64 3 + +#endif /* __ASM_SGIDEFS_H */ diff --git a/linux-headers/asm-mips/unistd.h b/linux-headers/asm-mips/unistd.h index 2a20209..9bfef7f 100644 --- a/linux-headers/asm-mips/unistd.h +++ b/linux-headers/asm-mips/unistd.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -377,16 +378,27 @@ #define __NR_memfd_create (__NR_Linux + 354) #define __NR_bpf (__NR_Linux + 355) #define __NR_execveat (__NR_Linux + 356) +#define __NR_userfaultfd (__NR_Linux + 357) +#define __NR_membarrier (__NR_Linux + 358) +#define __NR_mlock2 (__NR_Linux + 359) +#define __NR_copy_file_range (__NR_Linux + 360) +#define __NR_preadv2 (__NR_Linux + 361) +#define __NR_pwritev2 (__NR_Linux + 362) +#define __NR_pkey_mprotect (__NR_Linux + 363) +#define __NR_pkey_alloc (__NR_Linux + 364) +#define __NR_pkey_free (__NR_Linux + 365) +#define __NR_statx (__NR_Linux + 366) + /* * Offset of the last Linux o32 flavoured syscall */ -#define __NR_Linux_syscalls 356 +#define __NR_Linux_syscalls 366 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ #define __NR_O32_Linux 4000 -#define __NR_O32_Linux_syscalls 356 +#define __NR_O32_Linux_syscalls 366 #if _MIPS_SIM == _MIPS_SIM_ABI64 @@ -711,16 +723,26 @@ #define __NR_memfd_create (__NR_Linux + 314) #define __NR_bpf (__NR_Linux + 315) #define __NR_execveat (__NR_Linux + 316) +#define __NR_userfaultfd (__NR_Linux + 317) +#define __NR_membarrier (__NR_Linux + 318) +#define __NR_mlock2 (__NR_Linux + 319) +#define __NR_copy_file_range (__NR_Linux + 320) +#define __NR_preadv2 (__NR_Linux + 321) +#define __NR_pwritev2 (__NR_Linux + 322) +#define __NR_pkey_mprotect (__NR_Linux + 323) +#define __NR_pkey_alloc (__NR_Linux + 324) +#define __NR_pkey_free (__NR_Linux + 325) +#define __NR_statx (__NR_Linux + 326) /* * Offset of the last Linux 64-bit flavoured syscall */ -#define __NR_Linux_syscalls 316 +#define __NR_Linux_syscalls 326 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ #define __NR_64_Linux 5000 -#define __NR_64_Linux_syscalls 316 +#define __NR_64_Linux_syscalls 326 #if _MIPS_SIM == _MIPS_SIM_NABI32 @@ -1049,15 +1071,25 @@ #define __NR_memfd_create (__NR_Linux + 318) #define __NR_bpf (__NR_Linux + 319) #define __NR_execveat (__NR_Linux + 320) +#define __NR_userfaultfd (__NR_Linux + 321) +#define __NR_membarrier (__NR_Linux + 322) +#define __NR_mlock2 (__NR_Linux + 323) +#define __NR_copy_file_range (__NR_Linux + 324) +#define __NR_preadv2 (__NR_Linux + 325) +#define __NR_pwritev2 (__NR_Linux + 326) +#define __NR_pkey_mprotect (__NR_Linux + 327) +#define __NR_pkey_alloc (__NR_Linux + 328) +#define __NR_pkey_free (__NR_Linux + 329) +#define __NR_statx (__NR_Linux + 330) /* * Offset of the last N32 flavoured syscall */ -#define __NR_Linux_syscalls 320 +#define __NR_Linux_syscalls 330 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ #define __NR_N32_Linux 6000 -#define __NR_N32_Linux_syscalls 320 +#define __NR_N32_Linux_syscalls 330 #endif /* _ASM_UNISTD_H */ diff --git a/linux-headers/asm-powerpc/bitsperlong.h b/linux-headers/asm-powerpc/bitsperlong.h new file mode 100644 index 0000000..46ece3e --- /dev/null +++ b/linux-headers/asm-powerpc/bitsperlong.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_POWERPC_BITSPERLONG_H +#define __ASM_POWERPC_BITSPERLONG_H + +#if defined(__powerpc64__) +# define __BITS_PER_LONG 64 +#else +# define __BITS_PER_LONG 32 +#endif + +#include <asm-generic/bitsperlong.h> + +#endif /* __ASM_POWERPC_BITSPERLONG_H */ diff --git a/linux-headers/asm-powerpc/epapr_hcalls.h b/linux-headers/asm-powerpc/epapr_hcalls.h deleted file mode 100644 index 6cca559..0000000 --- a/linux-headers/asm-powerpc/epapr_hcalls.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */ -/* - * ePAPR hcall interface - * - * Copyright 2008-2011 Freescale Semiconductor, Inc. - * - * Author: Timur Tabi <timur@freescale.com> - * - * This file is provided under a dual BSD/GPL license. When using or - * redistributing this file, you may do so under either license. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _ASM_POWERPC_EPAPR_HCALLS_H -#define _ASM_POWERPC_EPAPR_HCALLS_H - -#define EV_BYTE_CHANNEL_SEND 1 -#define EV_BYTE_CHANNEL_RECEIVE 2 -#define EV_BYTE_CHANNEL_POLL 3 -#define EV_INT_SET_CONFIG 4 -#define EV_INT_GET_CONFIG 5 -#define EV_INT_SET_MASK 6 -#define EV_INT_GET_MASK 7 -#define EV_INT_IACK 9 -#define EV_INT_EOI 10 -#define EV_INT_SEND_IPI 11 -#define EV_INT_SET_TASK_PRIORITY 12 -#define EV_INT_GET_TASK_PRIORITY 13 -#define EV_DOORBELL_SEND 14 -#define EV_MSGSND 15 -#define EV_IDLE 16 - -/* vendor ID: epapr */ -#define EV_LOCAL_VENDOR_ID 0 /* for private use */ -#define EV_EPAPR_VENDOR_ID 1 -#define EV_FSL_VENDOR_ID 2 /* Freescale Semiconductor */ -#define EV_IBM_VENDOR_ID 3 /* IBM */ -#define EV_GHS_VENDOR_ID 4 /* Green Hills Software */ -#define EV_ENEA_VENDOR_ID 5 /* Enea */ -#define EV_WR_VENDOR_ID 6 /* Wind River Systems */ -#define EV_AMCC_VENDOR_ID 7 /* Applied Micro Circuits */ -#define EV_KVM_VENDOR_ID 42 /* KVM */ - -/* The max number of bytes that a byte channel can send or receive per call */ -#define EV_BYTE_CHANNEL_MAX_BYTES 16 - - -#define _EV_HCALL_TOKEN(id, num) (((id) << 16) | (num)) -#define EV_HCALL_TOKEN(hcall_num) _EV_HCALL_TOKEN(EV_EPAPR_VENDOR_ID, hcall_num) - -/* epapr return codes */ -#define EV_SUCCESS 0 -#define EV_EPERM 1 /* Operation not permitted */ -#define EV_ENOENT 2 /* Entry Not Found */ -#define EV_EIO 3 /* I/O error occurred */ -#define EV_EAGAIN 4 /* The operation had insufficient - * resources to complete and should be - * retried - */ -#define EV_ENOMEM 5 /* There was insufficient memory to - * complete the operation */ -#define EV_EFAULT 6 /* Bad guest address */ -#define EV_ENODEV 7 /* No such device */ -#define EV_EINVAL 8 /* An argument supplied to the hcall - was out of range or invalid */ -#define EV_INTERNAL 9 /* An internal error occurred */ -#define EV_CONFIG 10 /* A configuration error was detected */ -#define EV_INVALID_STATE 11 /* The object is in an invalid state */ -#define EV_UNIMPLEMENTED 12 /* Unimplemented hypercall */ -#define EV_BUFFER_OVERFLOW 13 /* Caller-supplied buffer too small */ - -#endif /* _ASM_POWERPC_EPAPR_HCALLS_H */ diff --git a/linux-headers/asm-powerpc/kvm_para.h b/linux-headers/asm-powerpc/kvm_para.h deleted file mode 100644 index 9beb49c..0000000 --- a/linux-headers/asm-powerpc/kvm_para.h +++ /dev/null @@ -1,98 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * 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. - * - * 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, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard <hollisb@us.ibm.com> - */ - -#ifndef __POWERPC_KVM_PARA_H__ -#define __POWERPC_KVM_PARA_H__ - -#include <linux/types.h> - -/* - * Additions to this struct must only occur at the end, and should be - * accompanied by a KVM_MAGIC_FEAT flag to advertise that they are present - * (albeit not necessarily relevant to the current target hardware platform). - * - * Struct fields are always 32 or 64 bit aligned, depending on them being 32 - * or 64 bit wide respectively. - * - * See Documentation/virtual/kvm/ppc-pv.txt - */ -struct kvm_vcpu_arch_shared { - __u64 scratch1; - __u64 scratch2; - __u64 scratch3; - __u64 critical; /* Guest may not get interrupts if == r1 */ - __u64 sprg0; - __u64 sprg1; - __u64 sprg2; - __u64 sprg3; - __u64 srr0; - __u64 srr1; - __u64 dar; /* dear on BookE */ - __u64 msr; - __u32 dsisr; - __u32 int_pending; /* Tells the guest if we have an interrupt */ - __u32 sr[16]; - __u32 mas0; - __u32 mas1; - __u64 mas7_3; - __u64 mas2; - __u32 mas4; - __u32 mas6; - __u32 esr; - __u32 pir; - - /* - * SPRG4-7 are user-readable, so we can only keep these consistent - * between the shared area and the real registers when there's an - * intervening exit to KVM. This also applies to SPRG3 on some - * chips. - * - * This suffices for access by guest userspace, since in PR-mode - * KVM, an exit must occur when changing the guest's MSR[PR]. - * If the guest kernel writes to SPRG3-7 via the shared area, it - * must also use the shared area for reading while in kernel space. - */ - __u64 sprg4; - __u64 sprg5; - __u64 sprg6; - __u64 sprg7; -}; - -#define KVM_SC_MAGIC_R0 0x4b564d21 /* "KVM!" */ - -#define KVM_HCALL_TOKEN(num) _EV_HCALL_TOKEN(EV_KVM_VENDOR_ID, num) - -#include <asm/epapr_hcalls.h> - -#define KVM_FEATURE_MAGIC_PAGE 1 - -/* Magic page flags from host to guest */ - -#define KVM_MAGIC_FEAT_SR (1 << 0) - -/* MASn, ESR, PIR, and high SPRGs */ -#define KVM_MAGIC_FEAT_MAS0_TO_SPRG7 (1 << 1) - -/* Magic page flags from guest to host */ - -#define MAGIC_PAGE_FLAG_NOT_MAPPED_NX (1 << 0) - - -#endif /* __POWERPC_KVM_PARA_H__ */ diff --git a/linux-headers/asm-s390/bitsperlong.h b/linux-headers/asm-s390/bitsperlong.h new file mode 100644 index 0000000..cceaf47 --- /dev/null +++ b/linux-headers/asm-s390/bitsperlong.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_S390_BITSPERLONG_H +#define __ASM_S390_BITSPERLONG_H + +#ifndef __s390x__ +#define __BITS_PER_LONG 32 +#else +#define __BITS_PER_LONG 64 +#endif + +#include <asm-generic/bitsperlong.h> + +#endif /* __ASM_S390_BITSPERLONG_H */ + diff --git a/linux-headers/asm-s390/kvm_para.h b/linux-headers/asm-s390/kvm_para.h deleted file mode 100644 index b9ab584..0000000 --- a/linux-headers/asm-s390/kvm_para.h +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * User API definitions for paravirtual devices on s390 - * - * Copyright IBM Corp. 2008 - * - * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> - */ diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index 1ae66a2..d0f97cd 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -360,5 +360,6 @@ #define __NR_s390_guarded_storage 378 #define __NR_statx 379 #define __NR_s390_sthyi 380 +#define __NR_kexec_file_load 381 #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 8aa9d04..23ffb97 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -327,5 +327,6 @@ #define __NR_s390_guarded_storage 378 #define __NR_statx 379 #define __NR_s390_sthyi 380 +#define __NR_kexec_file_load 381 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/bitsperlong.h b/linux-headers/asm-x86/bitsperlong.h new file mode 100644 index 0000000..5d72c84 --- /dev/null +++ b/linux-headers/asm-x86/bitsperlong.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_X86_BITSPERLONG_H +#define __ASM_X86_BITSPERLONG_H + +#if defined(__x86_64__) && !defined(__ILP32__) +# define __BITS_PER_LONG 64 +#else +# define __BITS_PER_LONG 32 +#endif + +#include <asm-generic/bitsperlong.h> + +#endif /* __ASM_X86_BITSPERLONG_H */ + diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h deleted file mode 100644 index 8bcd0aa..0000000 --- a/linux-headers/linux/kvm_para.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef __LINUX_KVM_PARA_H -#define __LINUX_KVM_PARA_H - -/* - * This header file provides a method for making a hypercall to the host - * Architectures should define: - * - kvm_hypercall0, kvm_hypercall1... - * - kvm_arch_para_features - * - kvm_para_available - */ - -/* Return values for hypercalls */ -#define KVM_ENOSYS 1000 -#define KVM_EFAULT EFAULT -#define KVM_E2BIG E2BIG -#define KVM_EPERM EPERM -#define KVM_EOPNOTSUPP 95 - -#define KVM_HC_VAPIC_POLL_IRQ 1 -#define KVM_HC_MMU_OP 2 -#define KVM_HC_FEATURES 3 -#define KVM_HC_PPC_MAP_MAGIC_PAGE 4 -#define KVM_HC_KICK_CPU 5 -#define KVM_HC_MIPS_GET_CLOCK_FREQ 6 -#define KVM_HC_MIPS_EXIT_VM 7 -#define KVM_HC_MIPS_CONSOLE_OUTPUT 8 -#define KVM_HC_CLOCK_PAIRING 9 - -/* - * hypercalls use architecture specific - */ -#include <asm/kvm_para.h> - -#endif /* __LINUX_KVM_PARA_H */ diff --git a/linux-user/aarch64/sockbits.h b/linux-user/aarch64/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/aarch64/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/alpha/sockbits.h b/linux-user/alpha/sockbits.h new file mode 100644 index 0000000..d54dc98 --- /dev/null +++ b/linux-user/alpha/sockbits.h @@ -0,0 +1,83 @@ +/* + * 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, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef ALPHA_SOCKBITS_H +#define ALPHA_SOCKBITS_H + +/* For setsockopt(2) */ +#define TARGET_SOL_SOCKET 0xffff + +#define TARGET_SO_DEBUG 0x0001 +#define TARGET_SO_REUSEADDR 0x0004 +#define TARGET_SO_KEEPALIVE 0x0008 +#define TARGET_SO_DONTROUTE 0x0010 +#define TARGET_SO_BROADCAST 0x0020 +#define TARGET_SO_LINGER 0x0080 +#define TARGET_SO_OOBINLINE 0x0100 +#define TARGET_SO_REUSEPORT 0x0200 + +#define TARGET_SO_TYPE 0x1008 +#define TARGET_SO_ERROR 0x1007 +#define TARGET_SO_SNDBUF 0x1001 +#define TARGET_SO_RCVBUF 0x1002 +#define TARGET_SO_SNDBUFFORCE 0x100a +#define TARGET_SO_RCVBUFFORCE 0x100b +#define TARGET_SO_RCVLOWAT 0x1010 +#define TARGET_SO_SNDLOWAT 0x1011 +#define TARGET_SO_RCVTIMEO 0x1012 +#define TARGET_SO_SNDTIMEO 0x1013 +#define TARGET_SO_ACCEPTCONN 0x1014 +#define TARGET_SO_PROTOCOL 0x1028 +#define TARGET_SO_DOMAIN 0x1029 + +/* linux-specific, might as well be the same as on i386 */ +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_BSDCOMPAT 14 + +#define TARGET_SO_PASSCRED 17 +#define TARGET_SO_PEERCRED 18 +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + +#define TARGET_SO_PEERSEC 30 +#define TARGET_SO_PASSSEC 34 +#define TARGET_SO_TIMESTAMPNS 35 +#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 19 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 20 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 21 + +#define TARGET_SO_MARK 36 + +#define TARGET_SO_TIMESTAMPING 37 +#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING + +#define TARGET_SO_RXQ_OVFL 40 + +#define TARGET_SO_WIFI_STATUS 41 +#define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS +#define TARGET_SO_PEEK_OFF 42 + +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define TARGET_SO_NOFCS 43 + +/* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. + */ +#define TARGET_SOCK_NONBLOCK 0x40000000 + +#endif diff --git a/linux-user/arm/sockbits.h b/linux-user/arm/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/arm/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/cris/sockbits.h b/linux-user/cris/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/cris/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/generic/sockbits.h b/linux-user/generic/sockbits.h new file mode 100644 index 0000000..e44733c --- /dev/null +++ b/linux-user/generic/sockbits.h @@ -0,0 +1,58 @@ +/* + * 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, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef GENERIC_SOCKBITS_H +#define GENERIC_SOCKBITS_H + +#define TARGET_SO_PASSSEC 34 + +/* For setsockopt(2) */ +#define TARGET_SOL_SOCKET 1 + +#define TARGET_SO_DEBUG 1 +#define TARGET_SO_REUSEADDR 2 +#define TARGET_SO_TYPE 3 +#define TARGET_SO_ERROR 4 +#define TARGET_SO_DONTROUTE 5 +#define TARGET_SO_BROADCAST 6 +#define TARGET_SO_SNDBUF 7 +#define TARGET_SO_RCVBUF 8 +#define TARGET_SO_SNDBUFFORCE 32 +#define TARGET_SO_RCVBUFFORCE 33 +#define TARGET_SO_KEEPALIVE 9 +#define TARGET_SO_OOBINLINE 10 +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_LINGER 13 +#define TARGET_SO_BSDCOMPAT 14 +#define TARGET_SO_REUSEPORT 15 +#define TARGET_SO_PASSCRED 16 +#define TARGET_SO_PEERCRED 17 +#define TARGET_SO_RCVLOWAT 18 +#define TARGET_SO_SNDLOWAT 19 +#define TARGET_SO_RCVTIMEO 20 +#define TARGET_SO_SNDTIMEO 21 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 22 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + +#define TARGET_SO_ACCEPTCONN 30 + +#define TARGET_SO_PEERSEC 31 +#endif diff --git a/linux-user/hppa/sockbits.h b/linux-user/hppa/sockbits.h index 5044619..2641aea 100644 --- a/linux-user/hppa/sockbits.h +++ b/linux-user/hppa/sockbits.h @@ -64,34 +64,7 @@ #define TARGET_SO_CNX_ADVICE 0x402E -/** sock_type - Socket types - default values - * - * - * @SOCK_STREAM - stream (connection) socket - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. +/* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. */ -enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 010000000, - TARGET_SOCK_NONBLOCK = 0x40000000, -}; - -#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) -#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - -#define ARCH_HAS_SOCKET_TYPES 1 +#define TARGET_SOCK_NONBLOCK 0x40000000 diff --git a/linux-user/i386/sockbits.h b/linux-user/i386/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/i386/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/m68k/sockbits.h b/linux-user/m68k/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/m68k/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/main.c b/linux-user/main.c index 3234754..78d6d3e 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -671,9 +671,8 @@ int main(int argc, char **argv, char **envp) } cpu_type = parse_cpu_model(cpu_model); + /* init tcg before creating CPUs and to get qemu_host_page_size */ tcg_exec_init(0); - /* NOTE: we need to init the CPU at this stage to get - qemu_host_page_size */ cpu = cpu_create(cpu_type); env = cpu->env_ptr; @@ -693,7 +692,7 @@ int main(int argc, char **argv, char **envp) envlist_free(envlist); /* - * Now that page sizes are configured in cpu_init() we can do + * Now that page sizes are configured in tcg_exec_init() we can do * proper page alignment for guest_base. */ guest_base = HOST_PAGE_ALIGN(guest_base); diff --git a/linux-user/microblaze/sockbits.h b/linux-user/microblaze/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/microblaze/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/mips/sockbits.h b/linux-user/mips/sockbits.h new file mode 100644 index 0000000..0f022cd --- /dev/null +++ b/linux-user/mips/sockbits.h @@ -0,0 +1,110 @@ +/* + * 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, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef MIPS_SOCKBITS_H +#define MIPS_SOCKBITS_H +/* MIPS special values for constants */ + +/* + * For setsockopt(2) + * + * This defines are ABI conformant as far as Linux supports these ... + */ +#define TARGET_SOL_SOCKET 0xffff + +#define TARGET_SO_DEBUG 0x0001 /* Record debugging information. */ +#define TARGET_SO_REUSEADDR 0x0004 /* Allow reuse of local addresses. */ +#define TARGET_SO_KEEPALIVE 0x0008 /* Keep connections alive and send + SIGPIPE when they die. */ +#define TARGET_SO_DONTROUTE 0x0010 /* Don't do local routing. */ +#define TARGET_SO_BROADCAST 0x0020 /* Allow transmission of + broadcast messages. */ +#define TARGET_SO_LINGER 0x0080 /* Block on close of a reliable + * socket to transmit pending data. + */ +#define TARGET_SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. + */ +#define TARGET_SO_REUSEPORT 0x0200 + +#define TARGET_SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */ +#define TARGET_SO_STYLE SO_TYPE /* Synonym */ +#define TARGET_SO_ERROR 0x1007 /* get error status and clear */ +#define TARGET_SO_SNDBUF 0x1001 /* Send buffer size. */ +#define TARGET_SO_RCVBUF 0x1002 /* Receive buffer. */ +#define TARGET_SO_SNDLOWAT 0x1003 /* send low-water mark */ +#define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */ +#define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */ +#define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */ +#define TARGET_SO_ACCEPTCONN 0x1009 + +/* linux-specific, might as well be the same as on i386 */ +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_BSDCOMPAT 14 + +#define TARGET_SO_PASSCRED 17 +#define TARGET_SO_PEERCRED 18 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 22 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define SCM_TIMESTAMP SO_TIMESTAMP + +#define TARGET_SO_PEERSEC 30 +#define TARGET_SO_SNDBUFFORCE 31 +#define TARGET_SO_RCVBUFFORCE 33 +#define TARGET_SO_PASSSEC 34 + +/** sock_type - Socket types + * + * Please notice that for binary compat reasons MIPS has to + * override the enum sock_type in include/linux/net.h, so + * we define ARCH_HAS_SOCKET_TYPES here. + * + * @SOCK_DGRAM - datagram (conn.less) socket + * @SOCK_STREAM - stream (connection) socket + * @SOCK_RAW - raw socket + * @SOCK_RDM - reliably-delivered message + * @SOCK_SEQPACKET - sequential packet socket + * @SOCK_DCCP - Datagram Congestion Control Protocol socket + * @SOCK_PACKET - linux specific way of getting packets at the dev level. + * For writing rarp and other similar things on the user + * level. + * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. + * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. + */ + +#define TARGET_ARCH_HAS_SOCKET_TYPES 1 + +enum sock_type { + TARGET_SOCK_DGRAM = 1, + TARGET_SOCK_STREAM = 2, + TARGET_SOCK_RAW = 3, + TARGET_SOCK_RDM = 4, + TARGET_SOCK_SEQPACKET = 5, + TARGET_SOCK_DCCP = 6, + TARGET_SOCK_PACKET = 10, +}; + +#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) +#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ + +/* Flags for socket, socketpair, paccept */ +#define TARGET_SOCK_CLOEXEC TARGET_O_CLOEXEC +#define TARGET_SOCK_NONBLOCK TARGET_O_NONBLOCK + +#endif diff --git a/linux-user/mips64/sockbits.h b/linux-user/mips64/sockbits.h new file mode 100644 index 0000000..e6b6d31 --- /dev/null +++ b/linux-user/mips64/sockbits.h @@ -0,0 +1 @@ +#include "../mips/sockbits.h" diff --git a/linux-user/nios2/sockbits.h b/linux-user/nios2/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/nios2/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/openrisc/sockbits.h b/linux-user/openrisc/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/openrisc/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/ppc/sockbits.h b/linux-user/ppc/sockbits.h new file mode 100644 index 0000000..ee45334 --- /dev/null +++ b/linux-user/ppc/sockbits.h @@ -0,0 +1,26 @@ +/* + * 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, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef PPC_SOCKBITS_H +#define PPC_SOCKBITS_H + +#include "../generic/sockbits.h" + +#undef TARGET_SO_RCVLOWAT +#define TARGET_SO_RCVLOWAT 16 +#undef TARGET_SO_SNDLOWAT +#define TARGET_SO_SNDLOWAT 17 +#undef TARGET_SO_RCVTIMEO +#define TARGET_SO_RCVTIMEO 18 +#undef TARGET_SO_SNDTIMEO +#define TARGET_SO_SNDTIMEO 19 +#undef TARGET_SO_PASSCRED +#define TARGET_SO_PASSCRED 20 +#undef TARGET_SO_PEERCRED +#define TARGET_SO_PEERCRED 21 + +#endif diff --git a/linux-user/riscv/sockbits.h b/linux-user/riscv/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/riscv/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/s390x/sockbits.h b/linux-user/s390x/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/s390x/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/sh4/sockbits.h b/linux-user/sh4/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/sh4/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/socket.h b/linux-user/socket.h index 7051cd2..4c0b5c2 100644 --- a/linux-user/socket.h +++ b/linux-user/socket.h @@ -1,342 +1,37 @@ - -#if defined(TARGET_MIPS) - /* MIPS special values for constants */ - - /* - * For setsockopt(2) - * - * This defines are ABI conformant as far as Linux supports these ... - */ - #define TARGET_SOL_SOCKET 0xffff - - #define TARGET_SO_DEBUG 0x0001 /* Record debugging information. */ - #define TARGET_SO_REUSEADDR 0x0004 /* Allow reuse of local addresses. */ - #define TARGET_SO_KEEPALIVE 0x0008 /* Keep connections alive and send - SIGPIPE when they die. */ - #define TARGET_SO_DONTROUTE 0x0010 /* Don't do local routing. */ - #define TARGET_SO_BROADCAST 0x0020 /* Allow transmission of - broadcast messages. */ - #define TARGET_SO_LINGER 0x0080 /* Block on close of a reliable - * socket to transmit pending data. - */ - #define TARGET_SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. - */ - #if 0 - /* To add: Allow local address and port reuse. */ - #define TARGET_SO_REUSEPORT 0x0200 - #endif - - #define TARGET_SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */ - #define TARGET_SO_STYLE SO_TYPE /* Synonym */ - #define TARGET_SO_ERROR 0x1007 /* get error status and clear */ - #define TARGET_SO_SNDBUF 0x1001 /* Send buffer size. */ - #define TARGET_SO_RCVBUF 0x1002 /* Receive buffer. */ - #define TARGET_SO_SNDLOWAT 0x1003 /* send low-water mark */ - #define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */ - #define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */ - #define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */ - #define TARGET_SO_ACCEPTCONN 0x1009 - - /* linux-specific, might as well be the same as on i386 */ - #define TARGET_SO_NO_CHECK 11 - #define TARGET_SO_PRIORITY 12 - #define TARGET_SO_BSDCOMPAT 14 - - #define TARGET_SO_PASSCRED 17 - #define TARGET_SO_PEERCRED 18 - - /* Security levels - as per NRL IPv6 - don't actually do anything */ - #define TARGET_SO_SECURITY_AUTHENTICATION 22 - #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 - #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 - - #define TARGET_SO_BINDTODEVICE 25 - - /* Socket filtering */ - #define TARGET_SO_ATTACH_FILTER 26 - #define TARGET_SO_DETACH_FILTER 27 - - #define TARGET_SO_PEERNAME 28 - #define TARGET_SO_TIMESTAMP 29 - #define SCM_TIMESTAMP SO_TIMESTAMP - - #define TARGET_SO_PEERSEC 30 - #define TARGET_SO_SNDBUFFORCE 31 - #define TARGET_SO_RCVBUFFORCE 33 - #define TARGET_SO_PASSSEC 34 - - /** sock_type - Socket types - * - * Please notice that for binary compat reasons MIPS has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - - #define ARCH_HAS_SOCKET_TYPES 1 - - enum sock_type { - TARGET_SOCK_DGRAM = 1, - TARGET_SOCK_STREAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 02000000, - TARGET_SOCK_NONBLOCK = 0200, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - -#elif defined(TARGET_ALPHA) - - /* For setsockopt(2) */ - #define TARGET_SOL_SOCKET 0xffff - - #define TARGET_SO_DEBUG 0x0001 - #define TARGET_SO_REUSEADDR 0x0004 - #define TARGET_SO_KEEPALIVE 0x0008 - #define TARGET_SO_DONTROUTE 0x0010 - #define TARGET_SO_BROADCAST 0x0020 - #define TARGET_SO_LINGER 0x0080 - #define TARGET_SO_OOBINLINE 0x0100 - /* To add :#define TARGET_SO_REUSEPORT 0x0200 */ - - #define TARGET_SO_TYPE 0x1008 - #define TARGET_SO_ERROR 0x1007 - #define TARGET_SO_SNDBUF 0x1001 - #define TARGET_SO_RCVBUF 0x1002 - #define TARGET_SO_SNDBUFFORCE 0x100a - #define TARGET_SO_RCVBUFFORCE 0x100b - #define TARGET_SO_RCVLOWAT 0x1010 - #define TARGET_SO_SNDLOWAT 0x1011 - #define TARGET_SO_RCVTIMEO 0x1012 - #define TARGET_SO_SNDTIMEO 0x1013 - #define TARGET_SO_ACCEPTCONN 0x1014 - #define TARGET_SO_PROTOCOL 0x1028 - #define TARGET_SO_DOMAIN 0x1029 - - /* linux-specific, might as well be the same as on i386 */ - #define TARGET_SO_NO_CHECK 11 - #define TARGET_SO_PRIORITY 12 - #define TARGET_SO_BSDCOMPAT 14 - - #define TARGET_SO_PASSCRED 17 - #define TARGET_SO_PEERCRED 18 - #define TARGET_SO_BINDTODEVICE 25 - - /* Socket filtering */ - #define TARGET_SO_ATTACH_FILTER 26 - #define TARGET_SO_DETACH_FILTER 27 - - #define TARGET_SO_PEERNAME 28 - #define TARGET_SO_TIMESTAMP 29 - #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP - - #define TARGET_SO_PEERSEC 30 - #define TARGET_SO_PASSSEC 34 - #define TARGET_SO_TIMESTAMPNS 35 - #define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS - - /* Security levels - as per NRL IPv6 - don't actually do anything */ - #define TARGET_SO_SECURITY_AUTHENTICATION 19 - #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 20 - #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 21 - - #define TARGET_SO_MARK 36 - - #define TARGET_SO_TIMESTAMPING 37 - #define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING - - #define TARGET_SO_RXQ_OVFL 40 - - #define TARGET_SO_WIFI_STATUS 41 - #define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS - #define TARGET_SO_PEEK_OFF 42 - - /* Instruct lower device to use last 4-bytes of skb data as FCS */ - #define TARGET_SO_NOFCS 43 - - /** sock_type - Socket types - * - * Please notice that for binary compat reasons ALPHA has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - - #define ARCH_HAS_SOCKET_TYPES 1 - - enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 010000000, - TARGET_SOCK_NONBLOCK = 010000000000, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ -#elif defined(TARGET_HPPA) -#include <hppa/sockbits.h> -#else - -#if defined(TARGET_SPARC) - /** sock_type - Socket types - * - * Please notice that for binary compat reasons SPARC has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - - #define ARCH_HAS_SOCKET_TYPES 1 - - enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 020000000, - TARGET_SOCK_NONBLOCK = 040000, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - - #define TARGET_SO_PASSSEC 31 -#else - #define TARGET_SO_PASSSEC 34 -#endif - - /* For setsockopt(2) */ - #define TARGET_SOL_SOCKET 1 - - #define TARGET_SO_DEBUG 1 - #define TARGET_SO_REUSEADDR 2 - #define TARGET_SO_TYPE 3 - #define TARGET_SO_ERROR 4 - #define TARGET_SO_DONTROUTE 5 - #define TARGET_SO_BROADCAST 6 - #define TARGET_SO_SNDBUF 7 - #define TARGET_SO_RCVBUF 8 - #define TARGET_SO_SNDBUFFORCE 32 - #define TARGET_SO_RCVBUFFORCE 33 - #define TARGET_SO_KEEPALIVE 9 - #define TARGET_SO_OOBINLINE 10 - #define TARGET_SO_NO_CHECK 11 - #define TARGET_SO_PRIORITY 12 - #define TARGET_SO_LINGER 13 - #define TARGET_SO_BSDCOMPAT 14 - /* To add :#define TARGET_SO_REUSEPORT 15 */ -#if defined(TARGET_PPC) - #define TARGET_SO_RCVLOWAT 16 - #define TARGET_SO_SNDLOWAT 17 - #define TARGET_SO_RCVTIMEO 18 - #define TARGET_SO_SNDTIMEO 19 - #define TARGET_SO_PASSCRED 20 - #define TARGET_SO_PEERCRED 21 -#else - #define TARGET_SO_PASSCRED 16 - #define TARGET_SO_PEERCRED 17 - #define TARGET_SO_RCVLOWAT 18 - #define TARGET_SO_SNDLOWAT 19 - #define TARGET_SO_RCVTIMEO 20 - #define TARGET_SO_SNDTIMEO 21 -#endif - - /* Security levels - as per NRL IPv6 - don't actually do anything */ - #define TARGET_SO_SECURITY_AUTHENTICATION 22 - #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 - #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 - - #define TARGET_SO_BINDTODEVICE 25 - - /* Socket filtering */ - #define TARGET_SO_ATTACH_FILTER 26 - #define TARGET_SO_DETACH_FILTER 27 - - #define TARGET_SO_PEERNAME 28 - #define TARGET_SO_TIMESTAMP 29 - #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP - - #define TARGET_SO_ACCEPTCONN 30 - - #define TARGET_SO_PEERSEC 31 - -#endif - -#ifndef ARCH_HAS_SOCKET_TYPES - /** sock_type - Socket types - default values - * - * - * @SOCK_STREAM - stream (connection) socket - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 02000000, - TARGET_SOCK_NONBLOCK = 04000, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - +#include "sockbits.h" + +#ifndef TARGET_ARCH_HAS_SOCKET_TYPES +/** sock_type - Socket types - default values + * + * + * @SOCK_STREAM - stream (connection) socket + * @SOCK_DGRAM - datagram (conn.less) socket + * @SOCK_RAW - raw socket + * @SOCK_RDM - reliably-delivered message + * @SOCK_SEQPACKET - sequential packet socket + * @SOCK_DCCP - Datagram Congestion Control Protocol socket + * @SOCK_PACKET - linux specific way of getting packets at the dev level. + * For writing rarp and other similar things on the user + * level. + * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. + * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. + */ +enum sock_type { + TARGET_SOCK_STREAM = 1, + TARGET_SOCK_DGRAM = 2, + TARGET_SOCK_RAW = 3, + TARGET_SOCK_RDM = 4, + TARGET_SOCK_SEQPACKET = 5, + TARGET_SOCK_DCCP = 6, + TARGET_SOCK_PACKET = 10, +}; + +#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) +#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ + +/* Flags for socket, socketpair, accept4 */ +#define TARGET_SOCK_CLOEXEC TARGET_O_CLOEXEC +#ifndef TARGET_SOCK_NONBLOCK +#define TARGET_SOCK_NONBLOCK TARGET_O_NONBLOCK #endif +#endif /* TARGET_ARCH_HAS_SOCKET_TYPES */ diff --git a/linux-user/sparc/sockbits.h b/linux-user/sparc/sockbits.h new file mode 100644 index 0000000..0a822e3 --- /dev/null +++ b/linux-user/sparc/sockbits.h @@ -0,0 +1,111 @@ +/* + * 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, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef SPARC_SOCKBITS_H +#define SPARC_SOCKBITS_H + +/* For setsockopt(2) */ +#define TARGET_SOL_SOCKET 0xffff + +#define TARGET_SO_DEBUG 0x0001 +#define TARGET_SO_PASSCRED 0x0002 +#define TARGET_SO_REUSEADDR 0x0004 +#define TARGET_SO_KEEPALIVE 0x0008 +#define TARGET_SO_DONTROUTE 0x0010 +#define TARGET_SO_BROADCAST 0x0020 +#define TARGET_SO_PEERCRED 0x0040 +#define TARGET_SO_LINGER 0x0080 +#define TARGET_SO_OOBINLINE 0x0100 +#define TARGET_SO_REUSEPORT 0x0200 +#define TARGET_SO_BSDCOMPAT 0x0400 +#define TARGET_SO_RCVLOWAT 0x0800 +#define TARGET_SO_SNDLOWAT 0x1000 +#define TARGET_SO_RCVTIMEO 0x2000 +#define TARGET_SO_SNDTIMEO 0x4000 +#define TARGET_SO_ACCEPTCONN 0x8000 + +#define TARGET_SO_SNDBUF 0x1001 +#define TARGET_SO_RCVBUF 0x1002 +#define TARGET_SO_SNDBUFFORCE 0x100a +#define TARGET_SO_RCVBUFFORCE 0x100b +#define TARGET_SO_ERROR 0x1007 +#define TARGET_SO_TYPE 0x1008 +#define TARGET_SO_PROTOCOL 0x1028 +#define TARGET_SO_DOMAIN 0x1029 + +/* Linux specific, keep the same. */ +#define TARGET_SO_NO_CHECK 0x000b +#define TARGET_SO_PRIORITY 0x000c + +#define TARGET_SO_BINDTODEVICE 0x000d + +#define TARGET_SO_ATTACH_FILTER 0x001a +#define TARGET_SO_DETACH_FILTER 0x001b +#define TARGET_SO_GET_FILTER TARGET_SO_ATTACH_FILTER + +#define TARGET_SO_PEERNAME 0x001c +#define TARGET_SO_TIMESTAMP 0x001d +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + +#define TARGET_SO_PEERSEC 0x001e +#define TARGET_SO_PASSSEC 0x001f +#define TARGET_SO_TIMESTAMPNS 0x0021 +#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS + +#define TARGET_SO_MARK 0x0022 + +#define TARGET_SO_TIMESTAMPING 0x0023 +#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING + +#define TARGET_SO_RXQ_OVFL 0x0024 + +#define TARGET_SO_WIFI_STATUS 0x0025 +#define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS +#define TARGET_SO_PEEK_OFF 0x0026 + +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define TARGET_SO_NOFCS 0x0027 + +#define TARGET_SO_LOCK_FILTER 0x0028 + +#define TARGET_SO_SELECT_ERR_QUEUE 0x0029 + +#define TARGET_SO_BUSY_POLL 0x0030 + +#define TARGET_SO_MAX_PACING_RATE 0x0031 + +#define TARGET_SO_BPF_EXTENSIONS 0x0032 + +#define TARGET_SO_INCOMING_CPU 0x0033 + +#define TARGET_SO_ATTACH_BPF 0x0034 +#define TARGET_SO_DETACH_BPF TARGET_SO_DETACH_FILTER + +#define TARGET_SO_ATTACH_REUSEPORT_CBPF 0x0035 +#define TARGET_SO_ATTACH_REUSEPORT_EBPF 0x0036 + +#define TARGET_SO_CNX_ADVICE 0x0037 + +#define TARGET_SCM_TIMESTAMPING_OPT_STATS 0x0038 + +#define TARGET_SO_MEMINFO 0x0039 + +#define TARGET_SO_INCOMING_NAPI_ID 0x003a + +#define TARGET_SO_COOKIE 0x003b + +#define TARGET_SCM_TIMESTAMPING_PKTINFO 0x003c + +#define TARGET_SO_PEERGROUPS 0x003d + +#define TARGET_SO_ZEROCOPY 0x003e + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 0x5001 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 0x5004 +#endif diff --git a/linux-user/sparc64/sockbits.h b/linux-user/sparc64/sockbits.h new file mode 100644 index 0000000..658899e --- /dev/null +++ b/linux-user/sparc64/sockbits.h @@ -0,0 +1 @@ +#include "../sparc/sockbits.h" diff --git a/linux-user/syscall.c b/linux-user/syscall.c index af8603f..d02c16b 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -405,6 +405,8 @@ enum { QEMU_IFLA_BR_PAD, QEMU_IFLA_BR_VLAN_STATS_ENABLED, QEMU_IFLA_BR_MCAST_STATS_ENABLED, + QEMU_IFLA_BR_MCAST_IGMP_VERSION, + QEMU_IFLA_BR_MCAST_MLD_VERSION, QEMU___IFLA_BR_MAX, }; @@ -453,6 +455,12 @@ enum { QEMU_IFLA_GSO_MAX_SIZE, QEMU_IFLA_PAD, QEMU_IFLA_XDP, + QEMU_IFLA_EVENT, + QEMU_IFLA_NEW_NETNSID, + QEMU_IFLA_IF_NETNSID, + QEMU_IFLA_CARRIER_UP_COUNT, + QEMU_IFLA_CARRIER_DOWN_COUNT, + QEMU_IFLA_NEW_IFINDEX, QEMU___IFLA_MAX }; @@ -484,6 +492,12 @@ enum { QEMU_IFLA_BRPORT_FLUSH, QEMU_IFLA_BRPORT_MULTICAST_ROUTER, QEMU_IFLA_BRPORT_PAD, + QEMU_IFLA_BRPORT_MCAST_FLOOD, + QEMU_IFLA_BRPORT_MCAST_TO_UCAST, + QEMU_IFLA_BRPORT_VLAN_TUNNEL, + QEMU_IFLA_BRPORT_BCAST_FLOOD, + QEMU_IFLA_BRPORT_GROUP_FWD_MASK, + QEMU_IFLA_BRPORT_NEIGH_SUPPRESS, QEMU___IFLA_BRPORT_MAX }; @@ -516,6 +530,15 @@ enum { QEMU___IFLA_INET6_MAX }; +enum { + QEMU_IFLA_XDP_UNSPEC, + QEMU_IFLA_XDP_FD, + QEMU_IFLA_XDP_ATTACHED, + QEMU_IFLA_XDP_FLAGS, + QEMU_IFLA_XDP_PROG_ID, + QEMU___IFLA_XDP_MAX, +}; + typedef abi_long (*TargetFdDataFunc)(void *, size_t); typedef abi_long (*TargetFdAddrFunc)(void *, abi_ulong, socklen_t); typedef struct TargetFdTrans { @@ -1825,6 +1848,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, /* Payload types which need a different size of payload on * the target must adjust tgt_len here. */ + tgt_len = len; switch (cmsg->cmsg_level) { case SOL_SOCKET: switch (cmsg->cmsg_type) { @@ -1834,8 +1858,8 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, default: break; } + break; default: - tgt_len = len; break; } @@ -2182,6 +2206,10 @@ static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BR_NF_CALL_IPTABLES: case QEMU_IFLA_BR_NF_CALL_IP6TABLES: case QEMU_IFLA_BR_NF_CALL_ARPTABLES: + case QEMU_IFLA_BR_VLAN_STATS_ENABLED: + case QEMU_IFLA_BR_MCAST_STATS_ENABLED: + case QEMU_IFLA_BR_MCAST_IGMP_VERSION: + case QEMU_IFLA_BR_MCAST_MLD_VERSION: break; /* uint16_t */ case QEMU_IFLA_BR_PRIORITY: @@ -2253,6 +2281,11 @@ static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: case QEMU_IFLA_BRPORT_CONFIG_PENDING: case QEMU_IFLA_BRPORT_MULTICAST_ROUTER: + case QEMU_IFLA_BRPORT_MCAST_FLOOD: + case QEMU_IFLA_BRPORT_MCAST_TO_UCAST: + case QEMU_IFLA_BRPORT_VLAN_TUNNEL: + case QEMU_IFLA_BRPORT_BCAST_FLOOD: + case QEMU_IFLA_BRPORT_NEIGH_SUPPRESS: break; /* uint16_t */ case QEMU_IFLA_BRPORT_PRIORITY: @@ -2260,6 +2293,7 @@ static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BRPORT_DESIGNATED_COST: case QEMU_IFLA_BRPORT_ID: case QEMU_IFLA_BRPORT_NO: + case QEMU_IFLA_BRPORT_GROUP_FWD_MASK: u16 = NLA_DATA(nlattr); *u16 = tswap16(*u16); break; @@ -2434,6 +2468,27 @@ static abi_long host_to_target_data_spec_nlattr(struct nlattr *nlattr, return 0; } +static abi_long host_to_target_data_xdp_nlattr(struct nlattr *nlattr, + void *context) +{ + uint32_t *u32; + + switch (nlattr->nla_type) { + /* uint8_t */ + case QEMU_IFLA_XDP_ATTACHED: + break; + /* uint32_t */ + case QEMU_IFLA_XDP_PROG_ID: + u32 = NLA_DATA(nlattr); + *u32 = tswap32(*u32); + break; + default: + gemu_log("Unknown host XDP type: %d\n", nlattr->nla_type); + break; + } + return 0; +} + static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) { uint32_t *u32; @@ -2559,6 +2614,10 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, NULL, host_to_target_data_spec_nlattr); + case QEMU_IFLA_XDP: + return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, + NULL, + host_to_target_data_xdp_nlattr); default: gemu_log("Unknown host QEMU_IFLA type: %d\n", rtattr->rta_type); break; diff --git a/linux-user/tilegx/sockbits.h b/linux-user/tilegx/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/tilegx/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/x86_64/sockbits.h b/linux-user/x86_64/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/x86_64/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/xtensa/sockbits.h b/linux-user/xtensa/sockbits.h new file mode 100644 index 0000000..0e4c8f0 --- /dev/null +++ b/linux-user/xtensa/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/migration/exec.c b/migration/exec.c index 9d0f82f..0bbeb63 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "exec.h" +#include "migration.h" #include "io/channel-command.h" #include "trace.h" @@ -48,6 +49,9 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc, { migration_channel_process_incoming(ioc); object_unref(OBJECT(ioc)); + if (!migrate_use_multifd()) { + migration_incoming_process(); + } return G_SOURCE_REMOVE; } diff --git a/migration/fd.c b/migration/fd.c index 9a380bb..fee34ff 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "fd.h" +#include "migration.h" #include "monitor/monitor.h" #include "io/channel-util.h" #include "trace.h" @@ -48,6 +49,9 @@ static gboolean fd_accept_incoming_migration(QIOChannel *ioc, { migration_channel_process_incoming(ioc); object_unref(OBJECT(ioc)); + if (!migrate_use_multifd()) { + migration_incoming_process(); + } return G_SOURCE_REMOVE; } diff --git a/migration/ram.c b/migration/ram.c index 5bcbf7a..c53e836 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -246,7 +246,7 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file, qemu_put_be64(file, RAMBLOCK_RECV_BITMAP_ENDING); qemu_fflush(file); - free(le_bitmap); + g_free(le_bitmap); if (qemu_file_get_error(file)) { return qemu_file_get_error(file); @@ -3514,7 +3514,7 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) ret = 0; out: - free(le_bitmap); + g_free(le_bitmap); return ret; } diff --git a/net/vhost-user.c b/net/vhost-user.c index e0f16c8..fa28aad 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -20,38 +20,38 @@ #include "qemu/option.h" #include "trace.h" -typedef struct VhostUserState { +typedef struct NetVhostUserState { NetClientState nc; CharBackend chr; /* only queue index 0 */ VHostNetState *vhost_net; guint watch; uint64_t acked_features; bool started; -} VhostUserState; +} NetVhostUserState; VHostNetState *vhost_user_get_vhost_net(NetClientState *nc) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); return s->vhost_net; } uint64_t vhost_user_get_acked_features(NetClientState *nc) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); return s->acked_features; } static void vhost_user_stop(int queues, NetClientState *ncs[]) { - VhostUserState *s; + NetVhostUserState *s; int i; for (i = 0; i < queues; i++) { assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER); - s = DO_UPCAST(VhostUserState, nc, ncs[i]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); if (s->vhost_net) { /* save acked features */ @@ -68,7 +68,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) { VhostNetOptions options; struct vhost_net *net = NULL; - VhostUserState *s; + NetVhostUserState *s; int max_queues; int i; @@ -77,7 +77,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) for (i = 0; i < queues; i++) { assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER); - s = DO_UPCAST(VhostUserState, nc, ncs[i]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); options.net_backend = ncs[i]; options.opaque = be; @@ -123,7 +123,7 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, without GUEST_ANNOUNCE capability. */ if (size == 60) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); int r; static int display_rarp_failure = 1; char mac_addr[6]; @@ -146,7 +146,7 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, static void vhost_user_cleanup(NetClientState *nc) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); if (s->vhost_net) { vhost_net_cleanup(s->vhost_net); @@ -180,7 +180,7 @@ static bool vhost_user_has_ufo(NetClientState *nc) static NetClientInfo net_vhost_user_info = { .type = NET_CLIENT_DRIVER_VHOST_USER, - .size = sizeof(VhostUserState), + .size = sizeof(NetVhostUserState), .receive = vhost_user_receive, .cleanup = vhost_user_cleanup, .has_vnet_hdr = vhost_user_has_vnet_hdr, @@ -190,7 +190,7 @@ static NetClientInfo net_vhost_user_info = { static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond, void *opaque) { - VhostUserState *s = opaque; + NetVhostUserState *s = opaque; qemu_chr_fe_disconnect(&s->chr); @@ -203,7 +203,7 @@ static void chr_closed_bh(void *opaque) { const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; - VhostUserState *s; + NetVhostUserState *s; Error *err = NULL; int queues; @@ -212,7 +212,7 @@ static void chr_closed_bh(void *opaque) MAX_QUEUE_NUM); assert(queues < MAX_QUEUE_NUM); - s = DO_UPCAST(VhostUserState, nc, ncs[0]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); qmp_set_link(name, false, &err); vhost_user_stop(queues, ncs); @@ -229,7 +229,7 @@ static void net_vhost_user_event(void *opaque, int event) { const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; - VhostUserState *s; + NetVhostUserState *s; Chardev *chr; Error *err = NULL; int queues; @@ -239,7 +239,7 @@ static void net_vhost_user_event(void *opaque, int event) MAX_QUEUE_NUM); assert(queues < MAX_QUEUE_NUM); - s = DO_UPCAST(VhostUserState, nc, ncs[0]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); chr = qemu_chr_fe_get_driver(&s->chr); trace_vhost_user_event(chr->label, event); switch (event) { @@ -283,7 +283,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, { Error *err = NULL; NetClientState *nc, *nc0 = NULL; - VhostUserState *s; + NetVhostUserState *s; int i; assert(name); @@ -296,7 +296,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, nc->queue_index = i; if (!nc0) { nc0 = nc; - s = DO_UPCAST(VhostUserState, nc, nc); + s = DO_UPCAST(NetVhostUserState, nc, nc); if (!qemu_chr_fe_init(&s->chr, chr, &err)) { error_report_err(err); return -1; @@ -305,7 +305,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, } - s = DO_UPCAST(VhostUserState, nc, nc0); + s = DO_UPCAST(NetVhostUserState, nc, nc0); do { if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) { error_report_err(err); diff --git a/qapi/block-core.json b/qapi/block-core.json index 55728cb..7dfa77a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -6,6 +6,7 @@ { 'include': 'common.json' } { 'include': 'crypto.json' } +{ 'include': 'job.json' } { 'include': 'sockets.json' } ## @@ -1050,95 +1051,6 @@ 'data': ['top', 'full', 'none', 'incremental'] } ## -# @BlockJobType: -# -# Type of a block job. -# -# @commit: block commit job type, see "block-commit" -# -# @stream: block stream job type, see "block-stream" -# -# @mirror: drive mirror job type, see "drive-mirror" -# -# @backup: drive backup job type, see "drive-backup" -# -# Since: 1.7 -## -{ 'enum': 'BlockJobType', - 'data': ['commit', 'stream', 'mirror', 'backup'] } - -## -# @BlockJobVerb: -# -# Represents command verbs that can be applied to a blockjob. -# -# @cancel: see @block-job-cancel -# -# @pause: see @block-job-pause -# -# @resume: see @block-job-resume -# -# @set-speed: see @block-job-set-speed -# -# @complete: see @block-job-complete -# -# @dismiss: see @block-job-dismiss -# -# @finalize: see @block-job-finalize -# -# Since: 2.12 -## -{ 'enum': 'BlockJobVerb', - 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', - 'finalize' ] } - -## -# @BlockJobStatus: -# -# Indicates the present state of a given blockjob in its lifetime. -# -# @undefined: Erroneous, default state. Should not ever be visible. -# -# @created: The job has been created, but not yet started. -# -# @running: The job is currently running. -# -# @paused: The job is running, but paused. The pause may be requested by -# either the QMP user or by internal processes. -# -# @ready: The job is running, but is ready for the user to signal completion. -# This is used for long-running jobs like mirror that are designed to -# run indefinitely. -# -# @standby: The job is ready, but paused. This is nearly identical to @paused. -# The job may return to @ready or otherwise be canceled. -# -# @waiting: The job is waiting for other jobs in the transaction to converge -# to the waiting state. This status will likely not be visible for -# the last job in a transaction. -# -# @pending: The job has finished its work, but has finalization steps that it -# needs to make prior to completing. These changes may require -# manual intervention by the management process if manual was set -# to true. These changes may still fail. -# -# @aborting: The job is in the process of being aborted, and will finish with -# an error. The job will afterwards report that it is @concluded. -# This status may not be visible to the management process. -# -# @concluded: The job has finished all work. If manual was set to true, the job -# will remain in the query list until it is dismissed. -# -# @null: The job is in the process of being dismantled. This state should not -# ever be visible externally. -# -# Since: 2.12 -## -{ 'enum': 'BlockJobStatus', - 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', - 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } - -## # @BlockJobInfo: # # Information about a long-running block device operation. @@ -1148,7 +1060,12 @@ # @device: The job identifier. Originally the device name but other # values are allowed since QEMU 2.7 # -# @len: the maximum progress value +# @len: Estimated @offset value at the completion of the job. This value can +# arbitrarily change while the job is running, in both directions. +# +# @offset: Progress made until now. The unit is arbitrary and the value can +# only meaningfully be used for the ratio of @offset to @len. The +# value is monotonically increasing. # # @busy: false if the job is known to be in a quiescent state, with # no pending I/O. Since 1.3. @@ -1156,8 +1073,6 @@ # @paused: whether the job is paused or, if @busy is true, will # pause itself as soon as possible. Since 1.3. # -# @offset: the current progress value -# # @speed: the rate limit, bytes per second # # @io-status: the status of the job (since 1.3) @@ -1181,7 +1096,7 @@ 'data': {'type': 'str', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', - 'status': 'BlockJobStatus', + 'status': 'JobStatus', 'auto-finalize': 'bool', 'auto-dismiss': 'bool', '*error': 'str' } } @@ -2338,8 +2253,7 @@ # # This command returns immediately after marking the active background block # operation for pausing. It is an error to call this command if no -# operation is in progress. Pausing an already paused job has no cumulative -# effect; a single block-job-resume command will resume the job. +# operation is in progress or if the job is already paused. # # The operation will pause as soon as possible. No event is emitted when # the operation is actually paused. Cancelling a paused job automatically @@ -2363,7 +2277,7 @@ # # This command returns immediately after resuming a paused background block # operation. It is an error to call this command if no operation is in -# progress. Resuming an already running job is not an error. +# progress or if the job is not paused. # # This command also clears the error status of the job. # @@ -2414,7 +2328,7 @@ # QEMU 2.12+ job lifetime management semantics. # # This command will refuse to operate on any job that has not yet reached -# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of +# its terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of the # BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need # to be used as appropriate. # @@ -4497,7 +4411,7 @@ # ## { 'event': 'BLOCK_JOB_COMPLETED', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'device': 'str', 'len' : 'int', 'offset': 'int', @@ -4533,7 +4447,7 @@ # ## { 'event': 'BLOCK_JOB_CANCELLED', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'device': 'str', 'len' : 'int', 'offset': 'int', @@ -4598,7 +4512,7 @@ # ## { 'event': 'BLOCK_JOB_READY', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'device': 'str', 'len' : 'int', 'offset': 'int', @@ -4625,7 +4539,7 @@ # ## { 'event': 'BLOCK_JOB_PENDING', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'id' : 'str' } } ## diff --git a/qapi/job.json b/qapi/job.json new file mode 100644 index 0000000..970124d --- /dev/null +++ b/qapi/job.json @@ -0,0 +1,253 @@ +# -*- Mode: Python -*- + +## +# == Background jobs +## + +## +# @JobType: +# +# Type of a background job. +# +# @commit: block commit job type, see "block-commit" +# +# @stream: block stream job type, see "block-stream" +# +# @mirror: drive mirror job type, see "drive-mirror" +# +# @backup: drive backup job type, see "drive-backup" +# +# Since: 1.7 +## +{ 'enum': 'JobType', + 'data': ['commit', 'stream', 'mirror', 'backup'] } + +## +# @JobStatus: +# +# Indicates the present state of a given job in its lifetime. +# +# @undefined: Erroneous, default state. Should not ever be visible. +# +# @created: The job has been created, but not yet started. +# +# @running: The job is currently running. +# +# @paused: The job is running, but paused. The pause may be requested by +# either the QMP user or by internal processes. +# +# @ready: The job is running, but is ready for the user to signal completion. +# This is used for long-running jobs like mirror that are designed to +# run indefinitely. +# +# @standby: The job is ready, but paused. This is nearly identical to @paused. +# The job may return to @ready or otherwise be canceled. +# +# @waiting: The job is waiting for other jobs in the transaction to converge +# to the waiting state. This status will likely not be visible for +# the last job in a transaction. +# +# @pending: The job has finished its work, but has finalization steps that it +# needs to make prior to completing. These changes may require +# manual intervention by the management process if manual was set +# to true. These changes may still fail. +# +# @aborting: The job is in the process of being aborted, and will finish with +# an error. The job will afterwards report that it is @concluded. +# This status may not be visible to the management process. +# +# @concluded: The job has finished all work. If manual was set to true, the job +# will remain in the query list until it is dismissed. +# +# @null: The job is in the process of being dismantled. This state should not +# ever be visible externally. +# +# Since: 2.12 +## +{ 'enum': 'JobStatus', + 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', + 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } + +## +# @JobVerb: +# +# Represents command verbs that can be applied to a job. +# +# @cancel: see @block-job-cancel +# +# @pause: see @block-job-pause +# +# @resume: see @block-job-resume +# +# @set-speed: see @block-job-set-speed +# +# @complete: see @block-job-complete +# +# @dismiss: see @block-job-dismiss +# +# @finalize: see @block-job-finalize +# +# Since: 2.12 +## +{ 'enum': 'JobVerb', + 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', + 'finalize' ] } + +## +# @JOB_STATUS_CHANGE: +# +# Emitted when a job transitions to a different status. +# +# @id: The job identifier +# @status: The new job status +# +# Since: 2.13 +## +{ 'event': 'JOB_STATUS_CHANGE', + 'data': { 'id': 'str', + 'status': 'JobStatus' } } + +## +# @job-pause: +# +# Pause an active job. +# +# This command returns immediately after marking the active job for pausing. +# Pausing an already paused job is an error. +# +# The job will pause as soon as possible, which means transitioning into the +# PAUSED state if it was RUNNING, or into STANDBY if it was READY. The +# corresponding JOB_STATUS_CHANGE event will be emitted. +# +# Cancelling a paused job automatically resumes it. +# +# @id: The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-pause', 'data': { 'id': 'str' } } + +## +# @job-resume: +# +# Resume a paused job. +# +# This command returns immediately after resuming a paused job. Resuming an +# already running job is an error. +# +# @id : The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-resume', 'data': { 'id': 'str' } } + +## +# @job-cancel: +# +# Instruct an active background job to cancel at the next opportunity. +# This command returns immediately after marking the active job for +# cancellation. +# +# The job will cancel as soon as possible and then emit a JOB_STATUS_CHANGE +# event. Usually, the status will change to ABORTING, but it is possible that +# a job successfully completes (e.g. because it was almost done and there was +# no opportunity to cancel earlier than completing the job) and transitions to +# PENDING instead. +# +# @id: The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-cancel', 'data': { 'id': 'str' } } + + +## +# @job-complete: +# +# Manually trigger completion of an active job in the READY state. +# +# @id: The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-complete', 'data': { 'id': 'str' } } + +## +# @job-dismiss: +# +# Deletes a job that is in the CONCLUDED state. This command only needs to be +# run explicitly for jobs that don't have automatic dismiss enabled. +# +# This command will refuse to operate on any job that has not yet reached its +# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY +# event, job-cancel or job-complete will still need to be used as appropriate. +# +# @id: The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-dismiss', 'data': { 'id': 'str' } } + +## +# @job-finalize: +# +# Instructs all jobs in a transaction (or a single job if it is not part of any +# transaction) to finalize any graph changes and do any necessary cleanup. This +# command requires that all involved jobs are in the PENDING state. +# +# For jobs in a transaction, instructing one job to finalize will force +# ALL jobs in the transaction to finalize, so it is only necessary to instruct +# a single member job to finalize. +# +# @id: The identifier of any job in the transaction, or of a job that is not +# part of any transaction. +# +# Since: 2.13 +## +{ 'command': 'job-finalize', 'data': { 'id': 'str' } } + +## +# @JobInfo: +# +# Information about a job. +# +# @id: The job identifier +# +# @type: The kind of job that is being performed +# +# @status: Current job state/status +# +# @current-progress: Progress made until now. The unit is arbitrary and the +# value can only meaningfully be used for the ratio of +# @current-progress to @total-progress. The value is +# monotonically increasing. +# +# @total-progress: Estimated @current-progress value at the completion of +# the job. This value can arbitrarily change while the +# job is running, in both directions. +# +# @error: If this field is present, the job failed; if it is +# still missing in the CONCLUDED state, this indicates +# successful completion. +# +# The value is a human-readable error message to describe +# the reason for the job failure. It should not be parsed +# by applications. +# +# Since: 2.13 +## +{ 'struct': 'JobInfo', + 'data': { 'id': 'str', 'type': 'JobType', 'status': 'JobStatus', + 'current-progress': 'int', 'total-progress': 'int', + '*error': 'str' } } + +## +# @query-jobs: +# +# Return information about jobs. +# +# Returns: a list with a @JobInfo for each active job +# +# Since: 2.13 +## +{ 'command': 'query-jobs', 'returns': ['JobInfo'] } diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 25bce78..65b6dc2 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -84,6 +84,7 @@ { 'include': 'crypto.json' } { 'include': 'block.json' } { 'include': 'char.json' } +{ 'include': 'job.json' } { 'include': 'net.json' } { 'include': 'rocker.json' } { 'include': 'tpm.json' } @@ -861,19 +861,23 @@ static void run_block_job(BlockJob *job, Error **errp) int ret = 0; aio_context_acquire(aio_context); - block_job_ref(job); + job_ref(&job->job); do { + float progress = 0.0f; aio_poll(aio_context, true); - qemu_progress_print(job->len ? - ((float)job->offset / job->len * 100.f) : 0.0f, 0); - } while (!job->ready && !job->completed); + if (job->job.progress_total) { + progress = (float)job->job.progress_current / + job->job.progress_total * 100.f; + } + qemu_progress_print(progress, 0); + } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); - if (!job->completed) { - ret = block_job_complete_sync(job, errp); + if (!job_is_completed(&job->job)) { + ret = job_complete_sync(&job->job, errp); } else { - ret = job->ret; + ret = job->job.ret; } - block_job_unref(job); + job_unref(&job->job); aio_context_release(aio_context); /* publish completion progress only when success */ @@ -1014,7 +1018,7 @@ static int img_commit(int argc, char **argv) aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0, + commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0, BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, &cbi, false, &local_err); aio_context_release(aio_context); @@ -482,6 +482,12 @@ static const char *socket_activation_validate_opts(const char *device, return NULL; } +static void qemu_nbd_shutdown(void) +{ + job_cancel_sync_all(); + bdrv_close_all(); +} + int main(int argc, char **argv) { BlockBackend *blk; @@ -928,7 +934,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } bdrv_init(); - atexit(bdrv_close_all); + atexit(qemu_nbd_shutdown); srcpath = argv[optind]; if (imageOpts) { diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index cb1b652..e3d8c2c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -271,7 +271,7 @@ our @typeList = ( qr{hwaddr}, # external libraries qr{xml${Ident}}, - qr{xendevicemodel_handle}, + qr{xen\w+_handle}, # Glib definitions qr{gchar}, qr{gshort}, diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index b65c03f..947dec2 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -43,6 +43,7 @@ cp_portable() { -e 'limits' \ -e 'linux/kernel' \ -e 'linux/sysinfo' \ + -e 'asm-generic/kvm_para' \ > /dev/null then echo "Unexpected #include in input file $f". @@ -83,11 +84,6 @@ for arch in $ARCHLIST; do continue fi - # Blacklist architectures which have KVM headers but are actually dead - if [ "$arch" = "ia64" -o "$arch" = "mips" ]; then - continue - fi - if [ "$arch" = x86 ]; then arch_var=SRCARCH else @@ -98,11 +94,12 @@ for arch in $ARCHLIST; do rm -rf "$output/linux-headers/asm-$arch" mkdir -p "$output/linux-headers/asm-$arch" - for header in kvm.h kvm_para.h unistd.h; do + for header in kvm.h unistd.h bitsperlong.h; do cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch" done - if [ $arch = powerpc ]; then - cp "$tmpdir/include/asm/epapr_hcalls.h" "$output/linux-headers/asm-powerpc/" + + if [ $arch = mips ]; then + cp "$tmpdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/" fi rm -rf "$output/include/standard-headers/asm-$arch" @@ -121,20 +118,23 @@ for arch in $ARCHLIST; do cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" cp "$tmpdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" + cp_portable "$tmpdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch" fi done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" -for header in kvm.h kvm_para.h vfio.h vfio_ccw.h vhost.h \ +for header in kvm.h vfio.h vfio_ccw.h vhost.h \ psci.h psp-sev.h userfaultfd.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done + rm -rf "$output/linux-headers/asm-generic" mkdir -p "$output/linux-headers/asm-generic" -for header in kvm_para.h; do +for header in unistd.h bitsperlong.h; do cp "$tmpdir/include/asm-generic/$header" "$output/linux-headers/asm-generic" done + if [ -L "$linux/source" ]; then cp "$linux/source/COPYING" "$output/linux-headers" else diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d95310f..9426041 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -40,9 +40,7 @@ #include "qom/qom-qobject.h" #include "sysemu/arch_init.h" -#if defined(CONFIG_KVM) -#include <linux/kvm_para.h> -#endif +#include "standard-headers/asm-x86/kvm_para.h" #include "sysemu/sysemu.h" #include "hw/qdev-properties.h" diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8ac13f6..6645046 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -688,8 +688,6 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ #define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) /* Speculative Store Bypass Disable */ -#define KVM_HINTS_DEDICATED (1U << 0) - #define CPUID_8000_0008_EBX_IBPB (1U << 12) /* Indirect Branch Prediction Barrier */ #define CPUID_XSAVE_XSAVEOPT (1U << 0) diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 0c656a9..6511329 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -18,7 +18,7 @@ #include <sys/utsname.h> #include <linux/kvm.h> -#include <linux/kvm_para.h> +#include "standard-headers/asm-x86/kvm_para.h" #include "qemu-common.h" #include "cpu.h" @@ -387,7 +387,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, ret &= ~(1U << KVM_FEATURE_PV_UNHALT); } } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) { - ret |= KVM_HINTS_DEDICATED; + ret |= 1U << KVM_HINTS_DEDICATED; found = 1; } diff --git a/target/i386/kvm_i386.h b/target/i386/kvm_i386.h index 1de9876..e5df24c 100644 --- a/target/i386/kvm_i386.h +++ b/target/i386/kvm_i386.h @@ -30,12 +30,6 @@ #define kvm_pic_in_kernel() 0 #define kvm_ioapic_in_kernel() 0 -/* These constants must never be used at runtime if kvm_enabled() is false. - * They exist so we don't need #ifdefs around KVM-specific code that already - * checks kvm_enabled() properly. - */ -#define KVM_CPUID_FEATURES 0 - #endif /* CONFIG_KVM */ bool kvm_allows_irq0_override(void); diff --git a/target/lm32/op_helper.c b/target/lm32/op_helper.c index 577f830..234d55e 100644 --- a/target/lm32/op_helper.c +++ b/target/lm32/op_helper.c @@ -102,12 +102,16 @@ void HELPER(wcsr_dc)(CPULM32State *env, uint32_t dc) void HELPER(wcsr_im)(CPULM32State *env, uint32_t im) { + qemu_mutex_lock_iothread(); lm32_pic_set_im(env->pic_state, im); + qemu_mutex_unlock_iothread(); } void HELPER(wcsr_ip)(CPULM32State *env, uint32_t im) { + qemu_mutex_lock_iothread(); lm32_pic_set_ip(env->pic_state, im); + qemu_mutex_unlock_iothread(); } void HELPER(wcsr_jtx)(CPULM32State *env, uint32_t jtx) diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 640a6df..1dbc2dd 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -304,8 +304,7 @@ class TestParallelOps(iotests.QMPTestCase): result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6') self.assert_qmp(result, 'error/class', 'GenericError') - event = self.vm.get_qmp_event(wait=True) - self.assertEqual(event['event'], 'BLOCK_JOB_READY') + event = self.vm.event_wait(name='BLOCK_JOB_READY') self.assert_qmp(event, 'data/device', 'commit-drive0') self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp_absent(event, 'data/error') @@ -565,6 +564,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -596,6 +597,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.image_len) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -637,6 +640,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.image_len) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -663,6 +668,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -722,6 +729,8 @@ class TestENOSPC(TestErrors): self.assert_qmp(event, 'data/offset', self.image_len) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -751,7 +760,9 @@ class TestStreamStop(iotests.QMPTestCase): time.sleep(0.1) events = self.vm.get_qmp_events(wait=False) - self.assertEqual(events, [], 'unexpected QMP event: %s' % events) + for e in events: + self.assert_qmp(e, 'event', 'JOB_STATUS_CHANGE') + self.assert_qmp(e, 'data/id', 'drive0') self.cancel_and_wait(resume=True) diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 90b5b4f..1beb5e6 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -162,6 +162,8 @@ class TestSingleDrive(ImageCommitTestCase): elif event['event'] == 'BLOCK_JOB_CANCELLED': self.assert_qmp(event, 'data/device', 'drive0') cancelled = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') else: self.fail("Unexpected event %s" % (event['event'])) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index a860a31..c20ac7d 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -445,6 +445,8 @@ new_state = "1" self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/error', 'Input/output error') completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -457,6 +459,10 @@ new_state = "1" self.assert_qmp(result, 'return', {}) event = self.vm.get_qmp_event(wait=True) + while event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') + event = self.vm.get_qmp_event(wait=True) + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') @@ -478,6 +484,10 @@ new_state = "1" self.assert_qmp(result, 'return', {}) event = self.vm.get_qmp_event(wait=True) + while event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') + event = self.vm.get_qmp_event(wait=True) + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') @@ -608,7 +618,7 @@ new_state = "1" on_target_error='ignore') self.assert_qmp(result, 'return', {}) - event = self.vm.get_qmp_event(wait=True) + event = self.vm.event_wait(name='BLOCK_JOB_ERROR') self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'write') @@ -784,7 +794,12 @@ class TestGranularity(iotests.QMPTestCase): sync='full', target=target_img, mode='absolute-paths', granularity=8192) self.assert_qmp(result, 'return', {}) + event = self.vm.get_qmp_event(wait=60.0) + while event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') + event = self.vm.get_qmp_event(wait=60.0) + # Failures will manifest as COMPLETED/ERROR. self.assert_qmp(event, 'event', 'BLOCK_JOB_READY') self.complete_and_wait(drive='drive0', wait_ready=False) @@ -1015,9 +1030,9 @@ class TestOrphanedSource(iotests.QMPTestCase): 'read-only': 'on' } self.vm = iotests.VM() - self.vm.add_blockdev(self.qmp_to_opts(blk0)) - self.vm.add_blockdev(self.qmp_to_opts(blk1)) - self.vm.add_blockdev(self.qmp_to_opts(blk2)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk0)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk1)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk2)) self.vm.launch() def tearDown(self): diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086 index cd4494a..84e3835 100755 --- a/tests/qemu-iotests/086 +++ b/tests/qemu-iotests/086 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 raw -_supported_proto file nfs +_supported_proto file _supported_os Linux function run_qemu_img() diff --git a/tests/qemu-iotests/094.out b/tests/qemu-iotests/094.out index f52baff..665b630 100644 --- a/tests/qemu-iotests/094.out +++ b/tests/qemu-iotests/094.out @@ -2,10 +2,17 @@ QA output created by 094 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} *** done diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095 index 030adb2..72ecc22 100755 --- a/tests/qemu-iotests/095 +++ b/tests/qemu-iotests/095 @@ -72,7 +72,7 @@ _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" _send_qemu_cmd $h "{ 'execute': 'block-commit', 'arguments': { 'device': 'test', - 'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED" + 'top': '"${TEST_IMG}.snp1"' } }" '"status": "null"' _cleanup_qemu diff --git a/tests/qemu-iotests/095.out b/tests/qemu-iotests/095.out index 73875ca..8c093df 100644 --- a/tests/qemu-iotests/095.out +++ b/tests/qemu-iotests/095.out @@ -11,8 +11,14 @@ virtual size: 5.0M (5242880 bytes) === Running QEMU Live Commit Test === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "test"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "test"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 104857600, "offset": 104857600, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "test"}} === Base image info after commit and resize === image: TEST_DIR/t.IMGFMT.base diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 index d70b574..acbd079 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -64,7 +64,7 @@ function run_qemu() _send_qemu_cmd $QEMU_HANDLE '' "$qmp_event" if test "$qmp_event" = BLOCK_JOB_ERROR; then - _send_qemu_cmd $QEMU_HANDLE '' "BLOCK_JOB_COMPLETED" + _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"' fi _send_qemu_cmd $QEMU_HANDLE '{"execute":"query-block-jobs"}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index 8a9b936..ad0ee6f 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -6,23 +6,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -32,23 +44,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 512, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -58,23 +82,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 262144, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -84,23 +120,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -110,23 +158,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -136,23 +196,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -161,23 +233,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -186,23 +270,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -211,23 +307,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -236,23 +344,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -261,23 +381,37 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. *** done diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index 8e76e62..3ea4ac5 100755 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -151,10 +151,17 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): return self.wait_qmp_backup(kwargs['device'], error) + def ignore_job_status_change_events(self): + while True: + e = self.vm.event_wait(name="JOB_STATUS_CHANGE") + if e['data']['status'] == 'null': + break + def wait_qmp_backup(self, device, error='Input/output error'): event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED", match={'data': {'device': device}}) self.assertNotEqual(event, None) + self.ignore_job_status_change_events() try: failure = self.dictpath(event, 'data/error') @@ -172,6 +179,7 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED', match={'data': {'device': device}}) self.assertNotEqual(event, None) + self.ignore_job_status_change_events() def create_anchor_backup(self, drive=None): diff --git a/tests/qemu-iotests/126.out b/tests/qemu-iotests/126.out index 50d7308..17d03d5 100644 --- a/tests/qemu-iotests/126.out +++ b/tests/qemu-iotests/126.out @@ -3,7 +3,7 @@ QA output created by 126 === Testing plain files === Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'file:TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864 === Testing relative backing filename resolution === diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out index 543d075..83b522d 100644 --- a/tests/qemu-iotests/127.out +++ b/tests/qemu-iotests/127.out @@ -5,10 +5,17 @@ Formatting 'TEST_DIR/t.IMGFMT.overlay1', fmt=IMGFMT size=65536 backing_file=TEST wrote 42/42 bytes at offset 0 42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "mirror"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} *** done diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index 2f9d7b9..4246d38 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -107,7 +107,7 @@ test_blockjob \ 'format': '$IMGFMT', 'sync': 'none'}}" \ 'return' \ - 'BLOCK_JOB_CANCELLED' + '"status": "null"' echo echo '=== Testing drive-mirror ===' @@ -124,7 +124,7 @@ test_blockjob \ 'format': '$IMGFMT', 'sync': 'none'}}" \ 'BLOCK_JOB_READY' \ - 'BLOCK_JOB_COMPLETED' + '"status": "null"' echo echo '=== Testing active block-commit ===' @@ -138,7 +138,7 @@ test_blockjob \ "{'execute': 'block-commit', 'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \ 'BLOCK_JOB_READY' \ - 'BLOCK_JOB_COMPLETED' + '"status": "null"' echo echo '=== Testing non-active block-commit ===' @@ -157,7 +157,7 @@ test_blockjob \ 'top': '$TEST_DIR/m.$IMGFMT', 'speed': 1}}" \ 'return' \ - 'BLOCK_JOB_CANCELLED' + '"status": "null"' echo echo '=== Testing block-stream ===' @@ -170,8 +170,7 @@ echo $QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io # With some data to stream (and @speed set to 1), block-stream will not complete -# until we send the block-job-cancel command. Therefore, no event other than -# BLOCK_JOB_CANCELLED will be emitted. +# until we send the block-job-cancel command. test_blockjob \ "{'execute': 'block-stream', @@ -179,7 +178,7 @@ test_blockjob \ 'device': 'drv0', 'speed': 1}}" \ 'return' \ - 'BLOCK_JOB_CANCELLED' + '"status": "null"' _cleanup_qemu diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out index 82e763b..f252c86 100644 --- a/tests/qemu-iotests/141.out +++ b/tests/qemu-iotests/141.out @@ -8,31 +8,50 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m. {"return": {}} 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"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} {"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"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing drive-mirror === {"return": {}} 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"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} {"return": {}} {"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": "waiting", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing active block-commit === {"return": {}} +{"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"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} {"return": {}} {"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": "waiting", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing non-active block-commit === @@ -40,10 +59,15 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t. wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"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"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} {"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": 524288, "speed": 1, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing block-stream === @@ -51,9 +75,14 @@ wrote 1048576/1048576 bytes at offset 0 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"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"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} {"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": 524288, "speed": 1, "type": "stream"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} *** done diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 index 00de3c33..4b91571 100755 --- a/tests/qemu-iotests/144 +++ b/tests/qemu-iotests/144 @@ -93,7 +93,7 @@ _send_qemu_cmd $h "{ 'execute': 'block-job-complete', 'arguments': { 'device': 'virtio0' } - }" "COMPLETED" + }" '"status": "null"' echo echo === Performing Live Snapshot 2 === diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out index 014b281..5529920 100644 --- a/tests/qemu-iotests/144.out +++ b/tests/qemu-iotests/144.out @@ -12,10 +12,17 @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/ === Performing block-commit on active layer === +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} {"return": {}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "virtio0"}} === Performing Live Snapshot 2 === diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index 42dae04..63a5b5e 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -63,7 +63,7 @@ class BaseClass(iotests.QMPTestCase): 'driver': iotests.imgfmt, 'file': {'driver': 'file', 'filename': source_img}} - self.vm.add_blockdev(self.qmp_to_opts(blockdev)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev)) self.vm.add_device('virtio-blk,id=qdev0,drive=source') self.vm.launch() diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index e75dc4d..0a9a098 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -119,7 +119,7 @@ _send_qemu_cmd $QEMU_HANDLE \ _send_qemu_cmd $QEMU_HANDLE \ '' \ - 'BLOCK_JOB_COMPLETED' + '"status": "null"' # Remove the source images rm -f "$TEST_IMG{,.backing,.overlay}" diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out index f96a564..34c057b 100644 --- a/tests/qemu-iotests/156.out +++ b/tests/qemu-iotests/156.out @@ -12,13 +12,20 @@ wrote 131072/131072 bytes at offset 131072 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "source"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "source"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "source"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "source", "len": 131072, "offset": 131072, "speed": 0, "type": "mirror"}} wrote 65536/65536 bytes at offset 196608 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "source"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "source"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "source", "len": 196608, "offset": 196608, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "source"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "source"}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 9a2d317..7dcfdea 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -27,8 +27,6 @@ echo "QA output created by $seq" here=`pwd` status=1 # failure is the default! -MIG_SOCKET="${TEST_DIR}/migrate" - _cleanup() { rm -f "${TEST_IMG}.mid" @@ -118,8 +116,10 @@ _send_qemu_cmd $h \ # If we don't sleep here 'quit' command races with disk I/O sleep 0.5 +# Ignore the JOB_STATUS_CHANGE events while shutting down the VM. Depending on +# the timing, jobs may or may not transition through a paused state. _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start active commit job and exit qemu === @@ -141,7 +141,7 @@ _send_qemu_cmd $h \ sleep 0.5 _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start mirror job and exit qemu === @@ -166,7 +166,7 @@ _send_qemu_cmd $h \ sleep 0.5 _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start backup job and exit qemu === @@ -190,7 +190,7 @@ _send_qemu_cmd $h \ sleep 0.5 _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start streaming job and exit qemu === @@ -211,7 +211,7 @@ _send_qemu_cmd $h \ sleep 0.5 _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' _check_test_img diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 57eaf8d..4e0ca0d 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -17,6 +17,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q === Start commit job and exit qemu === +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} @@ -25,6 +27,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q === Start active commit job and exit qemu === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} @@ -34,6 +38,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q {"return": {}} Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} @@ -43,6 +49,8 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l {"return": {}} Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} @@ -51,6 +59,8 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l === Start streaming job and exit qemu === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 index dfad655..d6860e7 100755 --- a/tests/qemu-iotests/191 +++ b/tests/qemu-iotests/191 @@ -27,8 +27,6 @@ echo "QA output created by $seq" here=`pwd` status=1 # failure is the default! -MIG_SOCKET="${TEST_DIR}/migrate" - _cleanup() { rm -f "${TEST_IMG}.mid" @@ -83,7 +81,7 @@ _send_qemu_cmd $h \ 'device': 'top', 'base':'$TEST_IMG.base', 'top': '$TEST_IMG.mid' } }" \ - "BLOCK_JOB_COMPLETED" + '"status": "null"' _send_qemu_cmd $h "" "^}" echo @@ -131,7 +129,7 @@ _send_qemu_cmd $h \ 'device': 'top', 'base':'$TEST_IMG.base', 'top': '$TEST_IMG.mid' } }" \ - "BLOCK_JOB_COMPLETED" + '"status": "null"' _send_qemu_cmd $h "" "^}" echo diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out index 190c5f0..31a0c7d 100644 --- a/tests/qemu-iotests/191.out +++ b/tests/qemu-iotests/191.out @@ -16,6 +16,28 @@ wrote 65536/65536 bytes at offset 1048576 === Perform commit job === { + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "created", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "running", + "id": "commit0" + } +} +{ "return": { } } @@ -24,6 +46,28 @@ wrote 65536/65536 bytes at offset 1048576 "seconds": TIMESTAMP, "microseconds": TIMESTAMP }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "waiting", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "pending", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, "event": "BLOCK_JOB_COMPLETED", "data": { "device": "commit0", @@ -33,6 +77,28 @@ wrote 65536/65536 bytes at offset 1048576 "type": "commit" } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "concluded", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "null", + "id": "commit0" + } +} === Check that both top and top2 point to base now === @@ -356,6 +422,28 @@ wrote 65536/65536 bytes at offset 1048576 === Perform commit job === { + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "created", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "running", + "id": "commit0" + } +} +{ "return": { } } @@ -364,6 +452,28 @@ wrote 65536/65536 bytes at offset 1048576 "seconds": TIMESTAMP, "microseconds": TIMESTAMP }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "waiting", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "pending", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, "event": "BLOCK_JOB_COMPLETED", "data": { "device": "commit0", @@ -373,6 +483,28 @@ wrote 65536/65536 bytes at offset 1048576 "type": "commit" } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "concluded", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "null", + "id": "commit0" + } +} === Check that both top and top2 point to base now === diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219 new file mode 100755 index 0000000..898a26e --- /dev/null +++ b/tests/qemu-iotests/219 @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Creator/Owner: Kevin Wolf <kwolf@redhat.com> +# +# Check using the job-* QMP commands with block jobs + +import iotests + +iotests.verify_image_format(supported_fmts=['qcow2']) + +def pause_wait(vm, job_id): + with iotests.Timeout(3, "Timeout waiting for job to pause"): + while True: + result = vm.qmp('query-jobs') + for job in result['return']: + if job['id'] == job_id and job['status'] in ['paused', 'standby']: + return job + +# Test that block-job-pause/resume and job-pause/resume can be mixed +def test_pause_resume(vm): + for pause_cmd, pause_arg in [('block-job-pause', 'device'), + ('job-pause', 'id')]: + for resume_cmd, resume_arg in [('block-job-resume', 'device'), + ('job-resume', 'id')]: + iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd)) + + iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'})) + pause_wait(vm, 'job0') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + + iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'})) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + +def test_job_lifecycle(vm, job, job_args, has_ready=False): + iotests.log('') + iotests.log('') + iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' % + (job, + job_args.get('auto-finalize', True), + job_args.get('auto-dismiss', True))) + iotests.log(vm.qmp(job, job_id='job0', **job_args)) + + # Depending on the storage, the first request may or may not have completed + # yet, so filter out the progress. Later query-job calls don't need the + # filtering because the progress is made deterministic by the block job + # speed + result = vm.qmp('query-jobs') + for j in result['return']: + del j['current-progress'] + iotests.log(result) + + # undefined -> created -> running + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + + # RUNNING state: + # pause/resume should work, complete/finalize/dismiss should error out + iotests.log('') + iotests.log('Pause/resume in RUNNING') + test_pause_resume(vm) + + iotests.log(vm.qmp('job-complete', id='job0')) + iotests.log(vm.qmp('job-finalize', id='job0')) + iotests.log(vm.qmp('job-dismiss', id='job0')) + + iotests.log(vm.qmp('block-job-complete', device='job0')) + iotests.log(vm.qmp('block-job-finalize', id='job0')) + iotests.log(vm.qmp('block-job-dismiss', id='job0')) + + # Let the job complete (or transition to READY if it supports that) + iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0)) + if has_ready: + iotests.log('') + iotests.log('Waiting for READY state...') + vm.event_wait('BLOCK_JOB_READY') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + + # READY state: + # pause/resume/complete should work, finalize/dismiss should error out + iotests.log('') + iotests.log('Pause/resume in READY') + test_pause_resume(vm) + + iotests.log(vm.qmp('job-finalize', id='job0')) + iotests.log(vm.qmp('job-dismiss', id='job0')) + + iotests.log(vm.qmp('block-job-finalize', id='job0')) + iotests.log(vm.qmp('block-job-dismiss', id='job0')) + + # Transition to WAITING + iotests.log(vm.qmp('job-complete', id='job0')) + + # Move to WAITING and PENDING state + iotests.log('') + iotests.log('Waiting for PENDING state...') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + + if not job_args.get('auto-finalize', True): + # PENDING state: + # finalize should work, pause/complete/dismiss should error out + iotests.log(vm.qmp('query-jobs')) + + iotests.log(vm.qmp('job-pause', id='job0')) + iotests.log(vm.qmp('job-complete', id='job0')) + iotests.log(vm.qmp('job-dismiss', id='job0')) + + iotests.log(vm.qmp('block-job-pause', device='job0')) + iotests.log(vm.qmp('block-job-complete', device='job0')) + iotests.log(vm.qmp('block-job-dismiss', id='job0')) + + # Transition to CONCLUDED + iotests.log(vm.qmp('job-finalize', id='job0')) + + + # Move to CONCLUDED state + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + + if not job_args.get('auto-dismiss', True): + # CONCLUDED state: + # dismiss should work, pause/complete/finalize should error out + iotests.log(vm.qmp('query-jobs')) + + iotests.log(vm.qmp('job-pause', id='job0')) + iotests.log(vm.qmp('job-complete', id='job0')) + iotests.log(vm.qmp('job-finalize', id='job0')) + + iotests.log(vm.qmp('block-job-pause', device='job0')) + iotests.log(vm.qmp('block-job-complete', device='job0')) + iotests.log(vm.qmp('block-job-finalize', id='job0')) + + # Transition to NULL + iotests.log(vm.qmp('job-dismiss', id='job0')) + + # Move to NULL state + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + + +with iotests.FilePath('disk.img') as disk_path, \ + iotests.FilePath('copy.img') as copy_path, \ + iotests.VM() as vm: + + img_size = '4M' + iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size) + iotests.qemu_io('-c', 'write 0 %s' % (img_size), + '-f', iotests.imgfmt, disk_path) + + iotests.log('Launching VM...') + vm.add_blockdev(vm.qmp_to_opts({ + 'driver': iotests.imgfmt, + 'node-name': 'drive0-node', + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + })) + vm.launch() + + # In order to keep things deterministic (especially progress in query-job, + # but related to this also automatic state transitions like job + # completion), but still get pause points often enough to avoid making this + # test very slow, it's important to have the right ratio between speed and + # buf_size. + # + # For backup, buf_size is hard-coded to the source image cluster size (64k), + # so we'll pick the same for mirror. The slice time, i.e. the granularity + # of the rate limiting is 100ms. With a speed of 256k per second, we can + # get four pause points per second. This gives us 250ms per iteration, + # which should be enough to stay deterministic. + + test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={ + 'device': 'drive0-node', + 'target': copy_path, + 'sync': 'full', + 'speed': 262144, + 'buf_size': 65536, + }) + + for auto_finalize in [True, False]: + for auto_dismiss in [True, False]: + test_job_lifecycle(vm, 'drive-backup', job_args={ + 'device': 'drive0-node', + 'target': copy_path, + 'sync': 'full', + 'speed': 262144, + 'auto-finalize': auto_finalize, + 'auto-dismiss': auto_dismiss, + }) + + vm.shutdown() diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out new file mode 100644 index 0000000..346801b --- /dev/null +++ b/tests/qemu-iotests/219.out @@ -0,0 +1,327 @@ +Launching VM... + + +Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for READY state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} + +Pause/resume in READY +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index c5f4bcf..f08ee55 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -119,7 +119,8 @@ _filter_actual_image_size() # replace driver-specific options in the "Formatting..." line _filter_img_create() { - sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's#nbd:127.0.0.1:10810#TEST_DIR/t.IMGFMT#g' \ @@ -154,7 +155,8 @@ _filter_img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' - sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \ diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 9a65a11..d054cb9 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -147,8 +147,9 @@ else TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "nfs" ]; then - TEST_DIR="nfs://127.0.0.1/$TEST_DIR" - TEST_IMG=$TEST_DIR/t.$IMGFMT + TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT + REMOTE_TEST_DIR="nfs://127.0.0.1$TEST_DIR" + TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "vxhs" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="vxhs://127.0.0.1:9999/t.$IMGFMT" @@ -173,6 +174,10 @@ if [ ! -d "$TEST_DIR" ]; then exit 1 fi +if [ -z "$REMOTE_TEST_DIR" ]; then + REMOTE_TEST_DIR="$TEST_DIR" +fi + if [ ! -d "$SAMPLE_IMG_DIR" ]; then echo "common.config: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory" exit 1 @@ -333,7 +338,8 @@ _img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' $QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \ - sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e "/^disk size:/ D" \ diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index cc8cd8c..93f93d7 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -97,7 +97,7 @@ 088 rw auto quick 089 rw auto quick 090 rw auto quick -091 rw auto +091 rw auto migration 092 rw auto quick 093 auto 094 rw auto quick @@ -169,7 +169,7 @@ 162 auto quick 163 rw auto 165 rw auto quick -169 rw auto quick +169 rw auto quick migration 170 rw auto quick 171 rw auto quick 172 auto @@ -194,14 +194,14 @@ 192 rw auto quick 194 rw auto migration quick 195 rw auto quick -196 rw auto quick +196 rw auto quick migration 197 rw auto quick 198 rw auto -199 rw auto +199 rw auto migration 200 rw auto 201 rw auto migration 202 rw auto quick -203 rw auto +203 rw auto migration 204 rw auto quick 205 rw auto quick 206 rw auto @@ -216,3 +216,4 @@ 215 rw auto quick 216 rw auto quick 218 rw auto quick +219 rw auto diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index b25d48a..28159d83 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -363,6 +363,27 @@ class VM(qtest.QEMUQtestMachine): return self.qmp('human-monitor-command', command_line='qemu-io %s "%s"' % (drive, cmd)) + def flatten_qmp_object(self, obj, output=None, basestr=''): + if output is None: + output = dict() + if isinstance(obj, list): + for i in range(len(obj)): + self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') + elif isinstance(obj, dict): + for key in obj: + self.flatten_qmp_object(obj[key], output, basestr + key + '.') + else: + output[basestr[:-1]] = obj # Strip trailing '.' + return output + + def qmp_to_opts(self, obj): + obj = self.flatten_qmp_object(obj) + output_list = list() + for key in obj: + output_list += [key + '=' + obj[key]] + return ','.join(output_list) + + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') @@ -390,26 +411,6 @@ class QMPTestCase(unittest.TestCase): self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d))) return d - def flatten_qmp_object(self, obj, output=None, basestr=''): - if output is None: - output = dict() - if isinstance(obj, list): - for i in range(len(obj)): - self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') - elif isinstance(obj, dict): - for key in obj: - self.flatten_qmp_object(obj[key], output, basestr + key + '.') - else: - output[basestr[:-1]] = obj # Strip trailing '.' - return output - - def qmp_to_opts(self, obj): - obj = self.flatten_qmp_object(obj) - output_list = list() - for key in obj: - output_list += [key + '=' + obj[key]] - return ','.join(output_list) - def assert_qmp_absent(self, d, path): try: result = self.dictpath(d, path) @@ -444,8 +445,8 @@ class QMPTestCase(unittest.TestCase): '''Asserts that the given filename is a json: filename and that its content is equal to the given reference object''' self.assertEqual(json_filename[:5], 'json:') - self.assertEqual(self.flatten_qmp_object(json.loads(json_filename[5:])), - self.flatten_qmp_object(reference)) + self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])), + self.vm.flatten_qmp_object(reference)) def cancel_and_wait(self, drive='drive0', force=False, resume=False): '''Cancel a block job and wait for it to finish, returning the event''' @@ -464,6 +465,9 @@ class QMPTestCase(unittest.TestCase): self.assert_qmp(event, 'data/device', drive) result = event cancelled = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', drive) + self.assert_no_active_block_jobs() return result @@ -479,6 +483,8 @@ class QMPTestCase(unittest.TestCase): self.assert_qmp(event, 'data/offset', event['data']['len']) self.assert_no_active_block_jobs() return event + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', drive) def wait_ready(self, drive='drive0'): '''Wait until a block job BLOCK_JOB_READY event''' diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 7673de1..2cba63b 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -496,33 +496,38 @@ typedef struct TestBlockJob { bool should_complete; } TestBlockJob; -static void test_job_completed(BlockJob *job, void *opaque) +static void test_job_completed(Job *job, void *opaque) { - block_job_completed(job, 0); + job_completed(job, 0); } static void coroutine_fn test_job_start(void *opaque) { TestBlockJob *s = opaque; - block_job_event_ready(&s->common); + job_transition_to_ready(&s->common.job); while (!s->should_complete) { - block_job_sleep_ns(&s->common, 100000); + job_sleep_ns(&s->common.job, 100000); } - block_job_defer_to_main_loop(&s->common, test_job_completed, NULL); + job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); } -static void test_job_complete(BlockJob *job, Error **errp) +static void test_job_complete(Job *job, Error **errp) { - TestBlockJob *s = container_of(job, TestBlockJob, common); + TestBlockJob *s = container_of(job, TestBlockJob, common.job); s->should_complete = true; } BlockJobDriver test_job_driver = { - .instance_size = sizeof(TestBlockJob), - .start = test_job_start, - .complete = test_job_complete, + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = test_job_start, + .complete = test_job_complete, + }, }; static void test_blockjob_common(enum drain_type drain_type) @@ -545,49 +550,49 @@ static void test_blockjob_common(enum drain_type drain_type) job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); - block_job_start(job); + job_start(&job->job); - g_assert_cmpint(job->pause_count, ==, 0); - g_assert_false(job->paused); - g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ do_drain_begin(drain_type, src); if (drain_type == BDRV_DRAIN_ALL) { /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->pause_count, ==, 2); + g_assert_cmpint(job->job.pause_count, ==, 2); } else { - g_assert_cmpint(job->pause_count, ==, 1); + g_assert_cmpint(job->job.pause_count, ==, 1); } /* XXX We don't wait until the job is actually paused. Is this okay? */ - /* g_assert_true(job->paused); */ - g_assert_false(job->busy); /* The job is paused */ + /* g_assert_true(job->job.paused); */ + g_assert_false(job->job.busy); /* The job is paused */ do_drain_end(drain_type, src); - g_assert_cmpint(job->pause_count, ==, 0); - g_assert_false(job->paused); - g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ do_drain_begin(drain_type, target); if (drain_type == BDRV_DRAIN_ALL) { /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->pause_count, ==, 2); + g_assert_cmpint(job->job.pause_count, ==, 2); } else { - g_assert_cmpint(job->pause_count, ==, 1); + g_assert_cmpint(job->job.pause_count, ==, 1); } /* XXX We don't wait until the job is actually paused. Is this okay? */ - /* g_assert_true(job->paused); */ - g_assert_false(job->busy); /* The job is paused */ + /* g_assert_true(job->job.paused); */ + g_assert_false(job->job.busy); /* The job is paused */ do_drain_end(drain_type, target); - g_assert_cmpint(job->pause_count, ==, 0); - g_assert_false(job->paused); - g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ - ret = block_job_complete_sync(job, &error_abort); + ret = job_complete_sync(&job->job, &error_abort); g_assert_cmpint(ret, ==, 0); blk_unref(blk_src); diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 5789893..fce8366 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -24,16 +24,17 @@ typedef struct { int *result; } TestBlockJob; -static void test_block_job_complete(BlockJob *job, void *opaque) +static void test_block_job_complete(Job *job, void *opaque) { - BlockDriverState *bs = blk_bs(job->blk); + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); int rc = (intptr_t)opaque; - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(job)) { rc = -ECANCELED; } - block_job_completed(job, rc); + job_completed(job, rc); bdrv_unref(bs); } @@ -44,18 +45,18 @@ static void coroutine_fn test_block_job_run(void *opaque) while (s->iterations--) { if (s->use_timer) { - block_job_sleep_ns(job, 0); + job_sleep_ns(&job->job, 0); } else { - block_job_yield(job); + job_yield(&job->job); } - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(&job->job)) { break; } } - block_job_defer_to_main_loop(job, test_block_job_complete, - (void *)(intptr_t)s->rc); + job_defer_to_main_loop(&job->job, test_block_job_complete, + (void *)(intptr_t)s->rc); } typedef struct { @@ -66,7 +67,7 @@ typedef struct { static void test_block_job_cb(void *opaque, int ret) { TestBlockJobCBData *data = opaque; - if (!ret && block_job_is_cancelled(&data->job->common)) { + if (!ret && job_is_cancelled(&data->job->common.job)) { ret = -ECANCELED; } *data->result = ret; @@ -74,8 +75,13 @@ static void test_block_job_cb(void *opaque, int ret) } static const BlockJobDriver test_block_job_driver = { - .instance_size = sizeof(TestBlockJob), - .start = test_block_job_run, + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = test_block_job_run, + }, }; /* Create a block job that completes with a given return code after a given @@ -87,7 +93,7 @@ static const BlockJobDriver test_block_job_driver = { */ static BlockJob *test_block_job_start(unsigned int iterations, bool use_timer, - int rc, int *result, BlockJobTxn *txn) + int rc, int *result, JobTxn *txn) { BlockDriverState *bs; TestBlockJob *s; @@ -102,7 +108,7 @@ static BlockJob *test_block_job_start(unsigned int iterations, snprintf(job_id, sizeof(job_id), "job%u", counter++); s = block_job_create(job_id, &test_block_job_driver, txn, bs, - 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, + 0, BLK_PERM_ALL, 0, JOB_DEFAULT, test_block_job_cb, data, &error_abort); s->iterations = iterations; s->use_timer = use_timer; @@ -116,15 +122,15 @@ static BlockJob *test_block_job_start(unsigned int iterations, static void test_single_job(int expected) { BlockJob *job; - BlockJobTxn *txn; + JobTxn *txn; int result = -EINPROGRESS; - txn = block_job_txn_new(); + txn = job_txn_new(); job = test_block_job_start(1, true, expected, &result, txn); - block_job_start(job); + job_start(&job->job); if (expected == -ECANCELED) { - block_job_cancel(job, false); + job_cancel(&job->job, false); } while (result == -EINPROGRESS) { @@ -132,7 +138,7 @@ static void test_single_job(int expected) } g_assert_cmpint(result, ==, expected); - block_job_txn_unref(txn); + job_txn_unref(txn); } static void test_single_job_success(void) @@ -154,26 +160,26 @@ static void test_pair_jobs(int expected1, int expected2) { BlockJob *job1; BlockJob *job2; - BlockJobTxn *txn; + JobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; - txn = block_job_txn_new(); + txn = job_txn_new(); job1 = test_block_job_start(1, true, expected1, &result1, txn); job2 = test_block_job_start(2, true, expected2, &result2, txn); - block_job_start(job1); - block_job_start(job2); + job_start(&job1->job); + job_start(&job2->job); /* Release our reference now to trigger as many nice * use-after-free bugs as possible. */ - block_job_txn_unref(txn); + job_txn_unref(txn); if (expected1 == -ECANCELED) { - block_job_cancel(job1, false); + job_cancel(&job1->job, false); } if (expected2 == -ECANCELED) { - block_job_cancel(job2, false); + job_cancel(&job2->job, false); } while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { @@ -216,23 +222,23 @@ static void test_pair_jobs_fail_cancel_race(void) { BlockJob *job1; BlockJob *job2; - BlockJobTxn *txn; + JobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; - txn = block_job_txn_new(); + txn = job_txn_new(); job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); job2 = test_block_job_start(2, false, 0, &result2, txn); - block_job_start(job1); - block_job_start(job2); + job_start(&job1->job); + job_start(&job2->job); - block_job_cancel(job1, false); + job_cancel(&job1->job, false); /* Now make job2 finish before the main loop kicks jobs. This simulates * the race between a pending kick and another job completing. */ - block_job_enter(job2); - block_job_enter(job2); + job_enter(&job2->job); + job_enter(&job2->job); while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); @@ -241,7 +247,7 @@ static void test_pair_jobs_fail_cancel_race(void) g_assert_cmpint(result1, ==, -ECANCELED); g_assert_cmpint(result2, ==, -ECANCELED); - block_job_txn_unref(txn); + job_txn_unref(txn); } int main(int argc, char **argv) diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 8946bfd..e408d52 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -17,7 +17,12 @@ #include "sysemu/block-backend.h" static const BlockJobDriver test_block_job_driver = { - .instance_size = sizeof(BlockJob), + .job_driver = { + .instance_size = sizeof(BlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + }, }; static void block_job_cb(void *opaque, int ret) @@ -38,9 +43,9 @@ static BlockJob *mk_job(BlockBackend *blk, const char *id, g_assert_null(errp); g_assert_nonnull(job); if (id) { - g_assert_cmpstr(job->id, ==, id); + g_assert_cmpstr(job->job.id, ==, id); } else { - g_assert_cmpstr(job->id, ==, blk_name(blk)); + g_assert_cmpstr(job->job.id, ==, blk_name(blk)); } } else { g_assert_nonnull(errp); @@ -55,7 +60,7 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id, bool should_succeed) { return mk_job(blk, id, &test_block_job_driver, - should_succeed, BLOCK_JOB_DEFAULT); + should_succeed, JOB_DEFAULT); } /* This creates a BlockBackend (optionally with a name) with a @@ -124,11 +129,11 @@ static void test_job_ids(void) job[1] = do_test_id(blk[1], "id0", false); /* But once job[0] finishes we can reuse its ID */ - block_job_early_fail(job[0]); + job_early_fail(&job[0]->job); job[1] = do_test_id(blk[1], "id0", true); /* No job ID specified, defaults to the backend name ('drive1') */ - block_job_early_fail(job[1]); + job_early_fail(&job[1]->job); job[1] = do_test_id(blk[1], NULL, true); /* Duplicate job ID */ @@ -141,9 +146,9 @@ static void test_job_ids(void) /* This one is valid */ job[2] = do_test_id(blk[2], "id_2", true); - block_job_early_fail(job[0]); - block_job_early_fail(job[1]); - block_job_early_fail(job[2]); + job_early_fail(&job[0]->job); + job_early_fail(&job[1]->job); + job_early_fail(&job[2]->job); destroy_blk(blk[0]); destroy_blk(blk[1]); @@ -158,16 +163,16 @@ typedef struct CancelJob { bool completed; } CancelJob; -static void cancel_job_completed(BlockJob *job, void *opaque) +static void cancel_job_completed(Job *job, void *opaque) { CancelJob *s = opaque; s->completed = true; - block_job_completed(job, 0); + job_completed(job, 0); } -static void cancel_job_complete(BlockJob *job, Error **errp) +static void cancel_job_complete(Job *job, Error **errp) { - CancelJob *s = container_of(job, CancelJob, common); + CancelJob *s = container_of(job, CancelJob, common.job); s->should_complete = true; } @@ -176,25 +181,30 @@ static void coroutine_fn cancel_job_start(void *opaque) CancelJob *s = opaque; while (!s->should_complete) { - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { goto defer; } - if (!s->common.ready && s->should_converge) { - block_job_event_ready(&s->common); + if (!job_is_ready(&s->common.job) && s->should_converge) { + job_transition_to_ready(&s->common.job); } - block_job_sleep_ns(&s->common, 100000); + job_sleep_ns(&s->common.job, 100000); } defer: - block_job_defer_to_main_loop(&s->common, cancel_job_completed, s); + job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); } static const BlockJobDriver test_cancel_driver = { - .instance_size = sizeof(CancelJob), - .start = cancel_job_start, - .complete = cancel_job_complete, + .job_driver = { + .instance_size = sizeof(CancelJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = cancel_job_start, + .complete = cancel_job_complete, + }, }; static CancelJob *create_common(BlockJob **pjob) @@ -205,9 +215,9 @@ static CancelJob *create_common(BlockJob **pjob) blk = create_blk(NULL); job = mk_job(blk, "Steve", &test_cancel_driver, true, - BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); - block_job_ref(job); - assert(job->status == BLOCK_JOB_STATUS_CREATED); + JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); + job_ref(&job->job); + assert(job->job.status == JOB_STATUS_CREATED); s = container_of(job, CancelJob, common); s->blk = blk; @@ -219,16 +229,15 @@ static void cancel_common(CancelJob *s) { BlockJob *job = &s->common; BlockBackend *blk = s->blk; - BlockJobStatus sts = job->status; + JobStatus sts = job->job.status; - block_job_cancel_sync(job); - if ((sts != BLOCK_JOB_STATUS_CREATED) && - (sts != BLOCK_JOB_STATUS_CONCLUDED)) { - BlockJob *dummy = job; - block_job_dismiss(&dummy, &error_abort); + job_cancel_sync(&job->job); + if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { + Job *dummy = &job->job; + job_dismiss(&dummy, &error_abort); } - assert(job->status == BLOCK_JOB_STATUS_NULL); - block_job_unref(job); + assert(job->job.status == JOB_STATUS_NULL); + job_unref(&job->job); destroy_blk(blk); } @@ -248,8 +257,8 @@ static void test_cancel_running(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); cancel_common(s); } @@ -261,12 +270,12 @@ static void test_cancel_paused(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); - block_job_user_pause(job, &error_abort); - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_PAUSED); + job_user_pause(&job->job, &error_abort); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_PAUSED); cancel_common(s); } @@ -278,12 +287,12 @@ static void test_cancel_ready(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); cancel_common(s); } @@ -295,16 +304,16 @@ static void test_cancel_standby(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); - block_job_user_pause(job, &error_abort); - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_STANDBY); + job_user_pause(&job->job, &error_abort); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_STANDBY); cancel_common(s); } @@ -316,19 +325,19 @@ static void test_cancel_pending(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); - block_job_complete(job, &error_abort); - block_job_enter(job); + job_complete(&job->job, &error_abort); + job_enter(&job->job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); } - assert(job->status == BLOCK_JOB_STATUS_PENDING); + assert(job->job.status == JOB_STATUS_PENDING); cancel_common(s); } @@ -340,22 +349,22 @@ static void test_cancel_concluded(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); - block_job_complete(job, &error_abort); - block_job_enter(job); + job_complete(&job->job, &error_abort); + job_enter(&job->job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); } - assert(job->status == BLOCK_JOB_STATUS_PENDING); + assert(job->job.status == JOB_STATUS_PENDING); - block_job_finalize(job, &error_abort); - assert(job->status == BLOCK_JOB_STATUS_CONCLUDED); + job_finalize(&job->job, &error_abort); + assert(job->job.status == JOB_STATUS_CONCLUDED); cancel_common(s); } diff --git a/trace-events b/trace-events index ed71f44..c445f54 100644 --- a/trace-events +++ b/trace-events @@ -104,6 +104,20 @@ gdbstub_err_invalid_rle(void) "got invalid RLE sequence" gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x" gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x" +# job.c +job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" +job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" +job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" + +# job-qmp.c +qmp_job_cancel(void *job) "job %p" +qmp_job_pause(void *job) "job %p" +qmp_job_resume(void *job) "job %p" +qmp_job_complete(void *job) "job %p" +qmp_job_finalize(void *job) "job %p" +qmp_job_dismiss(void *job) "job %p" + + ### Guest events, keep at bottom diff --git a/util/Makefile.objs b/util/Makefile.objs index 728c354..e1c3fed 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -47,4 +47,5 @@ util-obj-y += qht.o util-obj-y += range.o util-obj-y += stats64.o util-obj-y += systemd.o +util-obj-y += iova-tree.o util-obj-$(CONFIG_LINUX) += vfio-helpers.o diff --git a/util/iova-tree.c b/util/iova-tree.c new file mode 100644 index 0000000..2d9cebf --- /dev/null +++ b/util/iova-tree.c @@ -0,0 +1,114 @@ +/* + * IOVA tree implementation based on GTree. + * + * Copyright 2018 Red Hat, Inc. + * + * Authors: + * Peter Xu <peterx@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + */ + +#include <glib.h> +#include "qemu/iova-tree.h" + +struct IOVATree { + GTree *tree; +}; + +static int iova_tree_compare(gconstpointer a, gconstpointer b, gpointer data) +{ + const DMAMap *m1 = a, *m2 = b; + + if (m1->iova > m2->iova + m2->size) { + return 1; + } + + if (m1->iova + m1->size < m2->iova) { + return -1; + } + + /* Overlapped */ + return 0; +} + +IOVATree *iova_tree_new(void) +{ + IOVATree *iova_tree = g_new0(IOVATree, 1); + + /* We don't have values actually, no need to free */ + iova_tree->tree = g_tree_new_full(iova_tree_compare, NULL, g_free, NULL); + + return iova_tree; +} + +DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map) +{ + return g_tree_lookup(tree->tree, map); +} + +DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova) +{ + DMAMap map = { .iova = iova, .size = 0 }; + + return iova_tree_find(tree, &map); +} + +static inline void iova_tree_insert_internal(GTree *gtree, DMAMap *range) +{ + /* Key and value are sharing the same range data */ + g_tree_insert(gtree, range, range); +} + +int iova_tree_insert(IOVATree *tree, DMAMap *map) +{ + DMAMap *new; + + if (map->iova + map->size < map->iova || map->perm == IOMMU_NONE) { + return IOVA_ERR_INVALID; + } + + /* We don't allow to insert range that overlaps with existings */ + if (iova_tree_find(tree, map)) { + return IOVA_ERR_OVERLAP; + } + + new = g_new0(DMAMap, 1); + memcpy(new, map, sizeof(*new)); + iova_tree_insert_internal(tree->tree, new); + + return IOVA_OK; +} + +static gboolean iova_tree_traverse(gpointer key, gpointer value, + gpointer data) +{ + iova_tree_iterator iterator = data; + DMAMap *map = key; + + g_assert(key == value); + + return iterator(map); +} + +void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator) +{ + g_tree_foreach(tree->tree, iova_tree_traverse, iterator); +} + +int iova_tree_remove(IOVATree *tree, DMAMap *map) +{ + DMAMap *overlap; + + while ((overlap = iova_tree_find(tree, map))) { + g_tree_remove(tree->tree, overlap); + } + + return IOVA_OK; +} + +void iova_tree_destroy(IOVATree *tree) +{ + g_tree_destroy(tree->tree); + g_free(tree); +} @@ -4683,6 +4683,7 @@ int main(int argc, char **argv, char **envp) /* No more vcpu or device emulation activity beyond this point */ vm_shutdown(); + job_cancel_sync_all(); bdrv_close_all(); res_free(); |