* [PATCH v10 0/2] rust: xarray: Add a minimal abstraction for XArray @ 2024-11-20 11:48 ` Tamir Duberstein 2024-11-20 11:48 ` [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` Tamir Duberstein ` (2 more replies) 0 siblings, 3 replies; 16+ messages in thread From: Tamir Duberstein @ 2024-11-20 11:48 UTC (permalink / raw) To: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross Cc: Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel, Tamir Duberstein This is a reimagining relative to earlier versions[0] by Asahi Lina and Maíra Canal. It is needed to support rust-binder, though this version only provides enough machinery to support rnull. Link: https://lore.kernel.org/rust-for-linux/20240309235927.168915-2-mcanal@igalia.com/ [0] --- Changes in v10: - Guard::get takes &self instead of &mut self. (Andreas Hindborg) - Guard is !Send. (Boqun Feng) - Add Inspired-by tags. (Maíra Canal and Asahi Lina) - Rebase on linux-next, use NotThreadSafe. (Alice Ryhl) - Link to v9: https://lore.kernel.org/r/20241118-rust-xarray-bindings-v9-0-3219cdb53685@gmail.com --- Tamir Duberstein (2): rust: types: add `ForeignOwnable::PointedTo` rust: xarray: Add an abstraction for XArray rust/bindings/bindings_helper.h | 6 + rust/helpers/helpers.c | 1 + rust/helpers/xarray.c | 28 +++++ rust/kernel/alloc.rs | 5 + rust/kernel/alloc/kbox.rs | 38 +++--- rust/kernel/lib.rs | 1 + rust/kernel/miscdevice.rs | 6 +- rust/kernel/sync/arc.rs | 21 ++-- rust/kernel/types.rs | 46 ++++--- rust/kernel/xarray.rs | 266 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 373 insertions(+), 45 deletions(-) --- base-commit: 7510705aa41f7891c84dbb37965f492f09ae2077 change-id: 20241020-rust-xarray-bindings-bef514142968 prerequisite-change-id: 20241030-borrow-mut-75f181feef4c:v6 prerequisite-patch-id: f801fb31bb4f202b3327f5fdb50d3018e25347d1 prerequisite-patch-id: b57aa4f44b238d4cb80f00276a188d9ba0c743cc prerequisite-patch-id: 2387ec5af1cc03614d3dff5a95cefcd243befd65 prerequisite-patch-id: 75e26dd500888d9a27f8eac3d8304eab8d75c366 prerequisite-patch-id: 7f845443f373f975a888f01c3761fe8aa04b8a3c prerequisite-patch-id: 5a9856c7363b33f0adfe8658e076b35abf960d23 Best regards, -- Tamir Duberstein <tamird@gmail.com> ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` 2024-11-20 11:48 ` [PATCH v10 0/2] rust: xarray: Add a minimal abstraction for XArray Tamir Duberstein @ 2024-11-20 11:48 ` Tamir Duberstein 2024-11-25 14:49 ` Alice Ryhl 2024-12-03 11:51 ` Andreas Hindborg 2024-11-20 11:48 ` [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray Tamir Duberstein 2024-11-25 7:58 ` [PATCH v10 0/2] rust: xarray: Add a minimal " Andreas Hindborg 2 siblings, 2 replies; 16+ messages in thread From: Tamir Duberstein @ 2024-11-20 11:48 UTC (permalink / raw) To: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross Cc: Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel, Tamir Duberstein Allow implementors to specify the foreign pointer type; this exposes information about the pointed-to type such as its alignment. This requires the trait to be `unsafe` since it is now possible for implementors to break soundness by returning a misaligned pointer. Encoding the pointer type in the trait (and avoiding pointer casts) allows the compiler to check that implementors return the correct pointer type. This is preferable to directly encoding the alignment in the trait using a constant as the compiler would be unable to check it. Signed-off-by: Tamir Duberstein <tamird@gmail.com> --- rust/kernel/alloc/kbox.rs | 38 ++++++++++++++++++++------------------ rust/kernel/miscdevice.rs | 6 +++--- rust/kernel/sync/arc.rs | 21 ++++++++++++--------- rust/kernel/types.rs | 46 +++++++++++++++++++++++++++++++--------------- 4 files changed, 66 insertions(+), 45 deletions(-) diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs index 4ffc4e1b22b2b7c2ea8e8ed5b7f7a8534625249f..4e7a0ce9cc9c24f2e828f41e9105acc4048333d5 100644 --- a/rust/kernel/alloc/kbox.rs +++ b/rust/kernel/alloc/kbox.rs @@ -349,68 +349,70 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E> } } -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 core::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 core::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 diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index e58807ad28dc644fa384e9c1fb41fd6e53abea7a..ac6b591a83ad558f12ce2746b54e7f76d8b99c6f 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -193,7 +193,7 @@ impl<T: MiscDevice> VtableHelper<T> { }; // SAFETY: The open call of a file owns the private data. - unsafe { (*file).private_data = ptr.into_foreign() }; + unsafe { (*file).private_data = ptr.into_foreign().cast() }; 0 } @@ -209,7 +209,7 @@ impl<T: MiscDevice> VtableHelper<T> { // SAFETY: The release call of a file owns the private data. let private = unsafe { (*file).private_data }; // SAFETY: The release call of a file owns the private data. - let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) }; + let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private.cast()) }; T::release(ptr); @@ -227,7 +227,7 @@ impl<T: MiscDevice> VtableHelper<T> { // SAFETY: The ioctl call of a file can access the private data. let private = unsafe { (*file).private_data }; // SAFETY: Ioctl calls can borrow the private data of the file. - let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; + let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private.cast()) }; match T::ioctl(device, cmd, arg as usize) { Ok(ret) => ret as c_long, diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index eb5cd8b360a3507a527978aaf96dbc3a80d4ae2c..8e29c332db86ae869d81f75de9c21fa73174de9a 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -130,9 +130,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, } @@ -330,18 +331,20 @@ pub fn into_unique_or_drop(self) -> Option<Pin<UniqueArc<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 @@ -349,17 +352,17 @@ unsafe fn from_foreign(ptr: *mut crate::ffi::c_void) -> Self { 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 core::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) } diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 0dfaf45a755c7ce702027918e5fd3e97c407fda4..0cf93c293b884004a6ed64c2c09723efa7986270 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -18,7 +18,19 @@ /// /// 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 @@ unsafe fn try_from_foreign(ptr: *mut crate::ffi::c_void) -> Option<Self> { /// /// [`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 @@ unsafe fn try_from_foreign(ptr: *mut crate::ffi::c_void) -> Option<Self> { /// [`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. -- 2.47.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` 2024-11-20 11:48 ` [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` Tamir Duberstein @ 2024-11-25 14:49 ` Alice Ryhl 2024-11-25 14:52 ` Tamir Duberstein 2024-11-25 15:33 ` Andreas Hindborg 2024-12-03 11:51 ` Andreas Hindborg 1 sibling, 2 replies; 16+ messages in thread From: Alice Ryhl @ 2024-11-25 14:49 UTC (permalink / raw) To: Tamir Duberstein Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel On Wed, Nov 20, 2024 at 12:48 PM Tamir Duberstein <tamird@gmail.com> wrote: > > Allow implementors to specify the foreign pointer type; this exposes > information about the pointed-to type such as its alignment. > > This requires the trait to be `unsafe` since it is now possible for > implementors to break soundness by returning a misaligned pointer. > > Encoding the pointer type in the trait (and avoiding pointer casts) > allows the compiler to check that implementors return the correct > pointer type. This is preferable to directly encoding the alignment in > the trait using a constant as the compiler would be unable to check it. > > Signed-off-by: Tamir Duberstein <tamird@gmail.com> I'm not super convinced by this way forward. It introduces more casts to/from c_void in code using it, and forces us to expose internal types such as ArcInner. Does anyone else have thoughts on this? Alice ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` 2024-11-25 14:49 ` Alice Ryhl @ 2024-11-25 14:52 ` Tamir Duberstein 2024-11-25 15:33 ` Andreas Hindborg 1 sibling, 0 replies; 16+ messages in thread From: Tamir Duberstein @ 2024-11-25 14:52 UTC (permalink / raw) To: Alice Ryhl Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel On Mon, Nov 25, 2024 at 9:49 AM Alice Ryhl <aliceryhl@google.com> wrote: > > On Wed, Nov 20, 2024 at 12:48 PM Tamir Duberstein <tamird@gmail.com> wrote: > > > > Allow implementors to specify the foreign pointer type; this exposes > > information about the pointed-to type such as its alignment. > > > > This requires the trait to be `unsafe` since it is now possible for > > implementors to break soundness by returning a misaligned pointer. > > > > Encoding the pointer type in the trait (and avoiding pointer casts) > > allows the compiler to check that implementors return the correct > > pointer type. This is preferable to directly encoding the alignment in > > the trait using a constant as the compiler would be unable to check it. > > > > Signed-off-by: Tamir Duberstein <tamird@gmail.com> > > I'm not super convinced by this way forward. It introduces more casts > to/from c_void in code using it, and forces us to expose internal > types such as ArcInner. Does anyone else have thoughts on this? It's certainly a trade-off. The alternative (something like exposing `const ALIGNMENT: usize`) would nullify the compiler's ability to check implementations. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` 2024-11-25 14:49 ` Alice Ryhl 2024-11-25 14:52 ` Tamir Duberstein @ 2024-11-25 15:33 ` Andreas Hindborg 1 sibling, 0 replies; 16+ messages in thread From: Andreas Hindborg @ 2024-11-25 15:33 UTC (permalink / raw) To: Alice Ryhl Cc: Tamir Duberstein, Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel "Alice Ryhl" <aliceryhl@google.com> writes: > On Wed, Nov 20, 2024 at 12:48 PM Tamir Duberstein <tamird@gmail.com> wrote: >> >> Allow implementors to specify the foreign pointer type; this exposes >> information about the pointed-to type such as its alignment. >> >> This requires the trait to be `unsafe` since it is now possible for >> implementors to break soundness by returning a misaligned pointer. >> >> Encoding the pointer type in the trait (and avoiding pointer casts) >> allows the compiler to check that implementors return the correct >> pointer type. This is preferable to directly encoding the alignment in >> the trait using a constant as the compiler would be unable to check it. >> >> Signed-off-by: Tamir Duberstein <tamird@gmail.com> > > I'm not super convinced by this way forward. It introduces more casts > to/from c_void in code using it, and forces us to expose internal > types such as ArcInner. Does anyone else have thoughts on this? Erasing the type later rather than sooner seems like the right thing to do, giving the compiler more options to check things over. It was not really any significant work to add/remove casts where needed for `rnull`. Granted, even the downstream driver is quite small, you are going to spend a bit more time in binder. Best regards, Andreas Hindborg ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` 2024-11-20 11:48 ` [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` Tamir Duberstein 2024-11-25 14:49 ` Alice Ryhl @ 2024-12-03 11:51 ` Andreas Hindborg 1 sibling, 0 replies; 16+ messages in thread From: Andreas Hindborg @ 2024-12-03 11:51 UTC (permalink / raw) To: Tamir Duberstein Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel "Tamir Duberstein" <tamird@gmail.com> writes: > Allow implementors to specify the foreign pointer type; this exposes > information about the pointed-to type such as its alignment. > > This requires the trait to be `unsafe` since it is now possible for > implementors to break soundness by returning a misaligned pointer. > > Encoding the pointer type in the trait (and avoiding pointer casts) > allows the compiler to check that implementors return the correct > pointer type. This is preferable to directly encoding the alignment in > the trait using a constant as the compiler would be unable to check it. > > Signed-off-by: Tamir Duberstein <tamird@gmail.com> Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org> Best regards, Andreas Hindborg ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray 2024-11-20 11:48 ` [PATCH v10 0/2] rust: xarray: Add a minimal abstraction for XArray Tamir Duberstein 2024-11-20 11:48 ` [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` Tamir Duberstein @ 2024-11-20 11:48 ` Tamir Duberstein 2024-12-03 12:16 ` Andreas Hindborg 2024-12-03 12:30 ` Alice Ryhl 2024-11-25 7:58 ` [PATCH v10 0/2] rust: xarray: Add a minimal " Andreas Hindborg 2 siblings, 2 replies; 16+ messages in thread From: Tamir Duberstein @ 2024-11-20 11:48 UTC (permalink / raw) To: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross Cc: Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel, Tamir Duberstein `XArray` is an efficient sparse array of pointers. Add a Rust abstraction for this type. This implementation bounds the element type on `ForeignOwnable` and requires explicit locking for all operations. Future work may leverage RCU to enable lockless operation. Inspired-by: Maíra Canal <mcanal@igalia.com> Inspired-by: Asahi Lina <lina@asahilina.net> Signed-off-by: Tamir Duberstein <tamird@gmail.com> --- rust/bindings/bindings_helper.h | 6 + rust/helpers/helpers.c | 1 + rust/helpers/xarray.c | 28 +++++ rust/kernel/alloc.rs | 5 + rust/kernel/lib.rs | 1 + rust/kernel/xarray.rs | 266 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 307 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 5c4dfe22f41a5a106330e8c43ffbd342c69c4e0b..9f39d673b240281aed2759b5bd076aa43fb54951 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -30,6 +30,7 @@ #include <linux/tracepoint.h> #include <linux/wait.h> #include <linux/workqueue.h> +#include <linux/xarray.h> #include <trace/events/rust_sample.h> /* `bindgen` gets confused at certain things. */ @@ -43,3 +44,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; diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index dcf827a61b52e71e46fd5378878602eef5e538b6..ff28340e78c53c79baf18e2927cc90350d8ab513 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -30,3 +30,4 @@ #include "vmalloc.c" #include "wait.c" #include "workqueue.c" +#include "xarray.c" diff --git a/rust/helpers/xarray.c b/rust/helpers/xarray.c new file mode 100644 index 0000000000000000000000000000000000000000..60b299f11451d2c4a75e50e25dec4dac13f143f4 --- /dev/null +++ b/rust/helpers/xarray.c @@ -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); +} diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index f2f7f3a53d298cf899e062346202ba3285ce3676..be9f164ece2e0fe71143e0201247d2b70c193c51 100644 --- a/rust/kernel/alloc.rs +++ b/rust/kernel/alloc.rs @@ -39,6 +39,11 @@ pub struct Flags(u32); impl Flags { + /// Get a flags value with all bits unset. + pub fn empty() -> Self { + Self(0) + } + /// Get the raw representation of this flag. pub(crate) fn as_raw(self) -> u32 { self.0 diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index e1065a7551a39e68d6379031d80d4be336e652a3..9ca524b15920c525c7db419e60dec4c43522751d 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -68,6 +68,7 @@ pub mod types; pub mod uaccess; pub mod workqueue; +pub mod xarray; #[doc(hidden)] pub use bindings; diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs new file mode 100644 index 0000000000000000000000000000000000000000..acbac787dc9a38684538697d53f590880fa9903a --- /dev/null +++ b/rust/kernel/xarray.rs @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! XArray abstraction. +//! +//! C header: [`include/linux/xarray.h`](srctree/include/linux/xarray.h) + +use core::pin::Pin; + +use crate::{ + alloc, bindings, build_assert, build_error, + error::{Error, Result}, + init::PinInit, + pin_init, + types::{ForeignOwnable, NotThreadSafe, Opaque}, +}; +use core::{iter, marker::PhantomData, mem}; +use macros::{pin_data, pinned_drop}; + +/// 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).unwrap().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).unwrap().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 [`XArray`]. + 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. + xa <- Opaque::ffi_init(|xa| unsafe { + bindings::xa_init_flags(xa, flags) + }), + _p: PhantomData, + }) + } + + fn iter(&self) -> impl Iterator<Item = core::ptr::NonNull<T::PointedTo>> + '_ { + // TODO: Remove when https://lore.kernel.org/all/20240913213041.395655-5-gary@garyguo.net/ is applied. + const MIN: core::ffi::c_ulong = core::ffi::c_ulong::MIN; + const MAX: core::ffi::c_ulong = core::ffi::c_ulong::MAX; + + let mut index = MIN; + + // SAFETY: `self.xa` is always valid by the type invariant. + iter::once(unsafe { + bindings::xa_find(self.xa.get(), &mut index, 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, MAX, bindings::XA_PRESENT) + }) + })) + .map_while(|ptr| core::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. + (unsafe { bindings::xa_trylock(self.xa.get()) } != 0).then(|| Guard { + xa: self, + _not_send: NotThreadSafe, + }) + } + + /// 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. + // + // SAFETY: The caller holds the lock, so it is safe to unlock it. + unsafe { bindings::xa_unlock(self.xa.xa.get()) }; + } +} + +// TODO: Remove when https://lore.kernel.org/all/20240913213041.395655-5-gary@garyguo.net/ is applied. +fn to_index(i: usize) -> core::ffi::c_ulong { + i.try_into() + .unwrap_or_else(|_| build_error!("cannot convert usize to c_ulong")) +} + +impl<'a, T: ForeignOwnable> Guard<'a, T> { + fn load<F, U>(&self, index: usize, f: F) -> Option<U> + where + F: FnOnce(core::ptr::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(), to_index(index)) }; + let ptr = core::ptr::NonNull::new(ptr.cast())?; + Some(f(ptr)) + } + + /// Loads an entry from the array. + /// + /// Returns the entry 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()) } + }) + } + + /// Loads an entry from the array. + /// + /// Returns the entry 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()) } + }) + } + + /// Erases an entry from the array. + /// + /// Returns the entry which was previously at the given index. + pub fn remove(&mut self, index: usize) -> Option<T> { + // SAFETY: `self.xa.xa` is always valid by the type invariant. + // + // SAFETY: The caller holds the lock. + let ptr = unsafe { bindings::__xa_erase(self.xa.xa.get(), to_index(index)) }.cast(); + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. + unsafe { T::try_from_foreign(ptr) } + } + + /// Stores an entry in the array. + /// + /// On success, returns the entry which was previously at the given index. + /// + /// On failure, returns the entry which was attempted to be stored. + pub fn store( + &mut self, + index: usize, + value: T, + gfp: alloc::Flags, + ) -> Result<Option<T>, (T, Error)> { + 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. + // + // SAFETY: The caller holds the lock. + // + // INVARIANT: `new` came from `T::into_foreign`. + unsafe { bindings::__xa_store(self.xa.xa.get(), to_index(index), new, gfp.as_raw()) } + }; + + // SAFETY: `__xa_store` returns the old entry at this index on success or `xa_err` if an + // error happened. + match unsafe { bindings::xa_err(old) } { + 0 => { + let old = old.cast(); + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. + Ok(unsafe { T::try_from_foreign(old) }) + } + errno => { + // 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((value, Error::from_errno(errno))) + } + } + } +} + +// SAFETY: It is safe to send `XArray<T>` to another thread when the underlying `T` is `Send` +// because XArray is thread-safe and all mutation operations are synchronized. +unsafe impl<T: ForeignOwnable + Send> Send for XArray<T> {} + +// SAFETY: It is safe to send `&XArray<T>` to another thread when the underlying `T` is `Sync` +// because it effectively means sharing `&T` (which is safe because `T` is `Sync`). Additionally, +// `T` is `Send` because XArray is thread-safe and all mutation operations are internally locked. +unsafe impl<T: ForeignOwnable + Send + Sync> Sync for XArray<T> {} -- 2.47.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray 2024-11-20 11:48 ` [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray Tamir Duberstein @ 2024-12-03 12:16 ` Andreas Hindborg 2024-12-03 15:00 ` Tamir Duberstein 2024-12-03 12:30 ` Alice Ryhl 1 sibling, 1 reply; 16+ messages in thread From: Andreas Hindborg @ 2024-12-03 12:16 UTC (permalink / raw) To: Tamir Duberstein Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel "Tamir Duberstein" <tamird@gmail.com> writes: > `XArray` is an efficient sparse array of pointers. Add a Rust > abstraction for this type. > > This implementation bounds the element type on `ForeignOwnable` and > requires explicit locking for all operations. Future work may leverage > RCU to enable lockless operation. > > Inspired-by: Maíra Canal <mcanal@igalia.com> > Inspired-by: Asahi Lina <lina@asahilina.net> > Signed-off-by: Tamir Duberstein <tamird@gmail.com> > --- [cut] > + > +// SAFETY: It is safe to send `XArray<T>` to another thread when the underlying `T` is `Send` > +// because XArray is thread-safe and all mutation operations are synchronized. > +unsafe impl<T: ForeignOwnable + Send> Send for XArray<T> {} > + > +// SAFETY: It is safe to send `&XArray<T>` to another thread when the underlying `T` is `Sync` > +// because it effectively means sharing `&T` (which is safe because `T` is `Sync`). Additionally, > +// `T` is `Send` because XArray is thread-safe and all mutation operations are internally locked. > +unsafe impl<T: ForeignOwnable + Send + Sync> Sync for XArray<T> {} I don't understand the sentence: "Additionally, `T` is `Send` because XArray is thread-safe and all mutation operations are internally locked.", could you elaborate? Best regards, Andreas Hindborg ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray 2024-12-03 12:16 ` Andreas Hindborg @ 2024-12-03 15:00 ` Tamir Duberstein 0 siblings, 0 replies; 16+ messages in thread From: Tamir Duberstein @ 2024-12-03 15:00 UTC (permalink / raw) To: Andreas Hindborg Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel On Tue, Dec 3, 2024 at 7:16 AM Andreas Hindborg <a.hindborg@kernel.org> wrote: > > "Tamir Duberstein" <tamird@gmail.com> writes: > > > `XArray` is an efficient sparse array of pointers. Add a Rust > > abstraction for this type. > > > > This implementation bounds the element type on `ForeignOwnable` and > > requires explicit locking for all operations. Future work may leverage > > RCU to enable lockless operation. > > > > Inspired-by: Maíra Canal <mcanal@igalia.com> > > Inspired-by: Asahi Lina <lina@asahilina.net> > > Signed-off-by: Tamir Duberstein <tamird@gmail.com> > > --- > > [cut] > > > + > > +// SAFETY: It is safe to send `XArray<T>` to another thread when the underlying `T` is `Send` > > +// because XArray is thread-safe and all mutation operations are synchronized. > > +unsafe impl<T: ForeignOwnable + Send> Send for XArray<T> {} > > + > > +// SAFETY: It is safe to send `&XArray<T>` to another thread when the underlying `T` is `Sync` > > +// because it effectively means sharing `&T` (which is safe because `T` is `Sync`). Additionally, > > +// `T` is `Send` because XArray is thread-safe and all mutation operations are internally locked. > > +unsafe impl<T: ForeignOwnable + Send + Sync> Sync for XArray<T> {} > > I don't understand the sentence: "Additionally, `T` is `Send` because XArray is > thread-safe and all mutation operations are internally locked.", could > you elaborate? This comment came from the original series, I'll reword it in the next version. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray 2024-11-20 11:48 ` [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray Tamir Duberstein 2024-12-03 12:16 ` Andreas Hindborg @ 2024-12-03 12:30 ` Alice Ryhl 2024-12-03 15:00 ` Tamir Duberstein 1 sibling, 1 reply; 16+ messages in thread From: Alice Ryhl @ 2024-12-03 12:30 UTC (permalink / raw) To: Tamir Duberstein Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel On Wed, Nov 20, 2024 at 12:48 PM Tamir Duberstein <tamird@gmail.com> wrote: > > `XArray` is an efficient sparse array of pointers. Add a Rust > abstraction for this type. > > This implementation bounds the element type on `ForeignOwnable` and > requires explicit locking for all operations. Future work may leverage > RCU to enable lockless operation. > > Inspired-by: Maíra Canal <mcanal@igalia.com> > Inspired-by: Asahi Lina <lina@asahilina.net> > Signed-off-by: Tamir Duberstein <tamird@gmail.com> > --- > rust/bindings/bindings_helper.h | 6 + > rust/helpers/helpers.c | 1 + > rust/helpers/xarray.c | 28 +++++ > rust/kernel/alloc.rs | 5 + > rust/kernel/lib.rs | 1 + > rust/kernel/xarray.rs | 266 ++++++++++++++++++++++++++++++++++++++++ > 6 files changed, 307 insertions(+) > > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h > index 5c4dfe22f41a5a106330e8c43ffbd342c69c4e0b..9f39d673b240281aed2759b5bd076aa43fb54951 100644 > --- a/rust/bindings/bindings_helper.h > +++ b/rust/bindings/bindings_helper.h > @@ -30,6 +30,7 @@ > #include <linux/tracepoint.h> > #include <linux/wait.h> > #include <linux/workqueue.h> > +#include <linux/xarray.h> > #include <trace/events/rust_sample.h> > > /* `bindgen` gets confused at certain things. */ > @@ -43,3 +44,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; > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c > index dcf827a61b52e71e46fd5378878602eef5e538b6..ff28340e78c53c79baf18e2927cc90350d8ab513 100644 > --- a/rust/helpers/helpers.c > +++ b/rust/helpers/helpers.c > @@ -30,3 +30,4 @@ > #include "vmalloc.c" > #include "wait.c" > #include "workqueue.c" > +#include "xarray.c" > diff --git a/rust/helpers/xarray.c b/rust/helpers/xarray.c > new file mode 100644 > index 0000000000000000000000000000000000000000..60b299f11451d2c4a75e50e25dec4dac13f143f4 > --- /dev/null > +++ b/rust/helpers/xarray.c > @@ -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); > +} > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs > index f2f7f3a53d298cf899e062346202ba3285ce3676..be9f164ece2e0fe71143e0201247d2b70c193c51 100644 > --- a/rust/kernel/alloc.rs > +++ b/rust/kernel/alloc.rs > @@ -39,6 +39,11 @@ > pub struct Flags(u32); > > impl Flags { > + /// Get a flags value with all bits unset. > + pub fn empty() -> Self { > + Self(0) > + } Is this used anywhere? > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > index e1065a7551a39e68d6379031d80d4be336e652a3..9ca524b15920c525c7db419e60dec4c43522751d 100644 > --- a/rust/kernel/lib.rs > +++ b/rust/kernel/lib.rs > @@ -68,6 +68,7 @@ > pub mod types; > pub mod uaccess; > pub mod workqueue; > +pub mod xarray; > > #[doc(hidden)] > pub use bindings; > diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs > new file mode 100644 > index 0000000000000000000000000000000000000000..acbac787dc9a38684538697d53f590880fa9903a > --- /dev/null > +++ b/rust/kernel/xarray.rs > @@ -0,0 +1,266 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! XArray abstraction. > +//! > +//! C header: [`include/linux/xarray.h`](srctree/include/linux/xarray.h) > + > +use core::pin::Pin; Could be merged with the imports below. > +use crate::{ > + alloc, bindings, build_assert, build_error, > + error::{Error, Result}, > + init::PinInit, > + pin_init, > + types::{ForeignOwnable, NotThreadSafe, Opaque}, > +}; > +use core::{iter, marker::PhantomData, mem}; > +use macros::{pin_data, pinned_drop}; I think these are in crate::prelude. > +/// 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).unwrap().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).unwrap().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 [`XArray`]. > + 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. > + xa <- Opaque::ffi_init(|xa| unsafe { > + bindings::xa_init_flags(xa, flags) > + }), > + _p: PhantomData, > + }) > + } > + > + fn iter(&self) -> impl Iterator<Item = core::ptr::NonNull<T::PointedTo>> + '_ { > + // TODO: Remove when https://lore.kernel.org/all/20240913213041.395655-5-gary@garyguo.net/ is applied. > + const MIN: core::ffi::c_ulong = core::ffi::c_ulong::MIN; > + const MAX: core::ffi::c_ulong = core::ffi::c_ulong::MAX; Isn't MIN just zero? > + let mut index = MIN; > + > + // SAFETY: `self.xa` is always valid by the type invariant. > + iter::once(unsafe { > + bindings::xa_find(self.xa.get(), &mut index, 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, MAX, bindings::XA_PRESENT) > + }) > + })) > + .map_while(|ptr| core::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. > + (unsafe { bindings::xa_trylock(self.xa.get()) } != 0).then(|| Guard { > + xa: self, > + _not_send: NotThreadSafe, > + }) > + } > + > + /// 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. > + // > + // SAFETY: The caller holds the lock, so it is safe to unlock it. > + unsafe { bindings::xa_unlock(self.xa.xa.get()) }; > + } > +} > + > +// TODO: Remove when https://lore.kernel.org/all/20240913213041.395655-5-gary@garyguo.net/ is applied. > +fn to_index(i: usize) -> core::ffi::c_ulong { > + i.try_into() > + .unwrap_or_else(|_| build_error!("cannot convert usize to c_ulong")) > +} > + > +impl<'a, T: ForeignOwnable> Guard<'a, T> { > + fn load<F, U>(&self, index: usize, f: F) -> Option<U> > + where > + F: FnOnce(core::ptr::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(), to_index(index)) }; > + let ptr = core::ptr::NonNull::new(ptr.cast())?; > + Some(f(ptr)) > + } > + > + /// Loads an entry from the array. > + /// > + /// Returns the entry 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()) } > + }) > + } > + > + /// Loads an entry from the array. > + /// > + /// Returns the entry 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()) } > + }) > + } > + > + /// Erases an entry from the array. > + /// > + /// Returns the entry which was previously at the given index. > + pub fn remove(&mut self, index: usize) -> Option<T> { > + // SAFETY: `self.xa.xa` is always valid by the type invariant. > + // > + // SAFETY: The caller holds the lock. > + let ptr = unsafe { bindings::__xa_erase(self.xa.xa.get(), to_index(index)) }.cast(); Two safety comments? > + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. > + unsafe { T::try_from_foreign(ptr) } > + } > + > + /// Stores an entry in the array. > + /// > + /// On success, returns the entry which was previously at the given index. > + /// > + /// On failure, returns the entry which was attempted to be stored. I'd like to see documentation about the gfp flags. This may unlock the spinlock temporarily if GFP_KERNEL is used. > + pub fn store( > + &mut self, > + index: usize, > + value: T, > + gfp: alloc::Flags, > + ) -> Result<Option<T>, (T, Error)> { > + 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. > + // > + // SAFETY: The caller holds the lock. > + // > + // INVARIANT: `new` came from `T::into_foreign`. > + unsafe { bindings::__xa_store(self.xa.xa.get(), to_index(index), new, gfp.as_raw()) } > + }; > + > + // SAFETY: `__xa_store` returns the old entry at this index on success or `xa_err` if an > + // error happened. > + match unsafe { bindings::xa_err(old) } { > + 0 => { > + let old = old.cast(); > + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. > + Ok(unsafe { T::try_from_foreign(old) }) It can't be XA_ZERO_ENTRY? > + } > + errno => { > + // 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((value, Error::from_errno(errno))) > + } > + } > + } > +} > + > +// SAFETY: It is safe to send `XArray<T>` to another thread when the underlying `T` is `Send` > +// because XArray is thread-safe and all mutation operations are synchronized. > +unsafe impl<T: ForeignOwnable + Send> Send for XArray<T> {} > + > +// SAFETY: It is safe to send `&XArray<T>` to another thread when the underlying `T` is `Sync` > +// because it effectively means sharing `&T` (which is safe because `T` is `Sync`). Additionally, > +// `T` is `Send` because XArray is thread-safe and all mutation operations are internally locked. > +unsafe impl<T: ForeignOwnable + Send + Sync> Sync for XArray<T> {} > > -- > 2.47.0 > ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray 2024-12-03 12:30 ` Alice Ryhl @ 2024-12-03 15:00 ` Tamir Duberstein 2024-12-03 15:11 ` Alice Ryhl 2024-12-03 15:22 ` Miguel Ojeda 0 siblings, 2 replies; 16+ messages in thread From: Tamir Duberstein @ 2024-12-03 15:00 UTC (permalink / raw) To: Alice Ryhl Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel On Tue, Dec 3, 2024 at 7:30 AM Alice Ryhl <aliceryhl@google.com> wrote: > > On Wed, Nov 20, 2024 at 12:48 PM Tamir Duberstein <tamird@gmail.com> wrote: > > > > `XArray` is an efficient sparse array of pointers. Add a Rust > > abstraction for this type. > > > > This implementation bounds the element type on `ForeignOwnable` and > > requires explicit locking for all operations. Future work may leverage > > RCU to enable lockless operation. > > > > Inspired-by: Maíra Canal <mcanal@igalia.com> > > Inspired-by: Asahi Lina <lina@asahilina.net> > > Signed-off-by: Tamir Duberstein <tamird@gmail.com> > > --- > > rust/bindings/bindings_helper.h | 6 + > > rust/helpers/helpers.c | 1 + > > rust/helpers/xarray.c | 28 +++++ > > rust/kernel/alloc.rs | 5 + > > rust/kernel/lib.rs | 1 + > > rust/kernel/xarray.rs | 266 ++++++++++++++++++++++++++++++++++++++++ > > 6 files changed, 307 insertions(+) > > > > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h > > index 5c4dfe22f41a5a106330e8c43ffbd342c69c4e0b..9f39d673b240281aed2759b5bd076aa43fb54951 100644 > > --- a/rust/bindings/bindings_helper.h > > +++ b/rust/bindings/bindings_helper.h > > @@ -30,6 +30,7 @@ > > #include <linux/tracepoint.h> > > #include <linux/wait.h> > > #include <linux/workqueue.h> > > +#include <linux/xarray.h> > > #include <trace/events/rust_sample.h> > > > > /* `bindgen` gets confused at certain things. */ > > @@ -43,3 +44,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; > > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c > > index dcf827a61b52e71e46fd5378878602eef5e538b6..ff28340e78c53c79baf18e2927cc90350d8ab513 100644 > > --- a/rust/helpers/helpers.c > > +++ b/rust/helpers/helpers.c > > @@ -30,3 +30,4 @@ > > #include "vmalloc.c" > > #include "wait.c" > > #include "workqueue.c" > > +#include "xarray.c" > > diff --git a/rust/helpers/xarray.c b/rust/helpers/xarray.c > > new file mode 100644 > > index 0000000000000000000000000000000000000000..60b299f11451d2c4a75e50e25dec4dac13f143f4 > > --- /dev/null > > +++ b/rust/helpers/xarray.c > > @@ -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); > > +} > > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs > > index f2f7f3a53d298cf899e062346202ba3285ce3676..be9f164ece2e0fe71143e0201247d2b70c193c51 100644 > > --- a/rust/kernel/alloc.rs > > +++ b/rust/kernel/alloc.rs > > @@ -39,6 +39,11 @@ > > pub struct Flags(u32); > > > > impl Flags { > > + /// Get a flags value with all bits unset. > > + pub fn empty() -> Self { > > + Self(0) > > + } > > Is this used anywhere? It was used in an older version to fill reservations which were expected to not need to allocate memory. Technically it is public API so calling `store` on an occupied entry with `Flags::empty()` would be a valid use. > > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > > index e1065a7551a39e68d6379031d80d4be336e652a3..9ca524b15920c525c7db419e60dec4c43522751d 100644 > > --- a/rust/kernel/lib.rs > > +++ b/rust/kernel/lib.rs > > @@ -68,6 +68,7 @@ > > pub mod types; > > pub mod uaccess; > > pub mod workqueue; > > +pub mod xarray; > > > > #[doc(hidden)] > > pub use bindings; > > diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs > > new file mode 100644 > > index 0000000000000000000000000000000000000000..acbac787dc9a38684538697d53f590880fa9903a > > --- /dev/null > > +++ b/rust/kernel/xarray.rs > > @@ -0,0 +1,266 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +//! XArray abstraction. > > +//! > > +//! C header: [`include/linux/xarray.h`](srctree/include/linux/xarray.h) > > + > > +use core::pin::Pin; > > Could be merged with the imports below. Will do. > > +use crate::{ > > + alloc, bindings, build_assert, build_error, > > + error::{Error, Result}, > > + init::PinInit, > > + pin_init, > > + types::{ForeignOwnable, NotThreadSafe, Opaque}, > > +}; > > +use core::{iter, marker::PhantomData, mem}; > > +use macros::{pin_data, pinned_drop}; > > I think these are in crate::prelude. I prefer to be explicit, unless there's guidance on this somewhere? > > +/// 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).unwrap().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).unwrap().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 [`XArray`]. > > + 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. > > + xa <- Opaque::ffi_init(|xa| unsafe { > > + bindings::xa_init_flags(xa, flags) > > + }), > > + _p: PhantomData, > > + }) > > + } > > + > > + fn iter(&self) -> impl Iterator<Item = core::ptr::NonNull<T::PointedTo>> + '_ { > > + // TODO: Remove when https://lore.kernel.org/all/20240913213041.395655-5-gary@garyguo.net/ is applied. > > + const MIN: core::ffi::c_ulong = core::ffi::c_ulong::MIN; > > + const MAX: core::ffi::c_ulong = core::ffi::c_ulong::MAX; > > Isn't MIN just zero? I liked the symmetry, but I could change it if you feel strongly. > > + let mut index = MIN; > > + > > + // SAFETY: `self.xa` is always valid by the type invariant. > > + iter::once(unsafe { > > + bindings::xa_find(self.xa.get(), &mut index, 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, MAX, bindings::XA_PRESENT) > > + }) > > + })) > > + .map_while(|ptr| core::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. > > + (unsafe { bindings::xa_trylock(self.xa.get()) } != 0).then(|| Guard { > > + xa: self, > > + _not_send: NotThreadSafe, > > + }) > > + } > > + > > + /// 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. > > + // > > + // SAFETY: The caller holds the lock, so it is safe to unlock it. > > + unsafe { bindings::xa_unlock(self.xa.xa.get()) }; > > + } > > +} > > + > > +// TODO: Remove when https://lore.kernel.org/all/20240913213041.395655-5-gary@garyguo.net/ is applied. > > +fn to_index(i: usize) -> core::ffi::c_ulong { > > + i.try_into() > > + .unwrap_or_else(|_| build_error!("cannot convert usize to c_ulong")) > > +} > > + > > +impl<'a, T: ForeignOwnable> Guard<'a, T> { > > + fn load<F, U>(&self, index: usize, f: F) -> Option<U> > > + where > > + F: FnOnce(core::ptr::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(), to_index(index)) }; > > + let ptr = core::ptr::NonNull::new(ptr.cast())?; > > + Some(f(ptr)) > > + } > > + > > + /// Loads an entry from the array. > > + /// > > + /// Returns the entry 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()) } > > + }) > > + } > > + > > + /// Loads an entry from the array. > > + /// > > + /// Returns the entry 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()) } > > + }) > > + } > > + > > + /// Erases an entry from the array. > > + /// > > + /// Returns the entry which was previously at the given index. > > + pub fn remove(&mut self, index: usize) -> Option<T> { > > + // SAFETY: `self.xa.xa` is always valid by the type invariant. > > + // > > + // SAFETY: The caller holds the lock. > > + let ptr = unsafe { bindings::__xa_erase(self.xa.xa.get(), to_index(index)) }.cast(); > > Two safety comments? There are two properties that must be upheld. How would you like to see it formatted? > > + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. > > + unsafe { T::try_from_foreign(ptr) } > > + } > > + > > + /// Stores an entry in the array. > > + /// > > + /// On success, returns the entry which was previously at the given index. > > + /// > > + /// On failure, returns the entry which was attempted to be stored. > > I'd like to see documentation about the gfp flags. This may unlock the > spinlock temporarily if GFP_KERNEL is used. Will add the language from the C documentation: "May drop the lock if needed to allocate memory, and then reacquire it afterwards." > > + pub fn store( > > + &mut self, > > + index: usize, > > + value: T, > > + gfp: alloc::Flags, > > + ) -> Result<Option<T>, (T, Error)> { > > + 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. > > + // > > + // SAFETY: The caller holds the lock. > > + // > > + // INVARIANT: `new` came from `T::into_foreign`. > > + unsafe { bindings::__xa_store(self.xa.xa.get(), to_index(index), new, gfp.as_raw()) } > > + }; > > + > > + // SAFETY: `__xa_store` returns the old entry at this index on success or `xa_err` if an > > + // error happened. > > + match unsafe { bindings::xa_err(old) } { > > + 0 => { > > + let old = old.cast(); > > + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. > > + Ok(unsafe { T::try_from_foreign(old) }) > > It can't be XA_ZERO_ENTRY? No it can't. XA_ZERO_ENTRY is never returned from the "normal" API. XA_ZERO_ENTRY presents as NULL. > > + } > > + errno => { > > + // 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((value, Error::from_errno(errno))) > > + } > > + } > > + } > > +} > > + > > +// SAFETY: It is safe to send `XArray<T>` to another thread when the underlying `T` is `Send` > > +// because XArray is thread-safe and all mutation operations are synchronized. > > +unsafe impl<T: ForeignOwnable + Send> Send for XArray<T> {} > > + > > +// SAFETY: It is safe to send `&XArray<T>` to another thread when the underlying `T` is `Sync` > > +// because it effectively means sharing `&T` (which is safe because `T` is `Sync`). Additionally, > > +// `T` is `Send` because XArray is thread-safe and all mutation operations are internally locked. > > +unsafe impl<T: ForeignOwnable + Send + Sync> Sync for XArray<T> {} > > > > -- > > 2.47.0 > > ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray 2024-12-03 15:00 ` Tamir Duberstein @ 2024-12-03 15:11 ` Alice Ryhl 2024-12-03 15:57 ` Tamir Duberstein 2024-12-03 15:22 ` Miguel Ojeda 1 sibling, 1 reply; 16+ messages in thread From: Alice Ryhl @ 2024-12-03 15:11 UTC (permalink / raw) To: Tamir Duberstein Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel On Tue, Dec 3, 2024 at 4:00 PM Tamir Duberstein <tamird@gmail.com> wrote: > > On Tue, Dec 3, 2024 at 7:30 AM Alice Ryhl <aliceryhl@google.com> wrote: > > > > On Wed, Nov 20, 2024 at 12:48 PM Tamir Duberstein <tamird@gmail.com> wrote: > > > +use crate::{ > > > + alloc, bindings, build_assert, build_error, > > > + error::{Error, Result}, > > > + init::PinInit, > > > + pin_init, > > > + types::{ForeignOwnable, NotThreadSafe, Opaque}, > > > +}; > > > +use core::{iter, marker::PhantomData, mem}; > > > +use macros::{pin_data, pinned_drop}; > > > > I think these are in crate::prelude. > > I prefer to be explicit, unless there's guidance on this somewhere? I don't think I've ever seen anyone do a direct import from macros. > > > + fn iter(&self) -> impl Iterator<Item = core::ptr::NonNull<T::PointedTo>> + '_ { > > > + // TODO: Remove when https://lore.kernel.org/all/20240913213041.395655-5-gary@garyguo.net/ is applied. > > > + const MIN: core::ffi::c_ulong = core::ffi::c_ulong::MIN; > > > + const MAX: core::ffi::c_ulong = core::ffi::c_ulong::MAX; > > > > Isn't MIN just zero? > > I liked the symmetry, but I could change it if you feel strongly. I commented because I thought it was confusing; I spent some time figuring out whether the integer was signed. > > > + /// Erases an entry from the array. > > > + /// > > > + /// Returns the entry which was previously at the given index. > > > + pub fn remove(&mut self, index: usize) -> Option<T> { > > > + // SAFETY: `self.xa.xa` is always valid by the type invariant. > > > + // > > > + // SAFETY: The caller holds the lock. > > > + let ptr = unsafe { bindings::__xa_erase(self.xa.xa.get(), to_index(index)) }.cast(); > > > > Two safety comments? > > There are two properties that must be upheld. How would you like to > see it formatted? Usually multiple preconditions are listed using a bulleted list: // SAFETY: // - `self.xa.xa` is always valid by the type invariant. // - The caller holds the lock. > > > + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. > > > + unsafe { T::try_from_foreign(ptr) } > > > + } > > > + > > > + /// Stores an entry in the array. > > > + /// > > > + /// On success, returns the entry which was previously at the given index. > > > + /// > > > + /// On failure, returns the entry which was attempted to be stored. > > > > I'd like to see documentation about the gfp flags. This may unlock the > > spinlock temporarily if GFP_KERNEL is used. > > Will add the language from the C documentation: "May drop the lock if > needed to allocate memory, and then reacquire it afterwards." SGTM. > > > + // SAFETY: `__xa_store` returns the old entry at this index on success or `xa_err` if an > > > + // error happened. > > > + match unsafe { bindings::xa_err(old) } { > > > + 0 => { > > > + let old = old.cast(); > > > + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. > > > + Ok(unsafe { T::try_from_foreign(old) }) > > > > It can't be XA_ZERO_ENTRY? > > No it can't. XA_ZERO_ENTRY is never returned from the "normal" API. > XA_ZERO_ENTRY presents as NULL. It's probably worth mentioning in the safety comment. Alice ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray 2024-12-03 15:11 ` Alice Ryhl @ 2024-12-03 15:57 ` Tamir Duberstein 0 siblings, 0 replies; 16+ messages in thread From: Tamir Duberstein @ 2024-12-03 15:57 UTC (permalink / raw) To: Alice Ryhl Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel On Tue, Dec 3, 2024 at 10:12 AM Alice Ryhl <aliceryhl@google.com> wrote: > > On Tue, Dec 3, 2024 at 4:00 PM Tamir Duberstein <tamird@gmail.com> wrote: > > > > On Tue, Dec 3, 2024 at 7:30 AM Alice Ryhl <aliceryhl@google.com> wrote: > > > > > > On Wed, Nov 20, 2024 at 12:48 PM Tamir Duberstein <tamird@gmail.com> wrote: > > > > +use crate::{ > > > > + alloc, bindings, build_assert, build_error, > > > > + error::{Error, Result}, > > > > + init::PinInit, > > > > + pin_init, > > > > + types::{ForeignOwnable, NotThreadSafe, Opaque}, > > > > +}; > > > > +use core::{iter, marker::PhantomData, mem}; > > > > +use macros::{pin_data, pinned_drop}; > > > > > > I think these are in crate::prelude. > > > > I prefer to be explicit, unless there's guidance on this somewhere? > > I don't think I've ever seen anyone do a direct import from macros. This exact stanza is also in `rust/kernel/block/mq/tag_set.rs`. > > > > + fn iter(&self) -> impl Iterator<Item = core::ptr::NonNull<T::PointedTo>> + '_ { > > > > + // TODO: Remove when https://lore.kernel.org/all/20240913213041.395655-5-gary@garyguo.net/ is applied. > > > > + const MIN: core::ffi::c_ulong = core::ffi::c_ulong::MIN; > > > > + const MAX: core::ffi::c_ulong = core::ffi::c_ulong::MAX; > > > > > > Isn't MIN just zero? > > > > I liked the symmetry, but I could change it if you feel strongly. > > I commented because I thought it was confusing; I spent some time > figuring out whether the integer was signed. Alright, will replace with 0. > > > > + /// Erases an entry from the array. > > > > + /// > > > > + /// Returns the entry which was previously at the given index. > > > > + pub fn remove(&mut self, index: usize) -> Option<T> { > > > > + // SAFETY: `self.xa.xa` is always valid by the type invariant. > > > > + // > > > > + // SAFETY: The caller holds the lock. > > > > + let ptr = unsafe { bindings::__xa_erase(self.xa.xa.get(), to_index(index)) }.cast(); > > > > > > Two safety comments? > > > > There are two properties that must be upheld. How would you like to > > see it formatted? > > Usually multiple preconditions are listed using a bulleted list: > > // SAFETY: > // - `self.xa.xa` is always valid by the type invariant. > // - The caller holds the lock. Thanks for the suggestion, done. > > > > + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. > > > > + unsafe { T::try_from_foreign(ptr) } > > > > + } > > > > + > > > > + /// Stores an entry in the array. > > > > + /// > > > > + /// On success, returns the entry which was previously at the given index. > > > > + /// > > > > + /// On failure, returns the entry which was attempted to be stored. > > > > > > I'd like to see documentation about the gfp flags. This may unlock the > > > spinlock temporarily if GFP_KERNEL is used. > > > > Will add the language from the C documentation: "May drop the lock if > > needed to allocate memory, and then reacquire it afterwards." > > SGTM. > > > > > + // SAFETY: `__xa_store` returns the old entry at this index on success or `xa_err` if an > > > > + // error happened. > > > > + match unsafe { bindings::xa_err(old) } { > > > > + 0 => { > > > > + let old = old.cast(); > > > > + // SAFETY: `ptr` is either NULL or came from `T::into_foreign`. > > > > + Ok(unsafe { T::try_from_foreign(old) }) > > > > > > It can't be XA_ZERO_ENTRY? > > > > No it can't. XA_ZERO_ENTRY is never returned from the "normal" API. > > XA_ZERO_ENTRY presents as NULL. > > It's probably worth mentioning in the safety comment. Will do. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray 2024-12-03 15:00 ` Tamir Duberstein 2024-12-03 15:11 ` Alice Ryhl @ 2024-12-03 15:22 ` Miguel Ojeda 2024-12-03 16:01 ` Tamir Duberstein 1 sibling, 1 reply; 16+ messages in thread From: Miguel Ojeda @ 2024-12-03 15:22 UTC (permalink / raw) To: Tamir Duberstein Cc: Alice Ryhl, Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel On Tue, Dec 3, 2024 at 4:00 PM Tamir Duberstein <tamird@gmail.com> wrote: > > I prefer to be explicit, unless there's guidance on this somewhere? I like explicitness in general, but do you think there is an advantage for things in the prelude? I am asking because I am considering adding more things there, e.g. I have a series to be sent to add the `ffi::c_*` types -- having things like those in the prelude reduces the noise in imports that everyone knows about (and that nobody should be tempted to alias anyway), makes things more consistent (because then every use is unqualified vs. the mix we have now) and generally the code looks easier to read to focus on the other things that may be less common. Thanks! Cheers, Miguel ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray 2024-12-03 15:22 ` Miguel Ojeda @ 2024-12-03 16:01 ` Tamir Duberstein 0 siblings, 0 replies; 16+ messages in thread From: Tamir Duberstein @ 2024-12-03 16:01 UTC (permalink / raw) To: Miguel Ojeda Cc: Alice Ryhl, Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel On Tue, Dec 3, 2024 at 10:22 AM Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote: > > On Tue, Dec 3, 2024 at 4:00 PM Tamir Duberstein <tamird@gmail.com> wrote: > > > > I prefer to be explicit, unless there's guidance on this somewhere? > > I like explicitness in general, but do you think there is an advantage > for things in the prelude? > > I am asking because I am considering adding more things there, e.g. I > have a series to be sent to add the `ffi::c_*` types -- having things > like those in the prelude reduces the noise in imports that everyone > knows about (and that nobody should be tempted to alias anyway), makes > things more consistent (because then every use is unqualified vs. the > mix we have now) and generally the code looks easier to read to focus > on the other things that may be less common. I agree with these goals, but I personally disprefer the prelude approach. Probably the most common misuse involves traits in the prelude, where importing them can change function call resolution. It's very likely that this isn't a problem with the prelude currently, but it also doesn't seem to be used consistently everywhere. I don't feel very strongly, so if there's a mandate to use it then I'll do so. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v10 0/2] rust: xarray: Add a minimal abstraction for XArray 2024-11-20 11:48 ` [PATCH v10 0/2] rust: xarray: Add a minimal abstraction for XArray Tamir Duberstein 2024-11-20 11:48 ` [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` Tamir Duberstein 2024-11-20 11:48 ` [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray Tamir Duberstein @ 2024-11-25 7:58 ` Andreas Hindborg 2 siblings, 0 replies; 16+ messages in thread From: Andreas Hindborg @ 2024-11-25 7:58 UTC (permalink / raw) To: Tamir Duberstein Cc: Danilo Krummrich, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross, Maíra Canal, Asahi Lina, rust-for-linux, linux-kernel Hi Tamir, "Tamir Duberstein" <tamird@gmail.com> writes: > This is a reimagining relative to earlier versions[0] by Asahi Lina and > Maíra Canal. > > It is needed to support rust-binder, though this version only provides > enough machinery to support rnull. > > Link: https://lore.kernel.org/rust-for-linux/20240309235927.168915-2-mcanal@igalia.com/ [0] Thanks for picking this up! I am having some trouble with my benchmark system. When it comes back online, I'll run the `rnull` suite with these patches. Sorry for the delay. Best regards, Andreas Hindborg ^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2024-12-03 16:01 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <8afOg1gqssfIosuFD71f-eCLAHFSeGtzFU9qi-FzHX2T8kEkLMkk56HrWwUfVxoMCo8UlbKPaxjcJoixKhT8_g==@protonmail.internalid>
2024-11-20 11:48 ` [PATCH v10 0/2] rust: xarray: Add a minimal abstraction for XArray Tamir Duberstein
2024-11-20 11:48 ` [PATCH v10 1/2] rust: types: add `ForeignOwnable::PointedTo` Tamir Duberstein
2024-11-25 14:49 ` Alice Ryhl
2024-11-25 14:52 ` Tamir Duberstein
2024-11-25 15:33 ` Andreas Hindborg
2024-12-03 11:51 ` Andreas Hindborg
2024-11-20 11:48 ` [PATCH v10 2/2] rust: xarray: Add an abstraction for XArray Tamir Duberstein
2024-12-03 12:16 ` Andreas Hindborg
2024-12-03 15:00 ` Tamir Duberstein
2024-12-03 12:30 ` Alice Ryhl
2024-12-03 15:00 ` Tamir Duberstein
2024-12-03 15:11 ` Alice Ryhl
2024-12-03 15:57 ` Tamir Duberstein
2024-12-03 15:22 ` Miguel Ojeda
2024-12-03 16:01 ` Tamir Duberstein
2024-11-25 7:58 ` [PATCH v10 0/2] rust: xarray: Add a minimal " Andreas Hindborg
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).