From: Stefan Hajnoczi <stefanha@gmail.com>
To: Kevin Wolf <kwolf@redhat.com>
Cc: qemu-block@nongnu.org, hreitz@redhat.com, pbonzini@redhat.com,
manos.pitsidianakis@linaro.org, philmd@linaro.org,
qemu-devel@nongnu.org, qemu-rust@nongnu.org
Subject: Re: [PATCH v2 04/11] rust/qemu-api: Add wrappers to run futures in QEMU
Date: Wed, 5 Mar 2025 10:15:30 +0800 [thread overview]
Message-ID: <20250305021530.GA247800@fedora> (raw)
In-Reply-To: <20250218182019.111467-5-kwolf@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 9308 bytes --]
On Tue, Feb 18, 2025 at 07:20:12PM +0100, Kevin Wolf wrote:
> This adds helper functions that allow running Rust futures to completion
> using QEMU's event loops.
This commit is a cliff-hanger. I'm intrigued to find out how timer, fd,
etc event loop integration will work :).
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
> include/qemu/coroutine-rust.h | 24 +++++++++++
> rust/wrapper.h | 1 +
> util/qemu-co-rust-async.c | 55 +++++++++++++++++++++++++
> rust/qemu-api/meson.build | 1 +
> rust/qemu-api/src/futures.rs | 77 +++++++++++++++++++++++++++++++++++
> rust/qemu-api/src/lib.rs | 1 +
> util/meson.build | 3 ++
> 7 files changed, 162 insertions(+)
> create mode 100644 include/qemu/coroutine-rust.h
> create mode 100644 util/qemu-co-rust-async.c
> create mode 100644 rust/qemu-api/src/futures.rs
>
> diff --git a/include/qemu/coroutine-rust.h b/include/qemu/coroutine-rust.h
> new file mode 100644
> index 0000000000..0c5cf42a6b
> --- /dev/null
> +++ b/include/qemu/coroutine-rust.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Helpers to run Rust futures using QEMU coroutines
> + *
> + * Copyright Red Hat
> + *
> + * Author:
> + * Kevin Wolf <kwolf@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +#ifndef QEMU_COROUTINE_RUST_H
> +#define QEMU_COROUTINE_RUST_H
> +
> +typedef struct RustBoxedFuture RustBoxedFuture;
> +typedef void coroutine_fn RunFuture(RustBoxedFuture *future, void *opaque);
> +
> +void no_coroutine_fn rust_run_future(RustBoxedFuture *future,
> + RunFuture *entry,
> + void *opaque);
This adds a blocking (aio_poll()-style) API. The more blocking APIs we
add, the more points are created where QEMU hangs when the async
operation doesn't complete in a reasonable amount of time. It would be
best to avoid introducing new blocking APIs, but sometimes it is
unavoidable.
rust_run_future() is very generic and I think the downsides should be
pointed out to discourage people from using it when not absolutely
necessary. Can you document when it's appropriate to use this API?
> +
> +#endif
> diff --git a/rust/wrapper.h b/rust/wrapper.h
> index 303d7bba7f..3dc385e256 100644
> --- a/rust/wrapper.h
> +++ b/rust/wrapper.h
> @@ -58,3 +58,4 @@ typedef enum memory_order {
> #include "block/block_int.h"
> #include "block/qdict.h"
> #include "qapi/qapi-visit-block-core.h"
> +#include "qemu/coroutine-rust.h"
> diff --git a/util/qemu-co-rust-async.c b/util/qemu-co-rust-async.c
> new file mode 100644
> index 0000000000..d893dfb7bd
> --- /dev/null
> +++ b/util/qemu-co-rust-async.c
> @@ -0,0 +1,55 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Helpers to run Rust futures using QEMU coroutines
> + *
> + * Copyright Red Hat
> + *
> + * Author:
> + * Kevin Wolf <kwolf@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "block/aio-wait.h"
> +#include "qemu/coroutine.h"
> +#include "qemu/coroutine-rust.h"
> +#include "qemu/main-loop.h"
> +
> +typedef struct FutureCo {
> + RustBoxedFuture *future;
> + RunFuture *entry;
> + void *opaque;
> + bool done;
> +} FutureCo;
> +
> +static void coroutine_fn rust_co_run_future_entry(void *opaque)
> +{
> + FutureCo *data = opaque;
> +
> + data->entry(data->future, data->opaque);
> + data->done = true;
> + aio_wait_kick();
> +}
> +
> +void no_coroutine_fn rust_run_future(RustBoxedFuture *future,
> + RunFuture *entry,
> + void *opaque)
> +{
> + AioContext *ctx = qemu_get_current_aio_context();
> + Coroutine *co;
> + FutureCo data = {
> + .future = future,
> + .entry = entry,
> + .opaque = opaque,
> + .done = false,
> + };
> +
> + GLOBAL_STATE_CODE();
> +
> + co = qemu_coroutine_create(rust_co_run_future_entry, &data);
> + aio_co_enter(ctx, co);
> + AIO_WAIT_WHILE(ctx, !data.done);
> +}
> diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
> index e0a3052c79..44fd34e193 100644
> --- a/rust/qemu-api/meson.build
> +++ b/rust/qemu-api/meson.build
> @@ -22,6 +22,7 @@ sources_core = [
> 'src/chardev.rs',
> 'src/c_str.rs',
> 'src/errno.rs',
> + 'src/futures.rs',
> 'src/module.rs',
> 'src/offset_of.rs',
> 'src/prelude.rs',
> diff --git a/rust/qemu-api/src/futures.rs b/rust/qemu-api/src/futures.rs
> new file mode 100644
> index 0000000000..cd307a1d62
> --- /dev/null
> +++ b/rust/qemu-api/src/futures.rs
> @@ -0,0 +1,77 @@
> +use crate::bindings;
> +use std::ffi::c_void;
> +use std::future::Future;
> +use std::mem::MaybeUninit;
> +use std::sync::Arc;
> +use std::task::{Context, Poll, Wake, Waker};
> +
> +struct RunFutureWaker {
> + co: *mut bindings::Coroutine,
> +}
> +unsafe impl Send for RunFutureWaker {}
> +unsafe impl Sync for RunFutureWaker {}
> +
> +impl Wake for RunFutureWaker {
> + fn wake(self: Arc<Self>) {
> + unsafe {
> + bindings::aio_co_wake(self.co);
> + }
> + }
> +}
> +
> +/// Use QEMU's event loops to run a Rust [`Future`] to completion and return its result.
> +///
> +/// This function must be called in coroutine context. If the future isn't ready yet, it yields.
> +pub fn qemu_co_run_future<F: Future>(future: F) -> F::Output {
> + let waker = Waker::from(Arc::new(RunFutureWaker {
> + co: unsafe { bindings::qemu_coroutine_self() },
> + }));
> + let mut cx = Context::from_waker(&waker);
> +
> + let mut pinned_future = std::pin::pin!(future);
> + loop {
> + match pinned_future.as_mut().poll(&mut cx) {
> + Poll::Ready(res) => return res,
> + Poll::Pending => unsafe {
> + bindings::qemu_coroutine_yield();
> + },
> + }
> + }
> +}
> +
> +/// Wrapper around [`qemu_co_run_future`] that can be called from C.
> +///
> +/// # Safety
> +///
> +/// `future` must be a valid pointer to an owned `F` (it will be freed in this function). `output`
> +/// must be a valid pointer representing a mutable reference to an `F::Output` where the result can
> +/// be stored.
> +unsafe extern "C" fn rust_co_run_future<F: Future>(
> + future: *mut bindings::RustBoxedFuture,
> + output: *mut c_void,
> +) {
> + let future = unsafe { Box::from_raw(future.cast::<F>()) };
> + let output = output.cast::<F::Output>();
> + let ret = qemu_co_run_future(*future);
> + unsafe {
> + output.write(ret);
> + }
> +}
> +
> +/// Use QEMU's event loops to run a Rust [`Future`] to completion and return its result.
> +///
> +/// This function must be called outside of coroutine context to avoid deadlocks. It blocks and
rust_run_future() has GLOBAL_STATE_CODE() so qemu_run_future() needs to
run not just outside coroutine context, but also under the BQL. Should
this be mentioned?
> +/// runs a nested even loop until the future is ready and returns a result.
> +pub fn qemu_run_future<F: Future>(future: F) -> F::Output {
> + let future_ptr = Box::into_raw(Box::new(future));
> + let mut output = MaybeUninit::<F::Output>::uninit();
> + unsafe {
> + bindings::rust_run_future(
> + future_ptr.cast::<bindings::RustBoxedFuture>(),
> + #[allow(clippy::as_underscore)]
> + Some(rust_co_run_future::<F> as _),
This line is hard to follow. I think it's casting to the C equivalent
type:
void coroutine_fn (*)(RustBoxedFuture *future, void *opaque)
I wonder if there's a clearer way of writing this. Maybe being explicit
rather than implicit here would be helpful.
If not, it's not a big deal, but I spent some time trying to figure out
what this does and others might too.
> + output.as_mut_ptr().cast::<c_void>(),
> + );
> + output.assume_init()
> + }
> +}
> diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
> index 825443abde..84928905f1 100644
> --- a/rust/qemu-api/src/lib.rs
> +++ b/rust/qemu-api/src/lib.rs
> @@ -20,6 +20,7 @@
> pub mod cell;
> pub mod chardev;
> pub mod errno;
> +pub mod futures;
> #[cfg(feature = "system")]
> pub mod irq;
> #[cfg(feature = "system")]
> diff --git a/util/meson.build b/util/meson.build
> index 780b5977a8..14a2ae17fd 100644
> --- a/util/meson.build
> +++ b/util/meson.build
> @@ -101,6 +101,9 @@ if have_block
> util_ss.add(files('qemu-coroutine-sleep.c'))
> util_ss.add(files('qemu-co-shared-resource.c'))
> util_ss.add(files('qemu-co-timeout.c'))
> + if have_rust
> + util_ss.add(files('qemu-co-rust-async.c'))
> + endif
> util_ss.add(files('readline.c'))
> util_ss.add(files('throttle.c'))
> util_ss.add(files('timed-average.c'))
> --
> 2.48.1
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
next prev parent reply other threads:[~2025-03-05 2:16 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-18 18:20 [PATCH v2 00/11] rust/block: Add minimal block driver bindings Kevin Wolf
2025-02-18 18:20 ` [PATCH v2 01/11] rust: Build separate qemu_api_tools and qemu_api_system Kevin Wolf
2025-02-20 7:10 ` Zhao Liu
2025-02-18 18:20 ` [PATCH v2 02/11] meson: Add rust_block_ss and link tools with it Kevin Wolf
2025-02-18 18:20 ` [PATCH v2 03/11] rust: Add some block layer bindings Kevin Wolf
2025-02-18 18:20 ` [PATCH v2 04/11] rust/qemu-api: Add wrappers to run futures in QEMU Kevin Wolf
2025-02-20 6:35 ` Zhao Liu
2025-02-20 14:58 ` Kevin Wolf
2025-03-05 2:15 ` Stefan Hajnoczi [this message]
2025-02-18 18:20 ` [PATCH v2 05/11] rust/block: Add empty crate Kevin Wolf
2025-02-19 6:46 ` Zhao Liu
2025-02-18 18:20 ` [PATCH v2 06/11] rust/block: Add I/O buffer traits Kevin Wolf
2025-02-18 18:20 ` [PATCH v2 07/11] block: Add bdrv_open_blockdev_ref_file() Kevin Wolf
2025-02-18 18:20 ` [PATCH v2 08/11] rust/block: Add driver module Kevin Wolf
2025-02-20 6:52 ` Zhao Liu
2025-03-05 2:43 ` Stefan Hajnoczi
2025-02-18 18:20 ` [PATCH v2 09/11] rust/block: Add read support for block drivers Kevin Wolf
2025-02-19 6:11 ` Paolo Bonzini
2025-02-19 13:02 ` Kevin Wolf
2025-02-19 22:42 ` Paolo Bonzini
2025-03-05 3:04 ` Stefan Hajnoczi
2025-03-05 9:56 ` Stefan Hajnoczi
2025-02-18 18:20 ` [PATCH v2 10/11] bochs-rs: Add bochs block driver reimplementation in Rust Kevin Wolf
2025-02-20 7:02 ` Zhao Liu
2025-03-05 10:21 ` Stefan Hajnoczi
2025-02-18 18:20 ` [PATCH v2 11/11] rust/block: Add format probing Kevin Wolf
2025-03-05 10:23 ` [PATCH v2 00/11] rust/block: Add minimal block driver bindings Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250305021530.GA247800@fedora \
--to=stefanha@gmail.com \
--cc=hreitz@redhat.com \
--cc=kwolf@redhat.com \
--cc=manos.pitsidianakis@linaro.org \
--cc=pbonzini@redhat.com \
--cc=philmd@linaro.org \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=qemu-rust@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.