[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL 03/38] rust: qom: add ParentField
From: |
Paolo Bonzini |
Subject: |
[PULL 03/38] rust: qom: add ParentField |
Date: |
Fri, 10 Jan 2025 19:45:44 +0100 |
Add a type that, together with the C function object_deinit, ensures the
correct drop order for QOM objects relative to their superclasses.
Right now it is not possible to implement the Drop trait for QOM classes
that are defined in Rust, as the drop() function would not be called when
the object goes away; instead what is called is ObjectImpl::INSTANCE_FINALIZE.
It would be nice for INSTANCE_FINALIZE to just drop the object, but this has
a problem: suppose you have
pub struct MySuperclass {
parent: DeviceState,
field: Box<MyData>,
...
}
impl Drop for MySuperclass {
...
}
pub struct MySubclass {
parent: MySuperclass,
...
}
and an instance_finalize implementation that is like
unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) {
unsafe { std::ptr::drop_in_place(obj.cast::<T>()) }
}
When instance_finalize is called for MySubclass, it will walk the struct's
list of fields and call the drop method for MySuperclass. Then, object_deinit
recurses to the superclass and calls the same drop method again. This
will cause double-freeing of the Box<Data>.
What's happening here is that QOM wants to control the drop order of
MySuperclass and MySubclass's fields. To do so, the parent field must
be marked ManuallyDrop<>, which is quite ugly. Instead, add a wrapper
type ParentField<> that is specific to QOM. This hides the implementation
detail of *what* is special about the ParentField, and will also be easy
to check in the #[derive(Object)] macro.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 6 +--
rust/qemu-api/src/qom.rs | 64 +++++++++++++++++++++++++++++---
rust/qemu-api/tests/tests.rs | 4 +-
3 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 18cc122951d..689202f4550 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -14,7 +14,7 @@
irq::InterruptSource,
prelude::*,
qdev::DeviceImpl,
- qom::ObjectImpl,
+ qom::{ObjectImpl, ParentField},
};
use crate::{
@@ -86,7 +86,7 @@ fn index(&self, idx: u32) -> &Self::Output {
#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
/// PL011 Device Model in QEMU
pub struct PL011State {
- pub parent_obj: SysBusDevice,
+ pub parent_obj: ParentField<SysBusDevice>,
pub iomem: MemoryRegion,
#[doc(alias = "fr")]
pub flags: registers::Flags,
@@ -645,7 +645,7 @@ pub fn post_load(&mut self, _version_id: u32) -> Result<(),
()> {
#[derive(Debug, qemu_api_macros::Object)]
/// PL011 Luminary device model.
pub struct PL011Luminary {
- parent_obj: PL011State,
+ parent_obj: ParentField<PL011State>,
}
impl PL011Luminary {
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 7d5fbef1e17..40d17a92e1f 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -55,6 +55,7 @@
use std::{
ffi::CStr,
+ fmt,
ops::{Deref, DerefMut},
os::raw::c_void,
};
@@ -105,6 +106,52 @@ fn as_ref(&self) -> &$parent {
};
}
+/// This is the same as [`ManuallyDrop<T>`](std::mem::ManuallyDrop), though
+/// it hides the standard methods of `ManuallyDrop`.
+///
+/// The first field of an `ObjectType` must be of type `ParentField<T>`.
+/// (Technically, this is only necessary if there is at least one Rust
+/// superclass in the hierarchy). This is to ensure that the parent field is
+/// dropped after the subclass; this drop order is enforced by the C
+/// `object_deinit` function.
+///
+/// # Examples
+///
+/// ```ignore
+/// #[repr(C)]
+/// #[derive(qemu_api_macros::Object)]
+/// pub struct MyDevice {
+/// parent: ParentField<DeviceState>,
+/// ...
+/// }
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct ParentField<T: ObjectType>(std::mem::ManuallyDrop<T>);
+
+impl<T: ObjectType> Deref for ParentField<T> {
+ type Target = T;
+
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<T: ObjectType> DerefMut for ParentField<T> {
+ #[inline(always)]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl<T: fmt::Display + ObjectType> fmt::Display for ParentField<T> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ self.0.fmt(f)
+ }
+}
+
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
// SAFETY: obj is an instance of T, since rust_instance_init<T>
// is called from QOM core as the instance_init function
@@ -151,11 +198,16 @@ fn as_ref(&self) -> &$parent {
///
/// - the struct must be `#[repr(C)]`;
///
-/// - the first field of the struct must be of the instance struct
corresponding
-/// to the superclass, which is `ObjectImpl::ParentType`
+/// - the first field of the struct must be of type
+/// [`ParentField<T>`](ParentField), where `T` is the parent type
+/// [`ObjectImpl::ParentType`]
///
-/// - likewise, the first field of the `Class` must be of the class struct
-/// corresponding to the superclass, which is
`ObjectImpl::ParentType::Class`.
+/// - the first field of the `Class` must be of the class struct corresponding
+/// to the superclass, which is `ObjectImpl::ParentType::Class`.
`ParentField`
+/// is not needed here.
+///
+/// In both cases, having a separate class type is not necessary if the
subclass
+/// does not add any field.
pub unsafe trait ObjectType: Sized {
/// The QOM class object corresponding to this struct. This is used
/// to automatically generate a `class_init` method.
@@ -384,8 +436,8 @@ impl<T: ObjectType> ObjectCastMut for &mut T {}
/// Trait a type must implement to be registered with QEMU.
pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
- /// The parent of the type. This should match the first field of
- /// the struct that implements `ObjectImpl`:
+ /// The parent of the type. This should match the first field of the
+ /// struct that implements `ObjectImpl`, minus the `ParentField<_>`
wrapper.
type ParentType: ObjectType;
/// Whether the object can be instantiated
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 1d2825b0986..526c3f4f8ea 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -15,7 +15,7 @@
declare_properties, define_property,
prelude::*,
qdev::{DeviceImpl, DeviceState, Property},
- qom::ObjectImpl,
+ qom::{ObjectImpl, ParentField},
vmstate::VMStateDescription,
zeroable::Zeroable,
};
@@ -31,7 +31,7 @@
#[repr(C)]
#[derive(qemu_api_macros::Object)]
pub struct DummyState {
- parent: DeviceState,
+ parent: ParentField<DeviceState>,
migrate_clock: bool,
}
--
2.47.1
- [PULL 07/38] rust: qom: automatically use Drop trait to implement instance_finalize, (continued)
- [PULL 07/38] rust: qom: automatically use Drop trait to implement instance_finalize, Paolo Bonzini, 2025/01/10
- [PULL 09/38] rust: pl011: only leave embedded object initialization in instance_init, Paolo Bonzini, 2025/01/10
- [PULL 08/38] rust: qom: move device_id to PL011 class side, Paolo Bonzini, 2025/01/10
- [PULL 14/38] rust: hide warnings for subprojects, Paolo Bonzini, 2025/01/10
- [PULL 02/38] rust: add --check-cfg test to rustc arguments, Paolo Bonzini, 2025/01/10
- [PULL 04/38] rust: add a utility module for compile-time type checks, Paolo Bonzini, 2025/01/10
- [PULL 16/38] make-release: only leave tarball of wrap-file subprojects, Paolo Bonzini, 2025/01/10
- [PULL 15/38] qom: remove unused field, Paolo Bonzini, 2025/01/10
- [PULL 03/38] rust: qom: add ParentField,
Paolo Bonzini <=
- [PULL 10/38] rust: qom: make INSTANCE_POST_INIT take a shared reference, Paolo Bonzini, 2025/01/10
- [PULL 17/38] target/i386: improve code generation for BT, Paolo Bonzini, 2025/01/10
- [PULL 06/38] rust: macros: check that the first field of a #[derive(Object)] struct is a ParentField, Paolo Bonzini, 2025/01/10
- [PULL 05/38] rust: macros: check that #[derive(Object)] requires #[repr(C)], Paolo Bonzini, 2025/01/10
- [PULL 11/38] rust: qemu-api-macros: extend error reporting facility to parse errors, Paolo Bonzini, 2025/01/10
- [PULL 13/38] rust: qdev: expose inherited methods to subclasses of SysBusDevice, Paolo Bonzini, 2025/01/10
- [PULL 21/38] target/i386/kvm: Remove local MSR_KVM_WALL_CLOCK and MSR_KVM_SYSTEM_TIME definitions, Paolo Bonzini, 2025/01/10
- [PULL 24/38] target/i386/confidential-guest: Fix comment of x86_confidential_guest_kvm_type(), Paolo Bonzini, 2025/01/10
- [PULL 33/38] i386/topology: Introduce helpers for various topology info of different level, Paolo Bonzini, 2025/01/10
- [PULL 30/38] i386/cpu: Drop the variable smp_cores and smp_threads in x86_cpu_pre_plug(), Paolo Bonzini, 2025/01/10