rust: cell: add wrapper for FFI types

Inspired by the same-named type in Linux.  This type provides the compiler
with a correct view of what goes on with FFI types.  In addition, it
separates the glue code from the bindgen-generated code, allowing
traits such as Send, Sync or Zeroable to be specified independently
for C and Rust structs.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2025-02-14 10:18:34 +01:00
parent cff666a3ae
commit 0b9d05e3c9
2 changed files with 223 additions and 15 deletions

View file

@ -296,15 +296,33 @@ of ``&mut self``; access to internal fields must use *interior mutability*
to go from a shared reference to a ``&mut``.
Whenever C code provides you with an opaque ``void *``, avoid converting it
to a Rust mutable reference, and use a shared reference instead. Rust code
will then have to use QEMU's ``BqlRefCell`` and ``BqlCell`` type, which
enforce that locking rules for the "Big QEMU Lock" are respected. These cell
types are also known to the ``vmstate`` crate, which is able to "look inside"
them when building an in-memory representation of a ``struct``'s layout.
Note that the same is not true of a ``RefCell`` or ``Mutex``.
to a Rust mutable reference, and use a shared reference instead. The
``qemu_api::cell`` module provides wrappers that can be used to tell the
Rust compiler about interior mutability, and optionally to enforce locking
rules for the "Big QEMU Lock". In the future, similar cell types might
also be provided for ``AioContext``-based locking as well.
In the future, similar cell types might also be provided for ``AioContext``-based
locking as well.
In particular, device code will usually rely on the ``BqlRefCell`` and
``BqlCell`` type to ensure that data is accessed correctly under the
"Big QEMU Lock". These cell types are also known to the ``vmstate``
crate, which is able to "look inside" them when building an in-memory
representation of a ``struct``'s layout. Note that the same is not true
of a ``RefCell`` or ``Mutex``.
Bindings code instead will usually use the ``Opaque`` type, which hides
the contents of the underlying struct and can be easily converted to
a raw pointer, for use in calls to C functions. It can be used for
example as follows::
#[repr(transparent)]
#[derive(Debug)]
pub struct Object(Opaque<bindings::Object>);
The bindings will then manually check for the big QEMU lock with
assertions, which allows the wrapper to be declared thread-safe::
unsafe impl Send for Object {}
unsafe impl Sync for Object {}
Writing bindings to C code
''''''''''''''''''''''''''

View file

@ -27,7 +27,7 @@
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//! BQL-protected mutable containers.
//! QEMU-specific mutable containers
//!
//! Rust memory safety is based on this rule: Given an object `T`, it is only
//! possible to have one of the following:
@ -43,8 +43,10 @@
//! usually have their pointer shared with the "outside world very early in
//! their lifetime", for example when they create their
//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual
//! parts of a device must be made mutable in a controlled manner through the
//! use of cell types.
//! parts of a device must be made mutable in a controlled manner; this module
//! provides the tools to do so.
//!
//! ## Cell types
//!
//! [`BqlCell<T>`] and [`BqlRefCell<T>`] allow doing this via the Big QEMU Lock.
//! While they are essentially the same single-threaded primitives that are
@ -71,7 +73,7 @@
//! QEMU device implementations is usually incorrect and can lead to
//! thread-safety issues.
//!
//! ## `BqlCell<T>`
//! ### `BqlCell<T>`
//!
//! [`BqlCell<T>`] implements interior mutability by moving values in and out of
//! the cell. That is, an `&mut T` to the inner value can never be obtained as
@ -91,7 +93,7 @@
//! - [`set`](BqlCell::set): this method replaces the interior value,
//! dropping the replaced value.
//!
//! ## `BqlRefCell<T>`
//! ### `BqlRefCell<T>`
//!
//! [`BqlRefCell<T>`] uses Rust's lifetimes to implement "dynamic borrowing", a
//! process whereby one can claim temporary, exclusive, mutable access to the
@ -111,13 +113,82 @@
//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow),
//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The
//! thread will panic if these rules are violated or if the BQL is not held.
//!
//! ## Opaque wrappers
//!
//! The cell types from the previous section are useful at the boundaries
//! of code that requires interior mutability. When writing glue code that
//! interacts directly with C structs, however, it is useful to operate
//! at a lower level.
//!
//! C functions often violate Rust's fundamental assumptions about memory
//! safety by modifying memory even if it is shared. Furthermore, C structs
//! often start their life uninitialized and may be populated lazily.
//!
//! For this reason, this module provides the [`Opaque<T>`] type to opt out
//! of Rust's usual guarantees about the wrapped type. Access to the wrapped
//! value is always through raw pointers, obtained via methods like
//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These
//! pointers can then be passed to C functions or dereferenced; both actions
//! require `unsafe` blocks, making it clear where safety guarantees must be
//! manually verified. For example
//!
//! ```ignore
//! unsafe {
//! let state = Opaque::<MyStruct>::uninit();
//! qemu_struct_init(state.as_mut_ptr());
//! }
//! ```
//!
//! [`Opaque<T>`] will usually be wrapped one level further, so that
//! bridge methods can be added to the wrapper:
//!
//! ```ignore
//! pub struct MyStruct(Opaque<bindings::MyStruct>);
//!
//! impl MyStruct {
//! fn new() -> Pin<Box<MyStruct>> {
//! let result = Box::pin(unsafe { Opaque::uninit() });
//! unsafe { qemu_struct_init(result.as_mut_ptr()) };
//! result
//! }
//! }
//! ```
//!
//! This pattern of wrapping bindgen-generated types in [`Opaque<T>`] provides
//! several advantages:
//!
//! * The choice of traits to be implemented is not limited by the
//! bindgen-generated code. For example, [`Drop`] can be added without
//! disabling [`Copy`] on the underlying bindgen type
//!
//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper
//! type rather than being automatically derived from the C struct's layout
//!
//! * Methods can be implemented in a separate crate from the bindgen-generated
//! bindings
//!
//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display)
//! implementations can be customized to be more readable than the raw C
//! struct representation
//!
//! The [`Opaque<T>`] type does not include BQL validation; it is possible to
//! assert in the code that the right lock is taken, to use it together
//! with a custom lock guard type, or to let C code take the lock, as
//! appropriate. It is also possible to use it with non-thread-safe
//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`]
//! it is neither `Sync` nor `Send`.
//!
//! While [`Opaque<T>`] is necessary for C interop, it should be used sparingly
//! and only at FFI boundaries. For QEMU-specific types that need interior
//! mutability, prefer [`BqlCell`] or [`BqlRefCell`].
use std::{
cell::{Cell, UnsafeCell},
cmp::Ordering,
fmt,
marker::PhantomData,
mem,
marker::{PhantomData, PhantomPinned},
mem::{self, MaybeUninit},
ops::{Deref, DerefMut},
ptr::NonNull,
};
@ -840,3 +911,122 @@ impl<T: fmt::Display> fmt::Display for BqlRefMut<'_, T> {
(**self).fmt(f)
}
}
/// Stores an opaque value that is shared with C code.
///
/// Often, C structs can changed when calling a C function even if they are
/// behind a shared Rust reference, or they can be initialized lazily and have
/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's
/// strict aliasing rules, which normally prevent mutation through shared
/// references.
///
/// Wrapping the struct with `Opaque<T>` ensures that the Rust compiler does not
/// assume the usual constraints that Rust structs require, and allows using
/// shared references on the Rust side.
///
/// `Opaque<T>` is `#[repr(transparent)]`, so that it matches the memory layout
/// of `T`.
#[repr(transparent)]
pub struct Opaque<T> {
value: UnsafeCell<MaybeUninit<T>>,
// PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
// one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
// see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
_pin: PhantomPinned,
}
impl<T> Opaque<T> {
/// Creates a new shared reference from a C pointer
///
/// # Safety
///
/// The pointer must be valid, though it need not point to a valid value.
pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self {
let ptr = NonNull::new(ptr).unwrap().cast::<Self>();
// SAFETY: Self is a transparent wrapper over T
unsafe { ptr.as_ref() }
}
/// Creates a new opaque object with uninitialized contents.
///
/// # Safety
///
/// Ultimately the pointer to the returned value will be dereferenced
/// in another `unsafe` block, for example when passing it to a C function,
/// but the functions containing the dereference are usually safe. The
/// value returned from `uninit()` must be initialized and pinned before
/// calling them.
#[allow(clippy::missing_const_for_fn)]
pub unsafe fn uninit() -> Self {
Self {
value: UnsafeCell::new(MaybeUninit::uninit()),
_pin: PhantomPinned,
}
}
/// Creates a new opaque object with zeroed contents.
///
/// # Safety
///
/// Ultimately the pointer to the returned value will be dereferenced
/// in another `unsafe` block, for example when passing it to a C function,
/// but the functions containing the dereference are usually safe. The
/// value returned from `uninit()` must be pinned (and possibly initialized)
/// before calling them.
#[allow(clippy::missing_const_for_fn)]
pub unsafe fn zeroed() -> Self {
Self {
value: UnsafeCell::new(MaybeUninit::zeroed()),
_pin: PhantomPinned,
}
}
/// Returns a raw mutable pointer to the opaque data.
pub const fn as_mut_ptr(&self) -> *mut T {
UnsafeCell::get(&self.value).cast()
}
/// Returns a raw pointer to the opaque data.
pub const fn as_ptr(&self) -> *const T {
self.as_mut_ptr() as *const _
}
/// Returns a raw pointer to the opaque data that can be passed to a
/// C function as `void *`.
pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void {
UnsafeCell::get(&self.value).cast()
}
/// Converts a raw pointer to the wrapped type.
pub const fn raw_get(slot: *mut Self) -> *mut T {
// Compare with Linux's raw_get method, which goes through an UnsafeCell
// because it takes a *const Self instead.
slot.cast()
}
}
impl<T> fmt::Debug for Opaque<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut name: String = "Opaque<".to_string();
name += std::any::type_name::<T>();
name += ">";
f.debug_tuple(&name).field(&self.as_ptr()).finish()
}
}
impl<T: Default> Opaque<T> {
/// Creates a new opaque object with default contents.
///
/// # Safety
///
/// Ultimately the pointer to the returned value will be dereferenced
/// in another `unsafe` block, for example when passing it to a C function,
/// but the functions containing the dereference are usually safe. The
/// value returned from `uninit()` must be pinned before calling them.
pub unsafe fn new() -> Self {
Self {
value: UnsafeCell::new(MaybeUninit::new(T::default())),
_pin: PhantomPinned,
}
}
}