rust: xarray: add entry API with preloading
Subsystems (6)
Andreas Hindborg
maintainerv2 cover rust: xarray: add entry API with preloading
This patch series is a mashup of cleanups, bugfixes and feature additions for the Rust XArray abstractions.
- Patch 1 starts by fixing minor formatting issues and bringing use statements up to date with the new coding guidelines.
- Patch 2-3 add some minor convenience functionality.
- Patch 4 adds an abstraction for the C xa_state structure. This is a prerequisite for all the subsequent patches.
- Patch 5 removes an unnecessary rcu lock in xarray::Guard::load.
- Patch 6 is a simplifying refactor of xarray::Guard::load.
- Patch 7 adds two new methods for finding items with keys that are larger than a given integer.
- Patch 8 adds an entry API.
- Patch 9-10 adds support for object caches based on sheafs.
- Patch 11 adds preloading to the new entry API.
The feature additions in this series are dependencies for the rust null block driver, most of which is still downstream.
Best regards,
Andreas
---
Changes in v2:
- Rebase on v6.19-rc8.
- Update the cover letter.
- Implement preloading with sheafs.
- Investigate generating RUST_CONST_HELPER_XAS_RESTART as pointer -> Not possible.
- Correct wording of commit message for patch "rust: xarray: use xas_load instead of xa_load in Guard::load".
- Correct wording of commit message for patch "rust: xarray: add find_next and find_next_mut".
- Remove last patch (lockdep static key fix) from series, to be sent separately.
- Expand note on why store to occupied slot cannot fail.
- Change signature of OccupiedEntry::swap to match core::mem::swap.
- Move // NOTEs about storing NULL closer to relevant checks.
- Move insert_internal to XArrayState.
- Share logic between find_next and find_next_entry.
- Rename XArray::get_entry to XArray::entry.
- Make load_next a method on XArrayState.
- Move load logic to XArrayState.
- Use PhantomData to capture lifetime of Guard for XArrayState.
- Link to v1: https://lore.kernel.org/r/[email protected]
---
Andreas Hindborg (11):
rust: xarray: minor formatting fixes
rust: xarray: add debug format for StoreError
rust: xarray: add contains_index method
rust: xarray: add XArrayState
rust: xarray: use xas_load instead of xa_load in Guard::load
rust: xarray: simplify Guard::load
rust: xarray: add find_next and find_next_mut
rust: xarray: add entry API
rust: mm: add abstractions for allocating from a sheaf
rust: mm: sheaf: allow use of C initialized static caches
rust: xarray: add preload API
MAINTAINERS | 1 + include/linux/radix-tree.h | 3 + lib/radix-tree.c | 5 +- mm/slub.c | 4 + rust/bindings/bindings_helper.h | 4 + rust/helpers/xarray.c | 17 + rust/kernel/mm.rs | 2 + rust/kernel/mm/sheaf.rs | 689 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/xarray.rs | 477 ++++++++++++++++++++++++++-- rust/kernel/xarray/entry.rs | 378 ++++++++++++++++++++++ rust/kernel/xarray/preload.rs | 3 + 11 files changed, 1549 insertions(+), 34 deletions(-)
---
base-commit: 18f7fcd5e69a04df57b563360b88be72471d6b62
change-id: 20251203-xarray-entry-send-00230f0744e6
Best regards,
Signature · 1 line
Andreas Hindborg <[email protected]>
Andreas Hindborg
maintainerPATCH v2 1/11 rust: xarray: minor formatting fixes
Fix formatting in xarray module to comply with kernel coding guidelines:
- Update use clauses to use vertical layout with each import on its own line.
- Add trailing empty comments to preserve formatting and prevent rustfmt from collapsing imports.
- Break long assert_eq! statement in documentation across multiple lines for better readability.
--- rust/kernel/xarray.rs | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-)
rust/kernel/xarray.rs +29 -7
@@ -4,14 +4,33 @@//! //! C header: [`include/linux/xarray.h`](srctree/include/linux/xarray.h) -use crate ::{ - alloc, bindings, build_assert, - error::{Error ,Result }, +use core::{ + iter, + marker::PhantomData , + pin::Pin , + ptr::NonNull ,// +}; +use kernel::{ + alloc, + bindings, + build_assert,// + error::{ +Error , +Result ,// + }, ffi::c_void, - types::{ForeignOwnable ,NotThreadSafe ,Opaque }, + types::{ +ForeignOwnable , +NotThreadSafe , +Opaque ,// + }, +}; +use pin_init::{ + pin_data, + pin_init, + pinned_drop, +PinInit ,// }; -use core::{iter, marker::PhantomData , 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. ///
@@ -44,7 +63,10 @@/// *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.store(0, beef, GFP_KERNEL)?.as_deref().copied(), +/// Some(0xffff) +/// ); /// assert_eq!(guard.get(0).copied(), Some(0xbeef)); /// /// guard.remove(0);
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 2/11 rust: xarray: add debug format for `StoreError`
In reply to Andreas HindborgAdd a Debug implementation for StoreError<T> to enable better error reporting and debugging. The implementation only displays the error field and omits the value field, as T may not implement Debug.
--- rust/kernel/xarray.rs | 8 ++++++++ 1 file changed, 8 insertions(+)
rust/kernel/xarray.rs +8 -0
@@ -193,6 +193,14 @@ pub struct StoreError<T> { pub value: T, } +impl <T > core::fmt::Debug for StoreError <T > { +fn fmt (&self, f: &mut core::fmt::Formatter <'_>) -> core::fmt::Result { + f.debug_struct ("StoreError" ) + .field ("error" , &self.error) + .finish () + } +} +impl <T >From <StoreError <T >>for Error {fn from (value:StoreError <T >) ->Self { value.error
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 3/11 rust: xarray: add `contains_index` method
In reply to Andreas HindborgAdd a convenience method contains_index to check whether an element exists at a given index in the XArray. This method provides a more ergonomic API compared to calling get and checking for Some.
--- rust/kernel/xarray.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
rust/kernel/xarray.rs +21 -0
@@ -218,6 +218,27 @@ fn load<F, U>(&self, index: usize, f: F) -> Option<U>Some (f (ptr)) } +/// Checks if the XArray contains an element at the specified index. +/// +/// # Examples +/// +/// ``` +/// # use kernel::{alloc::{flags::GFP_KERNEL, kbox::KBox}, xarray::{AllocKind, XArray}}; +/// let xa = KBox::pin_init(XArray::new(AllocKind::Alloc), GFP_KERNEL)?; +/// +/// let mut guard = xa.lock(); +/// assert_eq!(guard.contains_index(42), false); +/// +/// guard.store(42, KBox::new(0u32, GFP_KERNEL)?, GFP_KERNEL)?; +/// +/// assert_eq!(guard.contains_index(42), true); +/// +/// # Ok::<(), kernel::error::Error>(()) +/// ``` +pub fn contains_index (&self, index:usize ) ->bool { + self.get (index).is_some () + } +/// Provides a reference to the element at the given index. pub fn get (&self, index:usize ) ->Option <T ::Borrowed <'_>> { self.load (index, |ptr| {
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 4/11 rust: xarray: add `XArrayState`
In reply to Andreas HindborgAdd XArrayState as internal state for XArray iteration and entry operations. This struct wraps the C xa_state structure and holds a reference to a Guard to ensure exclusive access to the XArray for the lifetime of the state object.
The XAS_RESTART constant is also exposed through the bindings helper to properly initialize the xa_node field.
The struct and its constructor are marked with #[expect(dead_code)] as there are no users yet. We will remove this annotation in a later patch.
--- rust/bindings/bindings_helper.h | 1 + rust/kernel/xarray.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-)
rust/bindings/bindings_helper.h +1 -0
@@ -117,6 +117,7 @@ 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 ; +const size_t RUST_CONST_HELPER_XAS_RESTART = (size_t )XAS_RESTART ;const vm_flags_t RUST_CONST_HELPER_VM_MERGEABLE =VM_MERGEABLE ;const vm_flags_t RUST_CONST_HELPER_VM_READ =VM_READ ;
rust/kernel/xarray.rs +40 -1
53 lines not on this page · raw message
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 5/11 rust: xarray: use `xas_load` instead of `xa_load` in `Guard::load`
In reply to Andreas HindborgReplace the call to xa_load with xas_load in Guard::load. The xa_load function takes the RCU lock internally, which we do not need, since the Guard already holds an exclusive lock on the XArray. The xas_load function operates on xa_state and assumes the required locks are already held.
This change also removes the #[expect(dead_code)] annotation from XArrayState and its constructor, as they are now in use.
--- rust/kernel/xarray.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
rust/kernel/xarray.rs +9 -6
@@ -215,10 +215,8 @@ fn load<F, U>(&self, index: usize, f: F) -> Option<U> where F: FnOnce(NonNull<c_void>) -> 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)) +let mut state =XArrayState ::new (self, index); +Some (f (state.load ()?)) }/// Checks if the XArray contains an element at the specified index.
@@ -327,7 +325,6 @@ pub fn store(/// # Invariants /// /// - `state` is always a valid `bindings::xa_state`. -#[expect(dead_code)] pub (crate )struct XArrayState <'a, 'b,T :ForeignOwnable > {/// Holds a reference to the lock guard to ensure the lock is not dropped /// while `Self` is live.
@@ -336,7 +333,6 @@ pub(crate) struct XArrayState<'a, 'b, T: ForeignOwnable> { }impl <'a, 'b,T :ForeignOwnable >XArrayState <'a, 'b,T > { -#[expect(dead_code)] fn new (access: &'bGuard <'a,T >, index:usize ) ->Self {let ptr = access.xa.xa.get ();// INVARIANT: We initialize `self.state` to a valid value below.
@@ -356,6 +352,13 @@ fn new(access: &'b Guard<'a, T>, index: usize) -> Self { }, } } + +fn load (&mut self) ->Option <NonNull <c_void >> { +// SAFETY: `state.state` is always valid by the type invariant of +// `XArrayState and we hold the xarray lock`. +let ptr =unsafe { bindings::xas_load (&raw mut self.state) }; +NonNull ::new (ptr.cast ()) + } }// SAFETY: `XArray<T>` has no shared mutable state so it is `Send` iff `T` is `Send`.
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 6/11 rust: xarray: simplify `Guard::load`
In reply to Andreas HindborgSimplify the implementation by removing the closure-based API from Guard::load in favor of returning Option<NonNull<c_void>> directly.
--- rust/kernel/xarray.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-)
rust/kernel/xarray.rs +9 -14
@@ -211,12 +211,8 @@ fn from(value: StoreError<T>) -> Self { }impl <'a,T :ForeignOwnable >Guard <'a,T > { -fn load <F ,U >(&self, index:usize , f:F ) ->Option <U > -where -F :FnOnce (NonNull <c_void >) ->U , - { -let mut state =XArrayState ::new (self, index); -Some (f (state.load ()?)) +fn load (&self, index:usize ) ->Option <NonNull <c_void >> { +XArrayState ::new (self, index).load () }/// Checks if the XArray contains an element at the specified index.
@@ -242,18 +238,17 @@ pub fn contains_index(&self, index: usize) -> bool {/// 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 ()) } - }) +let ptr = self.load (index)?; +// SAFETY: `ptr` came from `T::into_foreign`. +Some (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 ()) } - }) +let ptr = self.load (index)?; + +// SAFETY: `ptr` came from `T::into_foreign`. +Some (unsafe {T ::borrow_mut (ptr.as_ptr ()) }) }/// Removes and returns the element at the given index.
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 7/11 rust: xarray: add `find_next` and `find_next_mut`
In reply to Andreas HindborgAdd methods to find the next element in an XArray starting from a given index. The methods return a tuple containing the index where the element was found and a reference to the element.
The implementation uses the XArray state API via xas_find to avoid taking the rcu lock as an exclusive lock is already held by Guard.
--- rust/kernel/xarray.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+)
rust/kernel/xarray.rs +68 -0
@@ -251,6 +251,67 @@ pub fn get_mut(&mut self, index: usize) -> Option<T::BorrowedMut<'_>> {Some (unsafe {T ::borrow_mut (ptr.as_ptr ()) }) } +fn load_next (&self, index:usize ) ->Option <(usize ,NonNull <c_void >)> { +XArrayState ::new (self, index).load_next () + } + +/// Finds the next element starting from the given index. +/// +/// # Examples +/// +/// ``` +/// # use kernel::{prelude::*, xarray::{AllocKind, XArray}}; +/// let mut xa = KBox::pin_init(XArray::<KBox<u32>>::new(AllocKind::Alloc), GFP_KERNEL)?; +/// let mut guard = xa.lock(); +/// +/// guard.store(10, KBox::new(10u32, GFP_KERNEL)?, GFP_KERNEL)?; +/// guard.store(20, KBox::new(20u32, GFP_KERNEL)?, GFP_KERNEL)?; +/// +/// if let Some((found_index, value)) = guard.find_next(11) { +/// assert_eq!(found_index, 20); +/// assert_eq!(*value, 20); +/// } +/// +/// if let Some((found_index, value)) = guard.find_next(5) { +/// assert_eq!(found_index, 10); +/// assert_eq!(*value, 10); +/// } +/// +/// # Ok::<(), kernel::error::Error>(()) +/// ``` +pub fn find_next (&self, index:usize ) ->Option <(usize ,T ::Borrowed <'_>)> { + self.load_next (index) +// SAFETY: `ptr` came from `T::into_foreign`. + .map (|(index, ptr)| (index,unsafe {T ::borrow (ptr.as_ptr ()) })) + } + +/// Finds the next element starting from the given index, returning a mutable reference. +/// +/// # Examples +/// +/// ``` +/// # use kernel::{prelude::*, xarray::{AllocKind, XArray}}; +/// let mut xa = KBox::pin_init(XArray::<KBox<u32>>::new(AllocKind::Alloc), GFP_KERNEL)?; +/// let mut guard = xa.lock(); +/// +/// guard.store(10, KBox::new(10u32, GFP_KERNEL)?, GFP_KERNEL)?; +/// guard.store(20, KBox::new(20u32, GFP_KERNEL)?, GFP_KERNEL)?; +/// +/// if let Some((found_index, mut_value)) = guard.find_next_mut(5) { +/// assert_eq!(found_index, 10); +/// *mut_value = 0x99; +/// } +/// +/// assert_eq!(guard.get(10).copied(), Some(0x99)); +/// +/// # Ok::<(), kernel::error::Error>(()) +/// ``` +pub fn find_next_mut (&mut self, index:usize ) ->Option <(usize ,T ::BorrowedMut <'_>)> { + self.load_next (index) +// SAFETY: `ptr` came from `T::into_foreign`. + .map (move |(index, ptr)| (index,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:
@@ -354,6 +415,13 @@ fn load(&mut self) -> Option<NonNull<c_void>> {let ptr =unsafe { bindings::xas_load (&raw mut self.state) };NonNull ::new (ptr.cast ()) } + +fn load_next (&mut self) ->Option <(usize ,NonNull <c_void >)> { +// SAFETY: `self.state` is always valid by the type invariant of +// `XArrayState` and the we hold the xarray lock. +let ptr =unsafe { bindings::xas_find (&raw mut self.state, usize::MAX ) }; +NonNull ::new (ptr).map (|ptr| (self.state.xa_index, ptr)) + } }// SAFETY: `XArray<T>` has no shared mutable state so it is `Send` iff `T` is `Send`.
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 8/11 rust: xarray: add entry API
In reply to Andreas HindborgAdd an Entry API for XArray that provides ergonomic access to array slots that may be vacant or occupied. The API follows the pattern of Rust's standard library HashMap entry API, allowing efficient conditional insertion and modification of entries.
The implementation uses the XArray state API (xas_* functions) for efficient operations without requiring multiple lookups. Helper functions are added to rust/helpers/xarray.c to wrap C macros that are not directly accessible from Rust.
Also update MAINTAINERS to cover the new rust files.
--- MAINTAINERS | 1 + rust/helpers/xarray.c | 17 ++ rust/kernel/xarray.rs | 123 +++++++++++++++ rust/kernel/xarray/entry.rs | 367 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 508 insertions(+)
MAINTAINERS +1 -0
@@ -28361,6 +28361,7 @@ 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 +F: rust/kernel/xarray/ XBOX DVD IR REMOTE M: Benjamin Valentin <[email protected]>
rust/helpers/xarray.c +17 -0
@@ -26,3 +26,20 @@ void rust_helper_xa_unlock(struct xarray *xa) {return xa_unlock (xa); } + +void *rust_helper_xas_result (struct xa_state *xas,void *curr) +{ +if (xa_err (xas->xa_node)) + curr = xas->xa_node; +return curr; +} + +void *rust_helper_xa_zero_to_null (void *entry) +{ +return xa_is_zero (entry) ?NULL : entry; +} + +int rust_helper_xas_error (const struct xa_state *xas) +{ +return xas_error (xas); +}
rust/kernel/xarray.rs +123 -0
154 lines not on this page · raw message
rust/kernel/xarray/entry.rs new file +367 -0
367 lines not on this page · raw message
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 9/11 rust: mm: add abstractions for allocating from a `sheaf`
In reply to Andreas HindborgAdd Rust APIs for allocating objects from a sheaf.
Introduce a reduced abstraction KMemCacheInit for struct kmem_cache to support management of the Sheafs.
Initialize objects using in-place initialization when objects are allocated from a Sheaf. This is different from C which tends to do some initialization when the cache is filled. This approach is chosen because there is no destructor/drop capability in struct kmem_cache that can be invoked when the cache is dropped.
--- rust/kernel/mm.rs | 2 + rust/kernel/mm/sheaf.rs | 406 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 408 insertions(+)
rust/kernel/mm.rs +2 -0
@@ -18,6 +18,8 @@ };use core::{ops::Deref , ptr::NonNull }; +#[cfg(not(any( CONFIG_SLUB_TINY , CONFIG_SLUB_DEBUG )))] +pub mod sheaf;pub mod virt;use virt::VmaRef ;
rust/kernel/mm/sheaf.rs new file +406 -0
406 lines not on this page · raw message
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 10/11 rust: mm: sheaf: allow use of C initialized static caches
In reply to Andreas HindborgExtend the sheaf abstraction to support caches initialized by C at kernel boot time, in addition to dynamically created Rust caches.
Introduce KMemCache<T> as a transparent wrapper around kmem_cache for static caches with 'static lifetime. Rename the previous KMemCache<T> to KMemCacheHandle<T> to represent dynamically created, reference-counted caches.
Add Static and Dynamic marker types along with StaticSheaf and DynamicSheaf type aliases to distinguish sheaves from each cache type. The Sheaf type now carries lifetime and allocation mode type parameters.
Add SBox::into_ptr() and SBox::static_from_ptr() methods for passing allocations through C code via raw pointers.
Add KMemCache::from_raw() for wrapping C-initialized static caches and Sheaf::refill() for replenishing a sheaf to a minimum size.
Export kmem_cache_prefill_sheaf, kmem_cache_return_sheaf, kmem_cache_refill_sheaf, and kmem_cache_alloc_from_sheaf_noprof to allow Rust module code to use the sheaf API.
--- mm/slub.c | 4 + rust/kernel/mm/sheaf.rs | 343 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 317 insertions(+), 30 deletions(-)
mm/slub.c +4 -0
@@ -5428,6 +5428,7 @@ kmem_cache_prefill_sheaf(struct kmem_cache *s, gfp_t gfp, unsigned int size)return sheaf; } +EXPORT_SYMBOL (kmem_cache_prefill_sheaf);/* * Use this to return a sheaf obtained by kmem_cache_prefill_sheaf()
@@ -5483,6 +5484,7 @@ void kmem_cache_return_sheaf(struct kmem_cache *s, gfp_t gfp,barn_put_full_sheaf (barn, sheaf);stat (s,BARN_PUT ); } +EXPORT_SYMBOL (kmem_cache_return_sheaf);/* * refill a sheaf previously returned by kmem_cache_prefill_sheaf to at least
@@ -5536,6 +5538,7 @@ int kmem_cache_refill_sheaf(struct kmem_cache *s, gfp_t gfp, *sheafp = sheaf;return 0 ; } +EXPORT_SYMBOL (kmem_cache_refill_sheaf);/* * Allocate from a sheaf obtained by kmem_cache_prefill_sheaf()
@@ -5573,6 +5576,7 @@ kmem_cache_alloc_from_sheaf_noprof(struct kmem_cache *s, gfp_t gfp,return ret; } +EXPORT_SYMBOL (kmem_cache_alloc_from_sheaf_noprof);unsigned int kmem_cache_sheaf_size (struct slab_sheaf *sheaf) {
rust/kernel/mm/sheaf.rs +313 -30
462 lines not on this page · raw message
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerPATCH v2 11/11 rust: xarray: add preload API
In reply to Andreas HindborgAdd a preload API that allows preallocating memory for XArray insertions. This enables insertions to proceed without allocation failures in contexts where memory allocation is not desirable, such as in atomic contexts or where reliability is critical.
The API includes:
- XArrayPreloadBuffer for managing a pool of preallocated nodes.
- XArrayPreloadNode representing a single preallocated node.
- Integration with the entry API, allowing VacantEntry::insert and VacantEntry::insert_entry to accept an optional preload buffer.
- A new Guard::insert_entry method for inserting with preload support.
The implementation uses a circular buffer to efficiently manage preallocated nodes. When an insertion would fail due to ENOMEM, the XArray state API automatically consumes a preallocated node from the buffer if available.
Export radix_tree_node_ctor from C to enable Rust code to work with the radix tree node cache.
--- include/linux/radix-tree.h | 3 + lib/radix-tree.c | 5 +- rust/bindings/bindings_helper.h | 3 + rust/kernel/xarray.rs | 172 +++++++++++++++++++++++++++++++++++----- rust/kernel/xarray/entry.rs | 29 ++++--- rust/kernel/xarray/preload.rs | 3 + 6 files changed, 185 insertions(+), 30 deletions(-)
include/linux/radix-tree.h +3 -0
@@ -469,4 +469,7 @@ static __always_inline void __rcu **radix_tree_next_slot(void __rcu **slot, slot = radix_tree_next_slot(slot, iter, \ RADIX_TREE_ITER_TAGGED | tag)) + +void radix_tree_node_ctor (void *arg); +#endif /* _LINUX_RADIX_TREE_H */
lib/radix-tree.c +3 -2
@@ -33,6 +33,7 @@* Radix tree node cache. */ struct kmem_cache *radix_tree_node_cachep; +EXPORT_SYMBOL (radix_tree_node_cachep);/* * The radix tree is variable-height, so an insert operation not only has
@@ -1566,14 +1567,14 @@ void idr_destroy(struct idr *idr) }EXPORT_SYMBOL (idr_destroy); -static void -radix_tree_node_ctor (void *arg) +void radix_tree_node_ctor (void *arg) {struct radix_tree_node *node = arg;memset (node,0 ,sizeof (*node));INIT_LIST_HEAD (&node->private_list); } +EXPORT_SYMBOL (radix_tree_node_ctor);static int radix_tree_cpu_dead (unsigned int cpu) {
rust/bindings/bindings_helper.h +3 -0
@@ -118,6 +118,9 @@ 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 ;const size_t RUST_CONST_HELPER_XAS_RESTART = (size_t )XAS_RESTART ; +const size_t RUST_CONST_HELPER_XA_CHUNK_SHIFT =XA_CHUNK_SHIFT ; +const size_t RUST_CONST_HELPER_XA_CHUNK_SIZE =XA_CHUNK_SIZE ; +extern struct kmem_cache *radix_tree_node_cachep;const vm_flags_t RUST_CONST_HELPER_VM_MERGEABLE =VM_MERGEABLE ;const vm_flags_t RUST_CONST_HELPER_VM_READ =VM_READ ;
rust/kernel/xarray.rs +153 -19
239 lines not on this page · raw message
rust/kernel/xarray/entry.rs +20 -9
@@ -3,6 +3,7 @@use super ::{Guard ,StoreError , +XArraySheaf ,XArrayState ,// };use core::ptr::NonNull ;
@@ -29,9 +30,9 @@ impl<T: ForeignOwnable> Entry<'_, '_, T> {/// let mut xa = KBox::pin_init(XArray::<KBox<u32>>::new(AllocKind::Alloc), GFP_KERNEL)?; /// let mut guard = xa.lock(); /// -/// /// let entry = guard.entry(42); /// assert_eq!(entry.is_occupied(), false); +/// drop(entry); /// /// guard.store(42, KBox::new(0x1337u32, GFP_KERNEL)?, GFP_KERNEL)?; /// let entry = guard.entry(42);
@@ -64,7 +65,8 @@ pub(crate) fn new(guard: &'b mut Guard<'a, T>, index: usize) -> Self {/// Returns a reference to the newly inserted value. /// /// - This method will fail if the nodes on the path to the index -/// represented by this entry are not present in the XArray. +/// represented by this entry are not present in the XArray and no memory +/// is available via the `preload` argument. /// - This method will not drop the XArray lock. /// ///
@@ -79,7 +81,7 @@ pub(crate) fn new(guard: &'b mut Guard<'a, T>, index: usize) -> Self {/// /// if let Entry::Vacant(entry) = guard.entry(42) { /// let value = KBox::new(0x1337u32, GFP_KERNEL)?; -/// let borrowed = entry.insert(value)?; +/// let borrowed = entry.insert(value, None)?; /// assert_eq!(*borrowed, 0x1337); /// } ///
@@ -87,8 +89,12 @@ pub(crate) fn new(guard: &'b mut Guard<'a, T>, index: usize) -> Self {/// /// # Ok::<(), kernel::error::Error>(()) /// ``` -pub fn insert (mut self, value:T ) ->Result <T ::BorrowedMut <'b>,StoreError <T >> { -let new = self.state.insert (value)?; +pub fn insert ( +mut self, + value:T , + preload:Option <&mut XArraySheaf <'_>>, + ) ->Result <T ::BorrowedMut <'b>,StoreError <T >> { +let new = self.state.insert (value, preload)?;// SAFETY: `new` came from `T::into_foreign`. The entry has exclusive // ownership of `new` as it holds a mutable reference to `Guard`.
@@ -98,7 +104,8 @@ pub fn insert(mut self, value: T) -> Result<T::BorrowedMut<'b>, StoreError<T>> {/// Inserts a value and returns an occupied entry representing the newly inserted value. /// /// - This method will fail if the nodes on the path to the index -/// represented by this entry are not present in the XArray. +/// represented by this entry are not present in the XArray and no memory +/// is available via the `preload` argument. /// - This method will not drop the XArray lock. /// /// # Examples
@@ -112,7 +119,7 @@ pub fn insert(mut self, value: T) -> Result<T::BorrowedMut<'b>, StoreError<T>> {/// /// if let Entry::Vacant(entry) = guard.entry(42) { /// let value = KBox::new(0x1337u32, GFP_KERNEL)?; -/// let occupied = entry.insert_entry(value)?; +/// let occupied = entry.insert_entry(value, None)?; /// assert_eq!(occupied.index(), 42); /// } ///
@@ -120,8 +127,12 @@ pub fn insert(mut self, value: T) -> Result<T::BorrowedMut<'b>, StoreError<T>> {/// /// # Ok::<(), kernel::error::Error>(()) /// ``` -pub fn insert_entry (mut self, value:T ) ->Result <OccupiedEntry <'a, 'b,T >,StoreError <T >> { -let new = self.state.insert (value)?; +pub fn insert_entry ( +mut self, + value:T , + preload:Option <&mut XArraySheaf <'_>>, + ) ->Result <OccupiedEntry <'a, 'b,T >,StoreError <T >> { +let new = self.state.insert (value, preload)?;Ok (OccupiedEntry ::<'a, 'b,T > { state: self.state,
rust/kernel/xarray/preload.rs new file +3 -0
@@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-2.0 + +use kernel::prelude::*;
Signature · 1 line
2.51.2
Andreas Hindborg
maintainerRe: [PATCH v2 11/11] rust: xarray: add preload API
Quoted 18 lines · Andreas Hindborg
Add a preload API that allows preallocating memory for XArray insertions. This enables insertions to proceed without allocation failures in contexts where memory allocation is not desirable, such as in atomic contexts or where reliability is critical.
The API includes:
-
XArrayPreloadBufferfor managing a pool of preallocated nodes.
-XArrayPreloadNoderepresenting a single preallocated node.
- Integration with the entry API, allowingVacantEntry::insertandVacantEntry::insert_entryto accept an optional preload buffer.
- A newGuard::insert_entrymethod for inserting with preload support.The implementation uses a circular buffer to efficiently manage preallocated nodes. When an insertion would fail due to ENOMEM, the XArray state API automatically consumes a preallocated node from the buffer if available.
Quoted 6 lines
Export
radix_tree_node_ctorfrom C to enable Rust code to work with the radix tree node cache.Cc "Liam R. Howlett" <[email protected]>Cc "Matthew Wilcox (Oracle)" <[email protected]>Signed-off-by Andreas Hindborg <[email protected]>
I somehow managed to not include this last bit of detail:
commit 09bdfa18f6f879eb42df2e032ad5224eed29eb25
Author: Andreas Hindborg <[email protected]> Date: Fri Feb 6 22:38:09 2026 +0100
radix-tree: enable sheaf suppport for kmem_cache
The rust null block driver plans to rely on preloading xarray nodes from the radix_tree_node_cachep kmem_cache.
lib/radix-tree.c +10 -4
@@ -1599,10 +1599,16 @@ void __init radix_tree_init(void)BUILD_BUG_ON (RADIX_TREE_MAX_TAGS + __GFP_BITS_SHIFT >32 );BUILD_BUG_ON (ROOT_IS_IDR & ~GFP_ZONEMASK );BUILD_BUG_ON (XA_CHUNK_SIZE >255 ); - radix_tree_node_cachep =kmem_cache_create ("radix_tree_node" , -sizeof (struct radix_tree_node ),0 , -SLAB_PANIC |SLAB_RECLAIM_ACCOUNT , - radix_tree_node_ctor); + +struct kmem_cache_args args = { + .ctor = radix_tree_node_ctor, + .sheaf_capacity =64 , + }; + + radix_tree_node_cachep =kmem_cache_create ( +"radix_tree_node" ,sizeof (struct radix_tree_node ), &args, +SLAB_PANIC |SLAB_RECLAIM_ACCOUNT ); + ret =cpuhp_setup_state_nocalls (CPUHP_RADIX_DEAD ,"lib/radix:dead" ,NULL , radix_tree_cpu_dead);WARN_ON (ret <0 );
Best regards,
Andreas Hindborg
kernel test robot
build botRe: [PATCH v2 11/11] rust: xarray: add preload API
In reply to Andreas Hindborgkernel test robot noticed the following build errors:
Hi Andreas,
kernel test robot noticed the following build errors:
[auto build test ERROR on 18f7fcd5e69a04df57b563360b88be72471d6b62]
url: https://github.com/intel-lab-lkp/linux/commits/Andreas-Hindborg/rust-xarray-minor-formatting-fixes/20260207-051500 base: 18f7fcd5e69a04df57b563360b88be72471d6b62 patch link: https://lore.kernel.org/r/20260206-xarray-entry-send-v2-11-91c41673fd30%40kernel.org patch subject: [PATCH v2 11/11] rust: xarray: add preload API config: arm64-randconfig-001-20260207 (https://download.01.org/0day-ci/archive/20260207/[email protected]/config) compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710) rustc: rustc 1.88.0 (6b00bc388 2025-06-23) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260207/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
>> error[E0432]: unresolved import `kernel::mm::sheaf`
--> rust/kernel/xarray.rs:33:9
|
33 | mm::sheaf::{
| ^^^^^ could not find sheaf in mm
|
note: found an item that was configured out
--> rust/kernel/mm.rs:22:9
|
22 | pub mod sheaf;
| ^^^^^
note: the item is gated here
--> rust/kernel/mm.rs:21:1
|
21 | #[cfg(not(any(CONFIG_SLUB_TINY, CONFIG_SLUB_DEBUG)))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--
>> error[E0433]: failed to resolve: could not find `sheaf` in `mm`
--> rust/kernel/xarray.rs:75:18
|
75 | impl kernel::mm::sheaf::KMemCacheInit<XArrayNode> for XArrayNode {
| ^^^^^ could not find sheaf in mm
|
note: found an item that was configured out
--> rust/kernel/mm.rs:22:9
|
22 | pub mod sheaf;
| ^^^^^
note: the item is gated here
--> rust/kernel/mm.rs:21:1
|
21 | #[cfg(not(any(CONFIG_SLUB_TINY, CONFIG_SLUB_DEBUG)))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki