Rust xarray API for v6.16

Introduce Rust support for the `xarray` data structure:
 
  - Add a rust abstraction for the `xarray` data structure. This abstraction
    allows rust code to leverage the `xarray` to store types that implement
    `ForeignOwnable`. This support is a dependency for memory backing feature of
    the rust null block driver, which is waiting to be merged.
 
  - Set up an entry in MAINTAINERS for the xarray rust support. Patches will go
    to the new rust xarray tree and then via the rust subsystem tree for now.
 
 `kernel` crate:
 
  - Allow `ForeignOwnable` to carry information about the pointed-to type. This
    helps asserting alignment requirements for the pointer passed to the foreign
    language.
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCAA0FiEEEsH5R1a/fCoV1sAS4bgaPnkoY3cFAmgZ6fgWHGEuaGluZGJv
 cmdAa2VybmVsLm9yZwAKCRDhuBo+eShjdxXVD/44Pko7d9oVLhLGG0nndMbWFjkk
 /pCTdtw0+0NkulbJ8hKoIC5vYg47P2lE/nx6MsKJSbFdwts2fWd3nKHbxAQq8ox/
 cWVLlzPESe719YmAUhLZhRPIOzRVyShWfbAIjyRDQZ0pc3txYbmiRHcBv9teu4In
 bKCaV+OTqZFZSwZomyqFCz41uDPHdQxxb1O4vV8Kwh2vwD2x5/bcYvG2veU/Bzil
 w1sSjhGvLAa0M3r/lP+72S4y5IbnA8HOsT9eBJMBVkdgWdw7avS9UfkuYeR1FcHg
 7/osUZK4qvGKs9WDU2jkzt7imj0Zegis2/etlwblpzhR+GtSsTr0TZRGuuFL0HSr
 bWb/2qbSDwKR28J3k4yfM7ksg3+PIztBA5MfTcffpMI4yjqaVQXkIzWGlFOoiGrx
 tF+sZYqTqFBOgsKgir1pTAcK2/IZh7Y39HXYG7Wr4EO6GCXGkNxM68uzVR4WEbeT
 PoxoxMzkjHaZe/sNyNYhhOfTjuhkikd9MazM+Ss8cpA8cFsWIcyDj/T5uyIuhE0+
 BxzN/NTTwIM37ixAbtb9ZWT5F4hV9iwzXtvp+OqC0HUKcZH8vpXn/VFr0jKh4NKT
 HpG3mwSYZt/eTSxvrGooVLYt36yvAKuP47jqqE8UaPzLtYEULwHuVDGJTfXNnErk
 lV7grpE36dgXaSaErg==
 =Ig1t
 -----END PGP SIGNATURE-----

Merge tag 'rust-xarray-for-v6.16' of https://github.com/Rust-for-Linux/linux into rust-next

Pull XArray updates from Andreas Hindborg:
 "Introduce Rust support for the 'xarray' data structure:

   - Add a Rust abstraction for the 'xarray' data structure. This
     abstraction allows Rust code to leverage the 'xarray' to store
     types that implement 'ForeignOwnable'. This support is a dependency
     for memory backing feature of the Rust null block driver, which is
     waiting to be merged.

   - Set up an entry in MAINTAINERS for the XArray Rust support. Patches
     will go to the new Rust XArray tree and then via the Rust subsystem
     tree for now.

  'kernel' crate:

   - Allow 'ForeignOwnable' to carry information about the pointed-to
     type. This helps asserting alignment requirements for the pointer
     passed to the foreign language."

* tag 'rust-xarray-for-v6.16' of https://github.com/Rust-for-Linux/linux:
  MAINTAINERS: add entry for Rust XArray API
  rust: xarray: Add an abstraction for XArray
  rust: types: add `ForeignOwnable::PointedTo`
This commit is contained in:
Miguel Ojeda 2025-05-18 20:36:56 +02:00
commit 06ff274f25
12 changed files with 392 additions and 49 deletions

View file

@ -26408,6 +26408,17 @@ F: lib/test_xarray.c
F: lib/xarray.c
F: tools/testing/radix-tree
XARRAY API [RUST]
M: Tamir Duberstein <tamird@gmail.com>
M: Andreas Hindborg <a.hindborg@kernel.org>
L: rust-for-linux@vger.kernel.org
S: Supported
W: https://rust-for-linux.com
B: https://github.com/Rust-for-Linux/linux/issues
C: https://rust-for-linux.zulipchat.com
T: git https://github.com/Rust-for-Linux/linux.git xarray-next
F: rust/kernel/xarray.rs
XBOX DVD IR REMOTE
M: Benjamin Valentin <benpicco@googlemail.com>
S: Maintained

View file

@ -37,6 +37,7 @@
#include <linux/tracepoint.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/xarray.h>
#include <trace/events/rust_sample.h>
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
@ -55,3 +56,8 @@ const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;
const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM;
const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;
const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL;
const xa_mark_t RUST_CONST_HELPER_XA_PRESENT = XA_PRESENT;
const gfp_t RUST_CONST_HELPER_XA_FLAGS_ALLOC = XA_FLAGS_ALLOC;
const gfp_t RUST_CONST_HELPER_XA_FLAGS_ALLOC1 = XA_FLAGS_ALLOC1;

View file

@ -38,3 +38,4 @@
#include "vmalloc.c"
#include "wait.c"
#include "workqueue.c"
#include "xarray.c"

28
rust/helpers/xarray.c Normal file
View file

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/xarray.h>
int rust_helper_xa_err(void *entry)
{
return xa_err(entry);
}
void rust_helper_xa_init_flags(struct xarray *xa, gfp_t flags)
{
return xa_init_flags(xa, flags);
}
int rust_helper_xa_trylock(struct xarray *xa)
{
return xa_trylock(xa);
}
void rust_helper_xa_lock(struct xarray *xa)
{
return xa_lock(xa);
}
void rust_helper_xa_unlock(struct xarray *xa)
{
return xa_unlock(xa);
}

View file

@ -360,68 +360,70 @@ where
}
}
impl<T: 'static, A> ForeignOwnable for Box<T, A>
// SAFETY: The `into_foreign` function returns a pointer that is well-aligned.
unsafe impl<T: 'static, A> ForeignOwnable for Box<T, A>
where
A: Allocator,
{
type PointedTo = T;
type Borrowed<'a> = &'a T;
type BorrowedMut<'a> = &'a mut T;
fn into_foreign(self) -> *mut crate::ffi::c_void {
Box::into_raw(self).cast()
fn into_foreign(self) -> *mut Self::PointedTo {
Box::into_raw(self)
}
unsafe fn from_foreign(ptr: *mut crate::ffi::c_void) -> Self {
unsafe fn from_foreign(ptr: *mut Self::PointedTo) -> Self {
// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
// call to `Self::into_foreign`.
unsafe { Box::from_raw(ptr.cast()) }
unsafe { Box::from_raw(ptr) }
}
unsafe fn borrow<'a>(ptr: *mut crate::ffi::c_void) -> &'a T {
unsafe fn borrow<'a>(ptr: *mut Self::PointedTo) -> &'a T {
// SAFETY: The safety requirements of this method ensure that the object remains alive and
// immutable for the duration of 'a.
unsafe { &*ptr.cast() }
unsafe { &*ptr }
}
unsafe fn borrow_mut<'a>(ptr: *mut crate::ffi::c_void) -> &'a mut T {
let ptr = ptr.cast();
unsafe fn borrow_mut<'a>(ptr: *mut Self::PointedTo) -> &'a mut T {
// SAFETY: The safety requirements of this method ensure that the pointer is valid and that
// nothing else will access the value for the duration of 'a.
unsafe { &mut *ptr }
}
}
impl<T: 'static, A> ForeignOwnable for Pin<Box<T, A>>
// SAFETY: The `into_foreign` function returns a pointer that is well-aligned.
unsafe impl<T: 'static, A> ForeignOwnable for Pin<Box<T, A>>
where
A: Allocator,
{
type PointedTo = T;
type Borrowed<'a> = Pin<&'a T>;
type BorrowedMut<'a> = Pin<&'a mut T>;
fn into_foreign(self) -> *mut crate::ffi::c_void {
fn into_foreign(self) -> *mut Self::PointedTo {
// SAFETY: We are still treating the box as pinned.
Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }).cast()
Box::into_raw(unsafe { Pin::into_inner_unchecked(self) })
}
unsafe fn from_foreign(ptr: *mut crate::ffi::c_void) -> Self {
unsafe fn from_foreign(ptr: *mut Self::PointedTo) -> Self {
// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
// call to `Self::into_foreign`.
unsafe { Pin::new_unchecked(Box::from_raw(ptr.cast())) }
unsafe { Pin::new_unchecked(Box::from_raw(ptr)) }
}
unsafe fn borrow<'a>(ptr: *mut crate::ffi::c_void) -> Pin<&'a T> {
unsafe fn borrow<'a>(ptr: *mut Self::PointedTo) -> Pin<&'a T> {
// SAFETY: The safety requirements for this function ensure that the object is still alive,
// so it is safe to dereference the raw pointer.
// The safety requirements of `from_foreign` also ensure that the object remains alive for
// the lifetime of the returned value.
let r = unsafe { &*ptr.cast() };
let r = unsafe { &*ptr };
// SAFETY: This pointer originates from a `Pin<Box<T>>`.
unsafe { Pin::new_unchecked(r) }
}
unsafe fn borrow_mut<'a>(ptr: *mut crate::ffi::c_void) -> Pin<&'a mut T> {
let ptr = ptr.cast();
unsafe fn borrow_mut<'a>(ptr: *mut Self::PointedTo) -> Pin<&'a mut T> {
// SAFETY: The safety requirements for this function ensure that the object is still alive,
// so it is safe to dereference the raw pointer.
// The safety requirements of `from_foreign` also ensure that the object remains alive for

View file

@ -102,6 +102,7 @@ pub mod transmute;
pub mod types;
pub mod uaccess;
pub mod workqueue;
pub mod xarray;
#[doc(hidden)]
pub use bindings;

View file

@ -200,7 +200,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
// type.
//
// SAFETY: The open call of a file can access the private data.
unsafe { (*raw_file).private_data = ptr.into_foreign() };
unsafe { (*raw_file).private_data = ptr.into_foreign().cast() };
0
}
@ -211,7 +211,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
/// must be associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
let private = unsafe { (*file).private_data }.cast();
// SAFETY: The release call of a file owns the private data.
let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
@ -228,7 +228,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long {
// SAFETY: The ioctl call of a file can access the private data.
let private = unsafe { (*file).private_data };
let private = unsafe { (*file).private_data }.cast();
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
@ -253,7 +253,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
arg: c_ulong,
) -> c_long {
// SAFETY: The compat ioctl call of a file can access the private data.
let private = unsafe { (*file).private_data };
let private = unsafe { (*file).private_data }.cast();
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
@ -274,7 +274,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
/// - `seq_file` must be a valid `struct seq_file` that we can write to.
unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file) {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
let private = unsafe { (*file).private_data }.cast();
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY:

View file

@ -89,7 +89,7 @@ impl<T: Driver + 'static> Adapter<T> {
extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
// SAFETY: The PCI bus only ever calls the remove callback with a valid pointer to a
// `struct pci_dev`.
let ptr = unsafe { bindings::pci_get_drvdata(pdev) };
let ptr = unsafe { bindings::pci_get_drvdata(pdev) }.cast();
// SAFETY: `remove_callback` is only ever called after a successful call to
// `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized

View file

@ -80,7 +80,7 @@ impl<T: Driver + 'static> Adapter<T> {
extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
// SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
let ptr = unsafe { bindings::platform_get_drvdata(pdev) }.cast();
// SAFETY: `remove_callback` is only ever called after a successful call to
// `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized

View file

@ -140,9 +140,10 @@ pub struct Arc<T: ?Sized> {
_p: PhantomData<ArcInner<T>>,
}
#[doc(hidden)]
#[pin_data]
#[repr(C)]
struct ArcInner<T: ?Sized> {
pub struct ArcInner<T: ?Sized> {
refcount: Opaque<bindings::refcount_t>,
data: T,
}
@ -371,18 +372,20 @@ impl<T: ?Sized> Arc<T> {
}
}
impl<T: 'static> ForeignOwnable for Arc<T> {
// SAFETY: The `into_foreign` function returns a pointer that is well-aligned.
unsafe impl<T: 'static> ForeignOwnable for Arc<T> {
type PointedTo = ArcInner<T>;
type Borrowed<'a> = ArcBorrow<'a, T>;
type BorrowedMut<'a> = Self::Borrowed<'a>;
fn into_foreign(self) -> *mut crate::ffi::c_void {
ManuallyDrop::new(self).ptr.as_ptr().cast()
fn into_foreign(self) -> *mut Self::PointedTo {
ManuallyDrop::new(self).ptr.as_ptr()
}
unsafe fn from_foreign(ptr: *mut crate::ffi::c_void) -> Self {
unsafe fn from_foreign(ptr: *mut Self::PointedTo) -> Self {
// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
// call to `Self::into_foreign`.
let inner = unsafe { NonNull::new_unchecked(ptr.cast::<ArcInner<T>>()) };
let inner = unsafe { NonNull::new_unchecked(ptr) };
// SAFETY: By the safety requirement of this function, we know that `ptr` came from
// a previous call to `Arc::into_foreign`, which guarantees that `ptr` is valid and
@ -390,17 +393,17 @@ impl<T: 'static> ForeignOwnable for Arc<T> {
unsafe { Self::from_inner(inner) }
}
unsafe fn borrow<'a>(ptr: *mut crate::ffi::c_void) -> ArcBorrow<'a, T> {
unsafe fn borrow<'a>(ptr: *mut Self::PointedTo) -> ArcBorrow<'a, T> {
// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
// call to `Self::into_foreign`.
let inner = unsafe { NonNull::new_unchecked(ptr.cast::<ArcInner<T>>()) };
let inner = unsafe { NonNull::new_unchecked(ptr) };
// SAFETY: The safety requirements of `from_foreign` ensure that the object remains alive
// for the lifetime of the returned value.
unsafe { ArcBorrow::new(inner) }
}
unsafe fn borrow_mut<'a>(ptr: *mut crate::ffi::c_void) -> ArcBorrow<'a, T> {
unsafe fn borrow_mut<'a>(ptr: *mut Self::PointedTo) -> ArcBorrow<'a, T> {
// SAFETY: The safety requirements for `borrow_mut` are a superset of the safety
// requirements for `borrow`.
unsafe { Self::borrow(ptr) }

View file

@ -18,7 +18,19 @@ use pin_init::{PinInit, Zeroable};
///
/// This trait is meant to be used in cases when Rust objects are stored in C objects and
/// eventually "freed" back to Rust.
pub trait ForeignOwnable: Sized {
///
/// # Safety
///
/// Implementers must ensure that [`into_foreign`] returns a pointer which meets the alignment
/// requirements of [`PointedTo`].
///
/// [`into_foreign`]: Self::into_foreign
/// [`PointedTo`]: Self::PointedTo
pub unsafe trait ForeignOwnable: Sized {
/// Type used when the value is foreign-owned. In practical terms only defines the alignment of
/// the pointer.
type PointedTo;
/// Type used to immutably borrow a value that is currently foreign-owned.
type Borrowed<'a>;
@ -27,16 +39,18 @@ pub trait ForeignOwnable: Sized {
/// Converts a Rust-owned object to a foreign-owned one.
///
/// The foreign representation is a pointer to void. There are no guarantees for this pointer.
/// For example, it might be invalid, dangling or pointing to uninitialized memory. Using it in
/// any way except for [`from_foreign`], [`try_from_foreign`], [`borrow`], or [`borrow_mut`] can
/// result in undefined behavior.
/// # Guarantees
///
/// The return value is guaranteed to be well-aligned, but there are no other guarantees for
/// this pointer. For example, it might be null, dangling, or point to uninitialized memory.
/// Using it in any way except for [`ForeignOwnable::from_foreign`], [`ForeignOwnable::borrow`],
/// [`ForeignOwnable::try_from_foreign`] can result in undefined behavior.
///
/// [`from_foreign`]: Self::from_foreign
/// [`try_from_foreign`]: Self::try_from_foreign
/// [`borrow`]: Self::borrow
/// [`borrow_mut`]: Self::borrow_mut
fn into_foreign(self) -> *mut crate::ffi::c_void;
fn into_foreign(self) -> *mut Self::PointedTo;
/// Converts a foreign-owned object back to a Rust-owned one.
///
@ -46,7 +60,7 @@ pub trait ForeignOwnable: Sized {
/// must not be passed to `from_foreign` more than once.
///
/// [`into_foreign`]: Self::into_foreign
unsafe fn from_foreign(ptr: *mut crate::ffi::c_void) -> Self;
unsafe fn from_foreign(ptr: *mut Self::PointedTo) -> Self;
/// Tries to convert a foreign-owned object back to a Rust-owned one.
///
@ -58,7 +72,7 @@ pub trait ForeignOwnable: Sized {
/// `ptr` must either be null or satisfy the safety requirements for [`from_foreign`].
///
/// [`from_foreign`]: Self::from_foreign
unsafe fn try_from_foreign(ptr: *mut crate::ffi::c_void) -> Option<Self> {
unsafe fn try_from_foreign(ptr: *mut Self::PointedTo) -> Option<Self> {
if ptr.is_null() {
None
} else {
@ -81,7 +95,7 @@ pub trait ForeignOwnable: Sized {
///
/// [`into_foreign`]: Self::into_foreign
/// [`from_foreign`]: Self::from_foreign
unsafe fn borrow<'a>(ptr: *mut crate::ffi::c_void) -> Self::Borrowed<'a>;
unsafe fn borrow<'a>(ptr: *mut Self::PointedTo) -> Self::Borrowed<'a>;
/// Borrows a foreign-owned object mutably.
///
@ -109,21 +123,23 @@ pub trait ForeignOwnable: Sized {
/// [`from_foreign`]: Self::from_foreign
/// [`borrow`]: Self::borrow
/// [`Arc`]: crate::sync::Arc
unsafe fn borrow_mut<'a>(ptr: *mut crate::ffi::c_void) -> Self::BorrowedMut<'a>;
unsafe fn borrow_mut<'a>(ptr: *mut Self::PointedTo) -> Self::BorrowedMut<'a>;
}
impl ForeignOwnable for () {
// SAFETY: The `into_foreign` function returns a pointer that is dangling, but well-aligned.
unsafe impl ForeignOwnable for () {
type PointedTo = ();
type Borrowed<'a> = ();
type BorrowedMut<'a> = ();
fn into_foreign(self) -> *mut crate::ffi::c_void {
fn into_foreign(self) -> *mut Self::PointedTo {
core::ptr::NonNull::dangling().as_ptr()
}
unsafe fn from_foreign(_: *mut crate::ffi::c_void) -> Self {}
unsafe fn from_foreign(_: *mut Self::PointedTo) -> Self {}
unsafe fn borrow<'a>(_: *mut crate::ffi::c_void) -> Self::Borrowed<'a> {}
unsafe fn borrow_mut<'a>(_: *mut crate::ffi::c_void) -> Self::BorrowedMut<'a> {}
unsafe fn borrow<'a>(_: *mut Self::PointedTo) -> Self::Borrowed<'a> {}
unsafe fn borrow_mut<'a>(_: *mut Self::PointedTo) -> Self::BorrowedMut<'a> {}
}
/// Runs a cleanup function/closure when dropped.

275
rust/kernel/xarray.rs Normal file
View file

@ -0,0 +1,275 @@
// SPDX-License-Identifier: GPL-2.0
//! XArray abstraction.
//!
//! C header: [`include/linux/xarray.h`](srctree/include/linux/xarray.h)
use crate::{
alloc, bindings, build_assert,
error::{Error, Result},
types::{ForeignOwnable, NotThreadSafe, Opaque},
};
use core::{iter, marker::PhantomData, mem, pin::Pin, ptr::NonNull};
use pin_init::{pin_data, pin_init, pinned_drop, PinInit};
/// An array which efficiently maps sparse integer indices to owned objects.
///
/// This is similar to a [`crate::alloc::kvec::Vec<Option<T>>`], but more efficient when there are
/// holes in the index space, and can be efficiently grown.
///
/// # Invariants
///
/// `self.xa` is always an initialized and valid [`bindings::xarray`] whose entries are either
/// `XA_ZERO_ENTRY` or came from `T::into_foreign`.
///
/// # Examples
///
/// ```rust
/// use kernel::alloc::KBox;
/// use kernel::xarray::{AllocKind, XArray};
///
/// let xa = KBox::pin_init(XArray::new(AllocKind::Alloc1), GFP_KERNEL)?;
///
/// let dead = KBox::new(0xdead, GFP_KERNEL)?;
/// let beef = KBox::new(0xbeef, GFP_KERNEL)?;
///
/// let mut guard = xa.lock();
///
/// assert_eq!(guard.get(0), None);
///
/// assert_eq!(guard.store(0, dead, GFP_KERNEL)?.as_deref(), None);
/// assert_eq!(guard.get(0).copied(), Some(0xdead));
///
/// *guard.get_mut(0).unwrap() = 0xffff;
/// assert_eq!(guard.get(0).copied(), Some(0xffff));
///
/// assert_eq!(guard.store(0, beef, GFP_KERNEL)?.as_deref().copied(), Some(0xffff));
/// assert_eq!(guard.get(0).copied(), Some(0xbeef));
///
/// guard.remove(0);
/// assert_eq!(guard.get(0), None);
///
/// # Ok::<(), Error>(())
/// ```
#[pin_data(PinnedDrop)]
pub struct XArray<T: ForeignOwnable> {
#[pin]
xa: Opaque<bindings::xarray>,
_p: PhantomData<T>,
}
#[pinned_drop]
impl<T: ForeignOwnable> PinnedDrop for XArray<T> {
fn drop(self: Pin<&mut Self>) {
self.iter().for_each(|ptr| {
let ptr = ptr.as_ptr();
// SAFETY: `ptr` came from `T::into_foreign`.
//
// INVARIANT: we own the only reference to the array which is being dropped so the
// broken invariant is not observable on function exit.
drop(unsafe { T::from_foreign(ptr) })
});
// SAFETY: `self.xa` is always valid by the type invariant.
unsafe { bindings::xa_destroy(self.xa.get()) };
}
}
/// Flags passed to [`XArray::new`] to configure the array's allocation tracking behavior.
pub enum AllocKind {
/// Consider the first element to be at index 0.
Alloc,
/// Consider the first element to be at index 1.
Alloc1,
}
impl<T: ForeignOwnable> XArray<T> {
/// Creates a new initializer for this type.
pub fn new(kind: AllocKind) -> impl PinInit<Self> {
let flags = match kind {
AllocKind::Alloc => bindings::XA_FLAGS_ALLOC,
AllocKind::Alloc1 => bindings::XA_FLAGS_ALLOC1,
};
pin_init!(Self {
// SAFETY: `xa` is valid while the closure is called.
//
// INVARIANT: `xa` is initialized here to an empty, valid [`bindings::xarray`].
xa <- Opaque::ffi_init(|xa| unsafe {
bindings::xa_init_flags(xa, flags)
}),
_p: PhantomData,
})
}
fn iter(&self) -> impl Iterator<Item = NonNull<T::PointedTo>> + '_ {
let mut index = 0;
// SAFETY: `self.xa` is always valid by the type invariant.
iter::once(unsafe {
bindings::xa_find(self.xa.get(), &mut index, usize::MAX, bindings::XA_PRESENT)
})
.chain(iter::from_fn(move || {
// SAFETY: `self.xa` is always valid by the type invariant.
Some(unsafe {
bindings::xa_find_after(self.xa.get(), &mut index, usize::MAX, bindings::XA_PRESENT)
})
}))
.map_while(|ptr| NonNull::new(ptr.cast()))
}
/// Attempts to lock the [`XArray`] for exclusive access.
pub fn try_lock(&self) -> Option<Guard<'_, T>> {
// SAFETY: `self.xa` is always valid by the type invariant.
if (unsafe { bindings::xa_trylock(self.xa.get()) } != 0) {
Some(Guard {
xa: self,
_not_send: NotThreadSafe,
})
} else {
None
}
}
/// Locks the [`XArray`] for exclusive access.
pub fn lock(&self) -> Guard<'_, T> {
// SAFETY: `self.xa` is always valid by the type invariant.
unsafe { bindings::xa_lock(self.xa.get()) };
Guard {
xa: self,
_not_send: NotThreadSafe,
}
}
}
/// A lock guard.
///
/// The lock is unlocked when the guard goes out of scope.
#[must_use = "the lock unlocks immediately when the guard is unused"]
pub struct Guard<'a, T: ForeignOwnable> {
xa: &'a XArray<T>,
_not_send: NotThreadSafe,
}
impl<T: ForeignOwnable> Drop for Guard<'_, T> {
fn drop(&mut self) {
// SAFETY:
// - `self.xa.xa` is always valid by the type invariant.
// - The caller holds the lock, so it is safe to unlock it.
unsafe { bindings::xa_unlock(self.xa.xa.get()) };
}
}
/// The error returned by [`store`](Guard::store).
///
/// Contains the underlying error and the value that was not stored.
pub struct StoreError<T> {
/// The error that occurred.
pub error: Error,
/// The value that was not stored.
pub value: T,
}
impl<T> From<StoreError<T>> for Error {
fn from(value: StoreError<T>) -> Self {
value.error
}
}
impl<'a, T: ForeignOwnable> Guard<'a, T> {
fn load<F, U>(&self, index: usize, f: F) -> Option<U>
where
F: FnOnce(NonNull<T::PointedTo>) -> U,
{
// SAFETY: `self.xa.xa` is always valid by the type invariant.
let ptr = unsafe { bindings::xa_load(self.xa.xa.get(), index) };
let ptr = NonNull::new(ptr.cast())?;
Some(f(ptr))
}
/// Provides a reference to the element at the given index.
pub fn get(&self, index: usize) -> Option<T::Borrowed<'_>> {
self.load(index, |ptr| {
// SAFETY: `ptr` came from `T::into_foreign`.
unsafe { T::borrow(ptr.as_ptr()) }
})
}
/// Provides a mutable reference to the element at the given index.
pub fn get_mut(&mut self, index: usize) -> Option<T::BorrowedMut<'_>> {
self.load(index, |ptr| {
// SAFETY: `ptr` came from `T::into_foreign`.
unsafe { T::borrow_mut(ptr.as_ptr()) }
})
}
/// Removes and returns the element at the given index.
pub fn remove(&mut self, index: usize) -> Option<T> {
// SAFETY:
// - `self.xa.xa` is always valid by the type invariant.
// - The caller holds the lock.
let ptr = unsafe { bindings::__xa_erase(self.xa.xa.get(), index) }.cast();
// SAFETY:
// - `ptr` is either NULL or came from `T::into_foreign`.
// - `&mut self` guarantees that the lifetimes of [`T::Borrowed`] and [`T::BorrowedMut`]
// borrowed from `self` have ended.
unsafe { T::try_from_foreign(ptr) }
}
/// Stores an element at the given index.
///
/// May drop the lock if needed to allocate memory, and then reacquire it afterwards.
///
/// On success, returns the element which was previously at the given index.
///
/// On failure, returns the element which was attempted to be stored.
pub fn store(
&mut self,
index: usize,
value: T,
gfp: alloc::Flags,
) -> Result<Option<T>, StoreError<T>> {
build_assert!(
mem::align_of::<T::PointedTo>() >= 4,
"pointers stored in XArray must be 4-byte aligned"
);
let new = value.into_foreign();
let old = {
let new = new.cast();
// SAFETY:
// - `self.xa.xa` is always valid by the type invariant.
// - The caller holds the lock.
//
// INVARIANT: `new` came from `T::into_foreign`.
unsafe { bindings::__xa_store(self.xa.xa.get(), index, new, gfp.as_raw()) }
};
// SAFETY: `__xa_store` returns the old entry at this index on success or `xa_err` if an
// error happened.
let errno = unsafe { bindings::xa_err(old) };
if errno != 0 {
// SAFETY: `new` came from `T::into_foreign` and `__xa_store` does not take
// ownership of the value on error.
let value = unsafe { T::from_foreign(new) };
Err(StoreError {
value,
error: Error::from_errno(errno),
})
} else {
let old = old.cast();
// SAFETY: `ptr` is either NULL or came from `T::into_foreign`.
//
// NB: `XA_ZERO_ENTRY` is never returned by functions belonging to the Normal XArray
// API; such entries present as `NULL`.
Ok(unsafe { T::try_from_foreign(old) })
}
}
}
// SAFETY: `XArray<T>` has no shared mutable state so it is `Send` iff `T` is `Send`.
unsafe impl<T: ForeignOwnable + Send> Send for XArray<T> {}
// SAFETY: `XArray<T>` serialises the interior mutability it provides so it is `Sync` iff `T` is
// `Send`.
unsafe impl<T: ForeignOwnable + Send> Sync for XArray<T> {}