aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.d/buildtest-template.yml3
-rw-r--r--.gitlab-ci.d/buildtest.yml11
-rw-r--r--MAINTAINERS10
-rw-r--r--backends/iommufd.c58
-rw-r--r--backends/trace-events1
-rw-r--r--block.c235
-rw-r--r--block/backup.c2
-rw-r--r--block/blklogwrites.c4
-rw-r--r--block/blkverify.c2
-rw-r--r--block/block-backend.c10
-rw-r--r--block/commit.c4
-rw-r--r--block/io.c1
-rw-r--r--block/mirror.c5
-rw-r--r--block/qcow2.c4
-rw-r--r--block/quorum.c4
-rw-r--r--block/replication.c7
-rw-r--r--block/snapshot.c28
-rw-r--r--block/stream.c10
-rw-r--r--block/vmdk.c10
-rw-r--r--blockdev.c78
-rw-r--r--blockjob.c12
-rw-r--r--clippy.toml (renamed from rust/clippy.toml)2
-rwxr-xr-xconfigure16
-rw-r--r--docs/devel/rust.rst12
-rw-r--r--hw/core/qdev-properties-system.c1
-rw-r--r--hw/i386/amd_iommu.c20
-rw-r--r--hw/i386/pc_piix.c5
-rw-r--r--hw/i386/tdvf.c6
-rw-r--r--hw/pci/pci.c206
-rw-r--r--hw/pci/pcie.c78
-rw-r--r--hw/vfio/container-base.c4
-rw-r--r--hw/vfio/container.c5
-rw-r--r--hw/vfio/cpr.c2
-rw-r--r--hw/vfio/igd.c22
-rw-r--r--hw/vfio/iommufd.c45
-rw-r--r--hw/vfio/listener.c74
-rw-r--r--hw/vfio/pci.c89
-rw-r--r--hw/vfio/vfio-cpr.h15
-rw-r--r--hw/virtio/vhost-vdpa.c116
-rw-r--r--hw/virtio/virtio-pci.c7
-rw-r--r--hw/virtio/virtio.c11
-rw-r--r--include/block/block-global-state.h19
-rw-r--r--include/block/block-io.h2
-rw-r--r--include/block/block_int-common.h32
-rw-r--r--include/block/blockjob.h2
-rw-r--r--include/hw/pci/pci.h316
-rw-r--r--include/hw/pci/pci_device.h1
-rw-r--r--include/hw/pci/pcie.h13
-rw-r--r--include/hw/pci/pcie_regs.h8
-rw-r--r--include/hw/vfio/vfio-container-base.h83
-rw-r--r--include/hw/vfio/vfio-cpr.h18
-rw-r--r--include/hw/virtio/vhost-vdpa.h22
-rw-r--r--include/system/host_iommu_device.h15
-rw-r--r--include/system/iommufd.h54
-rw-r--r--include/system/memory.h20
-rw-r--r--meson.build11
-rw-r--r--net/vhost-vdpa.c34
-rwxr-xr-xpython/scripts/vendor.py4
-rw-r--r--python/wheels/meson-1.5.0-py3-none-any.whlbin959846 -> 0 bytes
-rw-r--r--python/wheels/meson-1.8.1-py3-none-any.whlbin0 -> 1013001 bytes
-rw-r--r--pythondeps.toml2
-rw-r--r--qapi/acpi.json2
-rw-r--r--qapi/audio.json8
-rw-r--r--qapi/block-core.json184
-rw-r--r--qapi/block-export.json6
-rw-r--r--qapi/block.json2
-rw-r--r--qapi/char.json8
-rw-r--r--qapi/crypto.json21
-rw-r--r--qapi/cryptodev.json2
-rw-r--r--qapi/cxl.json2
-rw-r--r--qapi/dump.json6
-rw-r--r--qapi/introspect.json8
-rw-r--r--qapi/job.json28
-rw-r--r--qapi/machine.json14
-rw-r--r--qapi/migration.json100
-rw-r--r--qapi/misc-i386.json2
-rw-r--r--qapi/misc.json4
-rw-r--r--qapi/net.json18
-rw-r--r--qapi/qom.json2
-rw-r--r--qapi/run-state.json12
-rw-r--r--qapi/transaction.json4
-rw-r--r--qapi/uefi.json2
-rw-r--r--qapi/ui.json8
-rw-r--r--qemu-img.c2
-rw-r--r--rust/Cargo.lock8
-rw-r--r--rust/Cargo.toml2
-rw-r--r--rust/bits/Cargo.toml19
-rw-r--r--rust/bits/meson.build16
-rw-r--r--rust/bits/src/lib.rs443
-rw-r--r--rust/hw/char/pl011/Cargo.toml1
-rw-r--r--rust/hw/char/pl011/meson.build1
-rw-r--r--rust/hw/char/pl011/src/device.rs51
-rw-r--r--rust/hw/char/pl011/src/registers.rs39
-rw-r--r--rust/meson.build15
-rw-r--r--rust/qemu-api-macros/src/bits.rs229
-rw-r--r--rust/qemu-api-macros/src/lib.rs56
-rw-r--r--rust/qemu-api/meson.build34
-rw-r--r--rust/qemu-api/src/bindings.rs1
-rw-r--r--rust/qemu-api/src/cell.rs22
-rw-r--r--scripts/rust/rustc_args.py5
-rw-r--r--scripts/tracetool/backend/simple.py23
-rw-r--r--system/memory.c32
-rw-r--r--target/i386/cpu.c27
-rw-r--r--target/i386/cpu.h5
-rw-r--r--target/i386/kvm/tdx.c26
-rw-r--r--tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2bin0 -> 12800 bytes
-rw-r--r--tests/docker/dockerfiles/fedora-rust-nightly.docker2
-rw-r--r--tests/docker/dockerfiles/ubuntu2204.docker1
-rw-r--r--tests/lcitool/mappings.yml6
-rwxr-xr-xtests/lcitool/refresh3
-rwxr-xr-xtests/qemu-iotests/2402
-rw-r--r--tests/qemu-iotests/240.out4
-rwxr-xr-xtests/qemu-iotests/tests/graph-changes-while-io102
-rw-r--r--tests/qemu-iotests/tests/graph-changes-while-io.out4
-rw-r--r--tests/qtest/bios-tables-test.c20
-rw-r--r--tests/uefi-test-tools/Makefile5
-rw-r--r--tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc6
-rw-r--r--tests/uefi-test-tools/uefi-test-build.config10
-rw-r--r--tests/unit/test-bdrv-drain.c24
-rw-r--r--tests/unit/test-bdrv-graph-mod.c10
120 files changed, 2806 insertions, 784 deletions
diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml
index 118371e..fea4e8d 100644
--- a/.gitlab-ci.d/buildtest-template.yml
+++ b/.gitlab-ci.d/buildtest-template.yml
@@ -76,7 +76,8 @@
fi
- section_end buildenv
- section_start test "Running tests"
- - $MAKE NINJA=":" $MAKE_CHECK_ARGS
+ # doctests need all the compilation artifacts
+ - $MAKE NINJA=":" MTESTARGS="--no-suite doc" $MAKE_CHECK_ARGS
- section_end test
.native_test_job_template:
diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml
index ca1a9c6..d888a60 100644
--- a/.gitlab-ci.d/buildtest.yml
+++ b/.gitlab-ci.d/buildtest.yml
@@ -41,7 +41,7 @@ build-system-ubuntu:
IMAGE: ubuntu2204
CONFIGURE_ARGS: --enable-docs --enable-rust
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
- MAKE_CHECK_ARGS: check-build
+ MAKE_CHECK_ARGS: check-build check-doc
check-system-ubuntu:
extends: .native_test_job_template
@@ -115,7 +115,7 @@ build-system-fedora:
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust
TARGETS: microblaze-softmmu mips-softmmu
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
- MAKE_CHECK_ARGS: check-build
+ MAKE_CHECK_ARGS: check-build check-doc
build-system-fedora-rust-nightly:
extends:
@@ -127,12 +127,7 @@ build-system-fedora-rust-nightly:
IMAGE: fedora-rust-nightly
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
TARGETS: aarch64-softmmu
- MAKE_CHECK_ARGS: check-build
- after_script:
- - source scripts/ci/gitlab-ci-section
- - section_start test "Running Rust doctests"
- - cd build
- - pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} test --doc -p qemu_api
+ MAKE_CHECK_ARGS: check-build check-doc
allow_failure: true
diff --git a/MAINTAINERS b/MAINTAINERS
index 16af379..aa67630 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3032,6 +3032,16 @@ F: include/qemu/co-shared-resource.h
T: git https://gitlab.com/jsnow/qemu.git jobs
T: git https://gitlab.com/vsementsov/qemu.git block
+CheckPoint and Restart (CPR)
+R: Steve Sistare <steven.sistare@oracle.com>
+S: Supported
+F: hw/vfio/cpr*
+F: include/hw/vfio/vfio-cpr.h
+F: include/migration/cpr.h
+F: migration/cpr*
+F: tests/qtest/migration/cpr*
+F: docs/devel/migration/CPR.rst
+
Compute Express Link
M: Jonathan Cameron <jonathan.cameron@huawei.com>
R: Fan Ni <fan.ni@samsung.com>
diff --git a/backends/iommufd.c b/backends/iommufd.c
index b73f75c..c2c47ab 100644
--- a/backends/iommufd.c
+++ b/backends/iommufd.c
@@ -311,6 +311,62 @@ bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid,
return true;
}
+bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id,
+ uint32_t data_type, uint32_t entry_len,
+ uint32_t *entry_num, void *data,
+ Error **errp)
+{
+ int ret, fd = be->fd;
+ uint32_t total_entries = *entry_num;
+ struct iommu_hwpt_invalidate cache = {
+ .size = sizeof(cache),
+ .hwpt_id = id,
+ .data_type = data_type,
+ .entry_len = entry_len,
+ .entry_num = total_entries,
+ .data_uptr = (uintptr_t)data,
+ };
+
+ ret = ioctl(fd, IOMMU_HWPT_INVALIDATE, &cache);
+ trace_iommufd_backend_invalidate_cache(fd, id, data_type, entry_len,
+ total_entries, cache.entry_num,
+ (uintptr_t)data, ret ? errno : 0);
+ *entry_num = cache.entry_num;
+
+ if (ret) {
+ error_setg_errno(errp, errno, "IOMMU_HWPT_INVALIDATE failed:"
+ " total %d entries, processed %d entries",
+ total_entries, cache.entry_num);
+ } else if (total_entries != cache.entry_num) {
+ error_setg(errp, "IOMMU_HWPT_INVALIDATE succeed but with unprocessed"
+ " entries: total %d entries, processed %d entries."
+ " Kernel BUG?!", total_entries, cache.entry_num);
+ return false;
+ }
+
+ return !ret;
+}
+
+bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev,
+ uint32_t hwpt_id, Error **errp)
+{
+ HostIOMMUDeviceIOMMUFDClass *idevc =
+ HOST_IOMMU_DEVICE_IOMMUFD_GET_CLASS(idev);
+
+ g_assert(idevc->attach_hwpt);
+ return idevc->attach_hwpt(idev, hwpt_id, errp);
+}
+
+bool host_iommu_device_iommufd_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev,
+ Error **errp)
+{
+ HostIOMMUDeviceIOMMUFDClass *idevc =
+ HOST_IOMMU_DEVICE_IOMMUFD_GET_CLASS(idev);
+
+ g_assert(idevc->detach_hwpt);
+ return idevc->detach_hwpt(idev, errp);
+}
+
static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp)
{
HostIOMMUDeviceCaps *caps = &hiod->caps;
@@ -349,6 +405,8 @@ static const TypeInfo types[] = {
}, {
.name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD,
.parent = TYPE_HOST_IOMMU_DEVICE,
+ .instance_size = sizeof(HostIOMMUDeviceIOMMUFD),
+ .class_size = sizeof(HostIOMMUDeviceIOMMUFDClass),
.class_init = hiod_iommufd_class_init,
.abstract = true,
}
diff --git a/backends/trace-events b/backends/trace-events
index 40811a3..7278214 100644
--- a/backends/trace-events
+++ b/backends/trace-events
@@ -18,3 +18,4 @@ iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_
iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)"
iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)"
iommufd_backend_get_dirty_bitmap(int iommufd, uint32_t hwpt_id, uint64_t iova, uint64_t size, uint64_t page_size, int ret) " iommufd=%d hwpt=%u iova=0x%"PRIx64" size=0x%"PRIx64" page_size=0x%"PRIx64" (%d)"
+iommufd_backend_invalidate_cache(int iommufd, uint32_t id, uint32_t data_type, uint32_t entry_len, uint32_t entry_num, uint32_t done_num, uint64_t data_ptr, int ret) " iommufd=%d id=%u data_type=%u entry_len=%u entry_num=%u done_num=%u data_ptr=0x%"PRIx64" (%d)"
diff --git a/block.c b/block.c
index f222e1a..bfd4340 100644
--- a/block.c
+++ b/block.c
@@ -106,9 +106,9 @@ static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
static bool bdrv_backing_overridden(BlockDriverState *bs);
-static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
- GHashTable *visited, Transaction *tran,
- Error **errp);
+static bool GRAPH_RDLOCK
+bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
+ GHashTable *visited, Transaction *tran, Error **errp);
/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;
@@ -1226,9 +1226,10 @@ static int bdrv_child_cb_inactivate(BdrvChild *child)
return 0;
}
-static bool bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx,
- GHashTable *visited, Transaction *tran,
- Error **errp)
+static bool GRAPH_RDLOCK
+bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp)
{
BlockDriverState *bs = child->opaque;
return bdrv_change_aio_context(bs, ctx, visited, tran, errp);
@@ -1720,12 +1721,14 @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name,
open_failed:
bs->drv = NULL;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
if (bs->file != NULL) {
bdrv_unref_child(bs, bs->file);
assert(!bs->file);
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
g_free(bs->opaque);
bs->opaque = NULL;
@@ -3027,7 +3030,8 @@ static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque)
bdrv_replace_child_noperm(s->child, NULL);
if (bdrv_get_aio_context(bs) != s->old_child_ctx) {
- bdrv_try_change_aio_context(bs, s->old_child_ctx, NULL, &error_abort);
+ bdrv_try_change_aio_context_locked(bs, s->old_child_ctx, NULL,
+ &error_abort);
}
if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) {
@@ -3069,6 +3073,9 @@ static TransactionActionDrv bdrv_attach_child_common_drv = {
*
* Both @parent_bs and @child_bs can move to a different AioContext in this
* function.
+ *
+ * All block nodes must be drained before this function is called until after
+ * the transaction is finalized.
*/
static BdrvChild * GRAPH_WRLOCK
bdrv_attach_child_common(BlockDriverState *child_bs,
@@ -3112,8 +3119,8 @@ bdrv_attach_child_common(BlockDriverState *child_bs,
parent_ctx = bdrv_child_get_parent_aio_context(new_child);
if (child_ctx != parent_ctx) {
Error *local_err = NULL;
- int ret = bdrv_try_change_aio_context(child_bs, parent_ctx, NULL,
- &local_err);
+ int ret = bdrv_try_change_aio_context_locked(child_bs, parent_ctx, NULL,
+ &local_err);
if (ret < 0 && child_class->change_aio_ctx) {
Transaction *aio_ctx_tran = tran_new();
@@ -3179,6 +3186,9 @@ bdrv_attach_child_common(BlockDriverState *child_bs,
*
* After calling this function, the transaction @tran may only be completed
* while holding a writer lock for the graph.
+ *
+ * All block nodes must be drained before this function is called until after
+ * the transaction is finalized.
*/
static BdrvChild * GRAPH_WRLOCK
bdrv_attach_child_noperm(BlockDriverState *parent_bs,
@@ -3220,6 +3230,8 @@ bdrv_attach_child_noperm(BlockDriverState *parent_bs,
*
* On failure NULL is returned, errp is set and the reference to
* child_bs is also dropped.
+ *
+ * All block nodes must be drained.
*/
BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
const char *child_name,
@@ -3259,6 +3271,8 @@ out:
*
* On failure NULL is returned, errp is set and the reference to
* child_bs is also dropped.
+ *
+ * All block nodes must be drained.
*/
BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
BlockDriverState *child_bs,
@@ -3293,7 +3307,11 @@ out:
return ret < 0 ? NULL : child;
}
-/* Callers must ensure that child->frozen is false. */
+/*
+ * Callers must ensure that child->frozen is false.
+ *
+ * All block nodes must be drained.
+ */
void bdrv_root_unref_child(BdrvChild *child)
{
BlockDriverState *child_bs = child->bs;
@@ -3314,8 +3332,8 @@ void bdrv_root_unref_child(BdrvChild *child)
* When the parent requiring a non-default AioContext is removed, the
* node moves back to the main AioContext
*/
- bdrv_try_change_aio_context(child_bs, qemu_get_aio_context(), NULL,
- NULL);
+ bdrv_try_change_aio_context_locked(child_bs, qemu_get_aio_context(),
+ NULL, NULL);
}
bdrv_schedule_unref(child_bs);
@@ -3388,7 +3406,11 @@ bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child,
}
}
-/* Callers must ensure that child->frozen is false. */
+/*
+ * Callers must ensure that child->frozen is false.
+ *
+ * All block nodes must be drained.
+ */
void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
{
GLOBAL_STATE_CODE();
@@ -3453,6 +3475,9 @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs)
*
* After calling this function, the transaction @tran may only be completed
* while holding a writer lock for the graph.
+ *
+ * All block nodes must be drained before this function is called until after
+ * the transaction is finalized.
*/
static int GRAPH_WRLOCK
bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs,
@@ -3545,8 +3570,7 @@ out:
* Both @bs and @backing_hd can move to a different AioContext in this
* function.
*
- * If a backing child is already present (i.e. we're detaching a node), that
- * child node must be drained.
+ * All block nodes must be drained.
*/
int bdrv_set_backing_hd_drained(BlockDriverState *bs,
BlockDriverState *backing_hd,
@@ -3575,21 +3599,14 @@ out:
int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
Error **errp)
{
- BlockDriverState *drain_bs;
int ret;
GLOBAL_STATE_CODE();
- bdrv_graph_rdlock_main_loop();
- drain_bs = bs->backing ? bs->backing->bs : bs;
- bdrv_graph_rdunlock_main_loop();
-
- bdrv_ref(drain_bs);
- bdrv_drained_begin(drain_bs);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp);
bdrv_graph_wrunlock();
- bdrv_drained_end(drain_bs);
- bdrv_unref(drain_bs);
+ bdrv_drain_all_end();
return ret;
}
@@ -3780,10 +3797,12 @@ static BdrvChild *bdrv_open_child_common(const char *filename,
return NULL;
}
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role,
errp);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
return child;
}
@@ -4358,9 +4377,7 @@ bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child)
* returns a pointer to bs_queue, which is either the newly allocated
* bs_queue, or the existing bs_queue being used.
*
- * bs is drained here and undrained by bdrv_reopen_queue_free().
- *
- * To be called with bs->aio_context locked.
+ * bs must be drained.
*/
static BlockReopenQueue * GRAPH_RDLOCK
bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs,
@@ -4379,12 +4396,7 @@ bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs,
GLOBAL_STATE_CODE();
- /*
- * Strictly speaking, draining is illegal under GRAPH_RDLOCK. We know that
- * we've been called with bdrv_graph_rdlock_main_loop(), though, so it's ok
- * in practice.
- */
- bdrv_drained_begin(bs);
+ assert(bs->quiesce_counter > 0);
if (bs_queue == NULL) {
bs_queue = g_new0(BlockReopenQueue, 1);
@@ -4519,12 +4531,17 @@ bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs,
return bs_queue;
}
-/* To be called with bs->aio_context locked */
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
BlockDriverState *bs,
QDict *options, bool keep_old_opts)
{
GLOBAL_STATE_CODE();
+
+ if (bs_queue == NULL) {
+ /* Paired with bdrv_drain_all_end() in bdrv_reopen_queue_free(). */
+ bdrv_drain_all_begin();
+ }
+
GRAPH_RDLOCK_GUARD_MAINLOOP();
return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, 0, false,
@@ -4537,12 +4554,14 @@ void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue)
if (bs_queue) {
BlockReopenQueueEntry *bs_entry, *next;
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
- bdrv_drained_end(bs_entry->state.bs);
qobject_unref(bs_entry->state.explicit_options);
qobject_unref(bs_entry->state.options);
g_free(bs_entry);
}
g_free(bs_queue);
+
+ /* Paired with bdrv_drain_all_begin() in bdrv_reopen_queue(). */
+ bdrv_drain_all_end();
}
}
@@ -4709,6 +4728,9 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
* Return 0 on success, otherwise return < 0 and set @errp.
*
* @reopen_state->bs can move to a different AioContext in this function.
+ *
+ * All block nodes must be drained before this function is called until after
+ * the transaction is finalized.
*/
static int GRAPH_UNLOCKED
bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state,
@@ -4802,7 +4824,7 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state,
if (old_child_bs) {
bdrv_ref(old_child_bs);
- bdrv_drained_begin(old_child_bs);
+ assert(old_child_bs->quiesce_counter > 0);
}
bdrv_graph_rdunlock_main_loop();
@@ -4814,7 +4836,6 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state,
bdrv_graph_wrunlock();
if (old_child_bs) {
- bdrv_drained_end(old_child_bs);
bdrv_unref(old_child_bs);
}
@@ -4843,6 +4864,9 @@ out_rdlock:
*
* After calling this function, the transaction @change_child_tran may only be
* completed while holding a writer lock for the graph.
+ *
+ * All block nodes must be drained before this function is called until after
+ * the transaction is finalized.
*/
static int GRAPH_UNLOCKED
bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
@@ -5156,6 +5180,7 @@ static void bdrv_close(BlockDriverState *bs)
bs->drv = NULL;
}
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
bdrv_unref_child(bs, child);
@@ -5164,6 +5189,7 @@ static void bdrv_close(BlockDriverState *bs)
assert(!bs->backing);
assert(!bs->file);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
g_free(bs->opaque);
bs->opaque = NULL;
@@ -5489,9 +5515,7 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
assert(!bs_new->backing);
bdrv_graph_rdunlock_main_loop();
- bdrv_drained_begin(bs_top);
- bdrv_drained_begin(bs_new);
-
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
child = bdrv_attach_child_noperm(bs_new, bs_top, "backing",
@@ -5513,9 +5537,7 @@ out:
bdrv_refresh_limits(bs_top, NULL, NULL);
bdrv_graph_wrunlock();
-
- bdrv_drained_end(bs_top);
- bdrv_drained_end(bs_new);
+ bdrv_drain_all_end();
return ret;
}
@@ -6989,6 +7011,8 @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level)
GLOBAL_STATE_CODE();
+ assert(bs->quiesce_counter > 0);
+
if (!bs->drv) {
return -ENOMEDIUM;
}
@@ -7032,9 +7056,7 @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level)
return -EPERM;
}
- bdrv_drained_begin(bs);
bs->open_flags |= BDRV_O_INACTIVE;
- bdrv_drained_end(bs);
/*
* Update permissions, they may differ for inactive nodes.
@@ -7059,20 +7081,26 @@ int bdrv_inactivate(BlockDriverState *bs, Error **errp)
int ret;
GLOBAL_STATE_CODE();
- GRAPH_RDLOCK_GUARD_MAINLOOP();
+
+ bdrv_drain_all_begin();
+ bdrv_graph_rdlock_main_loop();
if (bdrv_has_bds_parent(bs, true)) {
error_setg(errp, "Node has active parent node");
- return -EPERM;
+ ret = -EPERM;
+ goto out;
}
ret = bdrv_inactivate_recurse(bs, true);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to inactivate node");
- return ret;
+ goto out;
}
- return 0;
+out:
+ bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_end();
+ return ret;
}
int bdrv_inactivate_all(void)
@@ -7082,7 +7110,9 @@ int bdrv_inactivate_all(void)
int ret = 0;
GLOBAL_STATE_CODE();
- GRAPH_RDLOCK_GUARD_MAINLOOP();
+
+ bdrv_drain_all_begin();
+ bdrv_graph_rdlock_main_loop();
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
/* Nodes with BDS parents are covered by recursion from the last
@@ -7098,6 +7128,9 @@ int bdrv_inactivate_all(void)
}
}
+ bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_end();
+
return ret;
}
@@ -7278,10 +7311,6 @@ bool bdrv_op_blocker_is_empty(BlockDriverState *bs)
return true;
}
-/*
- * Must not be called while holding the lock of an AioContext other than the
- * current one.
- */
void bdrv_img_create(const char *filename, const char *fmt,
const char *base_filename, const char *base_fmt,
char *options, uint64_t img_size, int flags, bool quiet,
@@ -7568,10 +7597,21 @@ typedef struct BdrvStateSetAioContext {
BlockDriverState *bs;
} BdrvStateSetAioContext;
-static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx,
- GHashTable *visited,
- Transaction *tran,
- Error **errp)
+/*
+ * Changes the AioContext of @child to @ctx and recursively for the associated
+ * block nodes and all their children and parents. Returns true if the change is
+ * possible and the transaction @tran can be continued. Returns false and sets
+ * @errp if not and the transaction must be aborted.
+ *
+ * @visited will accumulate all visited BdrvChild objects. The caller is
+ * responsible for freeing the list afterwards.
+ *
+ * Must be called with the affected block nodes drained.
+ */
+static bool GRAPH_RDLOCK
+bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp)
{
GLOBAL_STATE_CODE();
if (g_hash_table_contains(visited, c)) {
@@ -7596,6 +7636,17 @@ static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx,
return true;
}
+/*
+ * Changes the AioContext of @c->bs to @ctx and recursively for all its children
+ * and parents. Returns true if the change is possible and the transaction @tran
+ * can be continued. Returns false and sets @errp if not and the transaction
+ * must be aborted.
+ *
+ * @visited will accumulate all visited BdrvChild objects. The caller is
+ * responsible for freeing the list afterwards.
+ *
+ * Must be called with the affected block nodes drained.
+ */
bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx,
GHashTable *visited, Transaction *tran,
Error **errp)
@@ -7611,10 +7662,6 @@ bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx,
static void bdrv_set_aio_context_clean(void *opaque)
{
BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque;
- BlockDriverState *bs = (BlockDriverState *) state->bs;
-
- /* Paired with bdrv_drained_begin in bdrv_change_aio_context() */
- bdrv_drained_end(bs);
g_free(state);
}
@@ -7642,10 +7689,12 @@ static TransactionActionDrv set_aio_context = {
*
* @visited will accumulate all visited BdrvChild objects. The caller is
* responsible for freeing the list afterwards.
+ *
+ * @bs must be drained.
*/
-static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
- GHashTable *visited, Transaction *tran,
- Error **errp)
+static bool GRAPH_RDLOCK
+bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
+ GHashTable *visited, Transaction *tran, Error **errp)
{
BdrvChild *c;
BdrvStateSetAioContext *state;
@@ -7656,21 +7705,17 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
return true;
}
- bdrv_graph_rdlock_main_loop();
QLIST_FOREACH(c, &bs->parents, next_parent) {
if (!bdrv_parent_change_aio_context(c, ctx, visited, tran, errp)) {
- bdrv_graph_rdunlock_main_loop();
return false;
}
}
QLIST_FOREACH(c, &bs->children, next) {
if (!bdrv_child_change_aio_context(c, ctx, visited, tran, errp)) {
- bdrv_graph_rdunlock_main_loop();
return false;
}
}
- bdrv_graph_rdunlock_main_loop();
state = g_new(BdrvStateSetAioContext, 1);
*state = (BdrvStateSetAioContext) {
@@ -7678,8 +7723,7 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
.bs = bs,
};
- /* Paired with bdrv_drained_end in bdrv_set_aio_context_clean() */
- bdrv_drained_begin(bs);
+ assert(bs->quiesce_counter > 0);
tran_add(tran, &set_aio_context, state);
@@ -7692,9 +7736,13 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
*
* If ignore_child is not NULL, that child (and its subgraph) will not
* be touched.
+ *
+ * Called with the graph lock held.
+ *
+ * Called while all bs are drained.
*/
-int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
- BdrvChild *ignore_child, Error **errp)
+int bdrv_try_change_aio_context_locked(BlockDriverState *bs, AioContext *ctx,
+ BdrvChild *ignore_child, Error **errp)
{
Transaction *tran;
GHashTable *visited;
@@ -7703,9 +7751,9 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
/*
* Recursion phase: go through all nodes of the graph.
- * Take care of checking that all nodes support changing AioContext
- * and drain them, building a linear list of callbacks to run if everything
- * is successful (the transaction itself).
+ * Take care of checking that all nodes support changing AioContext,
+ * building a linear list of callbacks to run if everything is successful
+ * (the transaction itself).
*/
tran = tran_new();
visited = g_hash_table_new(NULL, NULL);
@@ -7732,6 +7780,29 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
return 0;
}
+/*
+ * Change bs's and recursively all of its parents' and children's AioContext
+ * to the given new context, returning an error if that isn't possible.
+ *
+ * If ignore_child is not NULL, that child (and its subgraph) will not
+ * be touched.
+ */
+int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
+ BdrvChild *ignore_child, Error **errp)
+{
+ int ret;
+
+ GLOBAL_STATE_CODE();
+
+ bdrv_drain_all_begin();
+ bdrv_graph_rdlock_main_loop();
+ ret = bdrv_try_change_aio_context_locked(bs, ctx, ignore_child, errp);
+ bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_end();
+
+ return ret;
+}
+
void bdrv_add_aio_context_notifier(BlockDriverState *bs,
void (*attached_aio_context)(AioContext *new_context, void *opaque),
void (*detach_aio_context)(void *opaque), void *opaque)
@@ -8159,8 +8230,10 @@ char *bdrv_dirname(BlockDriverState *bs, Error **errp)
}
/*
- * Hot add/remove a BDS's child. So the user can take a child offline when
- * it is broken and take a new child online
+ * Hot add a BDS's child. Used in combination with bdrv_del_child, so the user
+ * can take a child offline when it is broken and take a new child online.
+ *
+ * All block nodes must be drained.
*/
void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs,
Error **errp)
@@ -8200,6 +8273,12 @@ void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs,
parent_bs->drv->bdrv_add_child(parent_bs, child_bs, errp);
}
+/*
+ * Hot remove a BDS's child. Used in combination with bdrv_add_child, so the
+ * user can take a child offline when it is broken and take a new child online.
+ *
+ * All block nodes must be drained.
+ */
void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp)
{
BdrvChild *tmp;
diff --git a/block/backup.c b/block/backup.c
index 0151e84..909027c 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -498,10 +498,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
block_copy_set_speed(bcs, speed);
/* Required permissions are taken by copy-before-write filter target */
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
return &job->common;
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
index b0f78c4..70ac76f 100644
--- a/block/blklogwrites.c
+++ b/block/blklogwrites.c
@@ -281,9 +281,11 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
ret = 0;
fail_log:
if (ret < 0) {
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, s->log_file);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
s->log_file = NULL;
qemu_mutex_destroy(&s->mutex);
}
@@ -296,10 +298,12 @@ static void blk_log_writes_close(BlockDriverState *bs)
{
BDRVBlkLogWritesState *s = bs->opaque;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, s->log_file);
s->log_file = NULL;
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
qemu_mutex_destroy(&s->mutex);
}
diff --git a/block/blkverify.c b/block/blkverify.c
index db79a36..3a71f74 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -151,10 +151,12 @@ static void blkverify_close(BlockDriverState *bs)
{
BDRVBlkverifyState *s = bs->opaque;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, s->test_file);
s->test_file = NULL;
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
}
static int64_t coroutine_fn GRAPH_RDLOCK
diff --git a/block/block-backend.c b/block/block-backend.c
index a402db1..68209bb 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -136,9 +136,9 @@ static void blk_root_drained_end(BdrvChild *child);
static void blk_root_change_media(BdrvChild *child, bool load);
static void blk_root_resize(BdrvChild *child);
-static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx,
- GHashTable *visited, Transaction *tran,
- Error **errp);
+static bool GRAPH_RDLOCK
+blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, GHashTable *visited,
+ Transaction *tran, Error **errp);
static char *blk_root_get_parent_desc(BdrvChild *child)
{
@@ -889,9 +889,11 @@ void blk_remove_bs(BlockBackend *blk)
root = blk->root;
blk->root = NULL;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_root_unref_child(root);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
}
/*
@@ -904,6 +906,7 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
GLOBAL_STATE_CODE();
bdrv_ref(bs);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
if ((bs->open_flags & BDRV_O_INACTIVE) && blk_can_inactivate(blk)) {
@@ -919,6 +922,7 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
perm, shared_perm, blk, errp);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
if (blk->root == NULL) {
return -EPERM;
}
diff --git a/block/commit.c b/block/commit.c
index 7cc8c0f..6c4b736 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -392,6 +392,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
* this is the responsibility of the interface (i.e. whoever calls
* commit_start()).
*/
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
s->base_overlay = bdrv_find_overlay(top, base);
assert(s->base_overlay);
@@ -424,18 +425,21 @@ void commit_start(const char *job_id, BlockDriverState *bs,
iter_shared_perms, errp);
if (ret < 0) {
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
goto fail;
}
}
if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) {
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
goto fail;
}
s->chain_frozen = true;
ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
if (ret < 0) {
goto fail;
diff --git a/block/io.c b/block/io.c
index 4fd7768..ac5c717 100644
--- a/block/io.c
+++ b/block/io.c
@@ -413,7 +413,6 @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
/* At this point, we should be always running in the main loop. */
GLOBAL_STATE_CODE();
assert(bs->quiesce_counter > 0);
- GLOBAL_STATE_CODE();
/* Re-enable things in child-to-parent order */
old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter);
diff --git a/block/mirror.c b/block/mirror.c
index c2c5099..6e8caf4 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -2014,6 +2014,7 @@ static BlockJob *mirror_start_job(
*/
bdrv_disable_dirty_bitmap(s->dirty_bitmap);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
ret = block_job_add_bdrv(&s->common, "source", bs, 0,
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE |
@@ -2021,6 +2022,7 @@ static BlockJob *mirror_start_job(
errp);
if (ret < 0) {
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
goto fail;
}
@@ -2066,16 +2068,19 @@ static BlockJob *mirror_start_job(
iter_shared_perms, errp);
if (ret < 0) {
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
goto fail;
}
}
if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) {
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
goto fail;
}
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
QTAILQ_INIT(&s->ops_in_flight);
diff --git a/block/qcow2.c b/block/qcow2.c
index 66fba89..45451a7 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1895,7 +1895,9 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
g_free(s->image_data_file);
if (open_data_file && has_data_file(bs)) {
bdrv_graph_co_rdunlock();
+ bdrv_drain_all_begin();
bdrv_co_unref_child(bs, s->data_file);
+ bdrv_drain_all_end();
bdrv_graph_co_rdlock();
s->data_file = NULL;
}
@@ -2821,9 +2823,11 @@ qcow2_do_close(BlockDriverState *bs, bool close_data_file)
if (close_data_file && has_data_file(bs)) {
GLOBAL_STATE_CODE();
bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, s->data_file);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
s->data_file = NULL;
bdrv_graph_rdlock_main_loop();
}
diff --git a/block/quorum.c b/block/quorum.c
index ed8ce80..cc3bc5f 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -1037,6 +1037,7 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
close_exit:
/* cleanup on error */
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
for (i = 0; i < s->num_children; i++) {
if (!opened[i]) {
@@ -1045,6 +1046,7 @@ close_exit:
bdrv_unref_child(bs, s->children[i]);
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
g_free(s->children);
g_free(opened);
exit:
@@ -1057,11 +1059,13 @@ static void quorum_close(BlockDriverState *bs)
BDRVQuorumState *s = bs->opaque;
int i;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
for (i = 0; i < s->num_children; i++) {
bdrv_unref_child(bs, s->children[i]);
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
g_free(s->children);
}
diff --git a/block/replication.c b/block/replication.c
index 07f274d..0879718 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -540,6 +540,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
return;
}
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_ref(hidden_disk->bs);
@@ -549,6 +550,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
if (local_err) {
error_propagate(errp, local_err);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
return;
}
@@ -559,6 +561,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
if (local_err) {
error_propagate(errp, local_err);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
return;
}
@@ -571,12 +574,14 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
!check_top_bs(top_bs, bs)) {
error_setg(errp, "No top_bs or it is invalid");
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
reopen_backing_file(bs, false, NULL);
return;
}
bdrv_op_block_all(top_bs, s->blocker);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
s->backup_job = backup_job_create(
NULL, s->secondary_disk->bs, s->hidden_disk->bs,
@@ -651,12 +656,14 @@ static void replication_done(void *opaque, int ret)
if (ret == 0) {
s->stage = BLOCK_REPLICATION_DONE;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, s->secondary_disk);
s->secondary_disk = NULL;
bdrv_unref_child(bs, s->hidden_disk);
s->hidden_disk = NULL;
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
s->error = 0;
} else {
diff --git a/block/snapshot.c b/block/snapshot.c
index 22567f1..28c9c43 100644
--- a/block/snapshot.c
+++ b/block/snapshot.c
@@ -291,9 +291,11 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
}
/* .bdrv_open() will re-attach it */
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, fallback);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp);
memset(bs->opaque, 0, drv->instance_size);
@@ -327,7 +329,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
/**
* Delete an internal snapshot by @snapshot_id and @name.
- * @bs: block device used in the operation
+ * @bs: block device used in the operation, must be drained
* @snapshot_id: unique snapshot ID, or NULL
* @name: snapshot name, or NULL
* @errp: location to store error
@@ -358,6 +360,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
GLOBAL_STATE_CODE();
+ assert(bs->quiesce_counter > 0);
+
if (!drv) {
error_setg(errp, "Device '%s' has no medium",
bdrv_get_device_name(bs));
@@ -368,9 +372,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
return -EINVAL;
}
- /* drain all pending i/o before deleting snapshot */
- bdrv_drained_begin(bs);
-
if (drv->bdrv_snapshot_delete) {
ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
} else if (fallback_bs) {
@@ -382,7 +383,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
ret = -ENOTSUP;
}
- bdrv_drained_end(bs);
return ret;
}
@@ -571,19 +571,22 @@ int bdrv_all_delete_snapshot(const char *name,
ERRP_GUARD();
g_autoptr(GList) bdrvs = NULL;
GList *iterbdrvs;
+ int ret = 0;
GLOBAL_STATE_CODE();
- GRAPH_RDLOCK_GUARD_MAINLOOP();
- if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) {
- return -1;
+ bdrv_drain_all_begin();
+ bdrv_graph_rdlock_main_loop();
+
+ ret = bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp);
+ if (ret < 0) {
+ goto out;
}
iterbdrvs = bdrvs;
while (iterbdrvs) {
BlockDriverState *bs = iterbdrvs->data;
QEMUSnapshotInfo sn1, *snapshot = &sn1;
- int ret = 0;
if ((devices || bdrv_all_snapshots_includes_bs(bs)) &&
bdrv_snapshot_find(bs, snapshot, name) >= 0)
@@ -594,13 +597,16 @@ int bdrv_all_delete_snapshot(const char *name,
if (ret < 0) {
error_prepend(errp, "Could not delete snapshot '%s' on '%s': ",
name, bdrv_get_device_or_node_name(bs));
- return -1;
+ goto out;
}
iterbdrvs = iterbdrvs->next;
}
- return 0;
+out:
+ bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_end();
+ return ret;
}
diff --git a/block/stream.c b/block/stream.c
index 999d9e5..f5441f2 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -80,11 +80,10 @@ static int stream_prepare(Job *job)
* may end up working with the wrong base node (or it might even have gone
* away by the time we want to use it).
*/
- bdrv_drained_begin(unfiltered_bs);
if (unfiltered_bs_cow) {
bdrv_ref(unfiltered_bs_cow);
- bdrv_drained_begin(unfiltered_bs_cow);
}
+ bdrv_drain_all_begin();
bdrv_graph_rdlock_main_loop();
base = bdrv_filter_or_cow_bs(s->above_base);
@@ -123,11 +122,10 @@ static int stream_prepare(Job *job)
}
out:
+ bdrv_drain_all_end();
if (unfiltered_bs_cow) {
- bdrv_drained_end(unfiltered_bs_cow);
bdrv_unref(unfiltered_bs_cow);
}
- bdrv_drained_end(unfiltered_bs);
return ret;
}
@@ -373,10 +371,12 @@ void stream_start(const char *job_id, BlockDriverState *bs,
* already have our own plans. Also don't allow resize as the image size is
* queried only at the job start and then cached.
*/
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
if (block_job_add_bdrv(&s->common, "active node", bs, 0,
basic_flags | BLK_PERM_WRITE, errp)) {
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
goto fail;
}
@@ -397,10 +397,12 @@ void stream_start(const char *job_id, BlockDriverState *bs,
basic_flags, errp);
if (ret < 0) {
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
goto fail;
}
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
s->base_overlay = base_overlay;
s->above_base = above_base;
diff --git a/block/vmdk.c b/block/vmdk.c
index 9c7ab03..89a7250 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -271,6 +271,7 @@ static void vmdk_free_extents(BlockDriverState *bs)
BDRVVmdkState *s = bs->opaque;
VmdkExtent *e;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
for (i = 0; i < s->num_extents; i++) {
e = &s->extents[i];
@@ -283,6 +284,7 @@ static void vmdk_free_extents(BlockDriverState *bs)
}
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
g_free(s->extents);
}
@@ -1247,9 +1249,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
0, 0, 0, 0, 0, &extent, errp);
if (ret < 0) {
bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, extent_file);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
bdrv_graph_rdlock_main_loop();
goto out;
}
@@ -1266,9 +1270,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
g_free(buf);
if (ret) {
bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, extent_file);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
bdrv_graph_rdlock_main_loop();
goto out;
}
@@ -1277,9 +1283,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp);
if (ret) {
bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, extent_file);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
bdrv_graph_rdlock_main_loop();
goto out;
}
@@ -1287,9 +1295,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
} else {
error_setg(errp, "Unsupported extent type '%s'", type);
bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(bs, extent_file);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
bdrv_graph_rdlock_main_loop();
ret = -ENOTSUP;
goto out;
diff --git a/blockdev.c b/blockdev.c
index 21443b4..2e7fda6 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1132,39 +1132,41 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
int ret;
GLOBAL_STATE_CODE();
- GRAPH_RDLOCK_GUARD_MAINLOOP();
+
+ bdrv_drain_all_begin();
+ bdrv_graph_rdlock_main_loop();
bs = qmp_get_root_bs(device, errp);
if (!bs) {
- return NULL;
+ goto error;
}
if (!id && !name) {
error_setg(errp, "Name or id must be provided");
- return NULL;
+ goto error;
}
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) {
- return NULL;
+ goto error;
}
ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return NULL;
+ goto error;
}
if (!ret) {
error_setg(errp,
"Snapshot with id '%s' and name '%s' does not exist on "
"device '%s'",
STR_OR_NULL(id), STR_OR_NULL(name), device);
- return NULL;
+ goto error;
}
bdrv_snapshot_delete(bs, id, name, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return NULL;
+ goto error;
}
info = g_new0(SnapshotInfo, 1);
@@ -1180,6 +1182,9 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
info->has_icount = true;
}
+error:
+ bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_end();
return info;
}
@@ -1203,7 +1208,7 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal,
Error *local_err = NULL;
const char *device;
const char *name;
- BlockDriverState *bs;
+ BlockDriverState *bs, *check_bs;
QEMUSnapshotInfo old_sn, *sn;
bool ret;
int64_t rt;
@@ -1211,7 +1216,7 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal,
int ret1;
GLOBAL_STATE_CODE();
- GRAPH_RDLOCK_GUARD_MAINLOOP();
+ bdrv_graph_rdlock_main_loop();
tran_add(tran, &internal_snapshot_drv, state);
@@ -1220,14 +1225,29 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal,
bs = qmp_get_root_bs(device, errp);
if (!bs) {
+ bdrv_graph_rdunlock_main_loop();
return;
}
state->bs = bs;
+ /* Need to drain while unlocked. */
+ bdrv_graph_rdunlock_main_loop();
/* Paired with .clean() */
bdrv_drained_begin(bs);
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
+ /* Make sure the root bs did not change with the drain. */
+ check_bs = qmp_get_root_bs(device, errp);
+ if (bs != check_bs) {
+ if (check_bs) {
+ error_setg(errp, "Block node of device '%s' unexpectedly changed",
+ device);
+ } /* else errp is already set */
+ return;
+ }
+
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) {
return;
}
@@ -1295,12 +1315,14 @@ static void internal_snapshot_abort(void *opaque)
Error *local_error = NULL;
GLOBAL_STATE_CODE();
- GRAPH_RDLOCK_GUARD_MAINLOOP();
if (!state->created) {
return;
}
+ bdrv_drain_all_begin();
+ bdrv_graph_rdlock_main_loop();
+
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
error_reportf_err(local_error,
"Failed to delete snapshot with id '%s' and "
@@ -1308,6 +1330,8 @@ static void internal_snapshot_abort(void *opaque)
sn->id_str, sn->name,
bdrv_get_device_name(bs));
}
+ bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_end();
}
static void internal_snapshot_clean(void *opaque)
@@ -1353,9 +1377,10 @@ static void external_snapshot_action(TransactionAction *action,
const char *new_image_file;
ExternalSnapshotState *state = g_new0(ExternalSnapshotState, 1);
uint64_t perm, shared;
+ BlockDriverState *check_bs;
/* TODO We'll eventually have to take a writer lock in this function */
- GRAPH_RDLOCK_GUARD_MAINLOOP();
+ bdrv_graph_rdlock_main_loop();
tran_add(tran, &external_snapshot_drv, state);
@@ -1388,11 +1413,25 @@ static void external_snapshot_action(TransactionAction *action,
state->old_bs = bdrv_lookup_bs(device, node_name, errp);
if (!state->old_bs) {
+ bdrv_graph_rdunlock_main_loop();
return;
}
+ /* Need to drain while unlocked. */
+ bdrv_graph_rdunlock_main_loop();
/* Paired with .clean() */
bdrv_drained_begin(state->old_bs);
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
+ /* Make sure the associated bs did not change with the drain. */
+ check_bs = bdrv_lookup_bs(device, node_name, errp);
+ if (state->old_bs != check_bs) {
+ if (check_bs) {
+ error_setg(errp, "Block node of device '%s' unexpectedly changed",
+ device);
+ } /* else errp is already set */
+ return;
+ }
if (!bdrv_is_inserted(state->old_bs)) {
error_setg(errp, "Device '%s' has no medium",
@@ -3522,6 +3561,7 @@ void qmp_x_blockdev_change(const char *parent, const char *child,
BlockDriverState *parent_bs, *new_bs = NULL;
BdrvChild *p_child;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
parent_bs = bdrv_lookup_bs(parent, parent, errp);
@@ -3559,6 +3599,7 @@ void qmp_x_blockdev_change(const char *parent, const char *child,
out:
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
}
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
@@ -3592,12 +3633,13 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
AioContext *new_context;
BlockDriverState *bs;
- GRAPH_RDLOCK_GUARD_MAINLOOP();
+ bdrv_drain_all_begin();
+ bdrv_graph_rdlock_main_loop();
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Failed to find node with node-name='%s'", node_name);
- return;
+ goto out;
}
/* Protects against accidents. */
@@ -3605,14 +3647,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
error_setg(errp, "Node %s is associated with a BlockBackend and could "
"be in use (use force=true to override this check)",
node_name);
- return;
+ goto out;
}
if (iothread->type == QTYPE_QSTRING) {
IOThread *obj = iothread_by_id(iothread->u.s);
if (!obj) {
error_setg(errp, "Cannot find iothread %s", iothread->u.s);
- return;
+ goto out;
}
new_context = iothread_get_aio_context(obj);
@@ -3620,7 +3662,11 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
new_context = qemu_get_aio_context();
}
- bdrv_try_change_aio_context(bs, new_context, NULL, errp);
+ bdrv_try_change_aio_context_locked(bs, new_context, NULL, errp);
+
+out:
+ bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_end();
}
QemuOptsList qemu_common_drive_opts = {
diff --git a/blockjob.c b/blockjob.c
index 32007f3..e68181a 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -144,9 +144,9 @@ static TransactionActionDrv change_child_job_context = {
.clean = g_free,
};
-static bool child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx,
- GHashTable *visited, Transaction *tran,
- Error **errp)
+static bool GRAPH_RDLOCK
+child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx, GHashTable *visited,
+ Transaction *tran, Error **errp)
{
BlockJob *job = c->opaque;
BdrvStateChildJobContext *s;
@@ -198,6 +198,7 @@ void block_job_remove_all_bdrv(BlockJob *job)
* one to make sure that such a concurrent access does not attempt
* to process an already freed BdrvChild.
*/
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
while (job->nodes) {
GSList *l = job->nodes;
@@ -211,6 +212,7 @@ void block_job_remove_all_bdrv(BlockJob *job)
g_slist_free_1(l);
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
}
bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs)
@@ -496,6 +498,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
int ret;
GLOBAL_STATE_CODE();
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
if (job_id == NULL && !(flags & JOB_INTERNAL)) {
@@ -506,6 +509,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
flags, cb, opaque, errp);
if (job == NULL) {
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
return NULL;
}
@@ -544,10 +548,12 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
return job;
fail:
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
job_early_fail(&job->job);
return NULL;
}
diff --git a/rust/clippy.toml b/clippy.toml
index 58a62c0..9016172 100644
--- a/rust/clippy.toml
+++ b/clippy.toml
@@ -1,3 +1,3 @@
-doc-valid-idents = ["PrimeCell", ".."]
+doc-valid-idents = ["IrDA", "PrimeCell", ".."]
allow-mixed-uninlined-format-args = false
msrv = "1.77.0"
diff --git a/configure b/configure
index 2ce8d29..2b2b3d6 100755
--- a/configure
+++ b/configure
@@ -209,6 +209,8 @@ for opt do
;;
--rustc=*) RUSTC="$optarg"
;;
+ --rustdoc=*) RUSTDOC="$optarg"
+ ;;
--cpu=*) cpu="$optarg"
;;
--extra-cflags=*)
@@ -323,6 +325,7 @@ pkg_config="${PKG_CONFIG-${cross_prefix}pkg-config}"
sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}"
rustc="${RUSTC-rustc}"
+rustdoc="${RUSTDOC-rustdoc}"
check_define() {
cat > $TMPC <<EOF
@@ -660,6 +663,8 @@ for opt do
;;
--rustc=*)
;;
+ --rustdoc=*)
+ ;;
--make=*)
;;
--install=*)
@@ -890,6 +895,7 @@ Advanced options (experts only):
--cxx=CXX use C++ compiler CXX [$cxx]
--objcc=OBJCC use Objective-C compiler OBJCC [$objcc]
--rustc=RUSTC use Rust compiler RUSTC [$rustc]
+ --rustdoc=RUSTDOC use rustdoc binary RUSTDOC [$rustdoc]
--extra-cflags=CFLAGS append extra C compiler flags CFLAGS
--extra-cxxflags=CXXFLAGS append extra C++ compiler flags CXXFLAGS
--extra-objcflags=OBJCFLAGS append extra Objective C compiler flags OBJCFLAGS
@@ -1178,6 +1184,14 @@ fi
##########################################
# detect rust triple
+meson_version=$($meson --version)
+if test "$rust" != disabled && ! version_ge "$meson_version" 1.8.1; then
+ if test "$rust" = enabled; then
+ error_exit "Rust support needs Meson 1.8.1 or newer"
+ fi
+ echo "Rust needs Meson 1.8.1, disabling" 2>&1
+ rust=disabled
+fi
if test "$rust" != disabled && has "$rustc" && $rustc -vV > "${TMPDIR1}/${TMPB}.out"; then
rust_host_triple=$(sed -n 's/^host: //p' "${TMPDIR1}/${TMPB}.out")
else
@@ -1893,8 +1907,10 @@ if test "$skip_meson" = no; then
if test "$rust" != disabled; then
if test "$rust_host_triple" != "$rust_target_triple"; then
echo "rust = [$(meson_quote $rustc --target "$rust_target_triple")]" >> $cross
+ echo "rustdoc = [$(meson_quote $rustdoc --target "$rust_target_triple")]" >> $cross
else
echo "rust = [$(meson_quote $rustc)]" >> $cross
+ echo "rustdoc = [$(meson_quote $rustdoc)]" >> $cross
fi
fi
echo "ar = [$(meson_quote $ar)]" >> $cross
diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index 171d908..34d9c79 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -37,12 +37,16 @@ output directory (typically ``rust/target/``). A vanilla invocation
of Cargo will complain that it cannot find the generated sources,
which can be fixed in different ways:
-* by using special shorthand targets in the QEMU build directory::
+* by using Makefile targets, provided by Meson, that run ``clippy`` or
+ ``rustdoc``:
make clippy
- make rustfmt
make rustdoc
+A target for ``rustfmt`` is also declared in ``rust/meson.build``:
+
+ make rustfmt
+
* by invoking ``cargo`` through the Meson `development environment`__
feature::
@@ -50,7 +54,7 @@ which can be fixed in different ways:
pyvenv/bin/meson devenv -w ../rust cargo fmt
If you are going to use ``cargo`` repeatedly, ``pyvenv/bin/meson devenv``
- will enter a shell where commands like ``cargo clippy`` just work.
+ will enter a shell where commands like ``cargo fmt`` just work.
__ https://mesonbuild.com/Commands.html#devenv
@@ -66,7 +70,7 @@ be run via ``meson test`` or ``make``::
make check-rust
-Building Rust code with ``--enable-modules`` is not supported yet.
+Note that doctests require all ``.o`` files from the build to be available.
Supported tools
'''''''''''''''
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 8e11e63..24e145d 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -145,6 +145,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name,
if (ctx != bdrv_get_aio_context(bs)) {
error_setg(errp, "Different aio context is not supported for new "
"node");
+ return;
}
blk_replace_bs(blk, bs, errp);
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index 0775c8f..963aa24 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -1426,7 +1426,6 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
AMDVIState *s = opaque;
AMDVIAddressSpace **iommu_as, *amdvi_dev_as;
int bus_num = pci_bus_num(bus);
- X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
iommu_as = s->address_spaces[bus_num];
@@ -1486,15 +1485,8 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
AMDVI_INT_ADDR_FIRST,
&amdvi_dev_as->iommu_ir, 1);
- if (!x86_iommu->pt_supported) {
- memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false);
- memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu),
- true);
- } else {
- memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu),
- false);
- memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, true);
- }
+ memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false);
+ memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), true);
}
return &iommu_as[devfn]->as;
}
@@ -1723,6 +1715,14 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
exit(EXIT_FAILURE);
}
+ if (s->xtsup) {
+ if (kvm_irqchip_is_split() && !kvm_enable_x2apic()) {
+ error_report("AMD IOMMU xtsup=on requires x2APIC support on "
+ "the KVM side");
+ exit(EXIT_FAILURE);
+ }
+ }
+
pci_setup_iommu(bus, &amdvi_iommu_ops, s);
amdvi_init(s);
}
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 7a62bb0..ea7572e 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -285,6 +285,8 @@ static void pc_init1(MachineState *machine, const char *pci_type)
pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0");
pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1");
} else {
+ uint32_t irq;
+
isa_bus = isa_bus_new(NULL, system_memory, system_io,
&error_abort);
isa_bus_register_input_irqs(isa_bus, x86ms->gsi);
@@ -292,6 +294,9 @@ static void pc_init1(MachineState *machine, const char *pci_type)
x86ms->rtc = isa_new(TYPE_MC146818_RTC);
qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000);
isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal);
+ irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq",
+ &error_fatal);
+ isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq);
i8257_dma_init(OBJECT(machine), isa_bus, 0);
pcms->hpet_enabled = false;
diff --git a/hw/i386/tdvf.c b/hw/i386/tdvf.c
index bd993ea..645d9d1 100644
--- a/hw/i386/tdvf.c
+++ b/hw/i386/tdvf.c
@@ -101,16 +101,16 @@ static int tdvf_parse_and_check_section_entry(const TdvfSectionEntry *src,
/* sanity check */
if (entry->size < entry->data_len) {
- error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%lx",
+ error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%"PRIx64,
entry->data_len, entry->size);
return -1;
}
if (!QEMU_IS_ALIGNED(entry->address, TDVF_ALIGNMENT)) {
- error_report("MemoryAddress 0x%lx not page aligned", entry->address);
+ error_report("MemoryAddress 0x%"PRIx64" not page aligned", entry->address);
return -1;
}
if (!QEMU_IS_ALIGNED(entry->size, TDVF_ALIGNMENT)) {
- error_report("MemoryDataSize 0x%lx not page aligned", entry->size);
+ error_report("MemoryDataSize 0x%"PRIx64" not page aligned", entry->size);
return -1;
}
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index f5ab510..9b4bf48 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -128,6 +128,12 @@ static GSequence *pci_acpi_index_list(void)
return used_acpi_index_list;
}
+static void pci_set_master(PCIDevice *d, bool enable)
+{
+ memory_region_set_enabled(&d->bus_master_enable_region, enable);
+ d->is_master = enable; /* cache the status */
+}
+
static void pci_init_bus_master(PCIDevice *pci_dev)
{
AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev);
@@ -135,7 +141,7 @@ static void pci_init_bus_master(PCIDevice *pci_dev)
memory_region_init_alias(&pci_dev->bus_master_enable_region,
OBJECT(pci_dev), "bus master",
dma_as->root, 0, memory_region_size(dma_as->root));
- memory_region_set_enabled(&pci_dev->bus_master_enable_region, false);
+ pci_set_master(pci_dev, false);
memory_region_add_subregion(&pci_dev->bus_master_container_region, 0,
&pci_dev->bus_master_enable_region);
}
@@ -804,9 +810,8 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size,
pci_bridge_update_mappings(PCI_BRIDGE(s));
}
- memory_region_set_enabled(&s->bus_master_enable_region,
- pci_get_word(s->config + PCI_COMMAND)
- & PCI_COMMAND_MASTER);
+ pci_set_master(s, pci_get_word(s->config + PCI_COMMAND)
+ & PCI_COMMAND_MASTER);
g_free(config);
return 0;
@@ -1725,7 +1730,7 @@ static void pci_update_mappings(PCIDevice *d)
pci_update_vga(d);
}
-static inline int pci_irq_disabled(PCIDevice *d)
+int pci_irq_disabled(PCIDevice *d)
{
return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE;
}
@@ -1787,9 +1792,8 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int
if (ranges_overlap(addr, l, PCI_COMMAND, 2)) {
pci_update_irq_disabled(d, was_irq_disabled);
- memory_region_set_enabled(&d->bus_master_enable_region,
- (pci_get_word(d->config + PCI_COMMAND)
- & PCI_COMMAND_MASTER) && d->enabled);
+ pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND) &
+ PCI_COMMAND_MASTER) && d->enabled);
}
msi_write_config(d, addr, val_in, l);
@@ -2935,6 +2939,23 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
return &address_space_memory;
}
+int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n,
+ IOMMUNotify fn, void *opaque)
+{
+ PCIBus *bus;
+ PCIBus *iommu_bus;
+ int devfn;
+
+ pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
+ if (iommu_bus && iommu_bus->iommu_ops->init_iotlb_notifier) {
+ iommu_bus->iommu_ops->init_iotlb_notifier(bus, iommu_bus->iommu_opaque,
+ devfn, n, fn, opaque);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod,
Error **errp)
{
@@ -2966,6 +2987,170 @@ void pci_device_unset_iommu_device(PCIDevice *dev)
}
}
+int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req,
+ bool exec_req, hwaddr addr, bool lpig,
+ uint16_t prgi, bool is_read, bool is_write)
+{
+ PCIBus *bus;
+ PCIBus *iommu_bus;
+ int devfn;
+
+ if (!dev->is_master ||
+ ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) {
+ return -EPERM;
+ }
+
+ if (!pcie_pri_enabled(dev)) {
+ return -EPERM;
+ }
+
+ pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
+ if (iommu_bus && iommu_bus->iommu_ops->pri_request_page) {
+ return iommu_bus->iommu_ops->pri_request_page(bus,
+ iommu_bus->iommu_opaque,
+ devfn, pasid, priv_req,
+ exec_req, addr, lpig, prgi,
+ is_read, is_write);
+ }
+
+ return -ENODEV;
+}
+
+int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid,
+ IOMMUPRINotifier *notifier)
+{
+ PCIBus *bus;
+ PCIBus *iommu_bus;
+ int devfn;
+
+ if (!dev->is_master ||
+ ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) {
+ return -EPERM;
+ }
+
+ pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
+ if (iommu_bus && iommu_bus->iommu_ops->pri_register_notifier) {
+ iommu_bus->iommu_ops->pri_register_notifier(bus,
+ iommu_bus->iommu_opaque,
+ devfn, pasid, notifier);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid)
+{
+ PCIBus *bus;
+ PCIBus *iommu_bus;
+ int devfn;
+
+ pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
+ if (iommu_bus && iommu_bus->iommu_ops->pri_unregister_notifier) {
+ iommu_bus->iommu_ops->pri_unregister_notifier(bus,
+ iommu_bus->iommu_opaque,
+ devfn, pasid);
+ }
+}
+
+ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid,
+ bool priv_req, bool exec_req,
+ hwaddr addr, size_t length,
+ bool no_write, IOMMUTLBEntry *result,
+ size_t result_length,
+ uint32_t *err_count)
+{
+ PCIBus *bus;
+ PCIBus *iommu_bus;
+ int devfn;
+
+ if (!dev->is_master ||
+ ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) {
+ return -EPERM;
+ }
+
+ if (result_length == 0) {
+ return -ENOSPC;
+ }
+
+ if (!pcie_ats_enabled(dev)) {
+ return -EPERM;
+ }
+
+ pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
+ if (iommu_bus && iommu_bus->iommu_ops->ats_request_translation) {
+ return iommu_bus->iommu_ops->ats_request_translation(bus,
+ iommu_bus->iommu_opaque,
+ devfn, pasid, priv_req,
+ exec_req, addr, length,
+ no_write, result,
+ result_length, err_count);
+ }
+
+ return -ENODEV;
+}
+
+int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid,
+ IOMMUNotifier *n)
+{
+ PCIBus *bus;
+ PCIBus *iommu_bus;
+ int devfn;
+
+ if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) {
+ return -EPERM;
+ }
+
+ pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
+ if (iommu_bus && iommu_bus->iommu_ops->register_iotlb_notifier) {
+ iommu_bus->iommu_ops->register_iotlb_notifier(bus,
+ iommu_bus->iommu_opaque, devfn,
+ pasid, n);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid,
+ IOMMUNotifier *n)
+{
+ PCIBus *bus;
+ PCIBus *iommu_bus;
+ int devfn;
+
+ if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) {
+ return -EPERM;
+ }
+
+ pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
+ if (iommu_bus && iommu_bus->iommu_ops->unregister_iotlb_notifier) {
+ iommu_bus->iommu_ops->unregister_iotlb_notifier(bus,
+ iommu_bus->iommu_opaque,
+ devfn, pasid, n);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width,
+ uint32_t *min_page_size)
+{
+ PCIBus *bus;
+ PCIBus *iommu_bus;
+ int devfn;
+
+ pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn);
+ if (iommu_bus && iommu_bus->iommu_ops->get_iotlb_info) {
+ iommu_bus->iommu_ops->get_iotlb_info(iommu_bus->iommu_opaque,
+ addr_width, min_page_size);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque)
{
/*
@@ -3100,9 +3285,8 @@ void pci_set_enabled(PCIDevice *d, bool state)
d->enabled = state;
pci_update_mappings(d);
- memory_region_set_enabled(&d->bus_master_enable_region,
- (pci_get_word(d->config + PCI_COMMAND)
- & PCI_COMMAND_MASTER) && d->enabled);
+ pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND)
+ & PCI_COMMAND_MASTER) && d->enabled);
if (qdev_is_realized(&d->qdev)) {
pci_device_reset(d);
}
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 1b12db6..eaeb688 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -1214,3 +1214,81 @@ void pcie_acs_reset(PCIDevice *dev)
pci_set_word(dev->config + dev->exp.acs_cap + PCI_ACS_CTRL, 0);
}
}
+
+/* PASID */
+void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width,
+ bool exec_perm, bool priv_mod)
+{
+ static const uint16_t control_reg_rw_mask = 0x07;
+ uint16_t capability_reg;
+
+ assert(pasid_width <= PCI_EXT_CAP_PASID_MAX_WIDTH);
+
+ pcie_add_capability(dev, PCI_EXT_CAP_ID_PASID, PCI_PASID_VER, offset,
+ PCI_EXT_CAP_PASID_SIZEOF);
+
+ capability_reg = ((uint16_t)pasid_width) << PCI_PASID_CAP_WIDTH_SHIFT;
+ capability_reg |= exec_perm ? PCI_PASID_CAP_EXEC : 0;
+ capability_reg |= priv_mod ? PCI_PASID_CAP_PRIV : 0;
+ pci_set_word(dev->config + offset + PCI_PASID_CAP, capability_reg);
+
+ /* Everything is disabled by default */
+ pci_set_word(dev->config + offset + PCI_PASID_CTRL, 0);
+
+ pci_set_word(dev->wmask + offset + PCI_PASID_CTRL, control_reg_rw_mask);
+
+ dev->exp.pasid_cap = offset;
+}
+
+/* PRI */
+void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap,
+ bool prg_response_pasid_req)
+{
+ static const uint16_t control_reg_rw_mask = 0x3;
+ static const uint16_t status_reg_rw1_mask = 0x3;
+ static const uint32_t pr_alloc_reg_rw_mask = 0xffffffff;
+ uint16_t status_reg;
+
+ status_reg = prg_response_pasid_req ? PCI_PRI_STATUS_PASID : 0;
+ status_reg |= PCI_PRI_STATUS_STOPPED; /* Stopped by default */
+
+ pcie_add_capability(dev, PCI_EXT_CAP_ID_PRI, PCI_PRI_VER, offset,
+ PCI_EXT_CAP_PRI_SIZEOF);
+ /* Disabled by default */
+
+ pci_set_word(dev->config + offset + PCI_PRI_STATUS, status_reg);
+ pci_set_long(dev->config + offset + PCI_PRI_MAX_REQ, outstanding_pr_cap);
+
+ pci_set_word(dev->wmask + offset + PCI_PRI_CTRL, control_reg_rw_mask);
+ pci_set_word(dev->w1cmask + offset + PCI_PRI_STATUS, status_reg_rw1_mask);
+ pci_set_long(dev->wmask + offset + PCI_PRI_ALLOC_REQ, pr_alloc_reg_rw_mask);
+
+ dev->exp.pri_cap = offset;
+}
+
+bool pcie_pri_enabled(const PCIDevice *dev)
+{
+ if (!pci_is_express(dev) || !dev->exp.pri_cap) {
+ return false;
+ }
+ return (pci_get_word(dev->config + dev->exp.pri_cap + PCI_PRI_CTRL) &
+ PCI_PRI_CTRL_ENABLE) != 0;
+}
+
+bool pcie_pasid_enabled(const PCIDevice *dev)
+{
+ if (!pci_is_express(dev) || !dev->exp.pasid_cap) {
+ return false;
+ }
+ return (pci_get_word(dev->config + dev->exp.pasid_cap + PCI_PASID_CTRL) &
+ PCI_PASID_CTRL_ENABLE) != 0;
+}
+
+bool pcie_ats_enabled(const PCIDevice *dev)
+{
+ if (!pci_is_express(dev) || !dev->exp.ats_cap) {
+ return false;
+ }
+ return (pci_get_word(dev->config + dev->exp.ats_cap + PCI_ATS_CTRL) &
+ PCI_ATS_CTRL_ENABLE) != 0;
+}
diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c
index 1c6ca94..d834bd4 100644
--- a/hw/vfio/container-base.c
+++ b/hw/vfio/container-base.c
@@ -75,12 +75,12 @@ void vfio_address_space_insert(VFIOAddressSpace *space,
int vfio_container_dma_map(VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
- void *vaddr, bool readonly)
+ void *vaddr, bool readonly, MemoryRegion *mr)
{
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
g_assert(vioc->dma_map);
- return vioc->dma_map(bcontainer, iova, size, vaddr, readonly);
+ return vioc->dma_map(bcontainer, iova, size, vaddr, readonly, mr);
}
int vfio_container_dma_unmap(VFIOContainerBase *bcontainer,
diff --git a/hw/vfio/container.c b/hw/vfio/container.c
index a9f0dba..0f948d0 100644
--- a/hw/vfio/container.c
+++ b/hw/vfio/container.c
@@ -33,8 +33,8 @@
#include "qapi/error.h"
#include "pci.h"
#include "hw/vfio/vfio-container.h"
+#include "hw/vfio/vfio-cpr.h"
#include "vfio-helpers.h"
-#include "vfio-cpr.h"
#include "vfio-listener.h"
#define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio"
@@ -207,7 +207,8 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer,
}
static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova,
- ram_addr_t size, void *vaddr, bool readonly)
+ ram_addr_t size, void *vaddr, bool readonly,
+ MemoryRegion *mr)
{
const VFIOContainer *container = container_of(bcontainer, VFIOContainer,
bcontainer);
diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c
index 3214184..0210e76 100644
--- a/hw/vfio/cpr.c
+++ b/hw/vfio/cpr.c
@@ -8,9 +8,9 @@
#include "qemu/osdep.h"
#include "hw/vfio/vfio-device.h"
#include "migration/misc.h"
+#include "hw/vfio/vfio-cpr.h"
#include "qapi/error.h"
#include "system/runstate.h"
-#include "vfio-cpr.h"
static int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier,
MigrationEvent *e, Error **errp)
diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c
index e7952d1..e7a9d1f 100644
--- a/hw/vfio/igd.c
+++ b/hw/vfio/igd.c
@@ -187,23 +187,21 @@ static bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
}
static bool vfio_pci_igd_opregion_detect(VFIOPCIDevice *vdev,
- struct vfio_region_info **opregion,
- Error **errp)
+ struct vfio_region_info **opregion)
{
int ret;
- /* Hotplugging is not supported for opregion access */
- if (vdev->pdev.qdev.hotplugged) {
- error_setg(errp, "IGD OpRegion is not supported on hotplugged device");
- return false;
- }
-
ret = vfio_device_get_region_info_type(&vdev->vbasedev,
VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, opregion);
if (ret) {
- error_setg_errno(errp, -ret,
- "Device does not supports IGD OpRegion feature");
+ return false;
+ }
+
+ /* Hotplugging is not supported for opregion access */
+ if (vdev->pdev.qdev.hotplugged) {
+ warn_report("IGD device detected, but OpRegion is not supported "
+ "on hotplugged device.");
return false;
}
@@ -524,7 +522,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp)
}
/* IGD device always comes with OpRegion */
- if (!vfio_pci_igd_opregion_detect(vdev, &opregion, errp)) {
+ if (!vfio_pci_igd_opregion_detect(vdev, &opregion)) {
return true;
}
info_report("OpRegion detected on Intel display %x.", vdev->device_id);
@@ -695,7 +693,7 @@ static bool vfio_pci_kvmgt_config_quirk(VFIOPCIDevice *vdev, Error **errp)
return true;
}
- if (!vfio_pci_igd_opregion_detect(vdev, &opregion, errp)) {
+ if (!vfio_pci_igd_opregion_detect(vdev, &opregion)) {
/* Should never reach here, KVMGT always emulates OpRegion */
return false;
}
diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c
index af1c7ab..d3efef7 100644
--- a/hw/vfio/iommufd.c
+++ b/hw/vfio/iommufd.c
@@ -21,20 +21,21 @@
#include "qapi/error.h"
#include "system/iommufd.h"
#include "hw/qdev-core.h"
+#include "hw/vfio/vfio-cpr.h"
#include "system/reset.h"
#include "qemu/cutils.h"
#include "qemu/chardev_open.h"
#include "pci.h"
#include "vfio-iommufd.h"
#include "vfio-helpers.h"
-#include "vfio-cpr.h"
#include "vfio-listener.h"
#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \
TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio"
static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova,
- ram_addr_t size, void *vaddr, bool readonly)
+ ram_addr_t size, void *vaddr, bool readonly,
+ MemoryRegion *mr)
{
const VFIOIOMMUFDContainer *container =
container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer);
@@ -592,6 +593,10 @@ found_container:
goto err_listener_register;
}
+ /*
+ * Do not move this code before attachment! The nested IOMMU support
+ * needs device and hwpt id which are generated only after attachment.
+ */
if (!vfio_device_hiod_create_and_realize(vbasedev,
TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO, errp)) {
goto err_listener_register;
@@ -810,21 +815,38 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, const void *data)
vioc->query_dirty_bitmap = iommufd_query_dirty_bitmap;
};
+static bool
+host_iommu_device_iommufd_vfio_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev,
+ uint32_t hwpt_id, Error **errp)
+{
+ VFIODevice *vbasedev = HOST_IOMMU_DEVICE(idev)->agent;
+
+ return !iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt_id, errp);
+}
+
+static bool
+host_iommu_device_iommufd_vfio_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev,
+ Error **errp)
+{
+ VFIODevice *vbasedev = HOST_IOMMU_DEVICE(idev)->agent;
+
+ return iommufd_cdev_detach_ioas_hwpt(vbasedev, errp);
+}
+
static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque,
Error **errp)
{
VFIODevice *vdev = opaque;
+ HostIOMMUDeviceIOMMUFD *idev;
HostIOMMUDeviceCaps *caps = &hiod->caps;
+ VendorCaps *vendor_caps = &caps->vendor_caps;
enum iommu_hw_info_type type;
- union {
- struct iommu_hw_info_vtd vtd;
- } data;
uint64_t hw_caps;
hiod->agent = opaque;
- if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid,
- &type, &data, sizeof(data),
+ if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid, &type,
+ vendor_caps, sizeof(*vendor_caps),
&hw_caps, errp)) {
return false;
}
@@ -833,6 +855,11 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque,
caps->type = type;
caps->hw_caps = hw_caps;
+ idev = HOST_IOMMU_DEVICE_IOMMUFD(hiod);
+ idev->iommufd = vdev->iommufd;
+ idev->devid = vdev->devid;
+ idev->hwpt_id = vdev->hwpt->hwpt_id;
+
return true;
}
@@ -858,10 +885,14 @@ hiod_iommufd_vfio_get_page_size_mask(HostIOMMUDevice *hiod)
static void hiod_iommufd_vfio_class_init(ObjectClass *oc, const void *data)
{
HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc);
+ HostIOMMUDeviceIOMMUFDClass *idevc = HOST_IOMMU_DEVICE_IOMMUFD_CLASS(oc);
hiodc->realize = hiod_iommufd_vfio_realize;
hiodc->get_iova_ranges = hiod_iommufd_vfio_get_iova_ranges;
hiodc->get_page_size_mask = hiod_iommufd_vfio_get_page_size_mask;
+
+ idevc->attach_hwpt = host_iommu_device_iommufd_vfio_attach_hwpt;
+ idevc->detach_hwpt = host_iommu_device_iommufd_vfio_detach_hwpt;
};
static const TypeInfo types[] = {
diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c
index bfacb3d..203ed03 100644
--- a/hw/vfio/listener.c
+++ b/hw/vfio/listener.c
@@ -90,16 +90,17 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section)
section->offset_within_address_space & (1ULL << 63);
}
-/* Called with rcu_read_lock held. */
-static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
- ram_addr_t *ram_addr, bool *read_only,
- Error **errp)
+/*
+ * Called with rcu_read_lock held.
+ * The returned MemoryRegion must not be accessed after calling rcu_read_unlock.
+ */
+static MemoryRegion *vfio_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p,
+ Error **errp)
{
- bool ret, mr_has_discard_manager;
+ MemoryRegion *mr;
- ret = memory_get_xlat_addr(iotlb, vaddr, ram_addr, read_only,
- &mr_has_discard_manager, errp);
- if (ret && mr_has_discard_manager) {
+ mr = memory_translate_iotlb(iotlb, xlat_p, errp);
+ if (mr && memory_region_has_ram_discard_manager(mr)) {
/*
* Malicious VMs might trigger discarding of IOMMU-mapped memory. The
* pages will remain pinned inside vfio until unmapped, resulting in a
@@ -118,7 +119,7 @@ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
" intended via an IOMMU. It's possible to mitigate "
" by setting/adjusting RLIMIT_MEMLOCK.");
}
- return ret;
+ return mr;
}
static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
@@ -126,6 +127,8 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n);
VFIOContainerBase *bcontainer = giommu->bcontainer;
hwaddr iova = iotlb->iova + giommu->iommu_offset;
+ MemoryRegion *mr;
+ hwaddr xlat;
void *vaddr;
int ret;
Error *local_err = NULL;
@@ -150,10 +153,14 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) {
bool read_only;
- if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, &local_err)) {
+ mr = vfio_translate_iotlb(iotlb, &xlat, &local_err);
+ if (!mr) {
error_report_err(local_err);
goto out;
}
+ vaddr = memory_region_get_ram_ptr(mr) + xlat;
+ read_only = !(iotlb->perm & IOMMU_WO) || mr->readonly;
+
/*
* vaddr is only valid until rcu_read_unlock(). But after
* vfio_dma_map has set up the mapping the pages will be
@@ -163,7 +170,7 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
*/
ret = vfio_container_dma_map(bcontainer, iova,
iotlb->addr_mask + 1, vaddr,
- read_only);
+ read_only, mr);
if (ret) {
error_report("vfio_container_dma_map(%p, 0x%"HWADDR_PRIx", "
"0x%"HWADDR_PRIx", %p) = %d (%s)",
@@ -233,7 +240,7 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl,
vaddr = memory_region_get_ram_ptr(section->mr) + start;
ret = vfio_container_dma_map(bcontainer, iova, next - start,
- vaddr, section->readonly);
+ vaddr, section->readonly, section->mr);
if (ret) {
/* Rollback */
vfio_ram_discard_notify_discard(rdl, section);
@@ -449,6 +456,26 @@ static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp)
}
}
+VFIORamDiscardListener *vfio_find_ram_discard_listener(
+ VFIOContainerBase *bcontainer, MemoryRegionSection *section)
+{
+ VFIORamDiscardListener *vrdl = NULL;
+
+ QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) {
+ if (vrdl->mr == section->mr &&
+ vrdl->offset_within_address_space ==
+ section->offset_within_address_space) {
+ break;
+ }
+ }
+
+ if (!vrdl) {
+ hw_error("vfio: Trying to sync missing RAM discard listener");
+ /* does not return */
+ }
+ return vrdl;
+}
+
static void vfio_listener_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
@@ -557,7 +584,7 @@ static void vfio_listener_region_add(MemoryListener *listener,
}
ret = vfio_container_dma_map(bcontainer, iova, int128_get64(llsize),
- vaddr, section->readonly);
+ vaddr, section->readonly, section->mr);
if (ret) {
error_setg(&err, "vfio_container_dma_map(%p, 0x%"HWADDR_PRIx", "
"0x%"HWADDR_PRIx", %p) = %d (%s)",
@@ -1010,6 +1037,8 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
ram_addr_t translated_addr;
Error *local_err = NULL;
int ret = -EINVAL;
+ MemoryRegion *mr;
+ hwaddr xlat;
trace_vfio_iommu_map_dirty_notify(iova, iova + iotlb->addr_mask);
@@ -1021,9 +1050,11 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
}
rcu_read_lock();
- if (!vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL, &local_err)) {
+ mr = vfio_translate_iotlb(iotlb, &xlat, &local_err);
+ if (!mr) {
goto out_unlock;
}
+ translated_addr = memory_region_get_ram_addr(mr) + xlat;
ret = vfio_container_query_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1,
translated_addr, &local_err);
@@ -1075,19 +1106,8 @@ vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer,
MemoryRegionSection *section)
{
RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr);
- VFIORamDiscardListener *vrdl = NULL;
-
- QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) {
- if (vrdl->mr == section->mr &&
- vrdl->offset_within_address_space ==
- section->offset_within_address_space) {
- break;
- }
- }
-
- if (!vrdl) {
- hw_error("vfio: Trying to sync missing RAM discard listener");
- }
+ VFIORamDiscardListener *vrdl =
+ vfio_find_ram_discard_listener(bcontainer, section);
/*
* We only want/can synchronize the bitmap for actually mapped parts -
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index a1bfdfe..b1250d8 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -511,6 +511,25 @@ static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg,
kvm_irqchip_commit_routes(kvm_state);
}
+static void set_irq_signalling(VFIODevice *vbasedev, VFIOMSIVector *vector,
+ unsigned int nr)
+{
+ Error *err = NULL;
+ int32_t fd;
+
+ if (vector->virq >= 0) {
+ fd = event_notifier_get_fd(&vector->kvm_interrupt);
+ } else {
+ fd = event_notifier_get_fd(&vector->interrupt);
+ }
+
+ if (!vfio_device_irq_set_signaling(vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, nr,
+ VFIO_IRQ_SET_ACTION_TRIGGER,
+ fd, &err)) {
+ error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name);
+ }
+}
+
static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
MSIMessage *msg, IOHandler *handler)
{
@@ -583,21 +602,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
strerror(-ret));
}
} else {
- Error *err = NULL;
- int32_t fd;
-
- if (vector->virq >= 0) {
- fd = event_notifier_get_fd(&vector->kvm_interrupt);
- } else {
- fd = event_notifier_get_fd(&vector->interrupt);
- }
-
- if (!vfio_device_irq_set_signaling(&vdev->vbasedev,
- VFIO_PCI_MSIX_IRQ_INDEX, nr,
- VFIO_IRQ_SET_ACTION_TRIGGER, fd,
- &err)) {
- error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
- }
+ set_irq_signalling(&vdev->vbasedev, vector, nr);
}
}
@@ -2854,6 +2859,18 @@ static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
static void vfio_pci_put_device(VFIOPCIDevice *vdev)
{
+ vfio_display_finalize(vdev);
+ vfio_bars_finalize(vdev);
+ g_free(vdev->emulated_config_bits);
+ g_free(vdev->rom);
+ /*
+ * XXX Leaking igd_opregion is not an oversight, we can't remove the
+ * fw_cfg entry therefore leaking this allocation seems like the safest
+ * option.
+ *
+ * g_free(vdev->igd_opregion);
+ */
+
vfio_device_detach(&vdev->vbasedev);
g_free(vdev->vbasedev.name);
@@ -3005,6 +3022,19 @@ static bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp)
{
PCIDevice *pdev = &vdev->pdev;
VFIODevice *vbasedev = &vdev->vbasedev;
+ uint32_t config_space_size;
+ int ret;
+
+ config_space_size = MIN(pci_config_size(&vdev->pdev), vdev->config_size);
+
+ /* Get a copy of config space */
+ ret = vfio_pci_config_space_read(vdev, 0, config_space_size,
+ vdev->pdev.config);
+ if (ret < (int)config_space_size) {
+ ret = ret < 0 ? -ret : EFAULT;
+ error_setg_errno(errp, ret, "failed to read device config space");
+ return false;
+ }
/* vfio emulates a lot for us, but some bits need extra love */
vdev->emulated_config_bits = g_malloc0(vdev->config_size);
@@ -3126,15 +3156,14 @@ static bool vfio_interrupt_setup(VFIOPCIDevice *vdev, Error **errp)
return true;
}
-static void vfio_realize(PCIDevice *pdev, Error **errp)
+static void vfio_pci_realize(PCIDevice *pdev, Error **errp)
{
ERRP_GUARD();
VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev);
VFIODevice *vbasedev = &vdev->vbasedev;
- int i, ret;
+ int i;
char uuid[UUID_STR_LEN];
g_autofree char *name = NULL;
- uint32_t config_space_size;
if (vbasedev->fd < 0 && !vbasedev->sysfsdev) {
if (!(~vdev->host.domain || ~vdev->host.bus ||
@@ -3189,17 +3218,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
goto error;
}
- config_space_size = MIN(pci_config_size(&vdev->pdev), vdev->config_size);
-
- /* Get a copy of config space */
- ret = vfio_pci_config_space_read(vdev, 0, config_space_size,
- vdev->pdev.config);
- if (ret < (int)config_space_size) {
- ret = ret < 0 ? -ret : EFAULT;
- error_setg_errno(errp, ret, "failed to read device config space");
- goto error;
- }
-
if (!vfio_pci_config_setup(vdev, errp)) {
goto error;
}
@@ -3302,17 +3320,6 @@ static void vfio_instance_finalize(Object *obj)
{
VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj);
- vfio_display_finalize(vdev);
- vfio_bars_finalize(vdev);
- g_free(vdev->emulated_config_bits);
- g_free(vdev->rom);
- /*
- * XXX Leaking igd_opregion is not an oversight, we can't remove the
- * fw_cfg entry therefore leaking this allocation seems like the safest
- * option.
- *
- * g_free(vdev->igd_opregion);
- */
vfio_pci_put_device(vdev);
}
@@ -3514,7 +3521,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, const void *data)
object_class_property_add_str(klass, "fd", NULL, vfio_pci_set_fd);
#endif
dc->desc = "VFIO-based PCI device assignment";
- pdc->realize = vfio_realize;
+ pdc->realize = vfio_pci_realize;
object_class_property_set_description(klass, /* 1.3 */
"host",
diff --git a/hw/vfio/vfio-cpr.h b/hw/vfio/vfio-cpr.h
deleted file mode 100644
index 134b83a..0000000
--- a/hw/vfio/vfio-cpr.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * VFIO CPR
- *
- * Copyright (c) 2025 Oracle and/or its affiliates.
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef HW_VFIO_CPR_H
-#define HW_VFIO_CPR_H
-
-bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp);
-void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer);
-
-#endif /* HW_VFIO_CPR_H */
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index 1ab2c11..7061b6e 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -209,6 +209,8 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
int ret;
Int128 llend;
Error *local_err = NULL;
+ MemoryRegion *mr;
+ hwaddr xlat;
if (iotlb->target_as != &address_space_memory) {
error_report("Wrong target AS \"%s\", only system memory is allowed",
@@ -228,11 +230,14 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) {
bool read_only;
- if (!memory_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, NULL,
- &local_err)) {
+ mr = memory_translate_iotlb(iotlb, &xlat, &local_err);
+ if (!mr) {
error_report_err(local_err);
return;
}
+ vaddr = memory_region_get_ram_ptr(mr) + xlat;
+ read_only = !(iotlb->perm & IOMMU_WO) || mr->readonly;
+
ret = vhost_vdpa_dma_map(s, VHOST_VDPA_GUEST_PA_ASID, iova,
iotlb->addr_mask + 1, vaddr, read_only);
if (ret) {
@@ -594,6 +599,36 @@ static void vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v)
v->shadow_vqs = g_steal_pointer(&shadow_vqs);
}
+static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev)
+{
+ struct vhost_vdpa *v = dev->opaque;
+
+ uint64_t features;
+ uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 |
+ 0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH |
+ 0x1ULL << VHOST_BACKEND_F_IOTLB_ASID |
+ 0x1ULL << VHOST_BACKEND_F_SUSPEND;
+ int r;
+
+ if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) {
+ return -EFAULT;
+ }
+
+ features &= f;
+
+ if (vhost_vdpa_first_dev(dev)) {
+ r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features);
+ if (r) {
+ return -EFAULT;
+ }
+ }
+
+ dev->backend_cap = features;
+ v->shared->backend_cap = features;
+
+ return 0;
+}
+
static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
{
struct vhost_vdpa *v = opaque;
@@ -603,7 +638,12 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
v->dev = dev;
dev->opaque = opaque ;
- v->shared->listener = vhost_vdpa_memory_listener;
+
+ ret = vhost_vdpa_set_backend_cap(dev);
+ if (unlikely(ret != 0)) {
+ return ret;
+ }
+
vhost_vdpa_init_svq(dev, v);
error_propagate(&dev->migration_blocker, v->migration_blocker);
@@ -639,6 +679,7 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
VIRTIO_CONFIG_S_DRIVER);
+ v->shared->listener = vhost_vdpa_memory_listener;
return 0;
}
@@ -841,36 +882,6 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev,
return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
}
-static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev)
-{
- struct vhost_vdpa *v = dev->opaque;
-
- uint64_t features;
- uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 |
- 0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH |
- 0x1ULL << VHOST_BACKEND_F_IOTLB_ASID |
- 0x1ULL << VHOST_BACKEND_F_SUSPEND;
- int r;
-
- if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) {
- return -EFAULT;
- }
-
- features &= f;
-
- if (vhost_vdpa_first_dev(dev)) {
- r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features);
- if (r) {
- return -EFAULT;
- }
- }
-
- dev->backend_cap = features;
- v->shared->backend_cap = features;
-
- return 0;
-}
-
static int vhost_vdpa_get_device_id(struct vhost_dev *dev,
uint32_t *device_id)
{
@@ -888,8 +899,14 @@ static int vhost_vdpa_reset_device(struct vhost_dev *dev)
ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status);
trace_vhost_vdpa_reset_device(dev);
+ if (ret) {
+ return ret;
+ }
+
+ memory_listener_unregister(&v->shared->listener);
+ v->shared->listener_registered = false;
v->suspended = false;
- return ret;
+ return 0;
}
static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx)
@@ -1373,7 +1390,15 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
"IOMMU and try again");
return -1;
}
- memory_listener_register(&v->shared->listener, dev->vdev->dma_as);
+ if (v->shared->listener_registered &&
+ dev->vdev->dma_as != v->shared->listener.address_space) {
+ memory_listener_unregister(&v->shared->listener);
+ v->shared->listener_registered = false;
+ }
+ if (!v->shared->listener_registered) {
+ memory_listener_register(&v->shared->listener, dev->vdev->dma_as);
+ v->shared->listener_registered = true;
+ }
return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
}
@@ -1383,8 +1408,6 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
static void vhost_vdpa_reset_status(struct vhost_dev *dev)
{
- struct vhost_vdpa *v = dev->opaque;
-
if (!vhost_vdpa_last_dev(dev)) {
return;
}
@@ -1392,7 +1415,6 @@ static void vhost_vdpa_reset_status(struct vhost_dev *dev)
vhost_vdpa_reset_device(dev);
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
VIRTIO_CONFIG_S_DRIVER);
- memory_listener_unregister(&v->shared->listener);
}
static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base,
@@ -1526,12 +1548,27 @@ static int vhost_vdpa_get_features(struct vhost_dev *dev,
static int vhost_vdpa_set_owner(struct vhost_dev *dev)
{
+ int r;
+ struct vhost_vdpa *v;
+
if (!vhost_vdpa_first_dev(dev)) {
return 0;
}
trace_vhost_vdpa_set_owner(dev);
- return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL);
+ r = vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL);
+ if (unlikely(r < 0)) {
+ return r;
+ }
+
+ /*
+ * Being optimistic and listening address space memory. If the device
+ * uses vIOMMU, it is changed at vhost_vdpa_dev_start.
+ */
+ v = dev->opaque;
+ memory_listener_register(&v->shared->listener, &address_space_memory);
+ v->shared->listener_registered = true;
+ return 0;
}
static int vhost_vdpa_vq_get_addr(struct vhost_dev *dev,
@@ -1563,7 +1600,6 @@ const VhostOps vdpa_ops = {
.vhost_set_vring_kick = vhost_vdpa_set_vring_kick,
.vhost_set_vring_call = vhost_vdpa_set_vring_call,
.vhost_get_features = vhost_vdpa_get_features,
- .vhost_set_backend_cap = vhost_vdpa_set_backend_cap,
.vhost_set_owner = vhost_vdpa_set_owner,
.vhost_set_vring_endian = NULL,
.vhost_backend_memslots_limit = vhost_vdpa_memslots_limit,
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index e62ae1e..fba2372 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1213,7 +1213,12 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
static bool virtio_pci_query_guest_notifiers(DeviceState *d)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
- return msix_enabled(&proxy->pci_dev);
+
+ if (msix_enabled(&proxy->pci_dev)) {
+ return true;
+ } else {
+ return pci_irq_disabled(&proxy->pci_dev);
+ }
}
static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 2e98cec..5534251 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -205,6 +205,15 @@ static const char *virtio_id_to_name(uint16_t device_id)
return name;
}
+static void virtio_check_indirect_feature(VirtIODevice *vdev)
+{
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Device %s: indirect_desc was not negotiated!\n",
+ vdev->name);
+ }
+}
+
/* Called within call_rcu(). */
static void virtio_free_region_cache(VRingMemoryRegionCaches *caches)
{
@@ -1733,6 +1742,7 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz)
virtio_error(vdev, "Invalid size for indirect buffer table");
goto done;
}
+ virtio_check_indirect_feature(vdev);
/* loop over the indirect descriptor table */
len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as,
@@ -1870,6 +1880,7 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz)
virtio_error(vdev, "Invalid size for indirect buffer table");
goto done;
}
+ virtio_check_indirect_feature(vdev);
/* loop over the indirect descriptor table */
len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as,
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
index 9be34b3..84a2a4e 100644
--- a/include/block/block-global-state.h
+++ b/include/block/block-global-state.h
@@ -192,10 +192,10 @@ int bdrv_inactivate_all(void);
int bdrv_flush_all(void);
void bdrv_close_all(void);
-void bdrv_drain_all_begin(void);
+void GRAPH_UNLOCKED bdrv_drain_all_begin(void);
void bdrv_drain_all_begin_nopoll(void);
void bdrv_drain_all_end(void);
-void bdrv_drain_all(void);
+void GRAPH_UNLOCKED bdrv_drain_all(void);
void bdrv_aio_cancel(BlockAIOCB *acb);
@@ -274,11 +274,16 @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag);
int bdrv_debug_resume(BlockDriverState *bs, const char *tag);
bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag);
-bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx,
- GHashTable *visited, Transaction *tran,
- Error **errp);
-int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
- BdrvChild *ignore_child, Error **errp);
+bool GRAPH_RDLOCK
+bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp);
+int GRAPH_UNLOCKED
+bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
+ BdrvChild *ignore_child, Error **errp);
+int GRAPH_RDLOCK
+bdrv_try_change_aio_context_locked(BlockDriverState *bs, AioContext *ctx,
+ BdrvChild *ignore_child, Error **errp);
int GRAPH_RDLOCK bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz);
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
diff --git a/include/block/block-io.h b/include/block/block-io.h
index b99cc98..4cf83fb 100644
--- a/include/block/block-io.h
+++ b/include/block/block-io.h
@@ -431,7 +431,7 @@ bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent,
*
* This function can be recursive.
*/
-void bdrv_drained_begin(BlockDriverState *bs);
+void GRAPH_UNLOCKED bdrv_drained_begin(BlockDriverState *bs);
/**
* bdrv_do_drained_begin_quiesce:
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
index 2982dd3..925a3e7 100644
--- a/include/block/block_int-common.h
+++ b/include/block/block_int-common.h
@@ -396,9 +396,23 @@ struct BlockDriver {
int GRAPH_RDLOCK_PTR (*bdrv_probe_geometry)(
BlockDriverState *bs, HDGeometry *geo);
+ /**
+ * Hot add a BDS's child. Used in combination with bdrv_del_child, so the
+ * user can take a child offline when it is broken and take a new child
+ * online.
+ *
+ * All block nodes must be drained.
+ */
void GRAPH_WRLOCK_PTR (*bdrv_add_child)(
BlockDriverState *parent, BlockDriverState *child, Error **errp);
+ /**
+ * Hot remove a BDS's child. Used in combination with bdrv_add_child, so the
+ * user can take a child offline when it is broken and take a new child
+ * online.
+ *
+ * All block nodes must be drained.
+ */
void GRAPH_WRLOCK_PTR (*bdrv_del_child)(
BlockDriverState *parent, BdrvChild *child, Error **errp);
@@ -983,9 +997,21 @@ struct BdrvChildClass {
bool backing_mask_protocol,
Error **errp);
- bool (*change_aio_ctx)(BdrvChild *child, AioContext *ctx,
- GHashTable *visited, Transaction *tran,
- Error **errp);
+ /*
+ * Notifies the parent that the child is trying to change its AioContext.
+ * The parent may in turn change the AioContext of other nodes in the same
+ * transaction. Returns true if the change is possible and the transaction
+ * can be continued. Returns false and sets @errp if not and the transaction
+ * must be aborted.
+ *
+ * @visited will accumulate all visited BdrvChild objects. The caller is
+ * responsible for freeing the list afterwards.
+ *
+ * Must be called with the affected block nodes drained.
+ */
+ bool GRAPH_RDLOCK_PTR (*change_aio_ctx)(BdrvChild *child, AioContext *ctx,
+ GHashTable *visited,
+ Transaction *tran, Error **errp);
/*
* I/O API functions. These functions are thread-safe.
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 7061ab7..990f3e1 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -137,6 +137,8 @@ BlockJob *block_job_get_locked(const char *id);
* Add @bs to the list of BlockDriverState that are involved in
* @job. This means that all operations will be blocked on @bs while
* @job exists.
+ *
+ * All block nodes must be drained.
*/
int GRAPH_WRLOCK
block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index c2fe6ca..35d59d7 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -375,6 +375,28 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range);
void pci_device_deassert_intx(PCIDevice *dev);
+/* Page Request Interface */
+typedef enum {
+ IOMMU_PRI_RESP_SUCCESS,
+ IOMMU_PRI_RESP_INVALID_REQUEST,
+ IOMMU_PRI_RESP_FAILURE,
+} IOMMUPRIResponseCode;
+
+typedef struct IOMMUPRIResponse {
+ IOMMUPRIResponseCode response_code;
+ uint16_t prgi;
+} IOMMUPRIResponse;
+
+struct IOMMUPRINotifier;
+
+typedef void (*IOMMUPRINotify)(struct IOMMUPRINotifier *notifier,
+ IOMMUPRIResponse *response);
+
+typedef struct IOMMUPRINotifier {
+ IOMMUPRINotify notify;
+} IOMMUPRINotifier;
+
+#define PCI_PRI_PRGI_MASK 0x1ffU
/**
* struct PCIIOMMUOps: callbacks structure for specific IOMMU handlers
@@ -429,6 +451,179 @@ typedef struct PCIIOMMUOps {
* @devfn: device and function number of the PCI device.
*/
void (*unset_iommu_device)(PCIBus *bus, void *opaque, int devfn);
+ /**
+ * @get_iotlb_info: get properties required to initialize a device IOTLB.
+ *
+ * Callback required if devices are allowed to cache translations.
+ *
+ * @opaque: the data passed to pci_setup_iommu().
+ *
+ * @addr_width: the address width of the IOMMU (output parameter).
+ *
+ * @min_page_size: the page size of the IOMMU (output parameter).
+ */
+ void (*get_iotlb_info)(void *opaque, uint8_t *addr_width,
+ uint32_t *min_page_size);
+ /**
+ * @init_iotlb_notifier: initialize an IOMMU notifier.
+ *
+ * Optional callback.
+ *
+ * @bus: the #PCIBus of the PCI device.
+ *
+ * @opaque: the data passed to pci_setup_iommu().
+ *
+ * @devfn: device and function number of the PCI device.
+ *
+ * @n: the notifier to be initialized.
+ *
+ * @fn: the callback to be installed.
+ *
+ * @user_opaque: a user pointer that can be used to track a state.
+ */
+ void (*init_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn,
+ IOMMUNotifier *n, IOMMUNotify fn,
+ void *user_opaque);
+ /**
+ * @register_iotlb_notifier: setup an IOTLB invalidation notifier.
+ *
+ * Callback required if devices are allowed to cache translations.
+ *
+ * @bus: the #PCIBus of the PCI device.
+ *
+ * @opaque: the data passed to pci_setup_iommu().
+ *
+ * @devfn: device and function number of the PCI device.
+ *
+ * @pasid: the pasid of the address space to watch.
+ *
+ * @n: the notifier to register.
+ */
+ void (*register_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn,
+ uint32_t pasid, IOMMUNotifier *n);
+ /**
+ * @unregister_iotlb_notifier: remove an IOTLB invalidation notifier.
+ *
+ * Callback required if devices are allowed to cache translations.
+ *
+ * @bus: the #PCIBus of the PCI device.
+ *
+ * @opaque: the data passed to pci_setup_iommu().
+ *
+ * @devfn: device and function number of the PCI device.
+ *
+ * @pasid: the pasid of the address space to stop watching.
+ *
+ * @n: the notifier to unregister.
+ */
+ void (*unregister_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn,
+ uint32_t pasid, IOMMUNotifier *n);
+ /**
+ * @ats_request_translation: issue an ATS request.
+ *
+ * Callback required if devices are allowed to use the address
+ * translation service.
+ *
+ * @bus: the #PCIBus of the PCI device.
+ *
+ * @opaque: the data passed to pci_setup_iommu().
+ *
+ * @devfn: device and function number of the PCI device.
+ *
+ * @pasid: the pasid of the address space to use for the request.
+ *
+ * @priv_req: privileged mode bit (PASID TLP).
+ *
+ * @exec_req: execute request bit (PASID TLP).
+ *
+ * @addr: start address of the memory range to be translated.
+ *
+ * @length: length of the memory range in bytes.
+ *
+ * @no_write: request a read-only translation (if supported).
+ *
+ * @result: buffer in which the TLB entries will be stored.
+ *
+ * @result_length: result buffer length.
+ *
+ * @err_count: number of untranslated subregions.
+ *
+ * Returns: the number of translations stored in the result buffer, or
+ * -ENOMEM if the buffer is not large enough.
+ */
+ ssize_t (*ats_request_translation)(PCIBus *bus, void *opaque, int devfn,
+ uint32_t pasid, bool priv_req,
+ bool exec_req, hwaddr addr,
+ size_t length, bool no_write,
+ IOMMUTLBEntry *result,
+ size_t result_length,
+ uint32_t *err_count);
+ /**
+ * @pri_register_notifier: setup the PRI completion callback.
+ *
+ * Callback required if devices are allowed to use the page request
+ * interface.
+ *
+ * @bus: the #PCIBus of the PCI device.
+ *
+ * @opaque: the data passed to pci_setup_iommu().
+ *
+ * @devfn: device and function number of the PCI device.
+ *
+ * @pasid: the pasid of the address space to track.
+ *
+ * @notifier: the notifier to register.
+ */
+ void (*pri_register_notifier)(PCIBus *bus, void *opaque, int devfn,
+ uint32_t pasid, IOMMUPRINotifier *notifier);
+ /**
+ * @pri_unregister_notifier: remove the PRI completion callback.
+ *
+ * Callback required if devices are allowed to use the page request
+ * interface.
+ *
+ * @bus: the #PCIBus of the PCI device.
+ *
+ * @opaque: the data passed to pci_setup_iommu().
+ *
+ * @devfn: device and function number of the PCI device.
+ *
+ * @pasid: the pasid of the address space to stop tracking.
+ */
+ void (*pri_unregister_notifier)(PCIBus *bus, void *opaque, int devfn,
+ uint32_t pasid);
+ /**
+ * @pri_request_page: issue a PRI request.
+ *
+ * Callback required if devices are allowed to use the page request
+ * interface.
+ *
+ * @bus: the #PCIBus of the PCI device.
+ *
+ * @opaque: the data passed to pci_setup_iommu().
+ *
+ * @devfn: device and function number of the PCI device.
+ *
+ * @pasid: the pasid of the address space to use for the request.
+ *
+ * @priv_req: privileged mode bit (PASID TLP).
+ *
+ * @exec_req: execute request bit (PASID TLP).
+ *
+ * @addr: untranslated address of the requested page.
+ *
+ * @lpig: last page in group.
+ *
+ * @prgi: page request group index.
+ *
+ * @is_read: request read access.
+ *
+ * @is_write: request write access.
+ */
+ int (*pri_request_page)(PCIBus *bus, void *opaque, int devfn,
+ uint32_t pasid, bool priv_req, bool exec_req,
+ hwaddr addr, bool lpig, uint16_t prgi, bool is_read,
+ bool is_write);
} PCIIOMMUOps;
AddressSpace *pci_device_iommu_address_space(PCIDevice *dev);
@@ -437,6 +632,126 @@ bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod,
void pci_device_unset_iommu_device(PCIDevice *dev);
/**
+ * pci_iommu_get_iotlb_info: get properties required to initialize a
+ * device IOTLB.
+ *
+ * Returns 0 on success, or a negative errno otherwise.
+ *
+ * @dev: the device that wants to get the information.
+ * @addr_width: the address width of the IOMMU (output parameter).
+ * @min_page_size: the page size of the IOMMU (output parameter).
+ */
+int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width,
+ uint32_t *min_page_size);
+
+/**
+ * pci_iommu_init_iotlb_notifier: initialize an IOMMU notifier.
+ *
+ * This function is used by devices before registering an IOTLB notifier.
+ *
+ * @dev: the device.
+ * @n: the notifier to be initialized.
+ * @fn: the callback to be installed.
+ * @opaque: a user pointer that can be used to track a state.
+ */
+int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n,
+ IOMMUNotify fn, void *opaque);
+
+/**
+ * pci_ats_request_translation: perform an ATS request.
+ *
+ * Returns the number of translations stored in @result in case of success,
+ * a negative error code otherwise.
+ * -ENOMEM is returned when the result buffer is not large enough to store
+ * all the translations.
+ *
+ * @dev: the ATS-capable PCI device.
+ * @pasid: the pasid of the address space in which the translation will be done.
+ * @priv_req: privileged mode bit (PASID TLP).
+ * @exec_req: execute request bit (PASID TLP).
+ * @addr: start address of the memory range to be translated.
+ * @length: length of the memory range in bytes.
+ * @no_write: request a read-only translation (if supported).
+ * @result: buffer in which the TLB entries will be stored.
+ * @result_length: result buffer length.
+ * @err_count: number of untranslated subregions.
+ */
+ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid,
+ bool priv_req, bool exec_req,
+ hwaddr addr, size_t length,
+ bool no_write, IOMMUTLBEntry *result,
+ size_t result_length,
+ uint32_t *err_count);
+
+/**
+ * pci_pri_request_page: perform a PRI request.
+ *
+ * Returns 0 if the PRI request has been sent to the guest OS,
+ * an error code otherwise.
+ *
+ * @dev: the PRI-capable PCI device.
+ * @pasid: the pasid of the address space in which the translation will be done.
+ * @priv_req: privileged mode bit (PASID TLP).
+ * @exec_req: execute request bit (PASID TLP).
+ * @addr: untranslated address of the requested page.
+ * @lpig: last page in group.
+ * @prgi: page request group index.
+ * @is_read: request read access.
+ * @is_write: request write access.
+ */
+int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req,
+ bool exec_req, hwaddr addr, bool lpig,
+ uint16_t prgi, bool is_read, bool is_write);
+
+/**
+ * pci_pri_register_notifier: register the PRI callback for a given address
+ * space.
+ *
+ * Returns 0 on success, an error code otherwise.
+ *
+ * @dev: the PRI-capable PCI device.
+ * @pasid: the pasid of the address space to track.
+ * @notifier: the notifier to register.
+ */
+int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid,
+ IOMMUPRINotifier *notifier);
+
+/**
+ * pci_pri_unregister_notifier: remove the PRI callback from a given address
+ * space.
+ *
+ * @dev: the PRI-capable PCI device.
+ * @pasid: the pasid of the address space to stop tracking.
+ */
+void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid);
+
+/**
+ * pci_iommu_register_iotlb_notifier: register a notifier for changes to
+ * IOMMU translation entries in a specific address space.
+ *
+ * Returns 0 on success, or a negative errno otherwise.
+ *
+ * @dev: the device that wants to get notified.
+ * @pasid: the pasid of the address space to track.
+ * @n: the notifier to register.
+ */
+int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid,
+ IOMMUNotifier *n);
+
+/**
+ * pci_iommu_unregister_iotlb_notifier: unregister a notifier that has been
+ * registerd with pci_iommu_register_iotlb_notifier.
+ *
+ * Returns 0 on success, or a negative errno otherwise.
+ *
+ * @dev: the device that wants to stop notifications.
+ * @pasid: the pasid of the address space to stop tracking.
+ * @n: the notifier to unregister.
+ */
+int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid,
+ IOMMUNotifier *n);
+
+/**
* pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus
*
* Let PCI host bridges define specific operations.
@@ -668,6 +983,7 @@ void lsi53c8xx_handle_legacy_cmdline(DeviceState *lsi_dev);
qemu_irq pci_allocate_irq(PCIDevice *pci_dev);
void pci_set_irq(PCIDevice *pci_dev, int level);
+int pci_irq_disabled(PCIDevice *d);
static inline void pci_irq_assert(PCIDevice *pci_dev)
{
diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
index e41d95b..eee0338 100644
--- a/include/hw/pci/pci_device.h
+++ b/include/hw/pci/pci_device.h
@@ -90,6 +90,7 @@ struct PCIDevice {
char name[64];
PCIIORegion io_regions[PCI_NUM_REGIONS];
AddressSpace bus_master_as;
+ bool is_master;
MemoryRegion bus_master_container_region;
MemoryRegion bus_master_enable_region;
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h
index 70a5de0..ff6ce08 100644
--- a/include/hw/pci/pcie.h
+++ b/include/hw/pci/pcie.h
@@ -70,8 +70,10 @@ struct PCIExpressDevice {
uint16_t aer_cap;
PCIEAERLog aer_log;
- /* Offset of ATS capability in config space */
+ /* Offset of ATS, PRI and PASID capabilities in config space */
uint16_t ats_cap;
+ uint16_t pasid_cap;
+ uint16_t pri_cap;
/* ACS */
uint16_t acs_cap;
@@ -150,4 +152,13 @@ void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp);
void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
+
+void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width,
+ bool exec_perm, bool priv_mod);
+void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap,
+ bool prg_response_pasid_req);
+
+bool pcie_pri_enabled(const PCIDevice *dev);
+bool pcie_pasid_enabled(const PCIDevice *dev);
+bool pcie_ats_enabled(const PCIDevice *dev);
#endif /* QEMU_PCIE_H */
diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h
index 9d3b686..33a2222 100644
--- a/include/hw/pci/pcie_regs.h
+++ b/include/hw/pci/pcie_regs.h
@@ -86,6 +86,14 @@ typedef enum PCIExpLinkWidth {
#define PCI_ARI_VER 1
#define PCI_ARI_SIZEOF 8
+/* PASID */
+#define PCI_PASID_VER 1
+#define PCI_EXT_CAP_PASID_MAX_WIDTH 20
+#define PCI_PASID_CAP_WIDTH_SHIFT 8
+
+/* PRI */
+#define PCI_PRI_VER 1
+
/* AER */
#define PCI_ERR_VER 2
#define PCI_ERR_SIZEOF 0x48
diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h
index 3d392b0..9d37f86 100644
--- a/include/hw/vfio/vfio-container-base.h
+++ b/include/hw/vfio/vfio-container-base.h
@@ -78,7 +78,7 @@ void vfio_address_space_insert(VFIOAddressSpace *space,
int vfio_container_dma_map(VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
- void *vaddr, bool readonly);
+ void *vaddr, bool readonly, MemoryRegion *mr);
int vfio_container_dma_unmap(VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
IOMMUTLBEntry *iotlb, bool unmap_all);
@@ -115,13 +115,57 @@ OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU)
struct VFIOIOMMUClass {
ObjectClass parent_class;
- /* basic feature */
+ /**
+ * @setup
+ *
+ * Perform basic setup of the container, including configuring IOMMU
+ * capabilities, IOVA ranges, supported page sizes, etc.
+ *
+ * @bcontainer: #VFIOContainerBase
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Returns true to indicate success and false for error.
+ */
bool (*setup)(VFIOContainerBase *bcontainer, Error **errp);
+
+ /**
+ * @listener_begin
+ *
+ * Called at the beginning of an address space update transaction.
+ * See #MemoryListener.
+ *
+ * @bcontainer: #VFIOContainerBase
+ */
void (*listener_begin)(VFIOContainerBase *bcontainer);
+
+ /**
+ * @listener_commit
+ *
+ * Called at the end of an address space update transaction,
+ * See #MemoryListener.
+ *
+ * @bcontainer: #VFIOContainerBase
+ */
void (*listener_commit)(VFIOContainerBase *bcontainer);
+
+ /**
+ * @dma_map
+ *
+ * Map an address range into the container. Note that the memory region is
+ * referenced within an RCU read lock region across this call.
+ *
+ * @bcontainer: #VFIOContainerBase to use
+ * @iova: start address to map
+ * @size: size of the range to map
+ * @vaddr: process virtual address of mapping
+ * @readonly: true if mapping should be readonly
+ * @mr: the memory region for this mapping
+ *
+ * Returns 0 to indicate success and -errno otherwise.
+ */
int (*dma_map)(const VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
- void *vaddr, bool readonly);
+ void *vaddr, bool readonly, MemoryRegion *mr);
/**
* @dma_unmap
*
@@ -132,12 +176,38 @@ struct VFIOIOMMUClass {
* @size: size of the range to unmap
* @iotlb: The IOMMU TLB mapping entry (or NULL)
* @unmap_all: if set, unmap the entire address space
+ *
+ * Returns 0 to indicate success and -errno otherwise.
*/
int (*dma_unmap)(const VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
IOMMUTLBEntry *iotlb, bool unmap_all);
+
+
+ /**
+ * @attach_device
+ *
+ * Associate the given device with a container and do some related
+ * initialization of the device context.
+ *
+ * @name: name of the device
+ * @vbasedev: the device
+ * @as: address space to use
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Returns true to indicate success and false for error.
+ */
bool (*attach_device)(const char *name, VFIODevice *vbasedev,
AddressSpace *as, Error **errp);
+
+ /*
+ * @detach_device
+ *
+ * Detach the given device from its container and clean up any necessary
+ * state.
+ *
+ * @vbasedev: the device to disassociate
+ */
void (*detach_device)(VFIODevice *vbasedev);
/* migration feature */
@@ -152,7 +222,7 @@ struct VFIOIOMMUClass {
* @start: indicates whether to start or stop dirty pages tracking
* @errp: pointer to Error*, to store an error if it happens.
*
- * Returns zero to indicate success and negative for error
+ * Returns zero to indicate success and negative for error.
*/
int (*set_dirty_page_tracking)(const VFIOContainerBase *bcontainer,
bool start, Error **errp);
@@ -167,7 +237,7 @@ struct VFIOIOMMUClass {
* @size: size of iova range
* @errp: pointer to Error*, to store an error if it happens.
*
- * Returns zero to indicate success and negative for error
+ * Returns zero to indicate success and negative for error.
*/
int (*query_dirty_bitmap)(const VFIOContainerBase *bcontainer,
VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp);
@@ -183,4 +253,7 @@ struct VFIOIOMMUClass {
void (*release)(VFIOContainerBase *bcontainer);
};
+VFIORamDiscardListener *vfio_find_ram_discard_listener(
+ VFIOContainerBase *bcontainer, MemoryRegionSection *section);
+
#endif /* HW_VFIO_VFIO_CONTAINER_BASE_H */
diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h
new file mode 100644
index 0000000..750ea5b
--- /dev/null
+++ b/include/hw/vfio/vfio-cpr.h
@@ -0,0 +1,18 @@
+/*
+ * VFIO CPR
+ *
+ * Copyright (c) 2025 Oracle and/or its affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_VFIO_VFIO_CPR_H
+#define HW_VFIO_VFIO_CPR_H
+
+struct VFIOContainerBase;
+
+bool vfio_cpr_register_container(struct VFIOContainerBase *bcontainer,
+ Error **errp);
+void vfio_cpr_unregister_container(struct VFIOContainerBase *bcontainer);
+
+#endif /* HW_VFIO_VFIO_CPR_H */
diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h
index 0a9575b..449bf5c 100644
--- a/include/hw/virtio/vhost-vdpa.h
+++ b/include/hw/virtio/vhost-vdpa.h
@@ -43,7 +43,21 @@ typedef struct vhost_vdpa_shared {
struct vhost_vdpa_iova_range iova_range;
QLIST_HEAD(, vdpa_iommu) iommu_list;
- /* IOVA mapping used by the Shadow Virtqueue */
+ /*
+ * IOVA mapping used by the Shadow Virtqueue
+ *
+ * It is shared among all ASID for simplicity, whether CVQ shares ASID with
+ * guest or not:
+ * - Memory listener need access to guest's memory addresses allocated in
+ * the IOVA tree.
+ * - There should be plenty of IOVA address space for both ASID not to
+ * worry about collisions between them. Guest's translations are still
+ * validated with virtio virtqueue_pop so there is no risk for the guest
+ * to access memory that it shouldn't.
+ *
+ * To allocate a iova tree per ASID is doable but it complicates the code
+ * and it is not worth it for the moment.
+ */
VhostIOVATree *iova_tree;
/* Copy of backend features */
@@ -51,6 +65,12 @@ typedef struct vhost_vdpa_shared {
bool iotlb_batch_begin_sent;
+ /*
+ * The memory listener has been registered, so DMA maps have been sent to
+ * the device.
+ */
+ bool listener_registered;
+
/* Vdpa must send shadow addresses as IOTLB key for data queues, not GPA */
bool shadow_data;
diff --git a/include/system/host_iommu_device.h b/include/system/host_iommu_device.h
index 809cced..ab849a4 100644
--- a/include/system/host_iommu_device.h
+++ b/include/system/host_iommu_device.h
@@ -14,6 +14,13 @@
#include "qom/object.h"
#include "qapi/error.h"
+#ifdef CONFIG_LINUX
+#include "linux/iommufd.h"
+
+typedef union VendorCaps {
+ struct iommu_hw_info_vtd vtd;
+ struct iommu_hw_info_arm_smmuv3 smmuv3;
+} VendorCaps;
/**
* struct HostIOMMUDeviceCaps - Define host IOMMU device capabilities.
@@ -22,11 +29,17 @@
*
* @hw_caps: host platform IOMMU capabilities (e.g. on IOMMUFD this represents
* the @out_capabilities value returned from IOMMU_GET_HW_INFO ioctl)
+ *
+ * @vendor_caps: host platform IOMMU vendor specific capabilities (e.g. on
+ * IOMMUFD this represents a user-space buffer filled by kernel
+ * with host IOMMU @type specific hardware information data)
*/
typedef struct HostIOMMUDeviceCaps {
uint32_t type;
uint64_t hw_caps;
+ VendorCaps vendor_caps;
} HostIOMMUDeviceCaps;
+#endif
#define TYPE_HOST_IOMMU_DEVICE "host-iommu-device"
OBJECT_DECLARE_TYPE(HostIOMMUDevice, HostIOMMUDeviceClass, HOST_IOMMU_DEVICE)
@@ -38,7 +51,9 @@ struct HostIOMMUDevice {
void *agent; /* pointer to agent device, ie. VFIO or VDPA device */
PCIBus *aliased_bus;
int aliased_devfn;
+#ifdef CONFIG_LINUX
HostIOMMUDeviceCaps caps;
+#endif
};
/**
diff --git a/include/system/iommufd.h b/include/system/iommufd.h
index cbab75b..283861b 100644
--- a/include/system/iommufd.h
+++ b/include/system/iommufd.h
@@ -61,6 +61,60 @@ bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, uint32_t hwpt_id,
uint64_t iova, ram_addr_t size,
uint64_t page_size, uint64_t *data,
Error **errp);
+bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id,
+ uint32_t data_type, uint32_t entry_len,
+ uint32_t *entry_num, void *data,
+ Error **errp);
#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd"
+OBJECT_DECLARE_TYPE(HostIOMMUDeviceIOMMUFD, HostIOMMUDeviceIOMMUFDClass,
+ HOST_IOMMU_DEVICE_IOMMUFD)
+
+/* Overload of the host IOMMU device for the iommufd backend */
+struct HostIOMMUDeviceIOMMUFD {
+ HostIOMMUDevice parent_obj;
+
+ IOMMUFDBackend *iommufd;
+ uint32_t devid;
+ uint32_t hwpt_id;
+};
+
+struct HostIOMMUDeviceIOMMUFDClass {
+ HostIOMMUDeviceClass parent_class;
+
+ /**
+ * @attach_hwpt: attach host IOMMU device to IOMMUFD hardware page table.
+ * VFIO and VDPA device can have different implementation.
+ *
+ * Mandatory callback.
+ *
+ * @idev: host IOMMU device backed by IOMMUFD backend.
+ *
+ * @hwpt_id: ID of IOMMUFD hardware page table.
+ *
+ * @errp: pass an Error out when attachment fails.
+ *
+ * Returns: true on success, false on failure.
+ */
+ bool (*attach_hwpt)(HostIOMMUDeviceIOMMUFD *idev, uint32_t hwpt_id,
+ Error **errp);
+ /**
+ * @detach_hwpt: detach host IOMMU device from IOMMUFD hardware page table.
+ * VFIO and VDPA device can have different implementation.
+ *
+ * Mandatory callback.
+ *
+ * @idev: host IOMMU device backed by IOMMUFD backend.
+ *
+ * @errp: pass an Error out when attachment fails.
+ *
+ * Returns: true on success, false on failure.
+ */
+ bool (*detach_hwpt)(HostIOMMUDeviceIOMMUFD *idev, Error **errp);
+};
+
+bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev,
+ uint32_t hwpt_id, Error **errp);
+bool host_iommu_device_iommufd_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev,
+ Error **errp);
#endif
diff --git a/include/system/memory.h b/include/system/memory.h
index fbbf4cf..0848690 100644
--- a/include/system/memory.h
+++ b/include/system/memory.h
@@ -183,6 +183,7 @@ struct IOMMUNotifier {
hwaddr start;
hwaddr end;
int iommu_idx;
+ void *opaque;
QLIST_ENTRY(IOMMUNotifier) node;
};
typedef struct IOMMUNotifier IOMMUNotifier;
@@ -738,21 +739,20 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
RamDiscardListener *rdl);
/**
- * memory_get_xlat_addr: Extract addresses from a TLB entry
+ * memory_translate_iotlb: Extract addresses from a TLB entry.
+ * Called with rcu_read_lock held.
*
* @iotlb: pointer to an #IOMMUTLBEntry
- * @vaddr: virtual address
- * @ram_addr: RAM address
- * @read_only: indicates if writes are allowed
- * @mr_has_discard_manager: indicates memory is controlled by a
- * RamDiscardManager
+ * @xlat_p: return the offset of the entry from the start of the returned
+ * MemoryRegion.
* @errp: pointer to Error*, to store an error if it happens.
*
- * Return: true on success, else false setting @errp with error.
+ * Return: On success, return the MemoryRegion containing the @iotlb translated
+ * addr. The MemoryRegion must not be accessed after rcu_read_unlock.
+ * On failure, return NULL, setting @errp with error.
*/
-bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
- ram_addr_t *ram_addr, bool *read_only,
- bool *mr_has_discard_manager, Error **errp);
+MemoryRegion *memory_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p,
+ Error **errp);
typedef struct CoalescedMemoryRange CoalescedMemoryRange;
typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd;
diff --git a/meson.build b/meson.build
index ef99467..967a10e 100644
--- a/meson.build
+++ b/meson.build
@@ -106,6 +106,7 @@ if have_rust
endif
if have_rust
+ rustdoc = find_program('rustdoc', required: get_option('rust'))
bindgen = find_program('bindgen', required: get_option('rust'))
if not bindgen.found() or bindgen.version().version_compare('<0.60.0')
if get_option('rust').enabled()
@@ -4134,13 +4135,12 @@ common_all = static_library('common',
target_common_arch_libs = {}
target_common_system_arch_libs = {}
foreach target_base_arch, config_base_arch : config_base_arch_mak
- config_target = config_target_mak[target]
target_inc = [include_directories('target' / target_base_arch)]
inc = [common_user_inc + target_inc]
- target_common = common_ss.apply(config_target, strict: false)
- target_system = system_ss.apply(config_target, strict: false)
- target_user = user_ss.apply(config_target, strict: false)
+ target_common = common_ss.apply(config_base_arch, strict: false)
+ target_system = system_ss.apply(config_base_arch, strict: false)
+ target_user = user_ss.apply(config_base_arch, strict: false)
common_deps = []
system_deps = []
user_deps = []
@@ -4403,7 +4403,7 @@ foreach target : target_dirs
build_by_default: true,
build_always_stale: true)
rlib = static_library('rust_' + target.underscorify(),
- rlib_rs,
+ structured_sources([], {'.': rlib_rs}),
dependencies: target_rust.dependencies(),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'c')
@@ -4757,6 +4757,7 @@ if have_rust
summary_info += {'Rust target': config_host['RUST_TARGET_TRIPLE']}
summary_info += {'rustc': ' '.join(rustc.cmd_array())}
summary_info += {'rustc version': rustc.version()}
+ summary_info += {'rustdoc': rustdoc}
summary_info += {'bindgen': bindgen.full_path()}
summary_info += {'bindgen version': bindgen.version()}
endif
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
index 7ca8b46..58d7389 100644
--- a/net/vhost-vdpa.c
+++ b/net/vhost-vdpa.c
@@ -235,6 +235,7 @@ static void vhost_vdpa_cleanup(NetClientState *nc)
return;
}
qemu_close(s->vhost_vdpa.shared->device_fd);
+ g_clear_pointer(&s->vhost_vdpa.shared->iova_tree, vhost_iova_tree_delete);
g_free(s->vhost_vdpa.shared);
}
@@ -362,14 +363,8 @@ static int vdpa_net_migration_state_notifier(NotifierWithReturn *notifier,
static void vhost_vdpa_net_data_start_first(VhostVDPAState *s)
{
- struct vhost_vdpa *v = &s->vhost_vdpa;
-
migration_add_notifier(&s->migration_state,
vdpa_net_migration_state_notifier);
- if (v->shadow_vqs_enabled) {
- v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first,
- v->shared->iova_range.last);
- }
}
static int vhost_vdpa_net_data_start(NetClientState *nc)
@@ -416,19 +411,12 @@ static int vhost_vdpa_net_data_load(NetClientState *nc)
static void vhost_vdpa_net_client_stop(NetClientState *nc)
{
VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
- struct vhost_dev *dev;
assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
if (s->vhost_vdpa.index == 0) {
migration_remove_notifier(&s->migration_state);
}
-
- dev = s->vhost_vdpa.dev;
- if (dev->vq_index + dev->nvqs == dev->vq_index_end) {
- g_clear_pointer(&s->vhost_vdpa.shared->iova_tree,
- vhost_iova_tree_delete);
- }
}
static NetClientInfo net_vhost_vdpa_info = {
@@ -600,24 +588,6 @@ out:
return 0;
}
- /*
- * If other vhost_vdpa already have an iova_tree, reuse it for simplicity,
- * whether CVQ shares ASID with guest or not, because:
- * - Memory listener need access to guest's memory addresses allocated in
- * the IOVA tree.
- * - There should be plenty of IOVA address space for both ASID not to
- * worry about collisions between them. Guest's translations are still
- * validated with virtio virtqueue_pop so there is no risk for the guest
- * to access memory that it shouldn't.
- *
- * To allocate a iova tree per ASID is doable but it complicates the code
- * and it is not worth it for the moment.
- */
- if (!v->shared->iova_tree) {
- v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first,
- v->shared->iova_range.last);
- }
-
r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer,
vhost_vdpa_net_cvq_cmd_page_len(), false);
if (unlikely(r < 0)) {
@@ -1726,6 +1696,8 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
s->vhost_vdpa.shared->device_fd = vdpa_device_fd;
s->vhost_vdpa.shared->iova_range = iova_range;
s->vhost_vdpa.shared->shadow_data = svq;
+ s->vhost_vdpa.shared->iova_tree = vhost_iova_tree_new(iova_range.first,
+ iova_range.last);
} else if (!is_datapath) {
s->cvq_cmd_out_buffer = mmap(NULL, vhost_vdpa_net_cvq_cmd_page_len(),
PROT_READ | PROT_WRITE,
diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py
index 0405e91..b47db00 100755
--- a/python/scripts/vendor.py
+++ b/python/scripts/vendor.py
@@ -41,8 +41,8 @@ def main() -> int:
parser.parse_args()
packages = {
- "meson==1.5.0":
- "52b34f4903b882df52ad0d533146d4b992c018ea77399f825579737672ae7b20",
+ "meson==1.8.1":
+ "374bbf71247e629475fc10b0bd2ef66fc418c2d8f4890572f74de0f97d0d42da",
}
vendor_dir = Path(__file__, "..", "..", "wheels").resolve()
diff --git a/python/wheels/meson-1.5.0-py3-none-any.whl b/python/wheels/meson-1.5.0-py3-none-any.whl
deleted file mode 100644
index c7edeb3..0000000
--- a/python/wheels/meson-1.5.0-py3-none-any.whl
+++ /dev/null
Binary files differ
diff --git a/python/wheels/meson-1.8.1-py3-none-any.whl b/python/wheels/meson-1.8.1-py3-none-any.whl
new file mode 100644
index 0000000..a885f0e
--- /dev/null
+++ b/python/wheels/meson-1.8.1-py3-none-any.whl
Binary files differ
diff --git a/pythondeps.toml b/pythondeps.toml
index 7eaaa0f..7884ab5 100644
--- a/pythondeps.toml
+++ b/pythondeps.toml
@@ -19,7 +19,7 @@
[meson]
# The install key should match the version in python/wheels/
-meson = { accepted = ">=1.5.0", installed = "1.5.0", canary = "meson" }
+meson = { accepted = ">=1.5.0", installed = "1.8.1", canary = "meson" }
pycotap = { accepted = ">=1.1.0", installed = "1.3.1" }
[docs]
diff --git a/qapi/acpi.json b/qapi/acpi.json
index 045dab6..2d53b82 100644
--- a/qapi/acpi.json
+++ b/qapi/acpi.json
@@ -80,7 +80,7 @@
##
# @ACPIOSTInfo:
#
-# OSPM Status Indication for a device For description of possible
+# OSPM Status Indication for a device. For description of possible
# values of @source and @status fields see "_OST (OSPM Status
# Indication)" chapter of ACPI5.0 spec.
#
diff --git a/qapi/audio.json b/qapi/audio.json
index 8de4430..16de231 100644
--- a/qapi/audio.json
+++ b/qapi/audio.json
@@ -309,9 +309,9 @@
#
# @name: name of the sink/source to use
#
-# @stream-name: name of the PulseAudio stream created by qemu. Can be
+# @stream-name: name of the PulseAudio stream created by QEMU. Can be
# used to identify the stream in PulseAudio when you create
-# multiple PulseAudio devices or run multiple qemu instances
+# multiple PulseAudio devices or run multiple QEMU instances
# (default: audiodev's id, since 4.2)
#
# @latency: latency you want PulseAudio to achieve in microseconds
@@ -353,9 +353,9 @@
#
# @name: name of the sink/source to use
#
-# @stream-name: name of the PipeWire stream created by qemu. Can be
+# @stream-name: name of the PipeWire stream created by QEMU. Can be
# used to identify the stream in PipeWire when you create multiple
-# PipeWire devices or run multiple qemu instances (default:
+# PipeWire devices or run multiple QEMU instances (default:
# audiodev's id)
#
# @latency: latency you want PipeWire to achieve in microseconds
diff --git a/qapi/block-core.json b/qapi/block-core.json
index b411511..1df6644 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -31,8 +31,8 @@
# @icount: Current instruction count. Appears when execution
# record/replay is enabled. Used for "time-traveling" to match
# the moment in the recorded execution with the snapshots. This
-# counter may be obtained through @query-replay command (since
-# 5.2)
+# counter may be obtained through @query-replay command
+# (since 5.2)
#
# Since: 1.3
##
@@ -488,7 +488,7 @@
#
# @active: true if the backend is active; typical cases for inactive backends
# are on the migration source instance after migration completes and on the
-# destination before it completes. (since: 10.0)
+# destination before it completes. (since: 10.0)
#
# @encrypted: true if the backing device is encrypted
#
@@ -510,11 +510,11 @@
#
# @bps_max: total throughput limit during bursts, in bytes (Since 1.7)
#
-# @bps_rd_max: read throughput limit during bursts, in bytes (Since
-# 1.7)
+# @bps_rd_max: read throughput limit during bursts, in bytes
+# (Since 1.7)
#
-# @bps_wr_max: write throughput limit during bursts, in bytes (Since
-# 1.7)
+# @bps_wr_max: write throughput limit during bursts, in bytes
+# (Since 1.7)
#
# @iops_max: total I/O operations per second during bursts, in bytes
# (Since 1.7)
@@ -951,11 +951,11 @@
# @unmap_operations: The number of unmap operations performed by the
# device (Since 4.2)
#
-# @rd_total_time_ns: Total time spent on reads in nanoseconds (since
-# 0.15).
+# @rd_total_time_ns: Total time spent on reads in nanoseconds
+# (since 0.15)
#
-# @wr_total_time_ns: Total time spent on writes in nanoseconds (since
-# 0.15).
+# @wr_total_time_ns: Total time spent on writes in nanoseconds
+# (since 0.15)
#
# @zone_append_total_time_ns: Total time spent on zone append writes
# in nanoseconds (since 8.1)
@@ -1322,8 +1322,8 @@
# @incremental: only copy data described by the dirty bitmap.
# (since: 2.4)
#
-# @bitmap: only copy data described by the dirty bitmap. (since: 4.2)
-# Behavior on completion is determined by the BitmapSyncMode.
+# @bitmap: only copy data described by the dirty bitmap. Behavior on
+# completion is determined by the BitmapSyncMode. (since: 4.2)
#
# Since: 1.3
##
@@ -1337,7 +1337,7 @@
# bitmap when used for data copy operations.
#
# @on-success: The bitmap is only synced when the operation is
-# successful. This is the behavior always used for 'INCREMENTAL'
+# successful. This is the behavior always used for incremental
# backups.
#
# @never: The bitmap is never synchronized with the operation, and is
@@ -1417,8 +1417,8 @@
# @auto-finalize: Job will finalize itself when PENDING, moving to the
# CONCLUDED state. (since 2.12)
#
-# @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the
-# NULL state and disappearing from the query list. (since 2.12)
+# @auto-dismiss: Job will dismiss itself when CONCLUDED, and
+# disappear. (since 2.12)
#
# @error: Error information if the job did not complete successfully.
# Not set if the job completed successfully. (since 2.12.1)
@@ -1502,15 +1502,15 @@
#
# @device: the name of the device to take a snapshot of.
#
-# @node-name: graph node name to generate the snapshot from (Since
-# 2.0)
+# @node-name: graph node name to generate the snapshot from
+# (Since 2.0)
#
# @snapshot-file: the target of the new overlay image. If the file
# exists, or if it is a device, the overlay will be created in the
# existing file/device. Otherwise, a new file will be created.
#
-# @snapshot-node-name: the graph node name of the new image (Since
-# 2.0)
+# @snapshot-node-name: the graph node name of the new image
+# (Since 2.0)
#
# @format: the format of the overlay image, default is 'qcow2'.
#
@@ -1589,7 +1589,7 @@
#
# @bitmap-mode: Specifies the type of data the bitmap should contain
# after the operation concludes. Must be present if a bitmap was
-# provided, Must NOT be present otherwise. (Since 4.2)
+# provided, must **not** be present otherwise. (Since 4.2)
#
# @compress: true to compress data, if the target format supports it.
# (default: false) (since 2.8)
@@ -1606,16 +1606,15 @@
# copy-before-write jobs; defaults to break-guest-write. (Since 10.1)
#
# @auto-finalize: When false, this job will wait in a PENDING state
-# after it has finished its work, waiting for @block-job-finalize
-# before making any block graph changes. When true, this job will
+# after it has finished its work, waiting for @job-finalize before
+# making any block graph changes. When true, this job will
# automatically perform its abort or commit actions. Defaults to
# true. (Since 2.12)
#
# @auto-dismiss: When false, this job will wait in a CONCLUDED state
# after it has completely ceased all work, and awaits
-# @block-job-dismiss. When true, this job will automatically
-# disappear from the query list without user intervention.
-# Defaults to true. (Since 2.12)
+# @job-dismiss. When true, this job will automatically disappear
+# without user intervention. Defaults to true. (Since 2.12)
#
# @filter-node-name: the node name that should be assigned to the
# filter driver that the backup job inserts into the graph above
@@ -1785,8 +1784,7 @@
# If top == base, that is an error. If top has no overlays on top of
# it, or if it is in use by a writer, the job will not be completed by
# itself. The user needs to complete the job with the
-# block-job-complete command after getting the ready event. (Since
-# 2.0)
+# job-complete command after getting the ready event. (Since 2.0)
#
# If the base image is smaller than top, then the base image will be
# resized to be the same size as top. If top is smaller than the base
@@ -1840,7 +1838,7 @@
# @speed: the maximum speed, in bytes per second
#
# @on-error: the action to take on an error. 'ignore' means that the
-# request should be retried. (default: report; Since: 5.0)
+# request should be retried. (default: report; since: 5.0)
#
# @filter-node-name: the node name that should be assigned to the
# filter driver that the commit job inserts into the graph above
@@ -1848,16 +1846,15 @@
# autogenerated. (Since: 2.9)
#
# @auto-finalize: When false, this job will wait in a PENDING state
-# after it has finished its work, waiting for @block-job-finalize
-# before making any block graph changes. When true, this job will
+# after it has finished its work, waiting for @job-finalize before
+# making any block graph changes. When true, this job will
# automatically perform its abort or commit actions. Defaults to
# true. (Since 3.1)
#
# @auto-dismiss: When false, this job will wait in a CONCLUDED state
# after it has completely ceased all work, and awaits
-# @block-job-dismiss. When true, this job will automatically
-# disappear from the query list without user intervention.
-# Defaults to true. (Since 3.1)
+# @job-dismiss. When true, this job will automatically disappear
+# without user intervention. Defaults to true. (Since 3.1)
#
# Features:
#
@@ -1895,7 +1892,7 @@
# The status of ongoing drive-backup operations can be checked with
# query-block-jobs where the BlockJobInfo.type field has the value
# 'backup'. The operation can be stopped before it has completed
-# using the block-job-cancel command.
+# using the job-cancel or block-job-cancel command.
#
# Features:
#
@@ -1926,7 +1923,7 @@
# The status of ongoing blockdev-backup operations can be checked with
# query-block-jobs where the BlockJobInfo.type field has the value
# 'backup'. The operation can be stopped before it has completed
-# using the block-job-cancel command.
+# using the job-cancel or block-job-cancel command.
#
# Errors:
# - If @device is not a valid block device, DeviceNotFound
@@ -2030,7 +2027,7 @@
#
# @id: Block graph node identifier. This @id is generated only for
# x-debug-query-block-graph and does not relate to any other
-# identifiers in Qemu.
+# identifiers in QEMU.
#
# @type: Type of graph node. Can be one of block-backend, block-job
# or block-driver-state.
@@ -2169,8 +2166,8 @@
# @format: the format of the new destination, default is to probe if
# @mode is 'existing', else the format of the source
#
-# @node-name: the new block driver state node name in the graph (Since
-# 2.1)
+# @node-name: the new block driver state node name in the graph
+# (Since 2.1)
#
# @replaces: with sync=full graph node name to be replaced by the new
# image when a whole image copy is done. This can be used to
@@ -2212,16 +2209,15 @@
# 'background' (Since: 3.0)
#
# @auto-finalize: When false, this job will wait in a PENDING state
-# after it has finished its work, waiting for @block-job-finalize
-# before making any block graph changes. When true, this job will
+# after it has finished its work, waiting for @job-finalize before
+# making any block graph changes. When true, this job will
# automatically perform its abort or commit actions. Defaults to
# true. (Since 3.1)
#
# @auto-dismiss: When false, this job will wait in a CONCLUDED state
# after it has completely ceased all work, and awaits
-# @block-job-dismiss. When true, this job will automatically
-# disappear from the query list without user intervention.
-# Defaults to true. (Since 3.1)
+# @job-dismiss. When true, this job will automatically disappear
+# without user intervention. Defaults to true. (Since 3.1)
#
# Since: 1.3
##
@@ -2531,16 +2527,15 @@
# 'background' (Since: 3.0)
#
# @auto-finalize: When false, this job will wait in a PENDING state
-# after it has finished its work, waiting for @block-job-finalize
-# before making any block graph changes. When true, this job will
+# after it has finished its work, waiting for @job-finalize before
+# making any block graph changes. When true, this job will
# automatically perform its abort or commit actions. Defaults to
# true. (Since 3.1)
#
# @auto-dismiss: When false, this job will wait in a CONCLUDED state
# after it has completely ceased all work, and awaits
-# @block-job-dismiss. When true, this job will automatically
-# disappear from the query list without user intervention.
-# Defaults to true. (Since 3.1)
+# @job-dismiss. When true, this job will automatically disappear
+# without user intervention. Defaults to true. (Since 3.1)
#
# @target-is-zero: Assume the destination reads as all zeroes before
# the mirror started. Setting this to true can speed up the
@@ -2593,11 +2588,11 @@
#
# @bps_max: total throughput limit during bursts, in bytes (Since 1.7)
#
-# @bps_rd_max: read throughput limit during bursts, in bytes (Since
-# 1.7)
+# @bps_rd_max: read throughput limit during bursts, in bytes
+# (Since 1.7)
#
-# @bps_wr_max: write throughput limit during bursts, in bytes (Since
-# 1.7)
+# @bps_wr_max: write throughput limit during bursts, in bytes
+# (Since 1.7)
#
# @iops_max: total I/O operations per second during bursts, in bytes
# (Since 1.7)
@@ -2667,7 +2662,7 @@
# @iops-total-max: I/O operations burst
#
# @iops-total-max-length: length of the iops-total-max burst period,
-# in seconds It must only be set if @iops-total-max is set as
+# in seconds. It must only be set if @iops-total-max is set as
# well.
#
# @iops-read: limit read operations per second
@@ -2675,14 +2670,14 @@
# @iops-read-max: I/O operations read burst
#
# @iops-read-max-length: length of the iops-read-max burst period, in
-# seconds It must only be set if @iops-read-max is set as well.
+# seconds. It must only be set if @iops-read-max is set as well.
#
# @iops-write: limit write operations per second
#
# @iops-write-max: I/O operations write burst
#
# @iops-write-max-length: length of the iops-write-max burst period,
-# in seconds It must only be set if @iops-write-max is set as
+# in seconds. It must only be set if @iops-write-max is set as
# well.
#
# @bps-total: limit total bytes per second
@@ -2697,14 +2692,14 @@
# @bps-read-max: total bytes read burst
#
# @bps-read-max-length: length of the bps-read-max burst period, in
-# seconds It must only be set if @bps-read-max is set as well.
+# seconds. It must only be set if @bps-read-max is set as well.
#
# @bps-write: limit write bytes per second
#
# @bps-write-max: total bytes write burst
#
# @bps-write-max-length: length of the bps-write-max burst period, in
-# seconds It must only be set if @bps-write-max is set as well.
+# seconds. It must only be set if @bps-write-max is set as well.
#
# @iops-size: when limiting by iops max size of an I/O in bytes
#
@@ -2789,12 +2784,12 @@
# immediately once streaming has started. The status of ongoing block
# streaming operations can be checked with query-block-jobs. The
# operation can be stopped before it has completed using the
-# block-job-cancel command.
+# job-cancel or block-job-cancel command.
#
# The node that receives the data is called the top image, can be
# located in any part of the chain (but always above the base image;
# see below) and can be specified using its device or node name.
-# Earlier qemu versions only allowed 'device' to name the top level
+# Earlier QEMU versions only allowed 'device' to name the top level
# node; presence of the 'base-node' parameter during introspection can
# be used as a witness of the enhanced semantics of 'device'.
#
@@ -2859,16 +2854,15 @@
# autogenerated. (Since: 6.0)
#
# @auto-finalize: When false, this job will wait in a PENDING state
-# after it has finished its work, waiting for @block-job-finalize
-# before making any block graph changes. When true, this job will
+# after it has finished its work, waiting for @job-finalize before
+# making any block graph changes. When true, this job will
# automatically perform its abort or commit actions. Defaults to
# true. (Since 3.1)
#
# @auto-dismiss: When false, this job will wait in a CONCLUDED state
# after it has completely ceased all work, and awaits
-# @block-job-dismiss. When true, this job will automatically
-# disappear from the query list without user intervention.
-# Defaults to true. (Since 3.1)
+# @job-dismiss. When true, this job will automatically disappear
+# without user intervention. Defaults to true. (Since 3.1)
#
# Errors:
# - If @device does not exist, DeviceNotFound.
@@ -3030,10 +3024,10 @@
# state. Completing the job in any other state is an error.
#
# This is supported only for drive mirroring, where it also switches
-# the device to write to the target path only. Note that drive
+# the device to write to the target path only. Note that drive
# mirroring includes drive-mirror, blockdev-mirror and block-commit
# job (only in case of "active commit", when the node being commited
-# is used by the guest). The ability to complete is signaled with a
+# is used by the guest). The ability to complete is signaled with a
# BLOCK_JOB_READY event.
#
# This command completes an active background block operation
@@ -3068,16 +3062,16 @@
#
# 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. In turn, automatic dismiss may be enabled only
+# dismiss enabled. In turn, automatic dismiss may be enabled only
# for jobs that have @auto-dismiss option, which are drive-backup,
# blockdev-backup, drive-mirror, blockdev-mirror, block-commit and
-# block-stream. @auto-dismiss is enabled by default for these
+# block-stream. @auto-dismiss is enabled by default for these
# jobs.
#
# 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 the BLOCK_JOB_READY event, block-job-cancel or
-# block-job-complete will still need to be used as appropriate.
+# reached its terminal state, CONCLUDED. For jobs that make use of
+# the BLOCK_JOB_READY event, job-cancel, block-job-cancel or
+# job-complete will still need to be used as appropriate.
#
# @id: The job identifier.
#
@@ -3196,7 +3190,7 @@
#
# Selects the AIO backend to handle I/O requests
#
-# @threads: Use qemu's thread pool
+# @threads: Use QEMU's thread pool
#
# @native: Use native AIO backend (only Linux and Windows)
#
@@ -3415,8 +3409,8 @@
# Driver specific block device options for LUKS.
#
# @key-secret: the ID of a QCryptoSecret object providing the
-# decryption key (since 2.6). Mandatory except when doing a
-# metadata-only probe of the image.
+# decryption key. Mandatory except when doing a metadata-only
+# probe of the image. (since 2.6)
#
# @header: block device holding a detached LUKS header. (since 9.0)
#
@@ -3655,8 +3649,8 @@
# this feature. (since 2.5)
#
# @encrypt: Image decryption options. Mandatory for encrypted images,
-# except when doing a metadata-only probe of the image. (since
-# 2.10)
+# except when doing a metadata-only probe of the image.
+# (since 2.10)
#
# @data-file: reference to or definition of the external data file.
# This may only be specified for images that require an external
@@ -4326,8 +4320,8 @@
# @user: Ceph id name.
#
# @auth-client-required: Acceptable authentication modes. This maps
-# to Ceph configuration option "auth_client_required". (Since
-# 3.0)
+# to Ceph configuration option "auth_client_required".
+# (Since 3.0)
#
# @key-secret: ID of a QCryptoSecret object providing a key for cephx
# authentication. This maps to Ceph configuration option "key".
@@ -4581,8 +4575,8 @@
# error. During the first @reconnect-delay seconds, all requests
# are paused and will be rerun on a successful reconnect. After
# that time, any delayed requests and all future requests before a
-# successful reconnect will immediately fail. Default 0 (Since
-# 4.2)
+# successful reconnect will immediately fail. Default 0
+# (Since 4.2)
#
# @open-timeout: In seconds. If zero, the nbd driver tries the
# connection only once, and fails to open if the connection fails.
@@ -4724,11 +4718,11 @@
#
# @driver: block driver name
#
-# @node-name: the node name of the new node (Since 2.0). This option
-# is required on the top level of blockdev-add. Valid node names
-# start with an alphabetic character and may contain only
-# alphanumeric characters, '-', '.' and '_'. Their maximum length
-# is 31 characters.
+# @node-name: the node name of the new node. This option is required
+# on the top level of blockdev-add. Valid node names start with
+# an alphabetic character and may contain only alphanumeric
+# characters, '-', '.' and '_'. Their maximum length is 31
+# characters. (Since 2.0)
#
# @discard: discard-related options (default: ignore)
#
@@ -4737,7 +4731,7 @@
# @active: whether the block node should be activated (default: true).
# Having inactive block nodes is useful primarily for migration because it
# allows opening an image on the destination while the source is still
-# holding locks for it. (Since 10.0)
+# holding locks for it. (Since 10.0)
#
# @read-only: whether the block device should be read-only (default:
# false). Note that some block drivers support only read-only
@@ -4947,7 +4941,7 @@
# 3) A reference to a different node: the current child is replaced
# with the specified one.
#
-# 4) NULL: the current child (if any) is detached.
+# 4) null: the current child (if any) is detached.
#
# Options (1) and (2) are supported in all cases. Option (3) is
# supported for @file and @backing, and option (4) for @backing only.
@@ -4999,14 +4993,14 @@
##
# @blockdev-set-active:
#
-# Activate or inactivate a block device. Use this to manage the handover of
+# Activate or inactivate a block device. Use this to manage the handover of
# block devices on migration with qemu-storage-daemon.
#
# Activating a node automatically activates all of its child nodes first.
# Inactivating a node automatically inactivates any of its child nodes that are
# not in use by a still active node.
#
-# @node-name: Name of the graph node to activate or inactivate. By default, all
+# @node-name: Name of the graph node to activate or inactivate. By default, all
# nodes are affected by the operation.
#
# @active: true if the nodes should be active when the command returns success,
@@ -5157,10 +5151,10 @@
##
# @BlockdevQcow2Version:
#
-# @v2: The original QCOW2 format as introduced in qemu 0.10 (version
+# @v2: The original QCOW2 format as introduced in QEMU 0.10 (version
# 2)
#
-# @v3: The extended QCOW2 format as introduced in qemu 1.1 (version 3)
+# @v3: The extended QCOW2 format as introduced in QEMU 1.1 (version 3)
#
# Since: 2.12
##
@@ -5580,7 +5574,7 @@
# @x-blockdev-amend:
#
# Starts a job to amend format specific options of an existing open
-# block device The job is automatically finalized, but a manual
+# block device. The job is automatically finalized, but a manual
# job-dismiss is required.
#
# @job-id: Identifier for the newly created job.
@@ -5589,7 +5583,7 @@
#
# @options: Options (driver specific)
#
-# @force: Allow unsafe operations, format specific For luks that
+# @force: Allow unsafe operations, format specific. For luks that
# allows erase of the last active keyslot (permanent loss of
# data), and replacement of an active keyslot (possible loss of
# data if IO error happens)
@@ -5866,7 +5860,7 @@
# @BLOCK_JOB_PENDING:
#
# Emitted when a block job is awaiting explicit authorization to
-# finalize graph changes via @block-job-finalize. If this job is part
+# finalize graph changes via @job-finalize. If this job is part
# of a transaction, it will not emit this event until the transaction
# has converged first.
#
diff --git a/qapi/block-export.json b/qapi/block-export.json
index c783e01..ed4deb5 100644
--- a/qapi/block-export.json
+++ b/qapi/block-export.json
@@ -169,7 +169,7 @@
# @growable: Whether writes beyond the EOF should grow the block node
# accordingly. (default: false)
#
-# @allow-other: If this is off, only qemu's user is allowed access to
+# @allow-other: If this is off, only QEMU's user is allowed access to
# this export. That cannot be changed even with chmod or chown.
# Enabling this option will allow other users access to the export
# with the FUSE mount option "allow_other". Note that using
@@ -373,9 +373,9 @@
# (since: 5.2)
#
# @allow-inactive: If true, the export allows the exported node to be inactive.
-# If it is created for an inactive block node, the node remains inactive. If
+# If it is created for an inactive block node, the node remains inactive. If
# the export type doesn't support running on an inactive node, an error is
-# returned. If false, inactive block nodes are automatically activated before
+# returned. If false, inactive block nodes are automatically activated before
# creating the export and trying to inactivate them later fails.
# (since: 10.0; default: false)
#
diff --git a/qapi/block.json b/qapi/block.json
index f5374bd..1490a1a 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -48,7 +48,7 @@
##
# @FloppyDriveType:
#
-# Type of Floppy drive to be emulated by the Floppy Disk Controller.
+# Type of floppy drive to be emulated by the Floppy Disk Controller.
#
# @144: 1.44MB 3.5" drive
#
diff --git a/qapi/char.json b/qapi/char.json
index 447c10b..df6e325 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -274,7 +274,7 @@
# @reconnect: For a client socket, if a socket is disconnected, then
# attempt a reconnect after the given number of seconds. Setting
# this to zero disables this function. The use of this member is
-# deprecated, use @reconnect-ms instead. (default: 0) (Since: 2.2)
+# deprecated, use @reconnect-ms instead. (default: 0) (Since: 2.2)
#
# @reconnect-ms: For a client socket, if a socket is disconnected,
# then attempt a reconnect after the given number of milliseconds.
@@ -351,7 +351,7 @@
# Configuration info for stdio chardevs.
#
# @signal: Allow signals (such as SIGINT triggered by ^C) be delivered
-# to qemu. Default: true.
+# to QEMU. Default: true.
#
# Since: 1.5
##
@@ -443,7 +443,7 @@
##
# @ChardevQemuVDAgent:
#
-# Configuration info for qemu vdagent implementation.
+# Configuration info for QEMU vdagent implementation.
#
# @mouse: enable/disable mouse, default is enabled.
#
@@ -656,7 +656,7 @@
##
# @ChardevQemuVDAgentWrapper:
#
-# @data: Configuration info for qemu vdagent implementation
+# @data: Configuration info for QEMU vdagent implementation
#
# Since: 6.1
##
diff --git a/qapi/crypto.json b/qapi/crypto.json
index c9d967d..9ec6301 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -55,7 +55,8 @@
# @sha512: SHA-512. (since 2.7)
#
# @ripemd160: RIPEMD-160. (since 2.7)
-# @sm3: SM3. (since 9.2.0)
+#
+# @sm3: SM3. (since 9.2.0)
#
# Since: 2.6
##
@@ -202,19 +203,19 @@
#
# The options that apply to LUKS encryption format initialization
#
-# @cipher-alg: the cipher algorithm for data encryption Currently
+# @cipher-alg: the cipher algorithm for data encryption. Currently
# defaults to 'aes-256'.
#
-# @cipher-mode: the cipher mode for data encryption Currently defaults
-# to 'xts'
+# @cipher-mode: the cipher mode for data encryption. Currently
+# defaults to 'xts'
#
-# @ivgen-alg: the initialization vector generator Currently defaults
+# @ivgen-alg: the initialization vector generator. Currently defaults
# to 'plain64'
#
-# @ivgen-hash-alg: the initialization vector generator hash Currently
-# defaults to 'sha256'
+# @ivgen-hash-alg: the initialization vector generator hash.
+# Currently defaults to 'sha256'
#
-# @hash-alg: the master key hash algorithm Currently defaults to
+# @hash-alg: the master key hash algorithm. Currently defaults to
# 'sha256'
#
# @iter-time: number of milliseconds to spend in PBKDF passphrase
@@ -370,11 +371,11 @@
# @new-secret: The ID of a QCryptoSecret object providing the password
# to be written into added active keyslots
#
-# @old-secret: Optional (for deactivation only) If given will
+# @old-secret: Optional (for deactivation only). If given will
# deactivate all keyslots that match password located in
# QCryptoSecret with this ID
#
-# @iter-time: Optional (for activation only) Number of milliseconds to
+# @iter-time: Optional (for activation only). Number of milliseconds to
# spend in PBKDF passphrase processing for the newly activated
# keyslot. Currently defaults to 2000.
#
diff --git a/qapi/cryptodev.json b/qapi/cryptodev.json
index 28b97eb..b13db26 100644
--- a/qapi/cryptodev.json
+++ b/qapi/cryptodev.json
@@ -15,7 +15,7 @@
#
# @sym: symmetric encryption
#
-# @asym: asymmetric Encryption
+# @asym: asymmetric encryption
#
# Since: 8.0
##
diff --git a/qapi/cxl.json b/qapi/cxl.json
index dd947d3..8f2e923 100644
--- a/qapi/cxl.json
+++ b/qapi/cxl.json
@@ -117,7 +117,7 @@
# @nibble-mask: Identifies one or more nibbles that the error affects
#
# @bank-group: Bank group of the memory event location, incorporating
-# a number of Banks.
+# a number of banks.
#
# @bank: Bank of the memory event location. A single bank is accessed
# per read or write of the memory.
diff --git a/qapi/dump.json b/qapi/dump.json
index f2835c0..d0ba1f0 100644
--- a/qapi/dump.json
+++ b/qapi/dump.json
@@ -54,9 +54,9 @@
# @paging: if true, do paging to get guest's memory mapping. This
# allows using gdb to process the core file.
#
-# IMPORTANT: this option can make QEMU allocate several gigabytes
-# of RAM. This can happen for a large guest, or a malicious guest
-# pretending to be large.
+# **Important**: this option can make QEMU allocate several
+# gigabytes of RAM. This can happen for a large guest, or a
+# malicious guest pretending to be large.
#
# Also, paging=true has the following limitations:
#
diff --git a/qapi/introspect.json b/qapi/introspect.json
index 01bb242..e9e0297 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -26,9 +26,9 @@
# the QAPI schema.
#
# Furthermore, while we strive to keep the QMP wire format
-# backwards-compatible across qemu versions, the introspection output
+# backwards-compatible across QEMU versions, the introspection output
# is not guaranteed to have the same stability. For example, one
-# version of qemu may list an object member as an optional
+# version of QEMU may list an object member as an optional
# non-variant, while another lists the same member only through the
# object's variants; or the type of a member may change from a generic
# string into a specific enum or from one specific type into an
@@ -154,8 +154,8 @@
#
# Additional SchemaInfo members for meta-type 'enum'.
#
-# @members: the enum type's members, in no particular order (since
-# 6.2).
+# @members: the enum type's members, in no particular order.
+# (since 6.2)
#
# @values: the enumeration type's member names, in no particular
# order. Redundant with @members. Just for backward
diff --git a/qapi/job.json b/qapi/job.json
index b03f80b..126fa5c 100644
--- a/qapi/job.json
+++ b/qapi/job.json
@@ -20,14 +20,14 @@
#
# @create: image creation job type, see "blockdev-create" (since 3.0)
#
-# @amend: image options amend job type, see "x-blockdev-amend" (since
-# 5.1)
+# @amend: image options amend job type, see "x-blockdev-amend"
+# (since 5.1)
#
-# @snapshot-load: snapshot load job type, see "snapshot-load" (since
-# 6.0)
+# @snapshot-load: snapshot load job type, see "snapshot-load"
+# (since 6.0)
#
-# @snapshot-save: snapshot save job type, see "snapshot-save" (since
-# 6.0)
+# @snapshot-save: snapshot save job type, see "snapshot-save"
+# (since 6.0)
#
# @snapshot-delete: snapshot delete job type, see "snapshot-delete"
# (since 6.0)
@@ -74,7 +74,7 @@
# process.
#
# @concluded: The job has finished all work. If auto-dismiss was set
-# to false, the job will remain in the query list until it is
+# to false, the job will remain in this state until it is
# dismissed via @job-dismiss.
#
# @null: The job is in the process of being dismantled. This state
@@ -191,10 +191,10 @@
# state. Completing the job in any other state is an error.
#
# This is supported only for drive mirroring, where it also switches
-# the device to write to the target path only. Note that drive
+# the device to write to the target path only. Note that drive
# mirroring includes drive-mirror, blockdev-mirror and block-commit
# job (only in case of "active commit", when the node being commited
-# is used by the guest). The ability to complete is signaled with a
+# is used by the guest). The ability to complete is signaled with a
# BLOCK_JOB_READY event.
#
# This command completes an active background block operation
@@ -216,16 +216,16 @@
#
# 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. In turn, automatic dismiss may be enabled only
+# dismiss enabled. In turn, automatic dismiss may be enabled only
# for jobs that have @auto-dismiss option, which are drive-backup,
# blockdev-backup, drive-mirror, blockdev-mirror, block-commit and
-# block-stream. @auto-dismiss is enabled by default for these
+# block-stream. @auto-dismiss is enabled by default for these
# jobs.
#
# 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.
+# reached its terminal state, CONCLUDED. For jobs that make use of
+# the JOB_READY event, job-cancel or job-complete will still need to
+# be used as appropriate.
#
# @id: The job identifier.
#
diff --git a/qapi/machine.json b/qapi/machine.json
index 5373e13..0650b8d 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -182,8 +182,8 @@
# @default-cpu-type: default CPU model typename if none is requested
# via the -cpu argument. (since 4.2)
#
-# @default-ram-id: the default ID of initial RAM memory backend (since
-# 5.2)
+# @default-ram-id: the default ID of initial RAM memory backend
+# (since 5.2)
#
# @acpi: machine type supports ACPI (since 8.0)
#
@@ -694,7 +694,7 @@
# Structure of HMAT (Heterogeneous Memory Attribute Table)
#
# For more information about @HmatLBDataType, see chapter 5.2.27.4:
-# Table 5-146: Field "Data Type" of ACPI 6.3 spec.
+# Table 5-146: Field "Data Type" of ACPI 6.3 spec.
#
# @access-latency: access latency (nanoseconds)
#
@@ -811,7 +811,7 @@
#
# @policy: the write policy, none/write-back/write-through.
#
-# @line: the cache Line size in bytes.
+# @line: the cache line size in bytes.
#
# Since: 5.0
##
@@ -1089,7 +1089,7 @@
# :annotated:
#
# For s390x-virtio-ccw machine type started with
-# ``-smp 1,maxcpus=2 -cpu qemu`` (Since: 2.11)::
+# ``-smp 1,maxcpus=2 -cpu qemu``::
#
# -> { "execute": "query-hotpluggable-cpus" }
# <- {"return": [
@@ -1160,7 +1160,7 @@
#
# Information about the guest balloon device.
#
-# @actual: the logical size of the VM in bytes Formula used:
+# @actual: the logical size of the VM in bytes. Formula used:
# logical_vm_size = vm_ram_size - balloon_size
#
# Since: 0.14
@@ -1199,7 +1199,7 @@
# is equivalent to the @actual field return by the 'query-balloon'
# command
#
-# @actual: the logical size of the VM in bytes Formula used:
+# @actual: the logical size of the VM in bytes. Formula used:
# logical_vm_size = vm_ram_size - balloon_size
#
# .. note:: This event is rate-limited.
diff --git a/qapi/migration.json b/qapi/migration.json
index 41826bd..4963f6c 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -57,8 +57,8 @@
#
# @dirty-sync-missed-zero-copy: Number of times dirty RAM
# synchronization could not avoid copying dirty pages. This is
-# between 0 and @dirty-sync-count * @multifd-channels. (since
-# 7.1)
+# between 0 and @dirty-sync-count * @multifd-channels.
+# (since 7.1)
#
# Since: 0.14
##
@@ -137,16 +137,16 @@
#
# @active: in the process of doing migration.
#
-# @postcopy-active: like active, but now in postcopy mode. (since
-# 2.5)
+# @postcopy-active: like active, but now in postcopy mode.
+# (since 2.5)
#
# @postcopy-paused: during postcopy but paused. (since 3.0)
#
# @postcopy-recover-setup: setup phase for a postcopy recovery
# process, preparing for a recovery phase to start. (since 9.1)
#
-# @postcopy-recover: trying to recover from a paused postcopy. (since
-# 3.0)
+# @postcopy-recover: trying to recover from a paused postcopy.
+# (since 3.0)
#
# @completed: migration is finished.
#
@@ -407,7 +407,7 @@
# @postcopy-ram: Start executing on the migration target before all of
# RAM has been migrated, pulling the remaining pages along as
# needed. The capacity must have the same setting on both source
-# and target or migration will not even start. NOTE: If the
+# and target or migration will not even start. **Note:** if the
# migration fails during postcopy the VM will fail. (since 2.6)
#
# @x-colo: If enabled, migration will never end, and the state of the
@@ -415,15 +415,15 @@
# on secondary side, this process is called COarse-Grain LOck
# Stepping (COLO) for Non-stop Service. (since 2.8)
#
-# @release-ram: if enabled, qemu will free the migrated ram pages on
+# @release-ram: if enabled, QEMU will free the migrated ram pages on
# the source during postcopy-ram migration. (since 2.9)
#
# @return-path: If enabled, migration will use the return path even
# for precopy. (since 2.10)
#
# @pause-before-switchover: Pause outgoing migration before
-# serialising device state and before disabling block IO (since
-# 2.11)
+# serialising device state and before disabling block IO
+# (since 2.11)
#
# @multifd: Use more than one fd for migration (since 4.0)
#
@@ -697,8 +697,8 @@
# @alias: An alias name for migration (for example the bitmap name on
# the opposite site).
#
-# @transform: Allows the modification of the migrated bitmap. (since
-# 6.0)
+# @transform: Allows the modification of the migrated bitmap.
+# (since 6.0)
#
# Since: 5.2
##
@@ -760,9 +760,9 @@
# auto-converge detects that migration is not making progress.
# The default value is 10. (Since 2.7)
#
-# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At
-# the tail stage of throttling, the Guest is very sensitive to CPU
-# percentage while the @cpu-throttle -increment is excessive
+# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage.
+# At the tail stage of throttling, the Guest is very sensitive to
+# CPU percentage while the @cpu-throttle -increment is excessive
# usually at tail stage. If this parameter is true, we will
# compute the ideal CPU percentage used by the Guest, which may
# exactly make the dirty rate match the dirty rate threshold.
@@ -770,8 +770,8 @@
# specified by @cpu-throttle-increment and the one generated by
# ideal CPU percentage. Therefore, it is compatible to
# traditional throttling, meanwhile the throttle increment won't
-# be excessive at tail stage. The default value is false. (Since
-# 5.1)
+# be excessive at tail stage. The default value is false.
+# (Since 5.1)
#
# @tls-creds: ID of the 'tls-creds' object that provides credentials
# for establishing a TLS connection over the migration data
@@ -801,10 +801,10 @@
# (Since 2.8)
#
# @avail-switchover-bandwidth: to set the available bandwidth that
-# migration can use during switchover phase. NOTE! This does not
-# limit the bandwidth during switchover, but only for calculations
-# when making decisions to switchover. By default, this value is
-# zero, which means QEMU will estimate the bandwidth
+# migration can use during switchover phase. **Note:** this does
+# not limit the bandwidth during switchover, but only for
+# calculations when making decisions to switchover. By default,
+# this value is zero, which means QEMU will estimate the bandwidth
# automatically. This can be set when the estimated value is not
# accurate, while the user is able to guarantee such bandwidth is
# available when switching over. When specified correctly, this
@@ -842,9 +842,9 @@
# more CPU. Defaults to 1. (Since 5.0)
#
# @multifd-qatzip-level: Set the compression level to be used in live
-# migration. The level is an integer between 1 and 9, where 1 means
+# migration. The level is an integer between 1 and 9, where 1 means
# the best compression speed, and 9 means the best compression
-# ratio which will consume more CPU. Defaults to 1. (Since 9.2)
+# ratio which will consume more CPU. Defaults to 1. (Since 9.2)
#
# @multifd-zstd-level: Set the compression level to be used in live
# migration, the compression level is an integer between 0 and 20,
@@ -941,9 +941,9 @@
# auto-converge detects that migration is not making progress.
# The default value is 10. (Since 2.7)
#
-# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At
-# the tail stage of throttling, the Guest is very sensitive to CPU
-# percentage while the @cpu-throttle -increment is excessive
+# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage.
+# At the tail stage of throttling, the Guest is very sensitive to
+# CPU percentage while the @cpu-throttle -increment is excessive
# usually at tail stage. If this parameter is true, we will
# compute the ideal CPU percentage used by the Guest, which may
# exactly make the dirty rate match the dirty rate threshold.
@@ -951,8 +951,8 @@
# specified by @cpu-throttle-increment and the one generated by
# ideal CPU percentage. Therefore, it is compatible to
# traditional throttling, meanwhile the throttle increment won't
-# be excessive at tail stage. The default value is false. (Since
-# 5.1)
+# be excessive at tail stage. The default value is false.
+# (Since 5.1)
#
# @tls-creds: ID of the 'tls-creds' object that provides credentials
# for establishing a TLS connection over the migration data
@@ -982,10 +982,10 @@
# (Since 2.8)
#
# @avail-switchover-bandwidth: to set the available bandwidth that
-# migration can use during switchover phase. NOTE! This does not
-# limit the bandwidth during switchover, but only for calculations
-# when making decisions to switchover. By default, this value is
-# zero, which means QEMU will estimate the bandwidth
+# migration can use during switchover phase. **Note:** this does
+# not limit the bandwidth during switchover, but only for
+# calculations when making decisions to switchover. By default,
+# this value is zero, which means QEMU will estimate the bandwidth
# automatically. This can be set when the estimated value is not
# accurate, while the user is able to guarantee such bandwidth is
# available when switching over. When specified correctly, this
@@ -1023,9 +1023,9 @@
# more CPU. Defaults to 1. (Since 5.0)
#
# @multifd-qatzip-level: Set the compression level to be used in live
-# migration. The level is an integer between 1 and 9, where 1 means
+# migration. The level is an integer between 1 and 9, where 1 means
# the best compression speed, and 9 means the best compression
-# ratio which will consume more CPU. Defaults to 1. (Since 9.2)
+# ratio which will consume more CPU. Defaults to 1. (Since 9.2)
#
# @multifd-zstd-level: Set the compression level to be used in live
# migration, the compression level is an integer between 0 and 20,
@@ -1148,16 +1148,16 @@
# percentage. The default value is 50. (Since 5.0)
#
# @cpu-throttle-initial: Initial percentage of time guest cpus are
-# throttled when migration auto-converge is activated. (Since
-# 2.7)
+# throttled when migration auto-converge is activated.
+# (Since 2.7)
#
# @cpu-throttle-increment: throttle percentage increase each time
# auto-converge detects that migration is not making progress.
# (Since 2.7)
#
-# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At
-# the tail stage of throttling, the Guest is very sensitive to CPU
-# percentage while the @cpu-throttle -increment is excessive
+# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage.
+# At the tail stage of throttling, the Guest is very sensitive to
+# CPU percentage while the @cpu-throttle -increment is excessive
# usually at tail stage. If this parameter is true, we will
# compute the ideal CPU percentage used by the Guest, which may
# exactly make the dirty rate match the dirty rate threshold.
@@ -1165,8 +1165,8 @@
# specified by @cpu-throttle-increment and the one generated by
# ideal CPU percentage. Therefore, it is compatible to
# traditional throttling, meanwhile the throttle increment won't
-# be excessive at tail stage. The default value is false. (Since
-# 5.1)
+# be excessive at tail stage. The default value is false.
+# (Since 5.1)
#
# @tls-creds: ID of the 'tls-creds' object that provides credentials
# for establishing a TLS connection over the migration data
@@ -1192,10 +1192,10 @@
# (Since 2.8)
#
# @avail-switchover-bandwidth: to set the available bandwidth that
-# migration can use during switchover phase. NOTE! This does not
-# limit the bandwidth during switchover, but only for calculations
-# when making decisions to switchover. By default, this value is
-# zero, which means QEMU will estimate the bandwidth
+# migration can use during switchover phase. **Note:** this does
+# not limit the bandwidth during switchover, but only for
+# calculations when making decisions to switchover. By default,
+# this value is zero, which means QEMU will estimate the bandwidth
# automatically. This can be set when the estimated value is not
# accurate, while the user is able to guarantee such bandwidth is
# available when switching over. When specified correctly, this
@@ -1233,9 +1233,9 @@
# more CPU. Defaults to 1. (Since 5.0)
#
# @multifd-qatzip-level: Set the compression level to be used in live
-# migration. The level is an integer between 1 and 9, where 1 means
+# migration. The level is an integer between 1 and 9, where 1 means
# the best compression speed, and 9 means the best compression
-# ratio which will consume more CPU. Defaults to 1. (Since 9.2)
+# ratio which will consume more CPU. Defaults to 1. (Since 9.2)
#
# @multifd-zstd-level: Set the compression level to be used in live
# migration, the compression level is an integer between 0 and 20,
@@ -1500,7 +1500,7 @@
##
# @x-colo-lost-heartbeat:
#
-# Tell qemu that heartbeat is lost, request it to do takeover
+# Tell QEMU that heartbeat is lost, request it to do takeover
# procedures. If this command is sent to the PVM, the Primary side
# will exit COLO mode. If sent to the Secondary, the Secondary side
# will run failover work, then takes over server operation to become
@@ -1729,8 +1729,8 @@
##
# @migrate-incoming:
#
-# Start an incoming migration, the qemu must have been started with
-# -incoming defer
+# Start an incoming migration. QEMU must have been started with
+# -incoming defer.
#
# @uri: The Uniform Resource Identifier identifying the source or
# address to listen on
diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json
index 3b53464..5fefa0a 100644
--- a/qapi/misc-i386.json
+++ b/qapi/misc-i386.json
@@ -195,7 +195,7 @@
#
# @cbitpos: C-bit location in page table entry
#
-# @reduced-phys-bits: Number of physical Address bit reduction when
+# @reduced-phys-bits: Number of physical address bit reduction when
# SEV is enabled
#
# Since: 2.12
diff --git a/qapi/misc.json b/qapi/misc.json
index dcf9f7d..4b9e601 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -222,8 +222,8 @@
# .. note:: This command only exists as a stop-gap. Its use is highly
# discouraged. The semantics of this command are not guaranteed:
# this means that command names, arguments and responses can change
-# or be removed at ANY time. Applications that rely on long term
-# stability guarantees should NOT use this command.
+# or be removed at **any** time. Applications that rely on long
+# term stability guarantees should **not** use this command.
#
# Known limitations:
#
diff --git a/qapi/net.json b/qapi/net.json
index 310cc4f..97ea183 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -150,12 +150,12 @@
# @domainname: guest-visible domain name of the virtual nameserver
# (since 3.0)
#
-# @ipv6-prefix: IPv6 network prefix (default is fec0::) (since 2.6).
-# The network prefix is given in the usual hexadecimal IPv6
-# address notation.
+# @ipv6-prefix: IPv6 network prefix (default is fec0::). The network
+# prefix is given in the usual hexadecimal IPv6 address notation.
+# (since 2.6)
#
-# @ipv6-prefixlen: IPv6 network prefix length (default is 64) (since
-# 2.6)
+# @ipv6-prefixlen: IPv6 network prefix length (default is 64)
+# (since 2.6)
#
# @ipv6-host: guest-visible IPv6 address of the host (since 2.6)
#
@@ -387,8 +387,8 @@
#
# @hubid: hub identifier number
#
-# @netdev: used to connect hub to a netdev instead of a device (since
-# 2.12)
+# @netdev: used to connect hub to a netdev instead of a device
+# (since 2.12)
#
# Since: 1.2
##
@@ -510,8 +510,8 @@
# @queues: number of queues to be created for multiqueue vhost-vdpa
# (default: 1)
#
-# @x-svq: Start device with (experimental) shadow virtqueue. (Since
-# 7.1) (default: false)
+# @x-svq: Start device with (experimental) shadow virtqueue.
+# (Since 7.1) (default: false)
#
# Features:
#
diff --git a/qapi/qom.json b/qapi/qom.json
index 45cd475..3e8debf 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -870,7 +870,7 @@
# information read from devices and switches in conjunction with
# link characteristics read from PCIe Configuration space.
# To get the full path latency from CPU to CXL attached DRAM
-# CXL device: Add the latency from CPU to Generic Port (from
+# CXL device: Add the latency from CPU to Generic Port (from
# HMAT indexed via the node ID in this SRAT structure) to
# that for CXL bus links, the latency across intermediate switches
# and from the EP port to the actual memory. Bandwidth is more
diff --git a/qapi/run-state.json b/qapi/run-state.json
index ee11adc..fd09beb 100644
--- a/qapi/run-state.json
+++ b/qapi/run-state.json
@@ -62,7 +62,7 @@
##
# @ShutdownCause:
#
-# An enumeration of reasons for a Shutdown.
+# An enumeration of reasons for a shutdown.
#
# @none: No shutdown request pending
#
@@ -135,19 +135,19 @@
##
# @SHUTDOWN:
#
-# Emitted when the virtual machine has shut down, indicating that qemu
+# Emitted when the virtual machine has shut down, indicating that QEMU
# is about to exit.
#
# @guest: If true, the shutdown was triggered by a guest request (such
# as a guest-initiated ACPI shutdown request or other
# hardware-specific action) rather than a host request (such as
-# sending qemu a SIGINT). (since 2.10)
+# sending QEMU a SIGINT). (since 2.10)
#
# @reason: The @ShutdownCause which resulted in the SHUTDOWN.
# (since 4.0)
#
# .. note:: If the command-line option ``-no-shutdown`` has been
-# specified, qemu will not exit, and a STOP event will eventually
+# specified, QEMU will not exit, and a STOP event will eventually
# follow the SHUTDOWN event.
#
# Since: 0.12
@@ -365,8 +365,8 @@
# @shutdown: Shutdown the VM and exit, according to the shutdown
# action
#
-# @exit-failure: Shutdown the VM and exit with nonzero status (since
-# 7.1)
+# @exit-failure: Shutdown the VM and exit with nonzero status
+# (since 7.1)
#
# Since: 6.0
##
diff --git a/qapi/transaction.json b/qapi/transaction.json
index 021e383..9d9e7af 100644
--- a/qapi/transaction.json
+++ b/qapi/transaction.json
@@ -21,7 +21,7 @@
##
# @ActionCompletionMode:
#
-# An enumeration of Transactional completion modes.
+# An enumeration of transactional completion modes.
#
# @individual: Do not attempt to cancel any other Actions if any
# Actions fail after the Transaction request succeeds. All
@@ -223,7 +223,7 @@
# exists, the request will be rejected. Only some image formats
# support it, for example, qcow2, and rbd,
#
-# On failure, qemu will try delete the newly created internal snapshot
+# On failure, QEMU will try delete the newly created internal snapshot
# in the transaction. When an I/O error occurs during deletion, the
# user needs to fix it later with qemu-img or other command.
#
diff --git a/qapi/uefi.json b/qapi/uefi.json
index bdfcabe..6592183 100644
--- a/qapi/uefi.json
+++ b/qapi/uefi.json
@@ -5,7 +5,7 @@
##
# = UEFI Variable Store
#
-# The qemu efi variable store implementation (hw/uefi/) uses this to
+# The QEMU efi variable store implementation (hw/uefi/) uses this to
# store non-volatile variables in json format on disk.
#
# This is an existing format already supported by (at least) two other
diff --git a/qapi/ui.json b/qapi/ui.json
index 3d0c853..514fa15 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -175,8 +175,8 @@
# @filename: the path of a new file to store the image
#
# @device: ID of the display device that should be dumped. If this
-# parameter is missing, the primary display will be used. (Since
-# 2.12)
+# parameter is missing, the primary display will be used.
+# (Since 2.12)
#
# @head: head to use in case the device supports multiple heads. If
# this parameter is missing, head #0 will be used. Also note that
@@ -1526,12 +1526,12 @@
#
# Display (user interface) options.
#
-# @type: Which DisplayType qemu should use.
+# @type: Which DisplayType QEMU should use.
#
# @full-screen: Start user interface in fullscreen mode
# (default: off).
#
-# @window-close: Allow to quit qemu with window close button
+# @window-close: Allow to quit QEMU with window close button
# (default: on).
#
# @show-cursor: Force showing the mouse cursor (default: off).
diff --git a/qemu-img.c b/qemu-img.c
index 139eeb5..e757071 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -3505,6 +3505,7 @@ static int img_snapshot(int argc, char **argv)
break;
case SNAPSHOT_DELETE:
+ bdrv_drain_all_begin();
bdrv_graph_rdlock_main_loop();
ret = bdrv_snapshot_find(bs, &sn, snapshot_name);
if (ret < 0) {
@@ -3520,6 +3521,7 @@ static int img_snapshot(int argc, char **argv)
}
}
bdrv_graph_rdunlock_main_loop();
+ bdrv_drain_all_end();
break;
}
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 13d580c..bccfe85 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -32,6 +32,13 @@ dependencies = [
]
[[package]]
+name = "bits"
+version = "0.1.0"
+dependencies = [
+ "qemu_api_macros",
+]
+
+[[package]]
name = "either"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -66,6 +73,7 @@ version = "0.1.0"
dependencies = [
"bilge",
"bilge-impl",
+ "bits",
"qemu_api",
"qemu_api_macros",
]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index d9faeec..fd4c2fb 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
+ "bits",
"qemu-api-macros",
"qemu-api",
"hw/char/pl011",
@@ -63,7 +64,6 @@ ignored_unit_patterns = "deny"
implicit_clone = "deny"
macro_use_imports = "deny"
missing_safety_doc = "deny"
-multiple_crate_versions = "deny"
mut_mut = "deny"
needless_bitwise_bool = "deny"
needless_pass_by_ref_mut = "deny"
diff --git a/rust/bits/Cargo.toml b/rust/bits/Cargo.toml
new file mode 100644
index 0000000..1ff38a4
--- /dev/null
+++ b/rust/bits/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "bits"
+version = "0.1.0"
+authors = ["Paolo Bonzini <pbonzini@redhat.com>"]
+description = "const-friendly bit flags"
+resolver = "2"
+publish = false
+
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+qemu_api_macros = { path = "../qemu-api-macros" }
+
+[lints]
+workspace = true
diff --git a/rust/bits/meson.build b/rust/bits/meson.build
new file mode 100644
index 0000000..2a41e13
--- /dev/null
+++ b/rust/bits/meson.build
@@ -0,0 +1,16 @@
+_bits_rs = static_library(
+ 'bits',
+ 'src/lib.rs',
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ dependencies: [qemu_api_macros],
+)
+
+bits_rs = declare_dependency(link_with: _bits_rs)
+
+rust.test('rust-bits-tests', _bits_rs,
+ suite: ['unit', 'rust'])
+
+rust.doctest('rust-bits-doctests', _bits_rs,
+ dependencies: bits_rs,
+ suite: ['doc', 'rust'])
diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs
new file mode 100644
index 0000000..d485d6b
--- /dev/null
+++ b/rust/bits/src/lib.rs
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later
+
+/// # Definition entry point
+///
+/// Define a struct with a single field of type $type. Include public constants
+/// for each element listed in braces.
+///
+/// The unnamed element at the end, if present, can be used to enlarge the set
+/// of valid bits. Bits that are valid but not listed are treated normally for
+/// the purpose of arithmetic operations, and are printed with their hexadecimal
+/// value.
+///
+/// The struct implements the following traits: [`BitAnd`](std::ops::BitAnd),
+/// [`BitOr`](std::ops::BitOr), [`BitXor`](std::ops::BitXor),
+/// [`Not`](std::ops::Not), [`Sub`](std::ops::Sub); [`Debug`](std::fmt::Debug),
+/// [`Display`](std::fmt::Display), [`Binary`](std::fmt::Binary),
+/// [`Octal`](std::fmt::Octal), [`LowerHex`](std::fmt::LowerHex),
+/// [`UpperHex`](std::fmt::UpperHex); [`From`]`<type>`/[`Into`]`<type>` where
+/// type is the type specified in the definition.
+///
+/// ## Example
+///
+/// ```
+/// # use bits::bits;
+/// bits! {
+/// pub struct Colors(u8) {
+/// BLACK = 0,
+/// RED = 1,
+/// GREEN = 1 << 1,
+/// BLUE = 1 << 2,
+/// WHITE = (1 << 0) | (1 << 1) | (1 << 2),
+/// }
+/// }
+/// ```
+///
+/// ```
+/// # use bits::bits;
+/// # bits! { pub struct Colors(u8) { BLACK = 0, RED = 1, GREEN = 1 << 1, BLUE = 1 << 2, } }
+///
+/// bits! {
+/// pub struct Colors8(u8) {
+/// BLACK = 0,
+/// RED = 1,
+/// GREEN = 1 << 1,
+/// BLUE = 1 << 2,
+/// WHITE = (1 << 0) | (1 << 1) | (1 << 2),
+///
+/// _ = 255,
+/// }
+/// }
+///
+/// // The previously defined struct ignores bits not explicitly defined.
+/// assert_eq!(
+/// Colors::from(255).into_bits(),
+/// (Colors::RED | Colors::GREEN | Colors::BLUE).into_bits()
+/// );
+///
+/// // Adding "_ = 255" makes it retain other bits as well.
+/// assert_eq!(Colors8::from(255).into_bits(), 255);
+///
+/// // all() does not include the additional bits, valid_bits() does
+/// assert_eq!(Colors8::all().into_bits(), Colors::all().into_bits());
+/// assert_eq!(Colors8::valid_bits().into_bits(), 255);
+/// ```
+///
+/// # Evaluation entry point
+///
+/// Return a constant corresponding to the boolean expression `$expr`.
+/// Identifiers in the expression correspond to values defined for the
+/// type `$type`. Supported operators are `!` (unary), `-`, `&`, `^`, `|`.
+///
+/// ## Examples
+///
+/// ```
+/// # use bits::bits;
+/// bits! {
+/// pub struct Colors(u8) {
+/// BLACK = 0,
+/// RED = 1,
+/// GREEN = 1 << 1,
+/// BLUE = 1 << 2,
+/// // same as "WHITE = 7",
+/// WHITE = bits!(Self as u8: RED | GREEN | BLUE),
+/// }
+/// }
+///
+/// let rgb = bits! { Colors: RED | GREEN | BLUE };
+/// assert_eq!(rgb, Colors::WHITE);
+/// ```
+#[macro_export]
+macro_rules! bits {
+ {
+ $(#[$struct_meta:meta])*
+ $struct_vis:vis struct $struct_name:ident($field_vis:vis $type:ty) {
+ $($(#[$const_meta:meta])* $const:ident = $val:expr),+
+ $(,_ = $mask:expr)?
+ $(,)?
+ }
+ } => {
+ $(#[$struct_meta])*
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ #[repr(transparent)]
+ $struct_vis struct $struct_name($field_vis $type);
+
+ impl $struct_name {
+ $( #[allow(dead_code)] $(#[$const_meta])*
+ pub const $const: $struct_name = $struct_name($val); )+
+
+ #[doc(hidden)]
+ const VALID__: $type = $( Self::$const.0 )|+ $(|$mask)?;
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn empty() -> Self {
+ Self(0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn all() -> Self {
+ Self($( Self::$const.0 )|+)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn valid_bits() -> Self {
+ Self(Self::VALID__)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn valid(val: $type) -> bool {
+ (val & !Self::VALID__) == 0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn any_set(self, mask: Self) -> bool {
+ (self.0 & mask.0) != 0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn all_set(self, mask: Self) -> bool {
+ (self.0 & mask.0) == mask.0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn none_set(self, mask: Self) -> bool {
+ (self.0 & mask.0) == 0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn from_bits(value: $type) -> Self {
+ $struct_name(value)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn into_bits(self) -> $type {
+ self.0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub fn set(&mut self, rhs: Self) {
+ self.0 |= rhs.0;
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub fn clear(&mut self, rhs: Self) {
+ self.0 &= !rhs.0;
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub fn toggle(&mut self, rhs: Self) {
+ self.0 ^= rhs.0;
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn intersection(self, rhs: Self) -> Self {
+ $struct_name(self.0 & rhs.0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn difference(self, rhs: Self) -> Self {
+ $struct_name(self.0 & !rhs.0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn symmetric_difference(self, rhs: Self) -> Self {
+ $struct_name(self.0 ^ rhs.0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn union(self, rhs: Self) -> Self {
+ $struct_name(self.0 | rhs.0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn invert(self) -> Self {
+ $struct_name(self.0 ^ Self::VALID__)
+ }
+ }
+
+ impl ::std::fmt::Binary for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ // If no width, use the highest valid bit
+ let width = f.width().unwrap_or((Self::VALID__.ilog2() + 1) as usize);
+ write!(f, "{:0>width$.precision$b}", self.0,
+ width = width,
+ precision = f.precision().unwrap_or(width))
+ }
+ }
+
+ impl ::std::fmt::LowerHex for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ <$type as ::std::fmt::LowerHex>::fmt(&self.0, f)
+ }
+ }
+
+ impl ::std::fmt::Octal for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ <$type as ::std::fmt::Octal>::fmt(&self.0, f)
+ }
+ }
+
+ impl ::std::fmt::UpperHex for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ <$type as ::std::fmt::UpperHex>::fmt(&self.0, f)
+ }
+ }
+
+ impl ::std::fmt::Debug for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ write!(f, "{}({})", stringify!($struct_name), self)
+ }
+ }
+
+ impl ::std::fmt::Display for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ use ::std::fmt::Display;
+ let mut first = true;
+ let mut left = self.0;
+ $(if Self::$const.0.is_power_of_two() && (self & Self::$const).0 != 0 {
+ if first { first = false } else { Display::fmt(&'|', f)?; }
+ Display::fmt(stringify!($const), f)?;
+ left -= Self::$const.0;
+ })+
+ if first {
+ Display::fmt(&'0', f)
+ } else if left != 0 {
+ write!(f, "|{left:#x}")
+ } else {
+ Ok(())
+ }
+ }
+ }
+
+ impl ::std::cmp::PartialEq<$type> for $struct_name {
+ fn eq(&self, rhs: &$type) -> bool {
+ self.0 == *rhs
+ }
+ }
+
+ impl ::std::ops::BitAnd<$struct_name> for &$struct_name {
+ type Output = $struct_name;
+ fn bitand(self, rhs: $struct_name) -> Self::Output {
+ $struct_name(self.0 & rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitAndAssign<$struct_name> for $struct_name {
+ fn bitand_assign(&mut self, rhs: $struct_name) {
+ self.0 = self.0 & rhs.0
+ }
+ }
+
+ impl ::std::ops::BitXor<$struct_name> for &$struct_name {
+ type Output = $struct_name;
+ fn bitxor(self, rhs: $struct_name) -> Self::Output {
+ $struct_name(self.0 ^ rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitXorAssign<$struct_name> for $struct_name {
+ fn bitxor_assign(&mut self, rhs: $struct_name) {
+ self.0 = self.0 ^ rhs.0
+ }
+ }
+
+ impl ::std::ops::BitOr<$struct_name> for &$struct_name {
+ type Output = $struct_name;
+ fn bitor(self, rhs: $struct_name) -> Self::Output {
+ $struct_name(self.0 | rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitOrAssign<$struct_name> for $struct_name {
+ fn bitor_assign(&mut self, rhs: $struct_name) {
+ self.0 = self.0 | rhs.0
+ }
+ }
+
+ impl ::std::ops::Sub<$struct_name> for &$struct_name {
+ type Output = $struct_name;
+ fn sub(self, rhs: $struct_name) -> Self::Output {
+ $struct_name(self.0 & !rhs.0)
+ }
+ }
+
+ impl ::std::ops::SubAssign<$struct_name> for $struct_name {
+ fn sub_assign(&mut self, rhs: $struct_name) {
+ self.0 = self.0 - rhs.0
+ }
+ }
+
+ impl ::std::ops::Not for &$struct_name {
+ type Output = $struct_name;
+ fn not(self) -> Self::Output {
+ $struct_name(self.0 ^ $struct_name::VALID__)
+ }
+ }
+
+ impl ::std::ops::BitAnd<$struct_name> for $struct_name {
+ type Output = Self;
+ fn bitand(self, rhs: Self) -> Self::Output {
+ $struct_name(self.0 & rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitXor<$struct_name> for $struct_name {
+ type Output = Self;
+ fn bitxor(self, rhs: Self) -> Self::Output {
+ $struct_name(self.0 ^ rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitOr<$struct_name> for $struct_name {
+ type Output = Self;
+ fn bitor(self, rhs: Self) -> Self::Output {
+ $struct_name(self.0 | rhs.0)
+ }
+ }
+
+ impl ::std::ops::Sub<$struct_name> for $struct_name {
+ type Output = Self;
+ fn sub(self, rhs: Self) -> Self::Output {
+ $struct_name(self.0 & !rhs.0)
+ }
+ }
+
+ impl ::std::ops::Not for $struct_name {
+ type Output = Self;
+ fn not(self) -> Self::Output {
+ $struct_name(self.0 ^ Self::VALID__)
+ }
+ }
+
+ impl From<$struct_name> for $type {
+ fn from(x: $struct_name) -> $type {
+ x.0
+ }
+ }
+
+ impl From<$type> for $struct_name {
+ fn from(x: $type) -> Self {
+ $struct_name(x & Self::VALID__)
+ }
+ }
+ };
+
+ { $type:ty: $expr:expr } => {
+ ::qemu_api_macros::bits_const_internal! { $type @ ($expr) }
+ };
+
+ { $type:ty as $int_type:ty: $expr:expr } => {
+ (::qemu_api_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
+ };
+}
+
+#[cfg(test)]
+mod test {
+ bits! {
+ pub struct InterruptMask(u32) {
+ OE = 1 << 10,
+ BE = 1 << 9,
+ PE = 1 << 8,
+ FE = 1 << 7,
+ RT = 1 << 6,
+ TX = 1 << 5,
+ RX = 1 << 4,
+ DSR = 1 << 3,
+ DCD = 1 << 2,
+ CTS = 1 << 1,
+ RI = 1 << 0,
+
+ E = bits!(Self as u32: OE | BE | PE | FE),
+ MS = bits!(Self as u32: RI | DSR | DCD | CTS),
+ }
+ }
+
+ #[test]
+ pub fn test_not() {
+ assert_eq!(
+ !InterruptMask::from(InterruptMask::RT.0),
+ InterruptMask::E | InterruptMask::MS | InterruptMask::TX | InterruptMask::RX
+ );
+ }
+
+ #[test]
+ pub fn test_and() {
+ assert_eq!(
+ InterruptMask::from(0),
+ InterruptMask::MS & InterruptMask::OE
+ )
+ }
+
+ #[test]
+ pub fn test_or() {
+ assert_eq!(
+ InterruptMask::E,
+ InterruptMask::OE | InterruptMask::BE | InterruptMask::PE | InterruptMask::FE
+ );
+ }
+
+ #[test]
+ pub fn test_xor() {
+ assert_eq!(
+ InterruptMask::E ^ InterruptMask::BE,
+ InterruptMask::OE | InterruptMask::PE | InterruptMask::FE
+ );
+ }
+}
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index a1f431a..003ef96 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -18,6 +18,7 @@ crate-type = ["staticlib"]
[dependencies]
bilge = { version = "0.2.0" }
bilge-impl = { version = "0.2.0" }
+bits = { path = "../../../bits" }
qemu_api = { path = "../../../qemu-api" }
qemu_api_macros = { path = "../../../qemu-api-macros" }
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 494b6c1..2a1be32 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -6,6 +6,7 @@ _libpl011_rs = static_library(
dependencies: [
bilge_rs,
bilge_impl_rs,
+ bits_rs,
qemu_api,
qemu_api_macros,
],
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index bd5cee0..0501fa5 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -85,8 +85,8 @@ pub struct PL011Registers {
#[doc(alias = "cr")]
pub control: registers::Control,
pub dmacr: u32,
- pub int_enabled: u32,
- pub int_level: u32,
+ pub int_enabled: Interrupt,
+ pub int_level: Interrupt,
pub read_fifo: Fifo,
pub ilpr: u32,
pub ibrd: u32,
@@ -199,9 +199,9 @@ impl PL011Registers {
LCR_H => u32::from(self.line_control),
CR => u32::from(self.control),
FLS => self.ifl,
- IMSC => self.int_enabled,
- RIS => self.int_level,
- MIS => self.int_level & self.int_enabled,
+ IMSC => u32::from(self.int_enabled),
+ RIS => u32::from(self.int_level),
+ MIS => u32::from(self.int_level & self.int_enabled),
ICR => {
// "The UARTICR Register is the interrupt clear register and is write-only"
// Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR
@@ -263,13 +263,13 @@ impl PL011Registers {
self.set_read_trigger();
}
IMSC => {
- self.int_enabled = value;
+ self.int_enabled = Interrupt::from(value);
return true;
}
RIS => {}
MIS => {}
ICR => {
- self.int_level &= !value;
+ self.int_level &= !Interrupt::from(value);
return true;
}
DMACR => {
@@ -295,7 +295,7 @@ impl PL011Registers {
self.flags.set_receive_fifo_empty(true);
}
if self.read_count + 1 == self.read_trigger {
- self.int_level &= !Interrupt::RX.0;
+ self.int_level &= !Interrupt::RX;
}
self.receive_status_error_clear.set_from_data(c);
*update = true;
@@ -305,7 +305,7 @@ impl PL011Registers {
fn write_data_register(&mut self, value: u32) -> bool {
// interrupts always checked
let _ = self.loopback_tx(value.into());
- self.int_level |= Interrupt::TX.0;
+ self.int_level |= Interrupt::TX;
true
}
@@ -361,19 +361,19 @@ impl PL011Registers {
// Change interrupts based on updated FR
let mut il = self.int_level;
- il &= !Interrupt::MS.0;
+ il &= !Interrupt::MS;
if self.flags.data_set_ready() {
- il |= Interrupt::DSR.0;
+ il |= Interrupt::DSR;
}
if self.flags.data_carrier_detect() {
- il |= Interrupt::DCD.0;
+ il |= Interrupt::DCD;
}
if self.flags.clear_to_send() {
- il |= Interrupt::CTS.0;
+ il |= Interrupt::CTS;
}
if self.flags.ring_indicator() {
- il |= Interrupt::RI.0;
+ il |= Interrupt::RI;
}
self.int_level = il;
true
@@ -391,8 +391,8 @@ impl PL011Registers {
self.line_control.reset();
self.receive_status_error_clear.reset();
self.dmacr = 0;
- self.int_enabled = 0;
- self.int_level = 0;
+ self.int_enabled = 0.into();
+ self.int_level = 0.into();
self.ilpr = 0;
self.ibrd = 0;
self.fbrd = 0;
@@ -451,7 +451,7 @@ impl PL011Registers {
}
if self.read_count == self.read_trigger {
- self.int_level |= Interrupt::RX.0;
+ self.int_level |= Interrupt::RX;
return true;
}
false
@@ -632,7 +632,7 @@ impl PL011State {
let regs = self.regs.borrow();
let flags = regs.int_level & regs.int_enabled;
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
- irq.set(flags & i != 0);
+ irq.set(flags.any_set(i));
}
}
@@ -642,14 +642,13 @@ impl PL011State {
}
/// Which bits in the interrupt status matter for each outbound IRQ line ?
-const IRQMASK: [u32; 6] = [
- /* combined IRQ */
- Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | Interrupt::RX.0,
- Interrupt::RX.0,
- Interrupt::TX.0,
- Interrupt::RT.0,
- Interrupt::MS.0,
- Interrupt::E.0,
+const IRQMASK: [Interrupt; 6] = [
+ Interrupt::all(),
+ Interrupt::RX,
+ Interrupt::TX,
+ Interrupt::RT,
+ Interrupt::MS,
+ Interrupt::E,
];
/// # Safety
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
index 690feb6..7ececd3 100644
--- a/rust/hw/char/pl011/src/registers.rs
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -9,7 +9,8 @@
// https://developer.arm.com/documentation/ddi0183/latest/
use bilge::prelude::*;
-use qemu_api::impl_vmstate_bitsized;
+use bits::bits;
+use qemu_api::{impl_vmstate_bitsized, impl_vmstate_forward};
/// Offset of each register from the base memory address of the device.
#[doc(alias = "offset")]
@@ -326,22 +327,24 @@ impl Default for Control {
}
}
-/// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
-pub struct Interrupt(pub u32);
+bits! {
+ /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
+ #[derive(Default)]
+ pub struct Interrupt(u32) {
+ OE = 1 << 10,
+ BE = 1 << 9,
+ PE = 1 << 8,
+ FE = 1 << 7,
+ RT = 1 << 6,
+ TX = 1 << 5,
+ RX = 1 << 4,
+ DSR = 1 << 3,
+ DCD = 1 << 2,
+ CTS = 1 << 1,
+ RI = 1 << 0,
-impl Interrupt {
- pub const OE: Self = Self(1 << 10);
- pub const BE: Self = Self(1 << 9);
- pub const PE: Self = Self(1 << 8);
- pub const FE: Self = Self(1 << 7);
- pub const RT: Self = Self(1 << 6);
- pub const TX: Self = Self(1 << 5);
- pub const RX: Self = Self(1 << 4);
- pub const DSR: Self = Self(1 << 3);
- pub const DCD: Self = Self(1 << 2);
- pub const CTS: Self = Self(1 << 1);
- pub const RI: Self = Self(1 << 0);
-
- pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0);
- pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0);
+ E = bits!(Self as u32: OE | BE | PE | FE),
+ MS = bits!(Self as u32: RI | DSR | DCD | CTS),
+ }
}
+impl_vmstate_forward!(Interrupt);
diff --git a/rust/meson.build b/rust/meson.build
index 1f0dcce..b1b3315 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -14,7 +14,10 @@ quote_rs_native = dependency('quote-1-rs', native: true)
syn_rs_native = dependency('syn-2-rs', native: true)
proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true)
+qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true)
+
subdir('qemu-api-macros')
+subdir('bits')
subdir('qemu-api')
subdir('hw')
@@ -22,21 +25,9 @@ subdir('hw')
cargo = find_program('cargo', required: false)
if cargo.found()
- run_target('clippy',
- command: [config_host['MESON'], 'devenv',
- '--workdir', '@CURRENT_SOURCE_DIR@',
- cargo, 'clippy', '--tests'],
- depends: bindings_rs)
-
run_target('rustfmt',
command: [config_host['MESON'], 'devenv',
'--workdir', '@CURRENT_SOURCE_DIR@',
cargo, 'fmt'],
depends: bindings_rs)
-
- run_target('rustdoc',
- command: [config_host['MESON'], 'devenv',
- '--workdir', '@CURRENT_SOURCE_DIR@',
- cargo, 'doc', '--no-deps', '--document-private-items'],
- depends: bindings_rs)
endif
diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-api-macros/src/bits.rs
new file mode 100644
index 0000000..5ba8475
--- /dev/null
+++ b/rust/qemu-api-macros/src/bits.rs
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later
+
+// shadowing is useful together with "if let"
+#![allow(clippy::shadow_unrelated)]
+
+use proc_macro2::{
+ Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT,
+};
+
+use crate::utils::MacroError;
+
+pub struct BitsConstInternal {
+ typ: TokenTree,
+}
+
+fn paren(ts: TokenStream) -> TokenTree {
+ TT::Group(Group::new(Delimiter::Parenthesis, ts))
+}
+
+fn ident(s: &'static str) -> TokenTree {
+ TT::Ident(Ident::new(s, Span::call_site()))
+}
+
+fn punct(ch: char) -> TokenTree {
+ TT::Punct(Punct::new(ch, Spacing::Alone))
+}
+
+/// Implements a recursive-descent parser that translates Boolean expressions on
+/// bitmasks to invocations of `const` functions defined by the `bits!` macro.
+impl BitsConstInternal {
+ // primary ::= '(' or ')'
+ // | ident
+ // | '!' ident
+ fn parse_primary(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ let next = match tok {
+ TT::Group(ref g) => {
+ if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None {
+ return Err(MacroError::Message("expected parenthesis".into(), g.span()));
+ }
+ let mut stream = g.stream().into_iter();
+ let Some(first_tok) = stream.next() else {
+ return Err(MacroError::Message(
+ "expected operand, found ')'".into(),
+ g.span(),
+ ));
+ };
+ let mut output = TokenStream::new();
+ // start from the lowest precedence
+ let next = self.parse_or(first_tok, &mut stream, &mut output)?;
+ if let Some(tok) = next {
+ return Err(MacroError::Message(
+ format!("unexpected token {tok}"),
+ tok.span(),
+ ));
+ }
+ out.extend(Some(paren(output)));
+ it.next()
+ }
+ TT::Ident(_) => {
+ let mut output = TokenStream::new();
+ output.extend([
+ self.typ.clone(),
+ TT::Punct(Punct::new(':', Spacing::Joint)),
+ TT::Punct(Punct::new(':', Spacing::Joint)),
+ tok,
+ ]);
+ out.extend(Some(paren(output)));
+ it.next()
+ }
+ TT::Punct(ref p) => {
+ if p.as_char() != '!' {
+ return Err(MacroError::Message("expected operand".into(), p.span()));
+ }
+ let Some(rhs_tok) = it.next() else {
+ return Err(MacroError::Message(
+ "expected operand at end of input".into(),
+ p.span(),
+ ));
+ };
+ let next = self.parse_primary(rhs_tok, it, out)?;
+ out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]);
+ next
+ }
+ _ => {
+ return Err(MacroError::Message("unexpected literal".into(), tok.span()));
+ }
+ };
+ Ok(next)
+ }
+
+ fn parse_binop<
+ F: Fn(
+ &Self,
+ TokenTree,
+ &mut dyn Iterator<Item = TokenTree>,
+ &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError>,
+ >(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ch: char,
+ f: F,
+ method: &'static str,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ let mut next = f(self, tok, it, out)?;
+ while next.is_some() {
+ let op = next.as_ref().unwrap();
+ let TT::Punct(ref p) = op else { break };
+ if p.as_char() != ch {
+ break;
+ }
+
+ let Some(rhs_tok) = it.next() else {
+ return Err(MacroError::Message(
+ "expected operand at end of input".into(),
+ p.span(),
+ ));
+ };
+ let mut rhs = TokenStream::new();
+ next = f(self, rhs_tok, it, &mut rhs)?;
+ out.extend([punct('.'), ident(method), paren(rhs)]);
+ }
+ Ok(next)
+ }
+
+ // sub ::= primary ('-' primary)*
+ pub fn parse_sub(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference")
+ }
+
+ // and ::= sub ('&' sub)*
+ fn parse_and(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection")
+ }
+
+ // xor ::= and ('&' and)*
+ fn parse_xor(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference")
+ }
+
+ // or ::= xor ('|' xor)*
+ pub fn parse_or(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ self.parse_binop(tok, it, out, '|', Self::parse_xor, "union")
+ }
+
+ pub fn parse(
+ it: &mut dyn Iterator<Item = TokenTree>,
+ ) -> Result<proc_macro2::TokenStream, MacroError> {
+ let mut pos = Span::call_site();
+ let mut typ = proc_macro2::TokenStream::new();
+
+ // Gobble everything up to an `@` sign, which is followed by a
+ // parenthesized expression; that is, all token trees except the
+ // last two form the type.
+ let next = loop {
+ let tok = it.next();
+ if let Some(ref t) = tok {
+ pos = t.span();
+ }
+ match tok {
+ None => break None,
+ Some(TT::Punct(ref p)) if p.as_char() == '@' => {
+ let tok = it.next();
+ if let Some(ref t) = tok {
+ pos = t.span();
+ }
+ break tok;
+ }
+ Some(x) => typ.extend(Some(x)),
+ }
+ };
+
+ let Some(tok) = next else {
+ return Err(MacroError::Message(
+ "expected expression, do not call this macro directly".into(),
+ pos,
+ ));
+ };
+ let TT::Group(ref _group) = tok else {
+ return Err(MacroError::Message(
+ "expected parenthesis, do not call this macro directly".into(),
+ tok.span(),
+ ));
+ };
+ let mut out = TokenStream::new();
+ let state = Self {
+ typ: TT::Group(Group::new(Delimiter::None, typ)),
+ };
+
+ let next = state.parse_primary(tok, it, &mut out)?;
+
+ // A parenthesized expression is a single production of the grammar,
+ // so the input must have reached the last token.
+ if let Some(tok) = next {
+ return Err(MacroError::Message(
+ format!("unexpected token {tok}"),
+ tok.span(),
+ ));
+ }
+ Ok(out)
+ }
+}
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index f97449b..1034707 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -12,6 +12,9 @@ use syn::{
mod utils;
use utils::MacroError;
+mod bits;
+use bits::BitsConstInternal;
+
fn get_fields<'a>(
input: &'a DeriveInput,
msg: &str,
@@ -190,23 +193,51 @@ fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, Macr
}
#[rustfmt::skip::macros(quote)]
+fn derive_tryinto_body(
+ name: &Ident,
+ variants: &Punctuated<Variant, Comma>,
+ repr: &Path,
+) -> Result<proc_macro2::TokenStream, MacroError> {
+ let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect();
+
+ Ok(quote! {
+ #(const #discriminants: #repr = #name::#discriminants as #repr;)*;
+ match value {
+ #(#discriminants => Ok(#name::#discriminants),)*
+ _ => Err(value),
+ }
+ })
+}
+
+#[rustfmt::skip::macros(quote)]
fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
let repr = get_repr_uN(&input, "#[derive(TryInto)]")?;
-
let name = &input.ident;
- let variants = get_variants(&input)?;
- let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect();
+ let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?;
+ let errmsg = format!("invalid value for {name}");
Ok(quote! {
+ impl #name {
+ #[allow(dead_code)]
+ pub const fn into_bits(self) -> #repr {
+ self as #repr
+ }
+
+ #[allow(dead_code)]
+ pub const fn from_bits(value: #repr) -> Self {
+ match ({
+ #body
+ }) {
+ Ok(x) => x,
+ Err(_) => panic!(#errmsg)
+ }
+ }
+ }
impl core::convert::TryFrom<#repr> for #name {
type Error = #repr;
fn try_from(value: #repr) -> Result<Self, Self::Error> {
- #(const #discriminants: #repr = #name::#discriminants as #repr;)*;
- match value {
- #(#discriminants => Ok(Self::#discriminants),)*
- _ => Err(value),
- }
+ #body
}
}
})
@@ -219,3 +250,12 @@ pub fn derive_tryinto(input: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}
+
+#[proc_macro]
+pub fn bits_const_internal(ts: TokenStream) -> TokenStream {
+ let ts = proc_macro2::TokenStream::from(ts);
+ let mut it = ts.into_iter();
+
+ let expanded = BitsConstInternal::parse(&mut it).unwrap_or_else(Into::into);
+ TokenStream::from(expanded)
+}
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 1ea86b8..b532281 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -35,32 +35,24 @@ _qemu_api_rs = static_library(
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
rust_args: _qemu_api_cfg,
- dependencies: [libc_rs, qemu_api_macros],
+ dependencies: [libc_rs, qemu_api_macros, qemuutil_rs,
+ qom, hwcore, chardev, migration],
)
rust.test('rust-qemu-api-tests', _qemu_api_rs,
suite: ['unit', 'rust'])
-qemu_api = declare_dependency(link_with: _qemu_api_rs)
+qemu_api = declare_dependency(link_with: [_qemu_api_rs],
+ dependencies: [qemu_api_macros, qom, hwcore, chardev, migration])
-# Rust executables do not support objects, so add an intermediate step.
-rust_qemu_api_objs = static_library(
- 'rust_qemu_api_objs',
- objects: [libqom.extract_all_objects(recursive: false),
- libhwcore.extract_all_objects(recursive: false),
- libchardev.extract_all_objects(recursive: false),
- libcrypto.extract_all_objects(recursive: false),
- libauthz.extract_all_objects(recursive: false),
- libio.extract_all_objects(recursive: false),
- libmigration.extract_all_objects(recursive: false)])
-rust_qemu_api_deps = declare_dependency(
- dependencies: [
- qom_ss.dependencies(),
- chardev_ss.dependencies(),
- crypto_ss.dependencies(),
- authz_ss.dependencies(),
- io_ss.dependencies()],
- link_whole: [rust_qemu_api_objs, libqemuutil])
+# Doctests are essentially integration tests, so they need the same dependencies.
+# Note that running them requires the object files for C code, so place them
+# in a separate suite that is run by the "build" CI jobs rather than "check".
+rust.doctest('rust-qemu-api-doctests',
+ _qemu_api_rs,
+ protocol: 'rust',
+ dependencies: qemu_api,
+ suite: ['doc', 'rust'])
test('rust-qemu-api-integration',
executable(
@@ -69,7 +61,7 @@ test('rust-qemu-api-integration',
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_args: ['--test'],
install: false,
- dependencies: [qemu_api, qemu_api_macros, rust_qemu_api_deps]),
+ dependencies: [qemu_api]),
args: [
'--test', '--test-threads', '1',
'--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 3c1d297..057de4b 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -11,6 +11,7 @@
clippy::restriction,
clippy::style,
clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
clippy::useless_transmute,
clippy::missing_safety_doc
)]
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index 05ce09f..27063b0 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -225,27 +225,23 @@ use crate::bindings;
/// An internal function that is used by doctests.
pub fn bql_start_test() {
- if cfg!(MESON) {
- // SAFETY: integration tests are run with --test-threads=1, while
- // unit tests and doctests are not multithreaded and do not have
- // any BQL-protected data. Just set bql_locked to true.
- unsafe {
- bindings::rust_bql_mock_lock();
- }
+ // SAFETY: integration tests are run with --test-threads=1, while
+ // unit tests and doctests are not multithreaded and do not have
+ // any BQL-protected data. Just set bql_locked to true.
+ unsafe {
+ bindings::rust_bql_mock_lock();
}
}
pub fn bql_locked() -> bool {
// SAFETY: the function does nothing but return a thread-local bool
- !cfg!(MESON) || unsafe { bindings::bql_locked() }
+ unsafe { bindings::bql_locked() }
}
fn bql_block_unlock(increase: bool) {
- if cfg!(MESON) {
- // SAFETY: this only adjusts a counter
- unsafe {
- bindings::bql_block_unlock(increase);
- }
+ // SAFETY: this only adjusts a counter
+ unsafe {
+ bindings::bql_block_unlock(increase);
}
}
diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py
index 2633157..63b0748 100644
--- a/scripts/rust/rustc_args.py
+++ b/scripts/rust/rustc_args.py
@@ -104,10 +104,7 @@ def generate_lint_flags(cargo_toml: CargoTOML, strict_lints: bool) -> Iterable[s
else:
raise Exception(f"invalid level {level} for {prefix}{lint}")
- # This may change if QEMU ever invokes clippy-driver or rustdoc by
- # hand. For now, check the syntax but do not add non-rustc lints to
- # the command line.
- if k == "rust" and not (strict_lints and lint in STRICT_LINTS):
+ if not (strict_lints and lint in STRICT_LINTS):
lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority))
if strict_lints:
diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py
index a74d61f..2688d4b 100644
--- a/scripts/tracetool/backend/simple.py
+++ b/scripts/tracetool/backend/simple.py
@@ -36,8 +36,17 @@ def generate_h_begin(events, group):
def generate_h(event, group):
- out(' _simple_%(api)s(%(args)s);',
+ event_id = 'TRACE_' + event.name.upper()
+ if "vcpu" in event.properties:
+ # already checked on the generic format code
+ cond = "true"
+ else:
+ cond = "trace_event_get_state(%s)" % event_id
+ out(' if (%(cond)s) {',
+ ' _simple_%(api)s(%(args)s);',
+ ' }',
api=event.api(),
+ cond=cond,
args=", ".join(event.args.names()))
@@ -72,22 +81,10 @@ def generate_c(event, group):
if len(event.args) == 0:
sizestr = '0'
- event_id = 'TRACE_' + event.name.upper()
- if "vcpu" in event.properties:
- # already checked on the generic format code
- cond = "true"
- else:
- cond = "trace_event_get_state(%s)" % event_id
-
out('',
- ' if (!%(cond)s) {',
- ' return;',
- ' }',
- '',
' if (trace_record_start(&rec, %(event_obj)s.id, %(size_str)s)) {',
' return; /* Trace Buffer Full, Event Dropped ! */',
' }',
- cond=cond,
event_obj=event.api(event.QEMU_EVENT),
size_str=sizestr)
diff --git a/system/memory.c b/system/memory.c
index 63b983e..306e9ff 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -2174,18 +2174,14 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
}
/* Called with rcu_read_lock held. */
-bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
- ram_addr_t *ram_addr, bool *read_only,
- bool *mr_has_discard_manager, Error **errp)
+MemoryRegion *memory_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p,
+ Error **errp)
{
MemoryRegion *mr;
hwaddr xlat;
hwaddr len = iotlb->addr_mask + 1;
bool writable = iotlb->perm & IOMMU_WO;
- if (mr_has_discard_manager) {
- *mr_has_discard_manager = false;
- }
/*
* The IOMMU TLB entry we have just covers translation through
* this IOMMU to its immediate target. We need to translate
@@ -2195,7 +2191,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
&xlat, &len, writable, MEMTXATTRS_UNSPECIFIED);
if (!memory_region_is_ram(mr)) {
error_setg(errp, "iommu map to non memory area %" HWADDR_PRIx "", xlat);
- return false;
+ return NULL;
} else if (memory_region_has_ram_discard_manager(mr)) {
RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr);
MemoryRegionSection tmp = {
@@ -2203,9 +2199,6 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
.offset_within_region = xlat,
.size = int128_make64(len),
};
- if (mr_has_discard_manager) {
- *mr_has_discard_manager = true;
- }
/*
* Malicious VMs can map memory into the IOMMU, which is expected
* to remain discarded. vfio will pin all pages, populating memory.
@@ -2216,7 +2209,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
error_setg(errp, "iommu map to discarded memory (e.g., unplugged"
" via virtio-mem): %" HWADDR_PRIx "",
iotlb->translated_addr);
- return false;
+ return NULL;
}
}
@@ -2226,22 +2219,11 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
*/
if (len & iotlb->addr_mask) {
error_setg(errp, "iommu has granularity incompatible with target AS");
- return false;
- }
-
- if (vaddr) {
- *vaddr = memory_region_get_ram_ptr(mr) + xlat;
- }
-
- if (ram_addr) {
- *ram_addr = memory_region_get_ram_addr(mr) + xlat;
- }
-
- if (read_only) {
- *read_only = !writable || mr->readonly;
+ return NULL;
}
- return true;
+ *xlat_p = xlat;
+ return mr;
}
void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index c9bd344..40aefb3 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -900,6 +900,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
#define TCG_7_1_EAX_FEATURES (CPUID_7_1_EAX_FZRM | CPUID_7_1_EAX_FSRS | \
CPUID_7_1_EAX_FSRC | CPUID_7_1_EAX_CMPCCXADD)
+#define TCG_7_1_ECX_FEATURES 0
#define TCG_7_1_EDX_FEATURES 0
#define TCG_7_2_EDX_FEATURES 0
#define TCG_APM_FEATURES 0
@@ -1150,6 +1151,25 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
.tcg_features = TCG_7_1_EAX_FEATURES,
},
+ [FEAT_7_1_ECX] = {
+ .type = CPUID_FEATURE_WORD,
+ .feat_names = {
+ NULL, NULL, NULL, NULL,
+ NULL, "msr-imm", NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ },
+ .cpuid = {
+ .eax = 7,
+ .needs_ecx = true, .ecx = 1,
+ .reg = R_ECX,
+ },
+ .tcg_features = TCG_7_1_ECX_FEATURES,
+ },
[FEAT_7_1_EDX] = {
.type = CPUID_FEATURE_WORD,
.feat_names = {
@@ -1804,10 +1824,6 @@ static FeatureDep feature_dependencies[] = {
.to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED },
},
{
- .from = { FEAT_7_1_EAX, CPUID_7_1_EAX_WRMSRNS },
- .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED },
- },
- {
.from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX },
.to = { FEAT_7_0_ECX, CPUID_7_0_ECX_SGX_LC },
},
@@ -7446,9 +7462,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*edx = env->features[FEAT_7_0_EDX]; /* Feature flags */
} else if (count == 1) {
*eax = env->features[FEAT_7_1_EAX];
+ *ecx = env->features[FEAT_7_1_ECX];
*edx = env->features[FEAT_7_1_EDX];
*ebx = 0;
- *ecx = 0;
} else if (count == 2) {
*edx = env->features[FEAT_7_2_EDX];
*eax = 0;
@@ -8353,6 +8369,7 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
x86_cpu_adjust_feat_level(cpu, FEAT_6_EAX);
x86_cpu_adjust_feat_level(cpu, FEAT_7_0_ECX);
x86_cpu_adjust_feat_level(cpu, FEAT_7_1_EAX);
+ x86_cpu_adjust_feat_level(cpu, FEAT_7_1_ECX);
x86_cpu_adjust_feat_level(cpu, FEAT_7_1_EDX);
x86_cpu_adjust_feat_level(cpu, FEAT_7_2_EDX);
x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_EDX);
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 1146465..545851c 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -668,6 +668,7 @@ typedef enum FeatureWord {
FEAT_SGX_12_1_EAX, /* CPUID[EAX=0x12,ECX=1].EAX (SGX ATTRIBUTES[31:0]) */
FEAT_XSAVE_XSS_LO, /* CPUID[EAX=0xd,ECX=1].ECX */
FEAT_XSAVE_XSS_HI, /* CPUID[EAX=0xd,ECX=1].EDX */
+ FEAT_7_1_ECX, /* CPUID[EAX=7,ECX=1].ECX */
FEAT_7_1_EDX, /* CPUID[EAX=7,ECX=1].EDX */
FEAT_7_2_EDX, /* CPUID[EAX=7,ECX=2].EDX */
FEAT_24_0_EBX, /* CPUID[EAX=0x24,ECX=0].EBX */
@@ -1000,6 +1001,9 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
/* Linear Address Masking */
#define CPUID_7_1_EAX_LAM (1U << 26)
+/* The immediate form of MSR access instructions */
+#define CPUID_7_1_ECX_MSR_IMM (1U << 5)
+
/* Support for VPDPB[SU,UU,SS]D[,S] */
#define CPUID_7_1_EDX_AVX_VNNI_INT8 (1U << 4)
/* AVX NE CONVERT Instructions */
@@ -1023,6 +1027,7 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
#define CPUID_7_2_EDX_DDPD_U (1U << 3)
/* Indicate bit 10 of the IA32_SPEC_CTRL MSR is supported */
#define CPUID_7_2_EDX_BHI_CTRL (1U << 4)
+
/* Do not exhibit MXCSR Configuration Dependent Timing (MCDT) behavior */
#define CPUID_7_2_EDX_MCDT_NO (1U << 5)
diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c
index 0a21ae5..820ca36 100644
--- a/target/i386/kvm/tdx.c
+++ b/target/i386/kvm/tdx.c
@@ -284,7 +284,7 @@ static void tdx_post_init_vcpus(void)
hob = tdx_get_hob_entry(tdx_guest);
CPU_FOREACH(cpu) {
- tdx_vcpu_ioctl(cpu, KVM_TDX_INIT_VCPU, 0, (void *)hob->address,
+ tdx_vcpu_ioctl(cpu, KVM_TDX_INIT_VCPU, 0, (void *)(uintptr_t)hob->address,
&error_fatal);
}
}
@@ -339,7 +339,7 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused)
uint32_t flags;
region = (struct kvm_tdx_init_mem_region) {
- .source_addr = (uint64_t)entry->mem_ptr,
+ .source_addr = (uintptr_t)entry->mem_ptr,
.gpa = entry->address,
.nr_pages = entry->size >> 12,
};
@@ -893,16 +893,16 @@ static int tdx_check_features(X86ConfidentialGuest *cg, CPUState *cs)
static int tdx_validate_attributes(TdxGuest *tdx, Error **errp)
{
if ((tdx->attributes & ~tdx_caps->supported_attrs)) {
- error_setg(errp, "Invalid attributes 0x%lx for TDX VM "
- "(KVM supported: 0x%llx)", tdx->attributes,
- tdx_caps->supported_attrs);
+ error_setg(errp, "Invalid attributes 0x%"PRIx64" for TDX VM "
+ "(KVM supported: 0x%"PRIx64")", tdx->attributes,
+ (uint64_t)tdx_caps->supported_attrs);
return -1;
}
if (tdx->attributes & ~TDX_SUPPORTED_TD_ATTRS) {
error_setg(errp, "Some QEMU unsupported TD attribute bits being "
- "requested: 0x%lx (QEMU supported: 0x%llx)",
- tdx->attributes, TDX_SUPPORTED_TD_ATTRS);
+ "requested: 0x%"PRIx64" (QEMU supported: 0x%"PRIx64")",
+ tdx->attributes, (uint64_t)TDX_SUPPORTED_TD_ATTRS);
return -1;
}
@@ -931,8 +931,8 @@ static int setup_td_xfam(X86CPU *x86cpu, Error **errp)
env->features[FEAT_XSAVE_XSS_HI];
if (xfam & ~tdx_caps->supported_xfam) {
- error_setg(errp, "Invalid XFAM 0x%lx for TDX VM (supported: 0x%llx))",
- xfam, tdx_caps->supported_xfam);
+ error_setg(errp, "Invalid XFAM 0x%"PRIx64" for TDX VM (supported: 0x%"PRIx64"))",
+ xfam, (uint64_t)tdx_caps->supported_xfam);
return -1;
}
@@ -999,14 +999,14 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp)
if (env->tsc_khz && (env->tsc_khz < TDX_MIN_TSC_FREQUENCY_KHZ ||
env->tsc_khz > TDX_MAX_TSC_FREQUENCY_KHZ)) {
- error_setg(errp, "Invalid TSC %ld KHz, must specify cpu_frequency "
+ error_setg(errp, "Invalid TSC %"PRId64" KHz, must specify cpu_frequency "
"between [%d, %d] kHz", env->tsc_khz,
TDX_MIN_TSC_FREQUENCY_KHZ, TDX_MAX_TSC_FREQUENCY_KHZ);
return -EINVAL;
}
if (env->tsc_khz % (25 * 1000)) {
- error_setg(errp, "Invalid TSC %ld KHz, it must be multiple of 25MHz",
+ error_setg(errp, "Invalid TSC %"PRId64" KHz, it must be multiple of 25MHz",
env->tsc_khz);
return -EINVAL;
}
@@ -1014,7 +1014,7 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp)
/* it's safe even env->tsc_khz is 0. KVM uses host's tsc_khz in this case */
r = kvm_vm_ioctl(kvm_state, KVM_SET_TSC_KHZ, env->tsc_khz);
if (r < 0) {
- error_setg_errno(errp, -r, "Unable to set TSC frequency to %ld kHz",
+ error_setg_errno(errp, -r, "Unable to set TSC frequency to %"PRId64" kHz",
env->tsc_khz);
return r;
}
@@ -1139,7 +1139,7 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run)
uint64_t gpa = -1ull;
if (error_code & 0xffff) {
- error_report("TDX: REPORT_FATAL_ERROR: invalid error code: 0x%lx",
+ error_report("TDX: REPORT_FATAL_ERROR: invalid error code: 0x%"PRIx64,
error_code);
return -1;
}
diff --git a/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2
new file mode 100644
index 0000000..18daee0
--- /dev/null
+++ b/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2
Binary files differ
diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker
index fe4a6ed..4a03330 100644
--- a/tests/docker/dockerfiles/fedora-rust-nightly.docker
+++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker
@@ -156,6 +156,7 @@ ENV PYTHON "/usr/bin/python3"
RUN dnf install -y wget
ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo
ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc
+ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc
ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo
RUN set -eux && \
rustArch='x86_64-unknown-linux-gnu' && \
@@ -170,6 +171,7 @@ RUN set -eux && \
/usr/local/cargo/bin/rustup run nightly cargo --version && \
/usr/local/cargo/bin/rustup run nightly rustc --version && \
test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \
+ test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \
test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"
ENV PATH=$CARGO_HOME/bin:$PATH
RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli
diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker
index 4a1cf2b..28a6f93 100644
--- a/tests/docker/dockerfiles/ubuntu2204.docker
+++ b/tests/docker/dockerfiles/ubuntu2204.docker
@@ -151,6 +151,7 @@ ENV MAKE "/usr/bin/make"
ENV NINJA "/usr/bin/ninja"
ENV PYTHON "/usr/bin/python3"
ENV RUSTC=/usr/bin/rustc-1.77
+ENV RUSTDOC=/usr/bin/rustdoc-1.77
ENV CARGO_HOME=/usr/local/cargo
ENV PATH=$CARGO_HOME/bin:$PATH
RUN DEBIAN_FRONTEND=noninteractive eatmydata \
diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml
index 673baf3..8f0e95e 100644
--- a/tests/lcitool/mappings.yml
+++ b/tests/lcitool/mappings.yml
@@ -8,6 +8,10 @@ mappings:
meson:
OpenSUSELeap15:
+ # Use Meson from PyPI wherever Rust is enabled
+ Debian:
+ Fedora:
+ Ubuntu:
python3:
OpenSUSELeap15: python311-base
@@ -72,7 +76,7 @@ mappings:
pypi_mappings:
# Request more recent version
meson:
- default: meson==1.5.0
+ default: meson==1.8.1
# Drop packages that need devel headers
python3-numpy:
diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh
index 8474ea8..d3488b2 100755
--- a/tests/lcitool/refresh
+++ b/tests/lcitool/refresh
@@ -121,6 +121,7 @@ fedora_rustup_nightly_extras = [
"RUN dnf install -y wget\n",
"ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo\n",
"ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc\n",
+ "ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc\n",
"ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo\n",
"RUN set -eux && \\\n",
" rustArch='x86_64-unknown-linux-gnu' && \\\n",
@@ -135,6 +136,7 @@ fedora_rustup_nightly_extras = [
" /usr/local/cargo/bin/rustup run nightly cargo --version && \\\n",
" /usr/local/cargo/bin/rustup run nightly rustc --version && \\\n",
' test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \\\n',
+ ' test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \\\n',
' test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"\n',
'ENV PATH=$CARGO_HOME/bin:$PATH\n',
'RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli\n',
@@ -143,6 +145,7 @@ fedora_rustup_nightly_extras = [
ubuntu2204_rust_extras = [
"ENV RUSTC=/usr/bin/rustc-1.77\n",
+ "ENV RUSTDOC=/usr/bin/rustdoc-1.77\n",
"ENV CARGO_HOME=/usr/local/cargo\n",
'ENV PATH=$CARGO_HOME/bin:$PATH\n',
"RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n",
diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240
index 9b281e1..f8af9ff 100755
--- a/tests/qemu-iotests/240
+++ b/tests/qemu-iotests/240
@@ -81,8 +81,6 @@ class TestCase(iotests.QMPTestCase):
self.vm.qmp_log('device_del', id='scsi-hd0')
self.vm.event_wait('DEVICE_DELETED')
- self.vm.qmp_log('device_add', id='scsi-hd1', driver='scsi-hd', drive='hd0', bus="scsi1.0")
-
self.vm.qmp_log('device_del', id='scsi-hd1')
self.vm.event_wait('DEVICE_DELETED')
self.vm.qmp_log('blockdev-del', node_name='hd0')
diff --git a/tests/qemu-iotests/240.out b/tests/qemu-iotests/240.out
index 89ed25e..10dcc42 100644
--- a/tests/qemu-iotests/240.out
+++ b/tests/qemu-iotests/240.out
@@ -46,10 +46,8 @@
{"execute": "device_add", "arguments": {"bus": "scsi0.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}}
{"return": {}}
{"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}}
-{"error": {"class": "GenericError", "desc": "Cannot change iothread of active block backend"}}
-{"execute": "device_del", "arguments": {"id": "scsi-hd0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}}
+{"execute": "device_del", "arguments": {"id": "scsi-hd0"}}
{"return": {}}
{"execute": "device_del", "arguments": {"id": "scsi-hd1"}}
{"return": {}}
diff --git a/tests/qemu-iotests/tests/graph-changes-while-io b/tests/qemu-iotests/tests/graph-changes-while-io
index 194fda5..dca1167 100755
--- a/tests/qemu-iotests/tests/graph-changes-while-io
+++ b/tests/qemu-iotests/tests/graph-changes-while-io
@@ -27,6 +27,7 @@ from iotests import imgfmt, qemu_img, qemu_img_create, qemu_io, \
top = os.path.join(iotests.test_dir, 'top.img')
+mid = os.path.join(iotests.test_dir, 'mid.img')
nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock')
@@ -57,6 +58,16 @@ class TestGraphChangesWhileIO(QMPTestCase):
def tearDown(self) -> None:
self.qsd.stop()
+ os.remove(top)
+
+ def _wait_for_blockjob(self, status: str) -> None:
+ done = False
+ while not done:
+ for event in self.qsd.get_qmp().get_events(wait=10.0):
+ if event['event'] != 'JOB_STATUS_CHANGE':
+ continue
+ if event['data']['status'] == status:
+ done = True
def test_blockdev_add_while_io(self) -> None:
# Run qemu-img bench in the background
@@ -116,15 +127,92 @@ class TestGraphChangesWhileIO(QMPTestCase):
'device': 'job0',
})
- cancelled = False
- while not cancelled:
- for event in self.qsd.get_qmp().get_events(wait=10.0):
- if event['event'] != 'JOB_STATUS_CHANGE':
- continue
- if event['data']['status'] == 'null':
- cancelled = True
+ self._wait_for_blockjob('null')
+
+ bench_thr.join()
+
+ def test_remove_lower_snapshot_while_io(self) -> None:
+ # Run qemu-img bench in the background
+ bench_thr = Thread(target=do_qemu_img_bench, args=(100000, ))
+ bench_thr.start()
+
+ # While I/O is performed on 'node0' node, consequently add 2 snapshots
+ # on top of it, then remove (commit) them starting from lower one.
+ while bench_thr.is_alive():
+ # Recreate snapshot images on every iteration
+ qemu_img_create('-f', imgfmt, mid, '1G')
+ qemu_img_create('-f', imgfmt, top, '1G')
+
+ self.qsd.cmd('blockdev-add', {
+ 'driver': imgfmt,
+ 'node-name': 'mid',
+ 'file': {
+ 'driver': 'file',
+ 'filename': mid
+ }
+ })
+
+ self.qsd.cmd('blockdev-snapshot', {
+ 'node': 'node0',
+ 'overlay': 'mid',
+ })
+
+ self.qsd.cmd('blockdev-add', {
+ 'driver': imgfmt,
+ 'node-name': 'top',
+ 'file': {
+ 'driver': 'file',
+ 'filename': top
+ }
+ })
+
+ self.qsd.cmd('blockdev-snapshot', {
+ 'node': 'mid',
+ 'overlay': 'top',
+ })
+
+ self.qsd.cmd('block-commit', {
+ 'job-id': 'commit-mid',
+ 'device': 'top',
+ 'top-node': 'mid',
+ 'base-node': 'node0',
+ 'auto-finalize': True,
+ 'auto-dismiss': False,
+ })
+
+ self._wait_for_blockjob('concluded')
+ self.qsd.cmd('job-dismiss', {
+ 'id': 'commit-mid',
+ })
+
+ self.qsd.cmd('block-commit', {
+ 'job-id': 'commit-top',
+ 'device': 'top',
+ 'top-node': 'top',
+ 'base-node': 'node0',
+ 'auto-finalize': True,
+ 'auto-dismiss': False,
+ })
+
+ self._wait_for_blockjob('ready')
+ self.qsd.cmd('job-complete', {
+ 'id': 'commit-top',
+ })
+
+ self._wait_for_blockjob('concluded')
+ self.qsd.cmd('job-dismiss', {
+ 'id': 'commit-top',
+ })
+
+ self.qsd.cmd('blockdev-del', {
+ 'node-name': 'mid'
+ })
+ self.qsd.cmd('blockdev-del', {
+ 'node-name': 'top'
+ })
bench_thr.join()
+ os.remove(mid)
if __name__ == '__main__':
# Format must support raw backing files
diff --git a/tests/qemu-iotests/tests/graph-changes-while-io.out b/tests/qemu-iotests/tests/graph-changes-while-io.out
index fbc63e6..8d7e9967 100644
--- a/tests/qemu-iotests/tests/graph-changes-while-io.out
+++ b/tests/qemu-iotests/tests/graph-changes-while-io.out
@@ -1,5 +1,5 @@
-..
+...
----------------------------------------------------------------------
-Ran 2 tests
+Ran 3 tests
OK
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index 0a333ec..0b2bdf9 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -1622,7 +1622,7 @@ static void test_acpi_aarch64_virt_tcg_memhp(void)
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 256ULL * 1024 * 1024,
+ .scan_len = 256ULL * MiB,
};
data.variant = ".memhp";
@@ -1717,7 +1717,7 @@ static void test_acpi_riscv64_virt_tcg_numamem(void)
.uefi_fl2 = "pc-bios/edk2-riscv-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2",
.ram_start = 0x80000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
data.variant = ".numamem";
@@ -1743,7 +1743,7 @@ static void test_acpi_aarch64_virt_tcg_numamem(void)
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
data.variant = ".numamem";
@@ -1765,7 +1765,7 @@ static void test_acpi_aarch64_virt_tcg_pxb(void)
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
/*
* While using -cdrom, the cdrom would auto plugged into pxb-pcie,
@@ -1841,7 +1841,7 @@ static void test_acpi_aarch64_virt_tcg_acpi_hmat(void)
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
data.variant = ".acpihmatvirt";
@@ -2095,7 +2095,7 @@ static void test_acpi_riscv64_virt_tcg(void)
.uefi_fl2 = "pc-bios/edk2-riscv-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2",
.ram_start = 0x80000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
/*
@@ -2117,7 +2117,7 @@ static void test_acpi_aarch64_virt_tcg(void)
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
data.smbios_cpu_max_speed = 2900;
@@ -2138,7 +2138,7 @@ static void test_acpi_aarch64_virt_tcg_topology(void)
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
test_acpi_one("-cpu cortex-a57 "
@@ -2223,7 +2223,7 @@ static void test_acpi_aarch64_virt_viot(void)
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
test_acpi_one("-cpu cortex-a57 "
@@ -2407,7 +2407,7 @@ static void test_acpi_aarch64_virt_oem_fields(void)
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
char *args;
diff --git a/tests/uefi-test-tools/Makefile b/tests/uefi-test-tools/Makefile
index f4eaebd..8ee6fb3 100644
--- a/tests/uefi-test-tools/Makefile
+++ b/tests/uefi-test-tools/Makefile
@@ -12,7 +12,7 @@
edk2_dir := ../../roms/edk2
images_dir := ../data/uefi-boot-images
-emulation_targets := arm aarch64 i386 x86_64 riscv64
+emulation_targets := arm aarch64 i386 x86_64 riscv64 loongarch64
uefi_binaries := bios-tables-test
intermediate_suffixes := .efi .fat .iso.raw
@@ -56,7 +56,8 @@ Build/%.iso.raw: Build/%.fat
# stripped from, the argument.
map_arm_to_uefi = $(subst arm,ARM,$(1))
map_aarch64_to_uefi = $(subst aarch64,AA64,$(call map_arm_to_uefi,$(1)))
-map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_aarch64_to_uefi,$(1)))
+map_loongarch64_to_uefi = $(subst loongarch64,LOONGARCH64,$(call map_aarch64_to_uefi,$(1)))
+map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_loongarch64_to_uefi,$(1)))
map_i386_to_uefi = $(subst i386,IA32,$(call map_riscv64_to_uefi,$(1)))
map_x86_64_to_uefi = $(subst x86_64,X64,$(call map_i386_to_uefi,$(1)))
map_to_uefi = $(subst .,,$(call map_x86_64_to_uefi,$(1)))
diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc
index 0902fd3..facf8df 100644
--- a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc
+++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc
@@ -19,7 +19,7 @@
PLATFORM_VERSION = 0.1
PLATFORM_NAME = UefiTestTools
SKUID_IDENTIFIER = DEFAULT
- SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64
+ SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64|LOONGARCH64
BUILD_TARGETS = DEBUG
[BuildOptions.IA32]
@@ -65,6 +65,10 @@
[LibraryClasses.RISCV64]
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+[LibraryClasses.LOONGARCH64]
+ BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+ StackCheckLib|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf
+
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8040004F
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2F
diff --git a/tests/uefi-test-tools/uefi-test-build.config b/tests/uefi-test-tools/uefi-test-build.config
index a4c61fc..8bf4826 100644
--- a/tests/uefi-test-tools/uefi-test-build.config
+++ b/tests/uefi-test-tools/uefi-test-build.config
@@ -22,6 +22,16 @@ arch = AARCH64
cpy1 = AARCH64/BiosTablesTest.efi bios-tables-test.aarch64.efi
####################################################################################
+# loongarch64
+
+[build.loongarch64]
+conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc
+plat = UefiTestTools
+dest = ./Build
+arch = LOONGARCH64
+cpy1 = LOONGARCH64/BiosTablesTest.efi bios-tables-test.loongarch64.efi
+
+####################################################################################
# riscv64
[build.riscv64]
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
index 290cd2a..59c2793 100644
--- a/tests/unit/test-bdrv-drain.c
+++ b/tests/unit/test-bdrv-drain.c
@@ -772,9 +772,11 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type,
tjob->bs = src;
job = &tjob->common;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
switch (result) {
case TEST_JOB_SUCCESS:
@@ -953,11 +955,13 @@ static void bdrv_test_top_close(BlockDriverState *bs)
{
BdrvChild *c, *next_c;
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) {
bdrv_unref_child(bs, c);
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
}
static int coroutine_fn GRAPH_RDLOCK
@@ -1014,7 +1018,9 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque)
bdrv_graph_co_rdlock();
QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) {
bdrv_graph_co_rdunlock();
+ bdrv_drain_all_begin();
bdrv_co_unref_child(bs, c);
+ bdrv_drain_all_end();
bdrv_graph_co_rdlock();
}
bdrv_graph_co_rdunlock();
@@ -1047,10 +1053,12 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete,
null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
&error_abort);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds,
BDRV_CHILD_DATA, &error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
/* This child will be the one to pass to requests through to, and
* it will stall until a drain occurs */
@@ -1058,21 +1066,25 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete,
&error_abort);
child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS;
/* Takes our reference to child_bs */
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child",
&child_of_bds,
BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY,
&error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
/* This child is just there to be deleted
* (for detach_instead_of_delete == true) */
null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
&error_abort);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA,
&error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL);
blk_insert_bs(blk, bs, &error_abort);
@@ -1155,6 +1167,7 @@ static void no_coroutine_fn detach_indirect_bh(void *opaque)
bdrv_dec_in_flight(data->child_b->bs);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_unref_child(data->parent_b, data->child_b);
@@ -1163,6 +1176,7 @@ static void no_coroutine_fn detach_indirect_bh(void *opaque)
&child_of_bds, BDRV_CHILD_DATA,
&error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
}
static void coroutine_mixed_fn detach_by_parent_aio_cb(void *opaque, int ret)
@@ -1260,6 +1274,7 @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb)
/* Set child relationships */
bdrv_ref(b);
bdrv_ref(a);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds,
BDRV_CHILD_DATA, &error_abort);
@@ -1271,6 +1286,7 @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb)
by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class,
BDRV_CHILD_DATA, &error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
g_assert_cmpint(parent_a->refcnt, ==, 1);
g_assert_cmpint(parent_b->refcnt, ==, 1);
@@ -1396,14 +1412,10 @@ static void test_set_aio_context(void)
bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
&error_abort);
- bdrv_drained_begin(bs);
bdrv_try_change_aio_context(bs, ctx_a, NULL, &error_abort);
- bdrv_drained_end(bs);
- bdrv_drained_begin(bs);
bdrv_try_change_aio_context(bs, ctx_b, NULL, &error_abort);
bdrv_try_change_aio_context(bs, qemu_get_aio_context(), NULL, &error_abort);
- bdrv_drained_end(bs);
bdrv_unref(bs);
iothread_join(a);
@@ -1687,6 +1699,7 @@ static void test_drop_intermediate_poll(void)
* Establish the chain last, so the chain links are the first
* elements in the BDS.parents lists
*/
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
for (i = 0; i < 3; i++) {
if (i) {
@@ -1696,6 +1709,7 @@ static void test_drop_intermediate_poll(void)
}
}
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
job = block_job_create("job", &test_simple_job_driver, NULL, job_node,
0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort);
@@ -1942,10 +1956,12 @@ static void do_test_replace_child_mid_drain(int old_drain_count,
new_child_bs->total_sectors = 1;
bdrv_ref(old_child_bs);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds,
BDRV_CHILD_COW, &error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
parent_s->setup_completed = true;
for (i = 0; i < old_drain_count; i++) {
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
index d743abb..7b03ebe 100644
--- a/tests/unit/test-bdrv-graph-mod.c
+++ b/tests/unit/test-bdrv-graph-mod.c
@@ -137,10 +137,12 @@ static void test_update_perm_tree(void)
blk_insert_bs(root, bs, &error_abort);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_attach_child(filter, bs, "child", &child_of_bds,
BDRV_CHILD_DATA, &error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
ret = bdrv_append(filter, bs, NULL);
g_assert_cmpint(ret, <, 0);
@@ -204,11 +206,13 @@ static void test_should_update_child(void)
bdrv_set_backing_hd(target, bs, &error_abort);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
g_assert(target->backing->bs == bs);
bdrv_attach_child(filter, target, "target", &child_of_bds,
BDRV_CHILD_DATA, &error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
bdrv_append(filter, bs, &error_abort);
bdrv_graph_rdlock_main_loop();
@@ -244,6 +248,7 @@ static void test_parallel_exclusive_write(void)
bdrv_ref(base);
bdrv_ref(fl1);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_attach_child(top, fl1, "backing", &child_of_bds,
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
@@ -257,6 +262,7 @@ static void test_parallel_exclusive_write(void)
bdrv_replace_node(fl1, fl2, &error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
bdrv_drained_end(fl2);
bdrv_drained_end(fl1);
@@ -363,6 +369,7 @@ static void test_parallel_perm_update(void)
*/
bdrv_ref(base);
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA,
&error_abort);
@@ -377,6 +384,7 @@ static void test_parallel_perm_update(void)
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
/* Select fl1 as first child to be active */
s->selected = c_fl1;
@@ -430,11 +438,13 @@ static void test_append_greedy_filter(void)
BlockDriverState *base = no_perm_node("base");
BlockDriverState *fl = exclusive_writer_node("fl1");
+ bdrv_drain_all_begin();
bdrv_graph_wrlock();
bdrv_attach_child(top, base, "backing", &child_of_bds,
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
bdrv_graph_wrunlock();
+ bdrv_drain_all_end();
bdrv_append(fl, base, &error_abort);
bdrv_unref(fl);