linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Add Rust abstraction for Maple Trees
@ 2025-07-26 13:23 Alice Ryhl
  2025-07-26 13:23 ` [PATCH 1/3] rust: maple_tree: add MapleTree Alice Ryhl
                   ` (3 more replies)
  0 siblings, 4 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-07-26 13:23 UTC (permalink / raw)
  To: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance
  Cc: Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm, Alice Ryhl

This will be used in the Tyr driver [1] to allocate from the GPU's VA
space that is not owned by userspace, but by the kernel, for kernel GPU
mappings.

Danilo tells me that in nouveau, the maple tree is used for keeping
track of "VM regions" on top of GPUVM, and that he will most likely end
up doing the same in the Rust Nova driver as well.

These abstractions intentionally do not expose any way to make use of
external locking. You are required to use the internal spinlock. For
now, we do not support loads that only utilize rcu for protection.

This contains some parts taken from Andrew Ballance's RFC [2] from
April. However, it has also been reworked significantly compared to that
RFC taking the use-cases in Tyr into account.

[1]: https://lore.kernel.org/r/20250627-tyr-v1-1-cb5f4c6ced46@collabora.com
[2]: https://lore.kernel.org/r/20250405060154.1550858-1-andrewjballance@gmail.com

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
Alice Ryhl (3):
      rust: maple_tree: add MapleTree
      rust: maple_tree: add MapleTree::lock() and load()
      rust: maple_tree: add MapleTreeAlloc

 MAINTAINERS               |   2 +
 rust/helpers/helpers.c    |   1 +
 rust/helpers/maple_tree.c |  14 ++
 rust/kernel/lib.rs        |   1 +
 rust/kernel/maple_tree.rs | 538 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 556 insertions(+)
---
base-commit: dff64b072708ffef23c117fa1ee1ea59eb417807
change-id: 20250726-maple-tree-1af0803ac524

Best regards,
-- 
Alice Ryhl <aliceryhl@google.com>



^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 1/3] rust: maple_tree: add MapleTree
  2025-07-26 13:23 [PATCH 0/3] Add Rust abstraction for Maple Trees Alice Ryhl
@ 2025-07-26 13:23 ` Alice Ryhl
  2025-07-26 15:45   ` Gary Guo
                     ` (3 more replies)
  2025-07-26 13:23 ` [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load() Alice Ryhl
                   ` (2 subsequent siblings)
  3 siblings, 4 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-07-26 13:23 UTC (permalink / raw)
  To: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance
  Cc: Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm, Alice Ryhl

The maple tree will be used in the Tyr driver to allocate and keep track
of GPU allocations created internally (i.e. not by userspace). It will
likely also be used in the Nova driver eventually.

This adds the simplest methods for additional and removal that do not
require any special care with respect to concurrency.

This implementation is based on the RFC by Andrew but with significant
changes to simplify the implementation.

Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 MAINTAINERS               |   2 +
 rust/helpers/helpers.c    |   1 +
 rust/helpers/maple_tree.c |  14 +++
 rust/kernel/lib.rs        |   1 +
 rust/kernel/maple_tree.rs | 286 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 304 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index dd810da5261b5d664ef9750f66ec022412e8014b..b7e7308ce07c050239c14c4f3a0fd89bdd8e8796 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15956,7 +15956,9 @@ L:	rust-for-linux@vger.kernel.org
 S:	Maintained
 W:	http://www.linux-mm.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
+F:	rust/helpers/maple_tree.c
 F:	rust/helpers/mm.c
+F:	rust/kernel/maple_tree.rs
 F:	rust/kernel/mm.rs
 F:	rust/kernel/mm/
 
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 0683fffdbde25b89d285e3b0d8e6d8f7f5fd7474..ed7888a2661ad91f0fb78023311b3a266d30615c 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -26,6 +26,7 @@
 #include "io.c"
 #include "jump_label.c"
 #include "kunit.c"
+#include "maple_tree.c"
 #include "mm.c"
 #include "mutex.c"
 #include "page.c"
diff --git a/rust/helpers/maple_tree.c b/rust/helpers/maple_tree.c
new file mode 100644
index 0000000000000000000000000000000000000000..119665846e8e8b018f8dc791a22fe20ace8e9c2c
--- /dev/null
+++ b/rust/helpers/maple_tree.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/maple_tree.h>
+
+void rust_helper_mt_init_flags(struct maple_tree *mt, unsigned int flags)
+{
+	mt_init_flags(mt, flags);
+}
+
+struct ma_state rust_helper_MA_STATE(struct maple_tree *mt, unsigned long start, unsigned long end)
+{
+	MA_STATE(mas, mt, start, end);
+	return mas;
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 11a6461e98daab597e1eb2b513c5123686a1bb73..6cc152090a2f1986781800897ad48947c2d02e40 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -88,6 +88,7 @@
 #[cfg(CONFIG_KUNIT)]
 pub mod kunit;
 pub mod list;
+pub mod maple_tree;
 pub mod miscdevice;
 pub mod mm;
 #[cfg(CONFIG_NET)]
diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c
--- /dev/null
+++ b/rust/kernel/maple_tree.rs
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Maple trees.
+//!
+//! C header: [`include/linux/maple_tree.h`](srctree/include/linux/maple_tree.h)
+//!
+//! Reference: <https://docs.kernel.org/core-api/maple_tree.html>
+
+use core::{
+    marker::PhantomData,
+    ops::{Bound, RangeBounds},
+};
+
+use kernel::{
+    alloc::Flags,
+    error::code::{EEXIST, ENOMEM},
+    error::to_result,
+    prelude::*,
+    types::{ForeignOwnable, Opaque},
+};
+
+/// A maple tree optimized for storing non-overlapping ranges.
+///
+/// # Invariants
+///
+/// Each range in the maple tree owns an instance of `T`.
+#[pin_data(PinnedDrop)]
+#[repr(transparent)]
+pub struct MapleTree<T: ForeignOwnable> {
+    #[pin]
+    tree: Opaque<bindings::maple_tree>,
+    _p: PhantomData<T>,
+}
+
+#[inline]
+fn to_maple_range(range: impl RangeBounds<usize>) -> Option<(usize, usize)> {
+    let first = match range.start_bound() {
+        Bound::Included(start) => *start,
+        Bound::Excluded(start) => start.checked_add(1)?,
+        Bound::Unbounded => 0,
+    };
+
+    let last = match range.end_bound() {
+        Bound::Included(end) => *end,
+        Bound::Excluded(end) => end.checked_sub(1)?,
+        Bound::Unbounded => usize::MAX,
+    };
+
+    if last < first {
+        return None;
+    }
+
+    Some((first, last))
+}
+
+impl<T: ForeignOwnable> MapleTree<T> {
+    /// Create a new maple tree.
+    ///
+    /// The tree will use the regular implementation with a higher branching factor.
+    #[inline]
+    pub fn new() -> impl PinInit<Self> {
+        pin_init!(MapleTree {
+            // SAFETY: This initializes a maple tree into a pinned slot. The maple tree will be
+            // destroyed in Drop before the memory location becomes invalid.
+            tree <- Opaque::ffi_init(|slot| unsafe { bindings::mt_init_flags(slot, 0) }),
+            _p: PhantomData,
+        })
+    }
+
+    /// Insert the value at the given index.
+    ///
+    /// If the maple tree already contains a range using the given index, then this call will fail.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
+    ///
+    /// let tree = KBox::pin_init(MapleTree::<KBox<i32>>::new(), GFP_KERNEL)?;
+    ///
+    /// let ten = KBox::new(10, GFP_KERNEL)?;
+    /// let twenty = KBox::new(20, GFP_KERNEL)?;
+    /// let the_answer = KBox::new(42, GFP_KERNEL)?;
+    ///
+    /// // These calls will succeed.
+    /// tree.insert(100, ten, GFP_KERNEL)?;
+    /// tree.insert(101, twenty, GFP_KERNEL)?;
+    ///
+    /// // This will fail because the index is already in use.
+    /// assert_eq!(
+    ///     tree.insert(100, the_answer, GFP_KERNEL).unwrap_err().cause,
+    ///     InsertErrorKind::Occupied,
+    /// );
+    /// # Ok::<_, Error>(())
+    /// ```
+    #[inline]
+    pub fn insert(&self, index: usize, value: T, gfp: Flags) -> Result<(), InsertError<T>> {
+        self.insert_range(index..=index, value, gfp)
+    }
+
+    /// Insert a value to the specified range, failing on overlap.
+    ///
+    /// This accepts the usual types of Rust ranges using the `..` and `..=` syntax for exclusive
+    /// and inclusive ranges respectively. The range must not be empty, and must not overlap with
+    /// any existing range.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
+    ///
+    /// let tree = KBox::pin_init(MapleTree::<KBox<i32>>::new(), GFP_KERNEL)?;
+    ///
+    /// let ten = KBox::new(10, GFP_KERNEL)?;
+    /// let twenty = KBox::new(20, GFP_KERNEL)?;
+    /// let the_answer = KBox::new(42, GFP_KERNEL)?;
+    /// let hundred = KBox::new(100, GFP_KERNEL)?;
+    ///
+    /// // Insert the value 10 at the indices 100 to 499.
+    /// tree.insert_range(100..500, ten, GFP_KERNEL)?;
+    ///
+    /// // Insert the value 20 at the indices 500 to 1000.
+    /// tree.insert_range(500..=1000, twenty, GFP_KERNEL)?;
+    ///
+    /// // This will fail due to overlap with the previous range on index 1000.
+    /// assert_eq!(
+    ///     tree.insert_range(1000..1200, the_answer, GFP_KERNEL).unwrap_err().cause,
+    ///     InsertErrorKind::Occupied,
+    /// );
+    ///
+    /// // When using .. to specify the range, you must be careful to ensure that the range is
+    /// // non-empty.
+    /// assert_eq!(
+    ///     tree.insert_range(72..72, hundred, GFP_KERNEL).unwrap_err().cause,
+    ///     InsertErrorKind::InvalidRequest,
+    /// );
+    /// # Ok::<_, Error>(())
+    /// ```
+    pub fn insert_range<R>(&self, range: R, value: T, gfp: Flags) -> Result<(), InsertError<T>>
+    where
+        R: RangeBounds<usize>,
+    {
+        let Some((first, last)) = to_maple_range(range) else {
+            return Err(InsertError {
+                value,
+                cause: InsertErrorKind::InvalidRequest,
+            });
+        };
+
+        let ptr = T::into_foreign(value);
+
+        // SAFETY: The tree is valid, and we are passing a pointer to an owned instance of `T`.
+        let res = to_result(unsafe {
+            bindings::mtree_insert_range(self.tree.get(), first, last, ptr, gfp.as_raw())
+        });
+
+        if let Err(err) = res {
+            // SAFETY: As `mtree_insert_range` failed, it is safe to take back ownership.
+            let value = unsafe { T::from_foreign(ptr) };
+
+            let cause = if err == ENOMEM {
+                InsertErrorKind::Nomem
+            } else if err == EEXIST {
+                InsertErrorKind::Occupied
+            } else {
+                InsertErrorKind::InvalidRequest
+            };
+            Err(InsertError { value, cause })
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Erase the range containing the given index.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
+    ///
+    /// let tree = KBox::pin_init(MapleTree::<KBox<i32>>::new(), GFP_KERNEL)?;
+    ///
+    /// let ten = KBox::new(10, GFP_KERNEL)?;
+    /// let twenty = KBox::new(20, GFP_KERNEL)?;
+    ///
+    /// tree.insert_range(100..500, ten, GFP_KERNEL)?;
+    /// tree.insert(67, twenty, GFP_KERNEL)?;
+    ///
+    /// let twenty = tree.erase(67).unwrap();
+    /// assert_eq!(*twenty, 20);
+    ///
+    /// let ten = tree.erase(275).unwrap();
+    /// assert_eq!(*ten, 10);
+    ///
+    /// // The previous call erased the entire range, not just index 275.
+    /// assert!(tree.erase(127).is_none());
+    /// # Ok::<_, Error>(())
+    /// ```
+    #[inline]
+    pub fn erase(&self, index: usize) -> Option<T> {
+        // SAFETY: `self.tree` contains a valid maple tree.
+        let ret = unsafe { bindings::mtree_erase(self.tree.get(), index) };
+
+        // SAFETY: If the pointer is not null, then we took ownership of a valid instance of `T`
+        // from the tree.
+        unsafe { T::try_from_foreign(ret) }
+    }
+
+    /// Free all `T` instances in this tree.
+    ///
+    /// # Safety
+    ///
+    /// This frees Rust data referenced by the maple tree without removing it from the maple tree.
+    /// The caller must ensure that no reference that remains in the maple tree is used incorrectly
+    /// after this call.
+    unsafe fn free_all_entries(self: Pin<&mut Self>) {
+        // SAFETY: The pointer references a valid maple tree.
+        let ma_state = unsafe { Opaque::new(bindings::MA_STATE(self.tree.get(), 0, usize::MAX)) };
+
+        loop {
+            // SAFETY: The maple tree is valid. This call to `free_all_entries` has exclusive
+            // access to the maple tree, so no further synchronization is required.
+            let ptr = unsafe { bindings::mas_find(ma_state.get(), usize::MAX) };
+            if ptr.is_null() {
+                break;
+            }
+            // SAFETY: By the type invariants, this pointer references a valid value of type `T`.
+            // By the safety requirements, it is okay to free it without removing it from the maple
+            // tree.
+            unsafe { drop(T::from_foreign(ptr)) };
+        }
+    }
+}
+
+#[pinned_drop]
+impl<T: ForeignOwnable> PinnedDrop for MapleTree<T> {
+    #[inline]
+    fn drop(mut self: Pin<&mut Self>) {
+        // We only iterate the tree if the Rust value have a destructor.
+        if core::mem::needs_drop::<T>() {
+            // SAFETY: The tree is valid, and other than the below `mtree_destroy` call, it will
+            // not be accessed after this call.
+            unsafe { self.as_mut().free_all_entries() };
+        }
+
+        // SAFETY: The tree is valid, and will not be accessed after this call.
+        unsafe { bindings::mtree_destroy(self.tree.get()) };
+    }
+}
+
+/// Error type for failure to insert a new value.
+pub struct InsertError<T> {
+    /// The value that could not be inserted.
+    pub value: T,
+    /// The reason for the failure to insert.
+    pub cause: InsertErrorKind,
+}
+
+/// The reason for the failure to insert.
+#[derive(PartialEq, Eq, Copy, Clone)]
+pub enum InsertErrorKind {
+    /// There is already a value in the requested range.
+    Occupied,
+    /// Failure to allocate memory.
+    Nomem,
+    /// The insertion request was invalid.
+    InvalidRequest,
+}
+
+impl From<InsertErrorKind> for Error {
+    #[inline]
+    fn from(kind: InsertErrorKind) -> Error {
+        match kind {
+            InsertErrorKind::Occupied => EEXIST,
+            InsertErrorKind::Nomem => ENOMEM,
+            InsertErrorKind::InvalidRequest => EINVAL,
+        }
+    }
+}
+
+impl<T> From<InsertError<T>> for Error {
+    #[inline]
+    fn from(insert_err: InsertError<T>) -> Error {
+        Error::from(insert_err.cause)
+    }
+}

-- 
2.50.1.470.g6ba607880d-goog



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-26 13:23 [PATCH 0/3] Add Rust abstraction for Maple Trees Alice Ryhl
  2025-07-26 13:23 ` [PATCH 1/3] rust: maple_tree: add MapleTree Alice Ryhl
@ 2025-07-26 13:23 ` Alice Ryhl
  2025-07-26 15:50   ` Gary Guo
                     ` (3 more replies)
  2025-07-26 13:23 ` [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc Alice Ryhl
  2025-08-06 19:24 ` [PATCH 0/3] Add Rust abstraction for Maple Trees Liam R. Howlett
  3 siblings, 4 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-07-26 13:23 UTC (permalink / raw)
  To: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance
  Cc: Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm, Alice Ryhl

To load a value, one must be careful to hold the lock while accessing
it. To enable this, we add a lock() method so that you can perform
operations on the value before the spinlock is released.

Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/kernel/maple_tree.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
index 0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c..c7ef504a9c78065b3d5752b4f5337fb6277182d1 100644
--- a/rust/kernel/maple_tree.rs
+++ b/rust/kernel/maple_tree.rs
@@ -206,6 +206,23 @@ pub fn erase(&self, index: usize) -> Option<T> {
         unsafe { T::try_from_foreign(ret) }
     }
 
+    /// Lock the internal spinlock.
+    #[inline]
+    pub fn lock(&self) -> MapleLock<'_, T> {
+        // SAFETY: It's safe to lock the spinlock in a maple tree.
+        unsafe { bindings::spin_lock(self.ma_lock()) };
+
+        // INVARIANT: We just took the spinlock.
+        MapleLock(self)
+    }
+
+    #[inline]
+    fn ma_lock(&self) -> *mut bindings::spinlock_t {
+        // SAFETY: This pointer offset operation stays in-bounds.
+        let lock = unsafe { &raw mut (*self.tree.get()).__bindgen_anon_1.ma_lock };
+        lock.cast()
+    }
+
     /// Free all `T` instances in this tree.
     ///
     /// # Safety
@@ -248,6 +265,83 @@ fn drop(mut self: Pin<&mut Self>) {
     }
 }
 
+/// A reference to a [`MapleTree`] that owns the inner lock.
+///
+/// # Invariants
+///
+/// This guard owns the inner spinlock.
+pub struct MapleLock<'tree, T: ForeignOwnable>(&'tree MapleTree<T>);
+
+impl<'tree, T: ForeignOwnable> Drop for MapleLock<'tree, T> {
+    #[inline]
+    fn drop(&mut self) {
+        // SAFETY: By the type invariants, we hold this spinlock.
+        unsafe { bindings::spin_unlock(self.0.ma_lock()) };
+    }
+}
+
+impl<'tree, T: ForeignOwnable> MapleLock<'tree, T> {
+    /// Load the value at the given index.
+    ///
+    /// # Examples
+    ///
+    /// Read the value while holding the spinlock.
+    ///
+    /// ```
+    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
+    ///
+    /// let tree = KBox::pin_init(MapleTree::<KBox<i32>>::new(), GFP_KERNEL)?;
+    ///
+    /// let ten = KBox::new(10, GFP_KERNEL)?;
+    /// let twenty = KBox::new(20, GFP_KERNEL)?;
+    /// tree.insert(100, ten, GFP_KERNEL)?;
+    /// tree.insert(200, twenty, GFP_KERNEL)?;
+    ///
+    /// let mut lock = tree.lock();
+    /// assert_eq!(lock.load(100), Some(&mut 10));
+    /// assert_eq!(lock.load(200), Some(&mut 20));
+    /// assert_eq!(lock.load(300), None);
+    /// # Ok::<_, Error>(())
+    /// ```
+    ///
+    /// Increment refcount while holding spinlock and read afterwards.
+    ///
+    /// ```
+    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
+    /// use kernel::sync::Arc;
+    ///
+    /// let tree = KBox::pin_init(MapleTree::<Arc<i32>>::new(), GFP_KERNEL)?;
+    ///
+    /// let ten = Arc::new(10, GFP_KERNEL)?;
+    /// let twenty = Arc::new(20, GFP_KERNEL)?;
+    /// tree.insert(100, ten, GFP_KERNEL)?;
+    /// tree.insert(200, twenty, GFP_KERNEL)?;
+    ///
+    /// // Briefly take the lock to increment the refcount.
+    /// let value = Arc::from(tree.lock().load(100).unwrap());
+    ///
+    /// // At this point, another thread might remove the value.
+    /// tree.erase(100);
+    ///
+    /// // But we can still access it because we took a refcount.
+    /// assert_eq!(*value, 10);
+    /// # Ok::<_, Error>(())
+    /// ```
+    #[inline]
+    pub fn load(&mut self, index: usize) -> Option<T::BorrowedMut<'_>> {
+        // SAFETY: `self.tree` contains a valid maple tree.
+        let ret = unsafe { bindings::mtree_load(self.0.tree.get(), index) };
+        if ret.is_null() {
+            return None;
+        }
+
+        // SAFETY: If the pointer is not null, then it references a valid instance of `T`. It is
+        // safe to borrow the instance mutably because the signature of this function enforces that
+        // the mutable borrow is not used after the spinlock is dropped.
+        Some(unsafe { T::borrow_mut(ret) })
+    }
+}
+
 /// Error type for failure to insert a new value.
 pub struct InsertError<T> {
     /// The value that could not be inserted.

-- 
2.50.1.470.g6ba607880d-goog



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc
  2025-07-26 13:23 [PATCH 0/3] Add Rust abstraction for Maple Trees Alice Ryhl
  2025-07-26 13:23 ` [PATCH 1/3] rust: maple_tree: add MapleTree Alice Ryhl
  2025-07-26 13:23 ` [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load() Alice Ryhl
@ 2025-07-26 13:23 ` Alice Ryhl
  2025-07-26 15:54   ` Gary Guo
  2025-08-07 16:29   ` Liam R. Howlett
  2025-08-06 19:24 ` [PATCH 0/3] Add Rust abstraction for Maple Trees Liam R. Howlett
  3 siblings, 2 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-07-26 13:23 UTC (permalink / raw)
  To: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance
  Cc: Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm, Alice Ryhl

To support allocation trees, we introduce a new type MapleTreeAlloc for
the case where the tree is created using MT_FLAGS_ALLOC_RANGE. To ensure
that you can only call mtree_alloc_range on an allocation tree, we
restrict thta method to the new MapleTreeAlloc type. However, all
methods on MapleTree remain accessible to MapleTreeAlloc as allocation
trees can use the other methods without issues.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/kernel/maple_tree.rs | 158 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 158 insertions(+)

diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
index c7ef504a9c78065b3d5752b4f5337fb6277182d1..8c025d2c395b6d57f1fb16214b4e87d4e7942d6f 100644
--- a/rust/kernel/maple_tree.rs
+++ b/rust/kernel/maple_tree.rs
@@ -32,6 +32,26 @@ pub struct MapleTree<T: ForeignOwnable> {
     _p: PhantomData<T>,
 }
 
+/// A maple tree with `MT_FLAGS_ALLOC_RANGE` set.
+///
+/// All methods on [`MapleTree`] are also accessible on this type.
+#[pin_data]
+#[repr(transparent)]
+pub struct MapleTreeAlloc<T: ForeignOwnable> {
+    #[pin]
+    tree: MapleTree<T>,
+}
+
+// Make MapleTree methods usable on MapleTreeAlloc.
+impl<T: ForeignOwnable> core::ops::Deref for MapleTreeAlloc<T> {
+    type Target = MapleTree<T>;
+
+    #[inline]
+    fn deref(&self) -> &MapleTree<T> {
+        &self.tree
+    }
+}
+
 #[inline]
 fn to_maple_range(range: impl RangeBounds<usize>) -> Option<(usize, usize)> {
     let first = match range.start_bound() {
@@ -342,6 +362,107 @@ pub fn load(&mut self, index: usize) -> Option<T::BorrowedMut<'_>> {
     }
 }
 
+impl<T: ForeignOwnable> MapleTreeAlloc<T> {
+    /// Create a new allocation tree.
+    pub fn new() -> impl PinInit<Self> {
+        let tree = pin_init!(MapleTree {
+            // SAFETY: This initializes a maple tree into a pinned slot. The maple tree will be
+            // destroyed in Drop before the memory location becomes invalid.
+            tree <- Opaque::ffi_init(|slot| unsafe {
+                bindings::mt_init_flags(slot, bindings::MT_FLAGS_ALLOC_RANGE)
+            }),
+            _p: PhantomData,
+        });
+
+        pin_init!(MapleTreeAlloc { tree <- tree })
+    }
+
+    /// Insert an entry with the given size somewhere in the given range.
+    ///
+    /// The maple tree will search for a location in the given range where there is space to insert
+    /// the new range. If there is not enough available space, then an error will be returned.
+    ///
+    /// The index of the new range is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::maple_tree::{MapleTreeAlloc, AllocErrorKind};
+    ///
+    /// let tree = KBox::pin_init(MapleTreeAlloc::<KBox<i32>>::new(), GFP_KERNEL)?;
+    ///
+    /// let ten = KBox::new(10, GFP_KERNEL)?;
+    /// let twenty = KBox::new(20, GFP_KERNEL)?;
+    /// let thirty = KBox::new(30, GFP_KERNEL)?;
+    /// let hundred = KBox::new(100, GFP_KERNEL)?;
+    ///
+    /// // Allocate three ranges.
+    /// let idx1 = tree.alloc_range(100, ten, ..1000, GFP_KERNEL)?;
+    /// let idx2 = tree.alloc_range(100, twenty, ..1000, GFP_KERNEL)?;
+    /// let idx3 = tree.alloc_range(100, thirty, ..1000, GFP_KERNEL)?;
+    ///
+    /// assert_eq!(idx1, 0);
+    /// assert_eq!(idx2, 100);
+    /// assert_eq!(idx3, 200);
+    ///
+    /// // This will fail because the remaining space is too small.
+    /// assert_eq!(
+    ///     tree.alloc_range(800, hundred, ..1000, GFP_KERNEL).unwrap_err().cause,
+    ///     AllocErrorKind::Busy,
+    /// );
+    /// # Ok::<_, Error>(())
+    /// ```
+    pub fn alloc_range<R>(
+        &self,
+        size: usize,
+        value: T,
+        range: R,
+        gfp: Flags,
+    ) -> Result<usize, AllocError<T>>
+    where
+        R: RangeBounds<usize>,
+    {
+        let Some((min, max)) = to_maple_range(range) else {
+            return Err(AllocError {
+                value,
+                cause: AllocErrorKind::InvalidRequest,
+            });
+        };
+
+        let ptr = T::into_foreign(value);
+        let mut index = 0;
+
+        // SAFETY: The tree is valid, and we are passing a pointer to an owned instance of `T`.
+        let res = to_result(unsafe {
+            bindings::mtree_alloc_range(
+                self.tree.tree.get(),
+                &mut index,
+                ptr,
+                size,
+                min,
+                max,
+                gfp.as_raw(),
+            )
+        });
+
+        if let Err(err) = res {
+            // SAFETY: As `mtree_alloc_range` failed, it is safe to take back ownership.
+            let value = unsafe { T::from_foreign(ptr) };
+
+            let cause = if err == ENOMEM {
+                AllocErrorKind::Nomem
+            } else if err == EBUSY {
+                AllocErrorKind::Busy
+            } else {
+                AllocErrorKind::InvalidRequest
+            };
+            Err(AllocError { value, cause })
+        } else {
+            Ok(index)
+        }
+    }
+}
+
 /// Error type for failure to insert a new value.
 pub struct InsertError<T> {
     /// The value that could not be inserted.
@@ -378,3 +499,40 @@ fn from(insert_err: InsertError<T>) -> Error {
         Error::from(insert_err.cause)
     }
 }
+
+/// Error type for failure to insert a new value.
+pub struct AllocError<T> {
+    /// The value that could not be inserted.
+    pub value: T,
+    /// The reason for the failure to insert.
+    pub cause: AllocErrorKind,
+}
+
+/// The reason for the failure to insert.
+#[derive(PartialEq, Eq, Copy, Clone)]
+pub enum AllocErrorKind {
+    /// There is not enough space for the requested allocation.
+    Busy,
+    /// Failure to allocate memory.
+    Nomem,
+    /// The insertion request was invalid.
+    InvalidRequest,
+}
+
+impl From<AllocErrorKind> for Error {
+    #[inline]
+    fn from(kind: AllocErrorKind) -> Error {
+        match kind {
+            AllocErrorKind::Busy => EBUSY,
+            AllocErrorKind::Nomem => ENOMEM,
+            AllocErrorKind::InvalidRequest => EINVAL,
+        }
+    }
+}
+
+impl<T> From<AllocError<T>> for Error {
+    #[inline]
+    fn from(insert_err: AllocError<T>) -> Error {
+        Error::from(insert_err.cause)
+    }
+}

-- 
2.50.1.470.g6ba607880d-goog



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] rust: maple_tree: add MapleTree
  2025-07-26 13:23 ` [PATCH 1/3] rust: maple_tree: add MapleTree Alice Ryhl
@ 2025-07-26 15:45   ` Gary Guo
  2025-08-19  9:09     ` Alice Ryhl
  2025-07-26 16:23   ` Matthew Wilcox
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 27+ messages in thread
From: Gary Guo @ 2025-07-26 15:45 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng,  Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, 26 Jul 2025 13:23:22 +0000
Alice Ryhl <aliceryhl@google.com> wrote:

> The maple tree will be used in the Tyr driver to allocate and keep track
> of GPU allocations created internally (i.e. not by userspace). It will
> likely also be used in the Nova driver eventually.
> 
> This adds the simplest methods for additional and removal that do not
> require any special care with respect to concurrency.
> 
> This implementation is based on the RFC by Andrew but with significant
> changes to simplify the implementation.
> 
> Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>

Overall looks good to me, some nits and thoughts about the error type
below.

Best,
GAry

> ---
>  MAINTAINERS               |   2 +
>  rust/helpers/helpers.c    |   1 +
>  rust/helpers/maple_tree.c |  14 +++
>  rust/kernel/lib.rs        |   1 +
>  rust/kernel/maple_tree.rs | 286 ++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 304 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index dd810da5261b5d664ef9750f66ec022412e8014b..b7e7308ce07c050239c14c4f3a0fd89bdd8e8796 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15956,7 +15956,9 @@ L:	rust-for-linux@vger.kernel.org
>  S:	Maintained
>  W:	http://www.linux-mm.org
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
> +F:	rust/helpers/maple_tree.c
>  F:	rust/helpers/mm.c
> +F:	rust/kernel/maple_tree.rs
>  F:	rust/kernel/mm.rs
>  F:	rust/kernel/mm/
>  
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 0683fffdbde25b89d285e3b0d8e6d8f7f5fd7474..ed7888a2661ad91f0fb78023311b3a266d30615c 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -26,6 +26,7 @@
>  #include "io.c"
>  #include "jump_label.c"
>  #include "kunit.c"
> +#include "maple_tree.c"
>  #include "mm.c"
>  #include "mutex.c"
>  #include "page.c"
> diff --git a/rust/helpers/maple_tree.c b/rust/helpers/maple_tree.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..119665846e8e8b018f8dc791a22fe20ace8e9c2c
> --- /dev/null
> +++ b/rust/helpers/maple_tree.c
> @@ -0,0 +1,14 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/maple_tree.h>
> +
> +void rust_helper_mt_init_flags(struct maple_tree *mt, unsigned int flags)
> +{
> +	mt_init_flags(mt, flags);
> +}
> +
> +struct ma_state rust_helper_MA_STATE(struct maple_tree *mt, unsigned long start, unsigned long end)
> +{
> +	MA_STATE(mas, mt, start, end);
> +	return mas;
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 11a6461e98daab597e1eb2b513c5123686a1bb73..6cc152090a2f1986781800897ad48947c2d02e40 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -88,6 +88,7 @@
>  #[cfg(CONFIG_KUNIT)]
>  pub mod kunit;
>  pub mod list;
> +pub mod maple_tree;
>  pub mod miscdevice;
>  pub mod mm;
>  #[cfg(CONFIG_NET)]
> diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> new file mode 100644
> index 0000000000000000000000000000000000000000..0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c
> --- /dev/null
> +++ b/rust/kernel/maple_tree.rs
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Maple trees.
> +//!
> +//! C header: [`include/linux/maple_tree.h`](srctree/include/linux/maple_tree.h)
> +//!
> +//! Reference: <https://docs.kernel.org/core-api/maple_tree.html>
> +
> +use core::{
> +    marker::PhantomData,
> +    ops::{Bound, RangeBounds},
> +};
> +
> +use kernel::{
> +    alloc::Flags,
> +    error::code::{EEXIST, ENOMEM},
> +    error::to_result,
> +    prelude::*,
> +    types::{ForeignOwnable, Opaque},
> +};
> +
> +/// A maple tree optimized for storing non-overlapping ranges.
> +///
> +/// # Invariants
> +///
> +/// Each range in the maple tree owns an instance of `T`.
> +#[pin_data(PinnedDrop)]
> +#[repr(transparent)]
> +pub struct MapleTree<T: ForeignOwnable> {
> +    #[pin]
> +    tree: Opaque<bindings::maple_tree>,
> +    _p: PhantomData<T>,
> +}
> +
> +#[inline]
> +fn to_maple_range(range: impl RangeBounds<usize>) -> Option<(usize, usize)> {
> +    let first = match range.start_bound() {
> +        Bound::Included(start) => *start,
> +        Bound::Excluded(start) => start.checked_add(1)?,
> +        Bound::Unbounded => 0,
> +    };
> +
> +    let last = match range.end_bound() {
> +        Bound::Included(end) => *end,
> +        Bound::Excluded(end) => end.checked_sub(1)?,
> +        Bound::Unbounded => usize::MAX,
> +    };
> +
> +    if last < first {
> +        return None;
> +    }
> +
> +    Some((first, last))
> +}
> +
> +impl<T: ForeignOwnable> MapleTree<T> {
> +    /// Create a new maple tree.
> +    ///
> +    /// The tree will use the regular implementation with a higher branching factor.
> +    #[inline]
> +    pub fn new() -> impl PinInit<Self> {
> +        pin_init!(MapleTree {
> +            // SAFETY: This initializes a maple tree into a pinned slot. The maple tree will be
> +            // destroyed in Drop before the memory location becomes invalid.
> +            tree <- Opaque::ffi_init(|slot| unsafe { bindings::mt_init_flags(slot, 0) }),
> +            _p: PhantomData,
> +        })
> +    }
> +
> +    /// Insert the value at the given index.
> +    ///
> +    /// If the maple tree already contains a range using the given index, then this call will fail.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
> +    ///
> +    /// let tree = KBox::pin_init(MapleTree::<KBox<i32>>::new(), GFP_KERNEL)?;
> +    ///
> +    /// let ten = KBox::new(10, GFP_KERNEL)?;
> +    /// let twenty = KBox::new(20, GFP_KERNEL)?;
> +    /// let the_answer = KBox::new(42, GFP_KERNEL)?;
> +    ///
> +    /// // These calls will succeed.
> +    /// tree.insert(100, ten, GFP_KERNEL)?;
> +    /// tree.insert(101, twenty, GFP_KERNEL)?;
> +    ///
> +    /// // This will fail because the index is already in use.
> +    /// assert_eq!(
> +    ///     tree.insert(100, the_answer, GFP_KERNEL).unwrap_err().cause,
> +    ///     InsertErrorKind::Occupied,
> +    /// );
> +    /// # Ok::<_, Error>(())
> +    /// ```
> +    #[inline]
> +    pub fn insert(&self, index: usize, value: T, gfp: Flags) -> Result<(), InsertError<T>> {
> +        self.insert_range(index..=index, value, gfp)
> +    }
> +
> +    /// Insert a value to the specified range, failing on overlap.
> +    ///
> +    /// This accepts the usual types of Rust ranges using the `..` and `..=` syntax for exclusive
> +    /// and inclusive ranges respectively. The range must not be empty, and must not overlap with
> +    /// any existing range.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
> +    ///
> +    /// let tree = KBox::pin_init(MapleTree::<KBox<i32>>::new(), GFP_KERNEL)?;
> +    ///
> +    /// let ten = KBox::new(10, GFP_KERNEL)?;
> +    /// let twenty = KBox::new(20, GFP_KERNEL)?;
> +    /// let the_answer = KBox::new(42, GFP_KERNEL)?;
> +    /// let hundred = KBox::new(100, GFP_KERNEL)?;
> +    ///
> +    /// // Insert the value 10 at the indices 100 to 499.
> +    /// tree.insert_range(100..500, ten, GFP_KERNEL)?;
> +    ///
> +    /// // Insert the value 20 at the indices 500 to 1000.
> +    /// tree.insert_range(500..=1000, twenty, GFP_KERNEL)?;
> +    ///
> +    /// // This will fail due to overlap with the previous range on index 1000.
> +    /// assert_eq!(
> +    ///     tree.insert_range(1000..1200, the_answer, GFP_KERNEL).unwrap_err().cause,
> +    ///     InsertErrorKind::Occupied,
> +    /// );
> +    ///
> +    /// // When using .. to specify the range, you must be careful to ensure that the range is
> +    /// // non-empty.
> +    /// assert_eq!(
> +    ///     tree.insert_range(72..72, hundred, GFP_KERNEL).unwrap_err().cause,
> +    ///     InsertErrorKind::InvalidRequest,
> +    /// );
> +    /// # Ok::<_, Error>(())
> +    /// ```
> +    pub fn insert_range<R>(&self, range: R, value: T, gfp: Flags) -> Result<(), InsertError<T>>
> +    where
> +        R: RangeBounds<usize>,
> +    {
> +        let Some((first, last)) = to_maple_range(range) else {
> +            return Err(InsertError {
> +                value,
> +                cause: InsertErrorKind::InvalidRequest,
> +            });
> +        };
> +
> +        let ptr = T::into_foreign(value);
> +
> +        // SAFETY: The tree is valid, and we are passing a pointer to an owned instance of `T`.
> +        let res = to_result(unsafe {
> +            bindings::mtree_insert_range(self.tree.get(), first, last, ptr, gfp.as_raw())
> +        });
> +
> +        if let Err(err) = res {
> +            // SAFETY: As `mtree_insert_range` failed, it is safe to take back ownership.
> +            let value = unsafe { T::from_foreign(ptr) };
> +
> +            let cause = if err == ENOMEM {
> +                InsertErrorKind::Nomem
> +            } else if err == EEXIST {
> +                InsertErrorKind::Occupied
> +            } else {
> +                InsertErrorKind::InvalidRequest
> +            };
> +            Err(InsertError { value, cause })
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Erase the range containing the given index.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
> +    ///
> +    /// let tree = KBox::pin_init(MapleTree::<KBox<i32>>::new(), GFP_KERNEL)?;
> +    ///
> +    /// let ten = KBox::new(10, GFP_KERNEL)?;
> +    /// let twenty = KBox::new(20, GFP_KERNEL)?;
> +    ///
> +    /// tree.insert_range(100..500, ten, GFP_KERNEL)?;
> +    /// tree.insert(67, twenty, GFP_KERNEL)?;
> +    ///
> +    /// let twenty = tree.erase(67).unwrap();
> +    /// assert_eq!(*twenty, 20);
> +    ///
> +    /// let ten = tree.erase(275).unwrap();
> +    /// assert_eq!(*ten, 10);
> +    ///
> +    /// // The previous call erased the entire range, not just index 275.
> +    /// assert!(tree.erase(127).is_none());
> +    /// # Ok::<_, Error>(())
> +    /// ```
> +    #[inline]
> +    pub fn erase(&self, index: usize) -> Option<T> {
> +        // SAFETY: `self.tree` contains a valid maple tree.
> +        let ret = unsafe { bindings::mtree_erase(self.tree.get(), index) };
> +
> +        // SAFETY: If the pointer is not null, then we took ownership of a valid instance of `T`
> +        // from the tree.
> +        unsafe { T::try_from_foreign(ret) }
> +    }
> +
> +    /// Free all `T` instances in this tree.
> +    ///
> +    /// # Safety
> +    ///
> +    /// This frees Rust data referenced by the maple tree without removing it from the maple tree.
> +    /// The caller must ensure that no reference that remains in the maple tree is used incorrectly
> +    /// after this call.
> +    unsafe fn free_all_entries(self: Pin<&mut Self>) {
> +        // SAFETY: The pointer references a valid maple tree.
> +        let ma_state = unsafe { Opaque::new(bindings::MA_STATE(self.tree.get(), 0, usize::MAX)) };
> +
> +        loop {
> +            // SAFETY: The maple tree is valid. This call to `free_all_entries` has exclusive
> +            // access to the maple tree, so no further synchronization is required.
> +            let ptr = unsafe { bindings::mas_find(ma_state.get(), usize::MAX) };
> +            if ptr.is_null() {
> +                break;
> +            }
> +            // SAFETY: By the type invariants, this pointer references a valid value of type `T`.
> +            // By the safety requirements, it is okay to free it without removing it from the maple
> +            // tree.
> +            unsafe { drop(T::from_foreign(ptr)) };
> +        }
> +    }
> +}
> +
> +#[pinned_drop]
> +impl<T: ForeignOwnable> PinnedDrop for MapleTree<T> {
> +    #[inline]
> +    fn drop(mut self: Pin<&mut Self>) {
> +        // We only iterate the tree if the Rust value have a destructor.
> +        if core::mem::needs_drop::<T>() {
> +            // SAFETY: The tree is valid, and other than the below `mtree_destroy` call, it will
> +            // not be accessed after this call.
> +            unsafe { self.as_mut().free_all_entries() };
> +        }
> +
> +        // SAFETY: The tree is valid, and will not be accessed after this call.
> +        unsafe { bindings::mtree_destroy(self.tree.get()) };
> +    }
> +}
> +
> +/// Error type for failure to insert a new value.
> +pub struct InsertError<T> {
> +    /// The value that could not be inserted.
> +    pub value: T,
> +    /// The reason for the failure to insert.
> +    pub cause: InsertErrorKind,
> +}

Hmm, we've already have quite a few errors that look like this, e.g.
`StoreError`. I wonder if we should just have a generic

    struct ErrroWithData<T, E> {
       pub value: T,
       pub cause: E,
    }

> +
> +/// The reason for the failure to insert.
> +#[derive(PartialEq, Eq, Copy, Clone)]
> +pub enum InsertErrorKind {
> +    /// There is already a value in the requested range.
> +    Occupied,
> +    /// Failure to allocate memory.
> +    Nomem,

Given that we already have an error type for allocation failure, how
about

    AllocError(crate::alloc::AllocError)

? I know we're getting ENOMEM from C, but this would match what the
error type would be if it were from pure Rust code.


> +    /// The insertion request was invalid.
> +    InvalidRequest,
> +}
> +
> +impl From<InsertErrorKind> for Error {
> +    #[inline]
> +    fn from(kind: InsertErrorKind) -> Error {
> +        match kind {
> +            InsertErrorKind::Occupied => EEXIST,
> +            InsertErrorKind::Nomem => ENOMEM,
> +            InsertErrorKind::InvalidRequest => EINVAL,
> +        }
> +    }
> +}
> +
> +impl<T> From<InsertError<T>> for Error {
> +    #[inline]
> +    fn from(insert_err: InsertError<T>) -> Error {
> +        Error::from(insert_err.cause)
> +    }
> +}
> 



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-26 13:23 ` [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load() Alice Ryhl
@ 2025-07-26 15:50   ` Gary Guo
  2025-07-26 16:15     ` Alice Ryhl
  2025-07-28 11:11   ` Andrew Ballance
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 27+ messages in thread
From: Gary Guo @ 2025-07-26 15:50 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng,  Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, 26 Jul 2025 13:23:23 +0000
Alice Ryhl <aliceryhl@google.com> wrote:

> To load a value, one must be careful to hold the lock while accessing
> it. To enable this, we add a lock() method so that you can perform
> operations on the value before the spinlock is released.
> 
> Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
>  rust/kernel/maple_tree.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 94 insertions(+)
> 
> diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> index 0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c..c7ef504a9c78065b3d5752b4f5337fb6277182d1 100644
> --- a/rust/kernel/maple_tree.rs
> +++ b/rust/kernel/maple_tree.rs
> @@ -206,6 +206,23 @@ pub fn erase(&self, index: usize) -> Option<T> {
>          unsafe { T::try_from_foreign(ret) }
>      }
>  
> +    /// Lock the internal spinlock.
> +    #[inline]
> +    pub fn lock(&self) -> MapleLock<'_, T> {
> +        // SAFETY: It's safe to lock the spinlock in a maple tree.
> +        unsafe { bindings::spin_lock(self.ma_lock()) };
> +
> +        // INVARIANT: We just took the spinlock.
> +        MapleLock(self)
> +    }
> +
> +    #[inline]
> +    fn ma_lock(&self) -> *mut bindings::spinlock_t {
> +        // SAFETY: This pointer offset operation stays in-bounds.
> +        let lock = unsafe { &raw mut (*self.tree.get()).__bindgen_anon_1.ma_lock };
> +        lock.cast()
> +    }

Could this return `&SpinLock<()>` using `Lock::from_raw`?

I guess it has the drawback of having `MapleLock` needing to store
`ma_lock` pointer but the guard is usually just all on stack and
inlined so it probably won't make a difference?

This way you remove `unsafe` from `lock` and gets a free `drop`.

> +
>      /// Free all `T` instances in this tree.
>      ///
>      /// # Safety
> @@ -248,6 +265,83 @@ fn drop(mut self: Pin<&mut Self>) {
>      }
>  }
>  
> +/// A reference to a [`MapleTree`] that owns the inner lock.
> +///
> +/// # Invariants
> +///
> +/// This guard owns the inner spinlock.
> +pub struct MapleLock<'tree, T: ForeignOwnable>(&'tree MapleTree<T>);
> +
> +impl<'tree, T: ForeignOwnable> Drop for MapleLock<'tree, T> {
> +    #[inline]
> +    fn drop(&mut self) {
> +        // SAFETY: By the type invariants, we hold this spinlock.
> +        unsafe { bindings::spin_unlock(self.0.ma_lock()) };
> +    }
> +}


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc
  2025-07-26 13:23 ` [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc Alice Ryhl
@ 2025-07-26 15:54   ` Gary Guo
  2025-07-26 16:13     ` Alice Ryhl
  2025-08-07 16:29   ` Liam R. Howlett
  1 sibling, 1 reply; 27+ messages in thread
From: Gary Guo @ 2025-07-26 15:54 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng,  Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, 26 Jul 2025 13:23:24 +0000
Alice Ryhl <aliceryhl@google.com> wrote:

> To support allocation trees, we introduce a new type MapleTreeAlloc for
> the case where the tree is created using MT_FLAGS_ALLOC_RANGE. To ensure
> that you can only call mtree_alloc_range on an allocation tree, we
> restrict thta method to the new MapleTreeAlloc type. However, all
> methods on MapleTree remain accessible to MapleTreeAlloc as allocation
> trees can use the other methods without issues.
> 
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
>  rust/kernel/maple_tree.rs | 158 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 158 insertions(+)
> 
> diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> index c7ef504a9c78065b3d5752b4f5337fb6277182d1..8c025d2c395b6d57f1fb16214b4e87d4e7942d6f 100644
> --- a/rust/kernel/maple_tree.rs
> +++ b/rust/kernel/maple_tree.rs
>
>  /// Error type for failure to insert a new value.
>  pub struct InsertError<T> {
>      /// The value that could not be inserted.
> @@ -378,3 +499,40 @@ fn from(insert_err: InsertError<T>) -> Error {
>          Error::from(insert_err.cause)
>      }
>  }
> +
> +/// Error type for failure to insert a new value.
> +pub struct AllocError<T> {
> +    /// The value that could not be inserted.
> +    pub value: T,
> +    /// The reason for the failure to insert.
> +    pub cause: AllocErrorKind,
> +}
> +
> +/// The reason for the failure to insert.
> +#[derive(PartialEq, Eq, Copy, Clone)]
> +pub enum AllocErrorKind {
> +    /// There is not enough space for the requested allocation.
> +    Busy,
> +    /// Failure to allocate memory.
> +    Nomem,
> +    /// The insertion request was invalid.
> +    InvalidRequest,
> +}
> +
> +impl From<AllocErrorKind> for Error {
> +    #[inline]
> +    fn from(kind: AllocErrorKind) -> Error {
> +        match kind {
> +            AllocErrorKind::Busy => EBUSY,
> +            AllocErrorKind::Nomem => ENOMEM,
> +            AllocErrorKind::InvalidRequest => EINVAL,
> +        }
> +    }
> +}
> +
> +impl<T> From<AllocError<T>> for Error {
> +    #[inline]
> +    fn from(insert_err: AllocError<T>) -> Error {
> +        Error::from(insert_err.cause)
> +    }
> +}
> 

This looks identical to `InsertError`?

Best,
Gary


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc
  2025-07-26 15:54   ` Gary Guo
@ 2025-07-26 16:13     ` Alice Ryhl
  0 siblings, 0 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-07-26 16:13 UTC (permalink / raw)
  To: Gary Guo
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, Jul 26, 2025 at 5:54 PM Gary Guo <gary@garyguo.net> wrote:
>
> On Sat, 26 Jul 2025 13:23:24 +0000
> Alice Ryhl <aliceryhl@google.com> wrote:
>
> > To support allocation trees, we introduce a new type MapleTreeAlloc for
> > the case where the tree is created using MT_FLAGS_ALLOC_RANGE. To ensure
> > that you can only call mtree_alloc_range on an allocation tree, we
> > restrict thta method to the new MapleTreeAlloc type. However, all
> > methods on MapleTree remain accessible to MapleTreeAlloc as allocation
> > trees can use the other methods without issues.
> >
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > ---
> >  rust/kernel/maple_tree.rs | 158 ++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 158 insertions(+)
> >
> > diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> > index c7ef504a9c78065b3d5752b4f5337fb6277182d1..8c025d2c395b6d57f1fb16214b4e87d4e7942d6f 100644
> > --- a/rust/kernel/maple_tree.rs
> > +++ b/rust/kernel/maple_tree.rs
> >
> >  /// Error type for failure to insert a new value.
> >  pub struct InsertError<T> {
> >      /// The value that could not be inserted.
> > @@ -378,3 +499,40 @@ fn from(insert_err: InsertError<T>) -> Error {
> >          Error::from(insert_err.cause)
> >      }
> >  }
> > +
> > +/// Error type for failure to insert a new value.
> > +pub struct AllocError<T> {
> > +    /// The value that could not be inserted.
> > +    pub value: T,
> > +    /// The reason for the failure to insert.
> > +    pub cause: AllocErrorKind,
> > +}
> > +
> > +/// The reason for the failure to insert.
> > +#[derive(PartialEq, Eq, Copy, Clone)]
> > +pub enum AllocErrorKind {
> > +    /// There is not enough space for the requested allocation.
> > +    Busy,
> > +    /// Failure to allocate memory.
> > +    Nomem,
> > +    /// The insertion request was invalid.
> > +    InvalidRequest,
> > +}
> > +
> > +impl From<AllocErrorKind> for Error {
> > +    #[inline]
> > +    fn from(kind: AllocErrorKind) -> Error {
> > +        match kind {
> > +            AllocErrorKind::Busy => EBUSY,
> > +            AllocErrorKind::Nomem => ENOMEM,
> > +            AllocErrorKind::InvalidRequest => EINVAL,
> > +        }
> > +    }
> > +}
> > +
> > +impl<T> From<AllocError<T>> for Error {
> > +    #[inline]
> > +    fn from(insert_err: AllocError<T>) -> Error {
> > +        Error::from(insert_err.cause)
> > +    }
> > +}
> >
>
> This looks identical to `InsertError`?

They differ on whether the error code is EBUSY or EEXIST.

Alice


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-26 15:50   ` Gary Guo
@ 2025-07-26 16:15     ` Alice Ryhl
  2025-07-26 16:18       ` Alice Ryhl
  0 siblings, 1 reply; 27+ messages in thread
From: Alice Ryhl @ 2025-07-26 16:15 UTC (permalink / raw)
  To: Gary Guo
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, Jul 26, 2025 at 5:50 PM Gary Guo <gary@garyguo.net> wrote:
>
> On Sat, 26 Jul 2025 13:23:23 +0000
> Alice Ryhl <aliceryhl@google.com> wrote:
>
> > To load a value, one must be careful to hold the lock while accessing
> > it. To enable this, we add a lock() method so that you can perform
> > operations on the value before the spinlock is released.
> >
> > Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> > Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > ---
> >  rust/kernel/maple_tree.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 94 insertions(+)
> >
> > diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> > index 0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c..c7ef504a9c78065b3d5752b4f5337fb6277182d1 100644
> > --- a/rust/kernel/maple_tree.rs
> > +++ b/rust/kernel/maple_tree.rs
> > @@ -206,6 +206,23 @@ pub fn erase(&self, index: usize) -> Option<T> {
> >          unsafe { T::try_from_foreign(ret) }
> >      }
> >
> > +    /// Lock the internal spinlock.
> > +    #[inline]
> > +    pub fn lock(&self) -> MapleLock<'_, T> {
> > +        // SAFETY: It's safe to lock the spinlock in a maple tree.
> > +        unsafe { bindings::spin_lock(self.ma_lock()) };
> > +
> > +        // INVARIANT: We just took the spinlock.
> > +        MapleLock(self)
> > +    }
> > +
> > +    #[inline]
> > +    fn ma_lock(&self) -> *mut bindings::spinlock_t {
> > +        // SAFETY: This pointer offset operation stays in-bounds.
> > +        let lock = unsafe { &raw mut (*self.tree.get()).__bindgen_anon_1.ma_lock };
> > +        lock.cast()
> > +    }
>
> Could this return `&SpinLock<()>` using `Lock::from_raw`?
>
> I guess it has the drawback of having `MapleLock` needing to store
> `ma_lock` pointer but the guard is usually just all on stack and
> inlined so it probably won't make a difference?
>
> This way you remove `unsafe` from `lock` and gets a free `drop`.

I ended up going this way to avoid the extra field in MapleLock, like
you mention.

Alice


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-26 16:15     ` Alice Ryhl
@ 2025-07-26 16:18       ` Alice Ryhl
  2025-07-27 12:02         ` Gary Guo
  0 siblings, 1 reply; 27+ messages in thread
From: Alice Ryhl @ 2025-07-26 16:18 UTC (permalink / raw)
  To: Gary Guo
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, Jul 26, 2025 at 6:15 PM Alice Ryhl <aliceryhl@google.com> wrote:
>
> On Sat, Jul 26, 2025 at 5:50 PM Gary Guo <gary@garyguo.net> wrote:
> >
> > On Sat, 26 Jul 2025 13:23:23 +0000
> > Alice Ryhl <aliceryhl@google.com> wrote:
> >
> > > To load a value, one must be careful to hold the lock while accessing
> > > it. To enable this, we add a lock() method so that you can perform
> > > operations on the value before the spinlock is released.
> > >
> > > Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> > > Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> > > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > > ---
> > >  rust/kernel/maple_tree.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 94 insertions(+)
> > >
> > > diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> > > index 0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c..c7ef504a9c78065b3d5752b4f5337fb6277182d1 100644
> > > --- a/rust/kernel/maple_tree.rs
> > > +++ b/rust/kernel/maple_tree.rs
> > > @@ -206,6 +206,23 @@ pub fn erase(&self, index: usize) -> Option<T> {
> > >          unsafe { T::try_from_foreign(ret) }
> > >      }
> > >
> > > +    /// Lock the internal spinlock.
> > > +    #[inline]
> > > +    pub fn lock(&self) -> MapleLock<'_, T> {
> > > +        // SAFETY: It's safe to lock the spinlock in a maple tree.
> > > +        unsafe { bindings::spin_lock(self.ma_lock()) };
> > > +
> > > +        // INVARIANT: We just took the spinlock.
> > > +        MapleLock(self)
> > > +    }
> > > +
> > > +    #[inline]
> > > +    fn ma_lock(&self) -> *mut bindings::spinlock_t {
> > > +        // SAFETY: This pointer offset operation stays in-bounds.
> > > +        let lock = unsafe { &raw mut (*self.tree.get()).__bindgen_anon_1.ma_lock };
> > > +        lock.cast()
> > > +    }
> >
> > Could this return `&SpinLock<()>` using `Lock::from_raw`?
> >
> > I guess it has the drawback of having `MapleLock` needing to store
> > `ma_lock` pointer but the guard is usually just all on stack and
> > inlined so it probably won't make a difference?
> >
> > This way you remove `unsafe` from `lock` and gets a free `drop`.
>
> I ended up going this way to avoid the extra field in MapleLock, like
> you mention.

Oh, and it also avoids assuming anything about the layout of SpinLock<()>

Alice


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] rust: maple_tree: add MapleTree
  2025-07-26 13:23 ` [PATCH 1/3] rust: maple_tree: add MapleTree Alice Ryhl
  2025-07-26 15:45   ` Gary Guo
@ 2025-07-26 16:23   ` Matthew Wilcox
  2025-07-26 16:41     ` Alice Ryhl
  2025-07-28 16:04   ` Boqun Feng
  2025-08-07 16:12   ` Liam R. Howlett
  3 siblings, 1 reply; 27+ messages in thread
From: Matthew Wilcox @ 2025-07-26 16:23 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	linux-kernel, maple-tree, rust-for-linux, linux-mm

On Sat, Jul 26, 2025 at 01:23:22PM +0000, Alice Ryhl wrote:
> +struct ma_state rust_helper_MA_STATE(struct maple_tree *mt, unsigned long start, unsigned long end)
> +{
> +	MA_STATE(mas, mt, start, end);
> +	return mas;
> +}

This seems very inefficient.  Returning a struct larger than two words
(on x86 anyway) means that the compiler implements this as:

void rust_helper_MA_STATE(struct ma_state *masp, ...)
{
	MA_STATE(mas, mt, start, end);
	*masp = mas;
}

so that's about 72 bytes being memcpy'd per access to the maple tree.
Sure, it's stack, so it's cache hot, but surely we can implement
the equivalent of MA_STATE in Rust and see a significant performance
win, at least on read operations.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] rust: maple_tree: add MapleTree
  2025-07-26 16:23   ` Matthew Wilcox
@ 2025-07-26 16:41     ` Alice Ryhl
  0 siblings, 0 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-07-26 16:41 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	linux-kernel, maple-tree, rust-for-linux, linux-mm

On Sat, Jul 26, 2025 at 6:23 PM Matthew Wilcox <willy@infradead.org> wrote:
>
> On Sat, Jul 26, 2025 at 01:23:22PM +0000, Alice Ryhl wrote:
> > +struct ma_state rust_helper_MA_STATE(struct maple_tree *mt, unsigned long start, unsigned long end)
> > +{
> > +     MA_STATE(mas, mt, start, end);
> > +     return mas;
> > +}
>
> This seems very inefficient.  Returning a struct larger than two words
> (on x86 anyway) means that the compiler implements this as:
>
> void rust_helper_MA_STATE(struct ma_state *masp, ...)
> {
>         MA_STATE(mas, mt, start, end);
>         *masp = mas;
> }
>
> so that's about 72 bytes being memcpy'd per access to the maple tree.
> Sure, it's stack, so it's cache hot, but surely we can implement
> the equivalent of MA_STATE in Rust and see a significant performance
> win, at least on read operations.

Some of the methods go through the mtree_* functions that create the
MA_STATE on the C side, so this isn't always the case.

Regardless, we definitely can avoid this helper. It has the
consequence that if MA_STATE is changed in the future, then the Rust
version of the method must also be changed. I can add a comment to
that effect to the header file to remind people of that.

Alice


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-26 16:18       ` Alice Ryhl
@ 2025-07-27 12:02         ` Gary Guo
  2025-08-07 16:15           ` Liam R. Howlett
  0 siblings, 1 reply; 27+ messages in thread
From: Gary Guo @ 2025-07-27 12:02 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, 26 Jul 2025 18:18:02 +0200
Alice Ryhl <aliceryhl@google.com> wrote:

> On Sat, Jul 26, 2025 at 6:15 PM Alice Ryhl <aliceryhl@google.com> wrote:
> >
> > On Sat, Jul 26, 2025 at 5:50 PM Gary Guo <gary@garyguo.net> wrote:  
> > >
> > > On Sat, 26 Jul 2025 13:23:23 +0000
> > > Alice Ryhl <aliceryhl@google.com> wrote:
> > >  
> > > > To load a value, one must be careful to hold the lock while accessing
> > > > it. To enable this, we add a lock() method so that you can perform
> > > > operations on the value before the spinlock is released.
> > > >
> > > > Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> > > > Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> > > > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > > > ---
> > > >  rust/kernel/maple_tree.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++
> > > >  1 file changed, 94 insertions(+)
> > > >
> > > > diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> > > > index 0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c..c7ef504a9c78065b3d5752b4f5337fb6277182d1 100644
> > > > --- a/rust/kernel/maple_tree.rs
> > > > +++ b/rust/kernel/maple_tree.rs
> > > > @@ -206,6 +206,23 @@ pub fn erase(&self, index: usize) -> Option<T> {
> > > >          unsafe { T::try_from_foreign(ret) }
> > > >      }
> > > >
> > > > +    /// Lock the internal spinlock.
> > > > +    #[inline]
> > > > +    pub fn lock(&self) -> MapleLock<'_, T> {
> > > > +        // SAFETY: It's safe to lock the spinlock in a maple tree.
> > > > +        unsafe { bindings::spin_lock(self.ma_lock()) };
> > > > +
> > > > +        // INVARIANT: We just took the spinlock.
> > > > +        MapleLock(self)
> > > > +    }
> > > > +
> > > > +    #[inline]
> > > > +    fn ma_lock(&self) -> *mut bindings::spinlock_t {
> > > > +        // SAFETY: This pointer offset operation stays in-bounds.
> > > > +        let lock = unsafe { &raw mut (*self.tree.get()).__bindgen_anon_1.ma_lock };
> > > > +        lock.cast()
> > > > +    }  
> > >
> > > Could this return `&SpinLock<()>` using `Lock::from_raw`?
> > >
> > > I guess it has the drawback of having `MapleLock` needing to store
> > > `ma_lock` pointer but the guard is usually just all on stack and
> > > inlined so it probably won't make a difference?
> > >
> > > This way you remove `unsafe` from `lock` and gets a free `drop`.  
> >
> > I ended up going this way to avoid the extra field in MapleLock, like
> > you mention.  
> 
> Oh, and it also avoids assuming anything about the layout of SpinLock<()>
> 
> Alice

Well, `Lock::from_raw` is designed to interact with a C-side lock:

> Construct a Lock from a raw pointer
>
> This can be useful for interacting with a lock which was initialised outside of Rust.

- Gary


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-26 13:23 ` [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load() Alice Ryhl
  2025-07-26 15:50   ` Gary Guo
@ 2025-07-28 11:11   ` Andrew Ballance
  2025-07-28 11:19     ` Alice Ryhl
  2025-07-28 11:52   ` Danilo Krummrich
  2025-07-28 15:19   ` Boqun Feng
  3 siblings, 1 reply; 27+ messages in thread
From: Andrew Ballance @ 2025-07-28 11:11 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Boqun Feng, Miguel Ojeda, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	Lorenzo Stoakes, linux-kernel, maple-tree, rust-for-linux,
	linux-mm, Andrew Morton, Liam R. Howlett

On 7/26/25 8:23 AM, Alice Ryhl wrote:
> To load a value, one must be careful to hold the lock while accessing
> it. To enable this, we add a lock() method so that you can perform
> operations on the value before the spinlock is released.
> 
> Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>

I have a couple of nits, but overall looks good to me.

> ---
>   rust/kernel/maple_tree.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 94 insertions(+)
> 
> diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> index 0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c..c7ef504a9c78065b3d5752b4f5337fb6277182d1 100644
> --- a/rust/kernel/maple_tree.rs
> +++ b/rust/kernel/maple_tree.rs
> @@ -206,6 +206,23 @@ pub fn erase(&self, index: usize) -> Option<T> {
>           unsafe { T::try_from_foreign(ret) }
>       }
>   
> +    /// Lock the internal spinlock.

probably should add #[must_use] here.

> +    #[inline]
> +    pub fn lock(&self) -> MapleLock<'_, T> {
> +        // SAFETY: It's safe to lock the spinlock in a maple tree.
> +        unsafe { bindings::spin_lock(self.ma_lock()) };
> +
> +        // INVARIANT: We just took the spinlock.
> +        MapleLock(self)
> +    }
> +
> +    #[inline]
> +    fn ma_lock(&self) -> *mut bindings::spinlock_t {
> +        // SAFETY: This pointer offset operation stays in-bounds.
> +        let lock = unsafe { &raw mut (*self.tree.get()).__bindgen_anon_1.ma_lock };
> +        lock.cast()

This cast seems unneeded. lock should already be a *mut spinlock_t.

> +    }
> +
>       /// Free all `T` instances in this tree.
>       ///
>       /// # Safety
> @@ -248,6 +265,83 @@ fn drop(mut self: Pin<&mut Self>) {
>       }
>   }
>   
> +/// A reference to a [`MapleTree`] that owns the inner lock.
> +///
> +/// # Invariants
> +///
> +/// This guard owns the inner spinlock.
> +pub struct MapleLock<'tree, T: ForeignOwnable>(&'tree MapleTree<T>);
> +
> +impl<'tree, T: ForeignOwnable> Drop for MapleLock<'tree, T> {
> +    #[inline]
> +    fn drop(&mut self) {
> +        // SAFETY: By the type invariants, we hold this spinlock.
> +        unsafe { bindings::spin_unlock(self.0.ma_lock()) };
> +    }
> +}
> +
> +impl<'tree, T: ForeignOwnable> MapleLock<'tree, T> {
> +    /// Load the value at the given index.
> +    ///
> +    /// # Examples
> +    ///
> +    /// Read the value while holding the spinlock.
> +    ///
> +    /// ```
> +    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
> +    ///
> +    /// let tree = KBox::pin_init(MapleTree::<KBox<i32>>::new(), GFP_KERNEL)?;
> +    ///
> +    /// let ten = KBox::new(10, GFP_KERNEL)?;
> +    /// let twenty = KBox::new(20, GFP_KERNEL)?;
> +    /// tree.insert(100, ten, GFP_KERNEL)?;
> +    /// tree.insert(200, twenty, GFP_KERNEL)?;
> +    ///
> +    /// let mut lock = tree.lock();
> +    /// assert_eq!(lock.load(100), Some(&mut 10));
> +    /// assert_eq!(lock.load(200), Some(&mut 20));
> +    /// assert_eq!(lock.load(300), None);
> +    /// # Ok::<_, Error>(())
> +    /// ```
> +    ///
> +    /// Increment refcount while holding spinlock and read afterwards.
> +    ///
> +    /// ```
> +    /// use kernel::maple_tree::{MapleTree, InsertErrorKind};
> +    /// use kernel::sync::Arc;
> +    ///
> +    /// let tree = KBox::pin_init(MapleTree::<Arc<i32>>::new(), GFP_KERNEL)?;
> +    ///
> +    /// let ten = Arc::new(10, GFP_KERNEL)?;
> +    /// let twenty = Arc::new(20, GFP_KERNEL)?;
> +    /// tree.insert(100, ten, GFP_KERNEL)?;
> +    /// tree.insert(200, twenty, GFP_KERNEL)?;
> +    ///
> +    /// // Briefly take the lock to increment the refcount.
> +    /// let value = Arc::from(tree.lock().load(100).unwrap());
> +    ///
> +    /// // At this point, another thread might remove the value.
> +    /// tree.erase(100);
> +    ///
> +    /// // But we can still access it because we took a refcount.
> +    /// assert_eq!(*value, 10);
> +    /// # Ok::<_, Error>(())
> +    /// ```
> +    #[inline]
> +    pub fn load(&mut self, index: usize) -> Option<T::BorrowedMut<'_>> {
> +        // SAFETY: `self.tree` contains a valid maple tree.
> +        let ret = unsafe { bindings::mtree_load(self.0.tree.get(), index) };
> +        if ret.is_null() {
> +            return None;
> +        }
> +
> +        // SAFETY: If the pointer is not null, then it references a valid instance of `T`. It is
> +        // safe to borrow the instance mutably because the signature of this function enforces that
> +        // the mutable borrow is not used after the spinlock is dropped.
> +        Some(unsafe { T::borrow_mut(ret) })
> +    }
> +}
> +
>   /// Error type for failure to insert a new value.
>   pub struct InsertError<T> {
>       /// The value that could not be inserted.
> 

with or without those fixes, for the entire series,
Reviewed-by: Andrew Ballance <andrewjballance@gmail.com>

Also, if you need one, I would be happy to be a co-maintainer of the
rust maple tree bindings.

Best Regards,
Andrew Ballance





^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-28 11:11   ` Andrew Ballance
@ 2025-07-28 11:19     ` Alice Ryhl
  0 siblings, 0 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-07-28 11:19 UTC (permalink / raw)
  To: Andrew Ballance
  Cc: Boqun Feng, Miguel Ojeda, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	Lorenzo Stoakes, linux-kernel, maple-tree, rust-for-linux,
	linux-mm, Andrew Morton, Liam R. Howlett

On Mon, Jul 28, 2025 at 1:11 PM Andrew Ballance
<andrewjballance@gmail.com> wrote:
>
> On 7/26/25 8:23 AM, Alice Ryhl wrote:
> > To load a value, one must be careful to hold the lock while accessing
> > it. To enable this, we add a lock() method so that you can perform
> > operations on the value before the spinlock is released.
> >
> > Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> > Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
>
> I have a couple of nits, but overall looks good to me.
>
> > ---
> >   rust/kernel/maple_tree.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++
> >   1 file changed, 94 insertions(+)
> >
> > diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> > index 0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c..c7ef504a9c78065b3d5752b4f5337fb6277182d1 100644
> > --- a/rust/kernel/maple_tree.rs
> > +++ b/rust/kernel/maple_tree.rs
> > @@ -206,6 +206,23 @@ pub fn erase(&self, index: usize) -> Option<T> {
> >           unsafe { T::try_from_foreign(ret) }
> >       }
> >
> > +    /// Lock the internal spinlock.
>
> probably should add #[must_use] here.
>
> > +    #[inline]
> > +    pub fn lock(&self) -> MapleLock<'_, T> {
> > +        // SAFETY: It's safe to lock the spinlock in a maple tree.
> > +        unsafe { bindings::spin_lock(self.ma_lock()) };
> > +
> > +        // INVARIANT: We just took the spinlock.
> > +        MapleLock(self)
> > +    }
> > +
> > +    #[inline]
> > +    fn ma_lock(&self) -> *mut bindings::spinlock_t {
> > +        // SAFETY: This pointer offset operation stays in-bounds.
> > +        let lock = unsafe { &raw mut (*self.tree.get()).__bindgen_anon_1.ma_lock };
> > +        lock.cast()
>
> This cast seems unneeded. lock should already be a *mut spinlock_t.

In some configurations, the type is BindgenUnionField<spinlock_t>.

Alice


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-26 13:23 ` [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load() Alice Ryhl
  2025-07-26 15:50   ` Gary Guo
  2025-07-28 11:11   ` Andrew Ballance
@ 2025-07-28 11:52   ` Danilo Krummrich
  2025-07-28 15:19   ` Boqun Feng
  3 siblings, 0 replies; 27+ messages in thread
From: Danilo Krummrich @ 2025-07-28 11:52 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat Jul 26, 2025 at 3:23 PM CEST, Alice Ryhl wrote:
> diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> index 0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c..c7ef504a9c78065b3d5752b4f5337fb6277182d1 100644
> --- a/rust/kernel/maple_tree.rs
> +++ b/rust/kernel/maple_tree.rs
> @@ -206,6 +206,23 @@ pub fn erase(&self, index: usize) -> Option<T> {
>          unsafe { T::try_from_foreign(ret) }
>      }
>  
> +    /// Lock the internal spinlock.
> +    #[inline]
> +    pub fn lock(&self) -> MapleLock<'_, T> {
> +        // SAFETY: It's safe to lock the spinlock in a maple tree.
> +        unsafe { bindings::spin_lock(self.ma_lock()) };
> +
> +        // INVARIANT: We just took the spinlock.
> +        MapleLock(self)
> +    }
> +
> +    #[inline]
> +    fn ma_lock(&self) -> *mut bindings::spinlock_t {
> +        // SAFETY: This pointer offset operation stays in-bounds.
> +        let lock = unsafe { &raw mut (*self.tree.get()).__bindgen_anon_1.ma_lock };
> +        lock.cast()
> +    }
> +
>      /// Free all `T` instances in this tree.
>      ///
>      /// # Safety
> @@ -248,6 +265,83 @@ fn drop(mut self: Pin<&mut Self>) {
>      }
>  }
>  
> +/// A reference to a [`MapleTree`] that owns the inner lock.
> +///
> +/// # Invariants
> +///
> +/// This guard owns the inner spinlock.
> +pub struct MapleLock<'tree, T: ForeignOwnable>(&'tree MapleTree<T>);
> +
> +impl<'tree, T: ForeignOwnable> Drop for MapleLock<'tree, T> {
> +    #[inline]
> +    fn drop(&mut self) {
> +        // SAFETY: By the type invariants, we hold this spinlock.
> +        unsafe { bindings::spin_unlock(self.0.ma_lock()) };
> +    }
> +}

I think in the future we also want to give users access to the mas_*() function
familiy.

I assume, MaState would represent a struct ma_state, but also carry a MapleLock
guard. And then the mas_*() functions would be methods of MaState?

In case we want to allow to release and re-acquire the lock for the same
MaState, we could probably use type states.

I wonder if this (at least partially) makes sense to have from the get-go, since
it could already be used to implement things like MapleTree::free_all_entries()
based on it.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-26 13:23 ` [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load() Alice Ryhl
                     ` (2 preceding siblings ...)
  2025-07-28 11:52   ` Danilo Krummrich
@ 2025-07-28 15:19   ` Boqun Feng
  3 siblings, 0 replies; 27+ messages in thread
From: Boqun Feng @ 2025-07-28 15:19 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, Jul 26, 2025 at 01:23:23PM +0000, Alice Ryhl wrote:
[...]
> +/// A reference to a [`MapleTree`] that owns the inner lock.
> +///
> +/// # Invariants
> +///
> +/// This guard owns the inner spinlock.
> +pub struct MapleLock<'tree, T: ForeignOwnable>(&'tree MapleTree<T>);

So it's a guard, how about we name it `MapleGuard`, or `MapleLockGuard`,
or just `Guard`?

Regards,
Boqun

> +
> +impl<'tree, T: ForeignOwnable> Drop for MapleLock<'tree, T> {
> +    #[inline]
> +    fn drop(&mut self) {
> +        // SAFETY: By the type invariants, we hold this spinlock.
> +        unsafe { bindings::spin_unlock(self.0.ma_lock()) };
> +    }
> +}
> +
[...]


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] rust: maple_tree: add MapleTree
  2025-07-26 13:23 ` [PATCH 1/3] rust: maple_tree: add MapleTree Alice Ryhl
  2025-07-26 15:45   ` Gary Guo
  2025-07-26 16:23   ` Matthew Wilcox
@ 2025-07-28 16:04   ` Boqun Feng
  2025-07-28 16:39     ` Danilo Krummrich
  2025-08-07 16:12   ` Liam R. Howlett
  3 siblings, 1 reply; 27+ messages in thread
From: Boqun Feng @ 2025-07-28 16:04 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, Jul 26, 2025 at 01:23:22PM +0000, Alice Ryhl wrote:
> The maple tree will be used in the Tyr driver to allocate and keep track
> of GPU allocations created internally (i.e. not by userspace). It will
> likely also be used in the Nova driver eventually.
> 
> This adds the simplest methods for additional and removal that do not
> require any special care with respect to concurrency.
> 
> This implementation is based on the RFC by Andrew but with significant
> changes to simplify the implementation.
> 
> Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
[...]
> +    /// Free all `T` instances in this tree.
> +    ///
> +    /// # Safety
> +    ///
> +    /// This frees Rust data referenced by the maple tree without removing it from the maple tree.
> +    /// The caller must ensure that no reference that remains in the maple tree is used incorrectly
> +    /// after this call.
> +    unsafe fn free_all_entries(self: Pin<&mut Self>) {
> +        // SAFETY: The pointer references a valid maple tree.
> +        let ma_state = unsafe { Opaque::new(bindings::MA_STATE(self.tree.get(), 0, usize::MAX)) };
> +

A meta comment here for the future direction: I think it really makes a
lot of sense if we could have the Rust abstraction for struct ma_state,
that'll allow us to have flexible locking strategy and Iterator-like
interface. Maybe it's something Andrew can take a deeper look when
MapleTree binding is in-tree (no word play intented ;-))?

For example, with a ma_state binding, we can do:

    let mas = MAState::new(self, 0..);

    while let Some(v) = mas.next() {
    	drop(v)
    }

Regards,
Boqun

> +        loop {
> +            // SAFETY: The maple tree is valid. This call to `free_all_entries` has exclusive
> +            // access to the maple tree, so no further synchronization is required.
> +            let ptr = unsafe { bindings::mas_find(ma_state.get(), usize::MAX) };
> +            if ptr.is_null() {
> +                break;
> +            }
> +            // SAFETY: By the type invariants, this pointer references a valid value of type `T`.
> +            // By the safety requirements, it is okay to free it without removing it from the maple
> +            // tree.
> +            unsafe { drop(T::from_foreign(ptr)) };
> +        }
> +    }
> +}
> +
[...]


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] rust: maple_tree: add MapleTree
  2025-07-28 16:04   ` Boqun Feng
@ 2025-07-28 16:39     ` Danilo Krummrich
  0 siblings, 0 replies; 27+ messages in thread
From: Danilo Krummrich @ 2025-07-28 16:39 UTC (permalink / raw)
  To: Boqun Feng
  Cc: Alice Ryhl, Andrew Morton, Liam R. Howlett, Lorenzo Stoakes,
	Miguel Ojeda, Andrew Ballance, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Mon Jul 28, 2025 at 6:04 PM CEST, Boqun Feng wrote:
> On Sat, Jul 26, 2025 at 01:23:22PM +0000, Alice Ryhl wrote:
>> The maple tree will be used in the Tyr driver to allocate and keep track
>> of GPU allocations created internally (i.e. not by userspace). It will
>> likely also be used in the Nova driver eventually.
>> 
>> This adds the simplest methods for additional and removal that do not
>> require any special care with respect to concurrency.
>> 
>> This implementation is based on the RFC by Andrew but with significant
>> changes to simplify the implementation.
>> 
>> Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
>> Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
>> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
>> ---
> [...]
>> +    /// Free all `T` instances in this tree.
>> +    ///
>> +    /// # Safety
>> +    ///
>> +    /// This frees Rust data referenced by the maple tree without removing it from the maple tree.
>> +    /// The caller must ensure that no reference that remains in the maple tree is used incorrectly
>> +    /// after this call.
>> +    unsafe fn free_all_entries(self: Pin<&mut Self>) {
>> +        // SAFETY: The pointer references a valid maple tree.
>> +        let ma_state = unsafe { Opaque::new(bindings::MA_STATE(self.tree.get(), 0, usize::MAX)) };
>> +
>
> A meta comment here for the future direction: I think it really makes a
> lot of sense if we could have the Rust abstraction for struct ma_state,
> that'll allow us to have flexible locking strategy and Iterator-like
> interface. Maybe it's something Andrew can take a deeper look when
> MapleTree binding is in-tree (no word play intented ;-))?
>
> For example, with a ma_state binding, we can do:
>
>     let mas = MAState::new(self, 0..);
>
>     while let Some(v) = mas.next() {
>     	drop(v)
>     }

FYI: Left a similar comment on MapleLock [1]. :)

I'd rather have that sooner than later, free_all_entries() is a good internal
user.

[1] https://lore.kernel.org/all/DBNO0N1TDAGI.2OEWH6Y60JNYZ@kernel.org/


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/3] Add Rust abstraction for Maple Trees
  2025-07-26 13:23 [PATCH 0/3] Add Rust abstraction for Maple Trees Alice Ryhl
                   ` (2 preceding siblings ...)
  2025-07-26 13:23 ` [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc Alice Ryhl
@ 2025-08-06 19:24 ` Liam R. Howlett
  3 siblings, 0 replies; 27+ messages in thread
From: Liam R. Howlett @ 2025-08-06 19:24 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Lorenzo Stoakes, Miguel Ojeda, Andrew Ballance,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

* Alice Ryhl <aliceryhl@google.com> [250726 09:23]:

Alice, Danilo, and others..

Sorry for the delay, I've been off for a few weeks.

I'm just digging my way out of the emails now, so I'll have a look at
this shortly.

Thanks,
Liam

> This will be used in the Tyr driver [1] to allocate from the GPU's VA
> space that is not owned by userspace, but by the kernel, for kernel GPU
> mappings.
> 
> Danilo tells me that in nouveau, the maple tree is used for keeping
> track of "VM regions" on top of GPUVM, and that he will most likely end
> up doing the same in the Rust Nova driver as well.
> 
> These abstractions intentionally do not expose any way to make use of
> external locking. You are required to use the internal spinlock. For
> now, we do not support loads that only utilize rcu for protection.
> 
> This contains some parts taken from Andrew Ballance's RFC [2] from
> April. However, it has also been reworked significantly compared to that
> RFC taking the use-cases in Tyr into account.
> 
> [1]: https://lore.kernel.org/r/20250627-tyr-v1-1-cb5f4c6ced46@collabora.com
> [2]: https://lore.kernel.org/r/20250405060154.1550858-1-andrewjballance@gmail.com
> 
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
> Alice Ryhl (3):
>       rust: maple_tree: add MapleTree
>       rust: maple_tree: add MapleTree::lock() and load()
>       rust: maple_tree: add MapleTreeAlloc
> 
>  MAINTAINERS               |   2 +
>  rust/helpers/helpers.c    |   1 +
>  rust/helpers/maple_tree.c |  14 ++
>  rust/kernel/lib.rs        |   1 +
>  rust/kernel/maple_tree.rs | 538 ++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 556 insertions(+)
> ---
> base-commit: dff64b072708ffef23c117fa1ee1ea59eb417807
> change-id: 20250726-maple-tree-1af0803ac524
> 
> Best regards,
> -- 
> Alice Ryhl <aliceryhl@google.com>
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] rust: maple_tree: add MapleTree
  2025-07-26 13:23 ` [PATCH 1/3] rust: maple_tree: add MapleTree Alice Ryhl
                     ` (2 preceding siblings ...)
  2025-07-28 16:04   ` Boqun Feng
@ 2025-08-07 16:12   ` Liam R. Howlett
  2025-08-08  8:37     ` Alice Ryhl
  3 siblings, 1 reply; 27+ messages in thread
From: Liam R. Howlett @ 2025-08-07 16:12 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Lorenzo Stoakes, Miguel Ojeda, Andrew Ballance,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

* Alice Ryhl <aliceryhl@google.com> [250726 09:23]:
> The maple tree will be used in the Tyr driver to allocate and keep track
> of GPU allocations created internally (i.e. not by userspace). It will
> likely also be used in the Nova driver eventually.
> 
> This adds the simplest methods for additional and removal that do not
> require any special care with respect to concurrency.
> 
> This implementation is based on the RFC by Andrew but with significant
> changes to simplify the implementation.
> 
> Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
>  MAINTAINERS               |   2 +
>  rust/helpers/helpers.c    |   1 +
>  rust/helpers/maple_tree.c |  14 +++
>  rust/kernel/lib.rs        |   1 +
>  rust/kernel/maple_tree.rs | 286 ++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 304 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index dd810da5261b5d664ef9750f66ec022412e8014b..b7e7308ce07c050239c14c4f3a0fd89bdd8e8796 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15956,7 +15956,9 @@ L:	rust-for-linux@vger.kernel.org
>  S:	Maintained
>  W:	http://www.linux-mm.org
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
> +F:	rust/helpers/maple_tree.c
>  F:	rust/helpers/mm.c
> +F:	rust/kernel/maple_tree.rs
>  F:	rust/kernel/mm.rs
>  F:	rust/kernel/mm/

We should have another section for the maple tree, since it's not just
used for mm.  Your stated plan is to use it for GPU allocations and the
code doesn't live in mm/, wdyt?

...

Thanks,
Liam


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-07-27 12:02         ` Gary Guo
@ 2025-08-07 16:15           ` Liam R. Howlett
  2025-08-07 18:30             ` Danilo Krummrich
  0 siblings, 1 reply; 27+ messages in thread
From: Liam R. Howlett @ 2025-08-07 16:15 UTC (permalink / raw)
  To: Gary Guo
  Cc: Alice Ryhl, Andrew Morton, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

* Gary Guo <gary@garyguo.net> [250727 08:03]:
> On Sat, 26 Jul 2025 18:18:02 +0200
> Alice Ryhl <aliceryhl@google.com> wrote:
> 
> > On Sat, Jul 26, 2025 at 6:15 PM Alice Ryhl <aliceryhl@google.com> wrote:
> > >
> > > On Sat, Jul 26, 2025 at 5:50 PM Gary Guo <gary@garyguo.net> wrote:  
> > > >
> > > > On Sat, 26 Jul 2025 13:23:23 +0000
> > > > Alice Ryhl <aliceryhl@google.com> wrote:
> > > >  
> > > > > To load a value, one must be careful to hold the lock while accessing
> > > > > it. To enable this, we add a lock() method so that you can perform
> > > > > operations on the value before the spinlock is released.
> > > > >
> > > > > Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> > > > > Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> > > > > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > > > > ---
> > > > >  rust/kernel/maple_tree.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++
> > > > >  1 file changed, 94 insertions(+)
> > > > >
> > > > > diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> > > > > index 0f26c173eedc7c79bb8e2b56fe85e8a266b3ae0c..c7ef504a9c78065b3d5752b4f5337fb6277182d1 100644
> > > > > --- a/rust/kernel/maple_tree.rs
> > > > > +++ b/rust/kernel/maple_tree.rs
> > > > > @@ -206,6 +206,23 @@ pub fn erase(&self, index: usize) -> Option<T> {
> > > > >          unsafe { T::try_from_foreign(ret) }
> > > > >      }
> > > > >
> > > > > +    /// Lock the internal spinlock.
> > > > > +    #[inline]
> > > > > +    pub fn lock(&self) -> MapleLock<'_, T> {
> > > > > +        // SAFETY: It's safe to lock the spinlock in a maple tree.
> > > > > +        unsafe { bindings::spin_lock(self.ma_lock()) };
> > > > > +
> > > > > +        // INVARIANT: We just took the spinlock.
> > > > > +        MapleLock(self)
> > > > > +    }
> > > > > +
> > > > > +    #[inline]
> > > > > +    fn ma_lock(&self) -> *mut bindings::spinlock_t {
> > > > > +        // SAFETY: This pointer offset operation stays in-bounds.
> > > > > +        let lock = unsafe { &raw mut (*self.tree.get()).__bindgen_anon_1.ma_lock };
> > > > > +        lock.cast()
> > > > > +    }  
> > > >
> > > > Could this return `&SpinLock<()>` using `Lock::from_raw`?
> > > >
> > > > I guess it has the drawback of having `MapleLock` needing to store
> > > > `ma_lock` pointer but the guard is usually just all on stack and
> > > > inlined so it probably won't make a difference?
> > > >
> > > > This way you remove `unsafe` from `lock` and gets a free `drop`.  
> > >
> > > I ended up going this way to avoid the extra field in MapleLock, like
> > > you mention.  
> > 
> > Oh, and it also avoids assuming anything about the layout of SpinLock<()>
> > 
> > Alice
> 
> Well, `Lock::from_raw` is designed to interact with a C-side lock:
> 
> > Construct a Lock from a raw pointer
> >
> > This can be useful for interacting with a lock which was initialised outside of Rust.
> 

If it matters for future build out, the tree supports an external lock
that may not be a spinlock.  This is currently used by the mm for the
vma management, and others (although willy wants it to go away
eventually).

Thanks,
Liam


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc
  2025-07-26 13:23 ` [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc Alice Ryhl
  2025-07-26 15:54   ` Gary Guo
@ 2025-08-07 16:29   ` Liam R. Howlett
  2025-08-08  8:35     ` Alice Ryhl
  1 sibling, 1 reply; 27+ messages in thread
From: Liam R. Howlett @ 2025-08-07 16:29 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Andrew Morton, Lorenzo Stoakes, Miguel Ojeda, Andrew Ballance,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

* Alice Ryhl <aliceryhl@google.com> [250726 09:23]:
> To support allocation trees, we introduce a new type MapleTreeAlloc for
> the case where the tree is created using MT_FLAGS_ALLOC_RANGE. To ensure
> that you can only call mtree_alloc_range on an allocation tree, we
> restrict thta method to the new MapleTreeAlloc type. However, all
Typo here  ^

> methods on MapleTree remain accessible to MapleTreeAlloc as allocation
> trees can use the other methods without issues.

I guess this is for some rust side error translation because the C side
already returns the error?

> 
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
>  rust/kernel/maple_tree.rs | 158 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 158 insertions(+)
> 
> diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> index c7ef504a9c78065b3d5752b4f5337fb6277182d1..8c025d2c395b6d57f1fb16214b4e87d4e7942d6f 100644
> --- a/rust/kernel/maple_tree.rs
> +++ b/rust/kernel/maple_tree.rs
> @@ -32,6 +32,26 @@ pub struct MapleTree<T: ForeignOwnable> {
>      _p: PhantomData<T>,
>  }
>  
> +/// A maple tree with `MT_FLAGS_ALLOC_RANGE` set.
> +///
> +/// All methods on [`MapleTree`] are also accessible on this type.
> +#[pin_data]
> +#[repr(transparent)]
> +pub struct MapleTreeAlloc<T: ForeignOwnable> {
> +    #[pin]
> +    tree: MapleTree<T>,
> +}
> +
> +// Make MapleTree methods usable on MapleTreeAlloc.
> +impl<T: ForeignOwnable> core::ops::Deref for MapleTreeAlloc<T> {
> +    type Target = MapleTree<T>;
> +
> +    #[inline]
> +    fn deref(&self) -> &MapleTree<T> {
> +        &self.tree
> +    }
> +}
> +
>  #[inline]
>  fn to_maple_range(range: impl RangeBounds<usize>) -> Option<(usize, usize)> {
>      let first = match range.start_bound() {
> @@ -342,6 +362,107 @@ pub fn load(&mut self, index: usize) -> Option<T::BorrowedMut<'_>> {
>      }
>  }
>  
> +impl<T: ForeignOwnable> MapleTreeAlloc<T> {
> +    /// Create a new allocation tree.
> +    pub fn new() -> impl PinInit<Self> {
> +        let tree = pin_init!(MapleTree {
> +            // SAFETY: This initializes a maple tree into a pinned slot. The maple tree will be
> +            // destroyed in Drop before the memory location becomes invalid.
> +            tree <- Opaque::ffi_init(|slot| unsafe {
> +                bindings::mt_init_flags(slot, bindings::MT_FLAGS_ALLOC_RANGE)
> +            }),
> +            _p: PhantomData,
> +        });
> +
> +        pin_init!(MapleTreeAlloc { tree <- tree })
> +    }
> +
> +    /// Insert an entry with the given size somewhere in the given range.
> +    ///
> +    /// The maple tree will search for a location in the given range where there is space to insert
> +    /// the new range. If there is not enough available space, then an error will be returned.
> +    ///
> +    /// The index of the new range is returned.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// use kernel::maple_tree::{MapleTreeAlloc, AllocErrorKind};
> +    ///
> +    /// let tree = KBox::pin_init(MapleTreeAlloc::<KBox<i32>>::new(), GFP_KERNEL)?;
> +    ///
> +    /// let ten = KBox::new(10, GFP_KERNEL)?;
> +    /// let twenty = KBox::new(20, GFP_KERNEL)?;
> +    /// let thirty = KBox::new(30, GFP_KERNEL)?;
> +    /// let hundred = KBox::new(100, GFP_KERNEL)?;
> +    ///
> +    /// // Allocate three ranges.
> +    /// let idx1 = tree.alloc_range(100, ten, ..1000, GFP_KERNEL)?;
> +    /// let idx2 = tree.alloc_range(100, twenty, ..1000, GFP_KERNEL)?;
> +    /// let idx3 = tree.alloc_range(100, thirty, ..1000, GFP_KERNEL)?;
> +    ///
> +    /// assert_eq!(idx1, 0);
> +    /// assert_eq!(idx2, 100);
> +    /// assert_eq!(idx3, 200);
> +    ///
> +    /// // This will fail because the remaining space is too small.
> +    /// assert_eq!(
> +    ///     tree.alloc_range(800, hundred, ..1000, GFP_KERNEL).unwrap_err().cause,
> +    ///     AllocErrorKind::Busy,
> +    /// );
> +    /// # Ok::<_, Error>(())
> +    /// ```
> +    pub fn alloc_range<R>(
> +        &self,
> +        size: usize,
> +        value: T,
> +        range: R,
> +        gfp: Flags,
> +    ) -> Result<usize, AllocError<T>>
> +    where
> +        R: RangeBounds<usize>,
> +    {
> +        let Some((min, max)) = to_maple_range(range) else {
> +            return Err(AllocError {
> +                value,
> +                cause: AllocErrorKind::InvalidRequest,
> +            });
> +        };
> +
> +        let ptr = T::into_foreign(value);
> +        let mut index = 0;
> +
> +        // SAFETY: The tree is valid, and we are passing a pointer to an owned instance of `T`.
> +        let res = to_result(unsafe {
> +            bindings::mtree_alloc_range(
> +                self.tree.tree.get(),
> +                &mut index,
> +                ptr,
> +                size,
> +                min,
> +                max,
> +                gfp.as_raw(),
> +            )
> +        });
> +
> +        if let Err(err) = res {
> +            // SAFETY: As `mtree_alloc_range` failed, it is safe to take back ownership.
> +            let value = unsafe { T::from_foreign(ptr) };
> +
> +            let cause = if err == ENOMEM {
> +                AllocErrorKind::Nomem
> +            } else if err == EBUSY {
> +                AllocErrorKind::Busy
> +            } else {
> +                AllocErrorKind::InvalidRequest
> +            };
> +            Err(AllocError { value, cause })
> +        } else {
> +            Ok(index)
> +        }
> +    }
> +}
> +
>  /// Error type for failure to insert a new value.
>  pub struct InsertError<T> {
>      /// The value that could not be inserted.
> @@ -378,3 +499,40 @@ fn from(insert_err: InsertError<T>) -> Error {
>          Error::from(insert_err.cause)
>      }
>  }
> +
> +/// Error type for failure to insert a new value.
> +pub struct AllocError<T> {
> +    /// The value that could not be inserted.
> +    pub value: T,
> +    /// The reason for the failure to insert.
> +    pub cause: AllocErrorKind,
> +}
> +
> +/// The reason for the failure to insert.
> +#[derive(PartialEq, Eq, Copy, Clone)]
> +pub enum AllocErrorKind {
> +    /// There is not enough space for the requested allocation.
> +    Busy,
> +    /// Failure to allocate memory.
> +    Nomem,
> +    /// The insertion request was invalid.
> +    InvalidRequest,
> +}
> +
> +impl From<AllocErrorKind> for Error {
> +    #[inline]
> +    fn from(kind: AllocErrorKind) -> Error {
> +        match kind {
> +            AllocErrorKind::Busy => EBUSY,
> +            AllocErrorKind::Nomem => ENOMEM,
> +            AllocErrorKind::InvalidRequest => EINVAL,
> +        }
> +    }
> +}
> +
> +impl<T> From<AllocError<T>> for Error {
> +    #[inline]
> +    fn from(insert_err: AllocError<T>) -> Error {
> +        Error::from(insert_err.cause)
> +    }
> +}
> 
> -- 
> 2.50.1.470.g6ba607880d-goog
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load()
  2025-08-07 16:15           ` Liam R. Howlett
@ 2025-08-07 18:30             ` Danilo Krummrich
  0 siblings, 0 replies; 27+ messages in thread
From: Danilo Krummrich @ 2025-08-07 18:30 UTC (permalink / raw)
  To: Liam R. Howlett
  Cc: Gary Guo, Alice Ryhl, Andrew Morton, Lorenzo Stoakes,
	Miguel Ojeda, Andrew Ballance, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Thu Aug 7, 2025 at 6:15 PM CEST, Liam R. Howlett wrote:
> * Gary Guo <gary@garyguo.net> [250727 08:03]:
>> On Sat, 26 Jul 2025 18:18:02 +0200
>> Well, `Lock::from_raw` is designed to interact with a C-side lock:
>> 
>> > Construct a Lock from a raw pointer
>> >
>> > This can be useful for interacting with a lock which was initialised outside of Rust.
>> 
>
> If it matters for future build out, the tree supports an external lock
> that may not be a spinlock.  This is currently used by the mm for the
> vma management, and others (although willy wants it to go away
> eventually).

When I was considering maple tree for GPUVM, i.e. vma management for GPUs, I
would have needed the external lock as well. For GPU VMA management we have
section for which we have to ensure that the tree won't be altered for a
sequence of sleeping operations.

In the worst case we could have used the internal spinlock and yet have an
external mutex for this purpose; an uncontended spinlock shouldn't be that big
a deal to take. So, long story short, I think there may be a few cases where an
external lock can make sense.

Just to recap why GPUVM couldn't leverage maple tree: we have cases where we
have to pre-allocate multiple entries for the tree, whose ranges are yet unknown
at the time we need to pre-allocate them. Unfortunately, I can't think of a
solution for this given that in this situation we can't predict the number of
new nodes this requires.

If however in the meantime there have been ideas to tackle this please let me
know, I'd still love to have maple tree for GPUVM.

- Danilo


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc
  2025-08-07 16:29   ` Liam R. Howlett
@ 2025-08-08  8:35     ` Alice Ryhl
  0 siblings, 0 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-08-08  8:35 UTC (permalink / raw)
  To: Liam R. Howlett, Andrew Morton, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	linux-kernel, maple-tree, rust-for-linux, linux-mm

On Thu, Aug 07, 2025 at 12:29:19PM -0400, Liam R. Howlett wrote:
> * Alice Ryhl <aliceryhl@google.com> [250726 09:23]:
> > To support allocation trees, we introduce a new type MapleTreeAlloc for
> > the case where the tree is created using MT_FLAGS_ALLOC_RANGE. To ensure
> > that you can only call mtree_alloc_range on an allocation tree, we
> > restrict thta method to the new MapleTreeAlloc type. However, all
> Typo here  ^
> 
> > methods on MapleTree remain accessible to MapleTreeAlloc as allocation
> > trees can use the other methods without issues.
> 
> I guess this is for some rust side error translation because the C side
> already returns the error?

Already returns what error?

The API here makes it so that it fails to compile when you call
alloc_range on a tree that isn't an allocation tree. That's why there is
a separate type.

Alice


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] rust: maple_tree: add MapleTree
  2025-08-07 16:12   ` Liam R. Howlett
@ 2025-08-08  8:37     ` Alice Ryhl
  0 siblings, 0 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-08-08  8:37 UTC (permalink / raw)
  To: Liam R. Howlett, Andrew Morton, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	linux-kernel, maple-tree, rust-for-linux, linux-mm

On Thu, Aug 07, 2025 at 12:12:47PM -0400, Liam R. Howlett wrote:
> * Alice Ryhl <aliceryhl@google.com> [250726 09:23]:
> > The maple tree will be used in the Tyr driver to allocate and keep track
> > of GPU allocations created internally (i.e. not by userspace). It will
> > likely also be used in the Nova driver eventually.
> > 
> > This adds the simplest methods for additional and removal that do not
> > require any special care with respect to concurrency.
> > 
> > This implementation is based on the RFC by Andrew but with significant
> > changes to simplify the implementation.
> > 
> > Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> > Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > ---
> >  MAINTAINERS               |   2 +
> >  rust/helpers/helpers.c    |   1 +
> >  rust/helpers/maple_tree.c |  14 +++
> >  rust/kernel/lib.rs        |   1 +
> >  rust/kernel/maple_tree.rs | 286 ++++++++++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 304 insertions(+)
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index dd810da5261b5d664ef9750f66ec022412e8014b..b7e7308ce07c050239c14c4f3a0fd89bdd8e8796 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -15956,7 +15956,9 @@ L:	rust-for-linux@vger.kernel.org
> >  S:	Maintained
> >  W:	http://www.linux-mm.org
> >  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
> > +F:	rust/helpers/maple_tree.c
> >  F:	rust/helpers/mm.c
> > +F:	rust/kernel/maple_tree.rs
> >  F:	rust/kernel/mm.rs
> >  F:	rust/kernel/mm/
> 
> We should have another section for the maple tree, since it's not just
> used for mm.  Your stated plan is to use it for GPU allocations and the
> code doesn't live in mm/, wdyt?

Sure, I can add a new section if you prefer that.

Alice


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] rust: maple_tree: add MapleTree
  2025-07-26 15:45   ` Gary Guo
@ 2025-08-19  9:09     ` Alice Ryhl
  0 siblings, 0 replies; 27+ messages in thread
From: Alice Ryhl @ 2025-08-19  9:09 UTC (permalink / raw)
  To: Gary Guo
  Cc: Andrew Morton, Liam R. Howlett, Lorenzo Stoakes, Miguel Ojeda,
	Andrew Ballance, Boqun Feng, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	maple-tree, rust-for-linux, linux-mm

On Sat, Jul 26, 2025 at 5:45 PM Gary Guo <gary@garyguo.net> wrote:
>
> On Sat, 26 Jul 2025 13:23:22 +0000
> Alice Ryhl <aliceryhl@google.com> wrote:
>
> > The maple tree will be used in the Tyr driver to allocate and keep track
> > of GPU allocations created internally (i.e. not by userspace). It will
> > likely also be used in the Nova driver eventually.
> >
> > This adds the simplest methods for additional and removal that do not
> > require any special care with respect to concurrency.
> >
> > This implementation is based on the RFC by Andrew but with significant
> > changes to simplify the implementation.
> >
> > Co-developed-by: Andrew Ballance <andrewjballance@gmail.com>
> > Signed-off-by: Andrew Ballance <andrewjballance@gmail.com>
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
>
> Overall looks good to me, some nits and thoughts about the error type
> below.
>
> Best,
> GAry
>
> > +/// Error type for failure to insert a new value.
> > +pub struct InsertError<T> {
> > +    /// The value that could not be inserted.
> > +    pub value: T,
> > +    /// The reason for the failure to insert.
> > +    pub cause: InsertErrorKind,
> > +}
>
> Hmm, we've already have quite a few errors that look like this, e.g.
> `StoreError`. I wonder if we should just have a generic
>
>     struct ErrroWithData<T, E> {
>        pub value: T,
>        pub cause: E,
>     }

I don't think we have any existing errors that look like this?

> > +
> > +/// The reason for the failure to insert.
> > +#[derive(PartialEq, Eq, Copy, Clone)]
> > +pub enum InsertErrorKind {
> > +    /// There is already a value in the requested range.
> > +    Occupied,
> > +    /// Failure to allocate memory.
> > +    Nomem,
>
> Given that we already have an error type for allocation failure, how
> about
>
>     AllocError(crate::alloc::AllocError)
>
> ? I know we're getting ENOMEM from C, but this would match what the
> error type would be if it were from pure Rust code.

I don't think it makes a big difference, but ok.

Alice


^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2025-08-19  9:10 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-26 13:23 [PATCH 0/3] Add Rust abstraction for Maple Trees Alice Ryhl
2025-07-26 13:23 ` [PATCH 1/3] rust: maple_tree: add MapleTree Alice Ryhl
2025-07-26 15:45   ` Gary Guo
2025-08-19  9:09     ` Alice Ryhl
2025-07-26 16:23   ` Matthew Wilcox
2025-07-26 16:41     ` Alice Ryhl
2025-07-28 16:04   ` Boqun Feng
2025-07-28 16:39     ` Danilo Krummrich
2025-08-07 16:12   ` Liam R. Howlett
2025-08-08  8:37     ` Alice Ryhl
2025-07-26 13:23 ` [PATCH 2/3] rust: maple_tree: add MapleTree::lock() and load() Alice Ryhl
2025-07-26 15:50   ` Gary Guo
2025-07-26 16:15     ` Alice Ryhl
2025-07-26 16:18       ` Alice Ryhl
2025-07-27 12:02         ` Gary Guo
2025-08-07 16:15           ` Liam R. Howlett
2025-08-07 18:30             ` Danilo Krummrich
2025-07-28 11:11   ` Andrew Ballance
2025-07-28 11:19     ` Alice Ryhl
2025-07-28 11:52   ` Danilo Krummrich
2025-07-28 15:19   ` Boqun Feng
2025-07-26 13:23 ` [PATCH 3/3] rust: maple_tree: add MapleTreeAlloc Alice Ryhl
2025-07-26 15:54   ` Gary Guo
2025-07-26 16:13     ` Alice Ryhl
2025-08-07 16:29   ` Liam R. Howlett
2025-08-08  8:35     ` Alice Ryhl
2025-08-06 19:24 ` [PATCH 0/3] Add Rust abstraction for Maple Trees Liam R. Howlett

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).