From: Zhao Liu <zhao1.liu@intel.com>
To: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org,
"Alex Bennée" <alex.bennee@linaro.org>,
"Paolo Bonzini" <pbonzini@redhat.com>
Subject: Re: [PATCH WIP RFC] rust: add qdev DeviceProperties derive macro
Date: Thu, 22 May 2025 19:17:56 +0800 [thread overview]
Message-ID: <aC8H5LOQSac7mdaE@intel.com> (raw)
In-Reply-To: <20250522-rust-qdev-properties-v1-1-5b18b218bad1@linaro.org>
On Thu, May 22, 2025 at 11:12:30AM +0300, Manos Pitsidianakis wrote:
> Date: Thu, 22 May 2025 11:12:30 +0300
> From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Subject: [PATCH WIP RFC] rust: add qdev DeviceProperties derive macro
> X-Mailer: b4 0.14.2
>
> Add derive macro for declaring qdev properties directly above the field
> definitions. To do this, we split DeviceImpl::properties method on a
> separate trait so we can implement only that part in the derive macro
> expansion (we cannot partially implement the DeviceImpl trait).
>
> Adding a `property` attribute above the field declaration will generate
> a `qemu_api::bindings::Property` array member in the device's property
> list.
>
> As a proof of concept, I added a typed alias for booleans,
> `bool_property` that allows you to skip specifying qdev_prop_bool.
>
> This is unnecessary though, because once we have the
> const_refs_to_static feature we can introduce a QdevProp trait that
> returns a reference to a type's qdev_prop_* global variable. We cannot
> do this now because for our minimum Rust version we cannot refer to
> statics from const values.
>
> It'd look like this:
>
> pub trait QDevProp {
> const VALUE: &'static bindings::PropertyInfo;
> }
>
> impl QDevProp for bool {
> const VALUE: &'static bindings::PropertyInfo = unsafe {
> &bindings::qdev_prop_bool };
> }
>
> impl QDevProp for u64 {
> const VALUE: &'static bindings::PropertyInfo = unsafe {
> &bindings::qdev_prop_uint64 };
> }
>
> // etc.. for all basic types
Good idea! We can have something like current impl_vmstate_scalar.
> So, this patch is not for merging yet, I will wait until we upgrade the
> Rust version and re-spin it with a proper trait-based implementation (and
> also split it into individual steps/patches).
I think this can be aligned with Paolo's optimization for vmstate [*]
[*]: https://lore.kernel.org/qemu-devel/20250505100854.73936-5-pbonzini@redhat.com/
> Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> ---
> rust/hw/char/pl011/src/device.rs | 13 +--
> rust/hw/char/pl011/src/device_class.rs | 26 +-----
> rust/hw/timer/hpet/src/hpet.rs | 4 +-
> rust/qemu-api-macros/src/lib.rs | 157 ++++++++++++++++++++++++++++++++-
> rust/qemu-api/src/qdev.rs | 22 +++--
> rust/qemu-api/tests/tests.rs | 9 +-
> 6 files changed, 187 insertions(+), 44 deletions(-)
...
> #[repr(C)]
> -#[derive(qemu_api_macros::Object)]
> +#[derive(qemu_api_macros::Object, qemu_api_macros::DeviceProperties)]
> /// PL011 Device Model in QEMU
> pub struct PL011State {
> pub parent_obj: ParentField<SysBusDevice>,
> pub iomem: MemoryRegion,
> #[doc(alias = "chr")]
> + #[property(name = c"chardev", qdev_prop = qemu_api::bindings::qdev_prop_chr)]
This macro pattern looks good!
As you mentioned above, with QDevProp, this could be nicer so that user
wouldn't have to bind type and PropertyInfo manually.
> pub char_backend: CharBackend,
> pub regs: BqlRefCell<PL011Registers>,
> /// QEMU interrupts
> @@ -122,6 +126,7 @@ pub struct PL011State {
> #[doc(alias = "clk")]
> pub clock: Owned<Clock>,
> #[doc(alias = "migrate_clk")]
> + #[bool_property(name = c"migrate-clk", default = true)]
> pub migrate_clock: bool,
> }
>
> @@ -169,9 +174,6 @@ impl ObjectImpl for PL011State {
> }
>
> impl DeviceImpl for PL011State {
> - fn properties() -> &'static [Property] {
> - &device_class::PL011_PROPERTIES
> - }
> fn vmsd() -> Option<&'static VMStateDescription> {
> Some(&device_class::VMSTATE_PL011)
> }
> @@ -703,6 +705,7 @@ impl PL011Impl for PL011Luminary {
> const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]);
> }
>
> +impl DevicePropertiesImpl for PL011Luminary {}
> impl DeviceImpl for PL011Luminary {}
> impl ResettablePhasesImpl for PL011Luminary {}
> impl SysBusDeviceImpl for PL011Luminary {}
...
> diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
> index 1279d7a58d50e1bf6c8d2e6f00d7229bbb19e003..2fd8b2750ffabcaa1065558d38a700e35fbc9136 100644
> --- a/rust/qemu-api/src/qdev.rs
> +++ b/rust/qemu-api/src/qdev.rs
> @@ -100,8 +100,19 @@ pub trait ResettablePhasesImpl {
> T::EXIT.unwrap()(unsafe { state.as_ref() }, typ);
> }
>
> +pub trait DevicePropertiesImpl {
> + /// An array providing the properties that the user can set on the
> + /// device. Not a `const` because referencing statics in constants
> + /// is unstable until Rust 1.83.0.
> + fn properties() -> &'static [Property] {
> + &[]
> + }
> +}
> +
> /// Trait providing the contents of [`DeviceClass`].
> -pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> {
> +pub trait DeviceImpl:
> + ObjectImpl + ResettablePhasesImpl + DevicePropertiesImpl + IsA<DeviceState>
> +{
DeviceImpl has a new bound: DevicePropertiesImpl, which means that
devices that don't need properties also need to implement the empty
DevicePropertiesImpl.
We now have ResettablePhasesImpl that needs to do this, but I'm not sure
if we should continue to add more examples?
> /// _Realization_ is the second stage of device creation. It contains
> /// all operations that depend on device properties and can fail (note:
> /// this is not yet supported for Rust devices).
> @@ -110,13 +121,6 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> {
> /// with the function pointed to by `REALIZE`.
> const REALIZE: Option<fn(&Self)> = None;
>
> - /// An array providing the properties that the user can set on the
> - /// device. Not a `const` because referencing statics in constants
> - /// is unstable until Rust 1.83.0.
> - fn properties() -> &'static [Property] {
> - &[]
> - }
> -
This default empty Property array looks more convenient than this:
impl DevicePropertiesImpl for DummyChildState {}
However, if there's no way to avoid adding DevicePropertiesImpl, the
cost of an empty trait could be acceptable relative to the benefit of
this more friendly macro, I think!
Thanks,
Zhao
> /// A `VMStateDescription` providing the migration format for the device
> /// Not a `const` because referencing statics in constants is unstable
> /// until Rust 1.83.0.
> @@ -171,7 +175,7 @@ pub fn class_init<T: DeviceImpl>(&mut self) {
> if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
> self.vmsd = vmsd;
> }
> - let prop = <T as DeviceImpl>::properties();
> + let prop = <T as DevicePropertiesImpl>::properties();
> if !prop.is_empty() {
> unsafe {
> bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len());
next prev parent reply other threads:[~2025-05-22 10:57 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-22 8:12 [PATCH WIP RFC] rust: add qdev DeviceProperties derive macro Manos Pitsidianakis
2025-05-22 11:17 ` Zhao Liu [this message]
2025-05-23 13:05 ` Paolo Bonzini
2025-05-28 10:49 ` Manos Pitsidianakis
2025-05-28 11:38 ` Paolo Bonzini
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=aC8H5LOQSac7mdaE@intel.com \
--to=zhao1.liu@intel.com \
--cc=alex.bennee@linaro.org \
--cc=manos.pitsidianakis@linaro.org \
--cc=pbonzini@redhat.com \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).