From: David Rheinsberg <david@readahead.eu>
To: rust-for-linux@vger.kernel.org
Cc: teg@jklm.no, Miguel Ojeda <ojeda@kernel.org>,
David Rheinsberg <david@readahead.eu>
Subject: [RFC 13/16] bus1/util: add intrusive rb-tree
Date: Tue, 31 Mar 2026 21:03:05 +0200 [thread overview]
Message-ID: <20260331190308.141622-14-david@readahead.eu> (raw)
In-Reply-To: <20260331190308.141622-1-david@readahead.eu>
Add `util::rb`, an intrusive RB-Tree using `util::intrusive` for the
API, and `linux/rbtree.h` for the implementation.
The API is designed for very easy use, without requiring any unsafe code
from a user. It tracks ownership via a simple atomic, and can thus
assert collection association in O(1) in a completely safe manner.
Unlike the owning version of RB-Trees in `kernel::rbtree`, the intrusive
version clearly documents the node<->collection relationship in
data-structures, avoids double pointer-chases in traversals, and can be
used by bus1 to queue release/destruction notifications without fallible
allocations.
Signed-off-by: David Rheinsberg <david@readahead.eu>
---
ipc/bus1/util/mod.rs | 1 +
ipc/bus1/util/rb.rs | 1324 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1325 insertions(+)
create mode 100644 ipc/bus1/util/rb.rs
diff --git a/ipc/bus1/util/mod.rs b/ipc/bus1/util/mod.rs
index 4639e40382c4..833f86d8ccbe 100644
--- a/ipc/bus1/util/mod.rs
+++ b/ipc/bus1/util/mod.rs
@@ -11,6 +11,7 @@
pub mod field;
pub mod intrusive;
pub mod lll;
+pub mod rb;
pub mod slist;
/// Convert an Arc to its pinned version.
diff --git a/ipc/bus1/util/rb.rs b/ipc/bus1/util/rb.rs
new file mode 100644
index 000000000000..52cd7bf714bb
--- /dev/null
+++ b/ipc/bus1/util/rb.rs
@@ -0,0 +1,1324 @@
+// SPDX-License-Identifier: GPL-2.0
+//! # Intrusive Red-Black Trees
+//!
+//! This module implements an intrusive Red-Black Tree. Internally, it uses the
+//! common infrastructure provided by the C implementation `lib/rbtree.c` and
+//! is designed to work in a very similar manner.
+//!
+//! The entire API is meant to be completely safe to use. However, in the C API
+//! you cannot assert whether a node is attached to a specific instance of a
+//! tree. This makes ad-hoc operations unsafe or expensive, since object
+//! ownership has to be verified. Therefore, this implementation extents all
+//! nodes with a tag that asserts ownership and thus circumvents this
+//! restriction. The cost is an additional pointer-sized field in each node.
+//!
+//! The API is designed to be very similar to the API of common Rust
+//! collections that own their entries (e.g., `alloc::collections::BTreeMap`).
+//! That is, [`Tree`] is the entry-point of every rb-tree operation, and it
+//! owns all entries stored in this tree. However, unlike the standard Rust
+//! collections, [`Tree`] only stores smart-pointers, and never allocates or
+//! moves entries. Instead, it relies on
+//! [`IntoDeref`](crate::util::convert::IntoDeref) to convert smart pointers
+//! into raw pointers. Furthermore, it uses an intrusive design, so it relies
+//! on metadata on the nodes to link/unlink. It uses field projections to be
+//! generic over where this metadata is stored (see
+//! [`Field`](crate::util::field::Field) for details).
+
+use core::ptr::NonNull;
+use kernel::prelude::*;
+use kernel::sync::atomic;
+
+use crate::util;
+
+/// Red-Black Tree that stores and manages elements.
+///
+/// A [`Tree`] can be used to link and unlink elements, and thus transfer
+/// ownership of an element into a tree. Those elements can then be searched
+/// for, or can be iterated, similar to other standard Rust collections.
+///
+/// Elements stored in a [`Tree`] are owned by that tree, but are not moved,
+/// nor allocated by the tree. Instead, the tree takes ownership of a pointer
+/// to the element (either via smart pointers, or via references that have a
+/// lifetime that exceeds the lifetime of the tree). Once an element is
+/// removed, ownership is transferred back to the caller.
+///
+/// Trees are intrusive in nature, meaning they rely on metadata on each
+/// element to manage tree internals. This metadata must be a member field of
+/// type [`Node`]. The exact member field that is used for a tree is provided
+/// via a generic field representing type (`FRT`). All elements stored in a
+/// single tree will use the same member field. But a single element can have
+/// multiple different member fields of type [`Node`], and thus be linked into
+/// multiple different trees simultaneously.
+pub struct Tree<Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ // Rb-tree metadata for the entire tree. In their most basic form, this is
+ // just a pointer to the root node (but caching variants are an option to
+ // improve lookup of the first and last entry).
+ root: kernel::bindings::rb_root,
+ // We need a unique identifier to store as `owner` marker in nodes. The
+ // address of the owning tree is a reasonable default that can be acquired
+ // without external interference. Downside is that the tree needs to be
+ // pinned. Since pinning is ubiquitious in kernel APIs, this seems like an
+ // acceptable price to pay.
+ _pin: core::marker::PhantomPinned,
+ // Different trees can store entries of type `Ref` via different nodes. By
+ // pinning the field-representing-type, it is always clear which node a
+ // tree is using.
+ _lrt: core::marker::PhantomData<Lrt>,
+}
+
+/// Red-Black Tree metadata required on each element.
+///
+/// Every element that is stored in a [`Tree`] must have a member field of type
+/// [`Node`]. A [`Tree`] is generic over the member field used, so a single
+/// element can have multiple nodes to be stored in multiple different trees.
+/// All elements stored in the same tree will use the same member field for
+/// that tree, though.
+pub struct Node {
+ // Since this is an owning intrusive collection, we carefully ensure to
+ // never create multiple references to a single node. With a non-owning
+ // intrusive collection, we would need an `UnsafePinned` here, to ensure
+ // references retained by the caller do not alias with references created
+ // during tree introspection or manipulation. Yet, for owning collections
+ // this is unnecessary.
+ // We still use `Opaque` over `UnsafeCell` here, since interior mutability
+ // is required, and we really don't want to rely too much on the
+ // implementation details of `rbtree.c`. And we get `UnsafePinned` that way
+ // as well, so future non-owning extensions to this API would need no
+ // adjustments.
+ bindings: kernel::types::Opaque<kernel::bindings::rb_node>,
+ // The owner field is a tag that uniquely identifies the tree that owns the
+ // entry. Anything could be used as tag, but we decided on the tree address
+ // as it is trivially unique for each pinned tree.
+ // A value of 0 means the entry is unlinked. Any other value marks the
+ // entry as owned by the tree with the given tag. The value can only be set
+ // or cleared when holding a mutable reference to the owning tree. Acquire
+ // and Release semantics are guaranteed by any link/unlink functions, so
+ // entries can be moved from one tree to another, even across tasks.
+ owner: atomic::Atomic<usize>,
+ // RB-Tree node bindings store pointers to other nodes, so nodes must
+ // always be pinned when linked.
+ _pin: core::marker::PhantomPinned,
+}
+
+enum CursorPos {
+ Empty,
+ Front(NonNull<Node>),
+ At(NonNull<Node>),
+ Back(NonNull<Node>),
+}
+
+/// Immutable cursor over entries of an RB-Tree.
+///
+/// This cursor either points at an empty tree, directly at a node in a
+/// non-empty tree, before the first node, or after the last node. The cursor
+/// can be moved back and forth.
+pub struct Cursor<'tree, Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ _tree: &'tree Tree<Lrt>,
+ pos: CursorPos,
+}
+
+/// Mutable cursor over entries of an RB-Tree.
+///
+/// This cursor either points at an empty tree, directly at a node in a
+/// non-empty tree, before the first node, or after the last node. The cursor
+/// can be moved back and forth, and elements can be inserted and removed at
+/// will.
+pub struct CursorMut<'tree, Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ tree: Pin<&'tree mut Tree<Lrt>>,
+ pos: CursorPos,
+}
+
+/// Mutable slot in an RB-Tree.
+///
+/// This slot points at a location in an RB-Tree, which can either be an
+/// existing element, or an empty slot. It is usually obtained by searching
+/// a tree for a specific key. If an element matching the key is found, the
+/// slot of that element is returned. Otherwise, an empty slot suitable for
+/// insertion of an element with such a key is returned.
+///
+/// Slots mutably borrow the tree they reference, and as such allow insertion
+/// of new elements into the tree.
+pub struct Slot<'tree, Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ tree: Pin<&'tree mut Tree<Lrt>>,
+ anchor: *mut kernel::bindings::rb_node,
+ slot: *mut *mut kernel::bindings::rb_node,
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! util_rb_node_of {
+ ($ref:ty, $field:ident $(,)?) => {
+ $crate::util::intrusive::link_of!{$ref, $field, $crate::util::rb::Node}
+ }
+}
+
+/// Alias of [`link_of!()`](util::intrusive::link_of) for [`Node`] members.
+#[doc(inline)]
+pub use util_rb_node_of as node_of;
+
+impl<Lrt> Tree<Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ /// Create a new empty tree.
+ ///
+ /// Note that empty trees do not have to be pinned, but to insert elements
+ /// into a tree, it must be pinned. Therefore, the returned tree must be
+ /// either pinned in place, or moved into a pinned structure to be used for
+ /// insertions.
+ pub fn new() -> Self {
+ Self {
+ root: kernel::bindings::rb_root {
+ rb_node: core::ptr::null_mut(),
+ },
+ _pin: core::marker::PhantomPinned,
+ _lrt: core::marker::PhantomData,
+ }
+ }
+
+ fn panic_acquire(_v: Pin<Lrt::Ref>) -> ! {
+ core::panic!("attempting to link a foreign node");
+ }
+
+ fn panic_claim(_v: Pin<&Lrt::Target>) -> ! {
+ core::panic!("attempting to claim a foreign node");
+ }
+
+ // Return the memory address of this tree as an integer. This is used to
+ // tag nodes belonging to this tree.
+ //
+ // Note that the address of a tree is only stable if the tree is pinned.
+ // Since all modifications to a tree take a `Pin<&mut Self>`, this is
+ // given. We avoid taking a pinned tree here, to allow read-only queries to
+ // work with `&self` for simpler APIs. It is up to the caller to ensure
+ // that the result is used coherently.
+ //
+ // Also note that the drop handler of a tree ensures all entries are
+ // unlinked before a tree is unpinned. Therefore, owner tags are usually
+ // never used longer than a tree is pinned.
+ fn as_owner(&self) -> usize {
+ util::ptr_addr(core::ptr::from_ref(self))
+ }
+
+ fn root_mut(self: Pin<&mut Self>) -> &mut kernel::bindings::rb_root {
+ // SAFETY: `Self.root` is not structurally pinned.
+ unsafe { &mut Pin::into_inner_unchecked(self).root }
+ }
+
+ /// Check whether the tree is empty.
+ ///
+ /// This returns `true` is no entries are linked, `false` if at least one
+ /// entry is linked.
+ ///
+ /// Note that the tree does not maintain a counter of how many elements are
+ /// linked.
+ pub fn is_empty(&self) -> bool {
+ self.root.rb_node.is_null()
+ }
+
+ /// Check whether this tree contains a given node.
+ ///
+ /// This returns `true` if the node is linked into this tree. It returns
+ /// `false` if the node is currently unlinked or linked into another tree.
+ ///
+ /// This operation is performed in O(1), regardless of the number of
+ /// elements in the tree.
+ ///
+ /// While holding a reference to [`Tree`], no node can be linked to, or
+ /// unlinked from, the tree. Hence, unlike [`Node::is_linked()`] the return
+ /// value of this method is stable for at least the lifetime of `self`.
+ pub fn contains(
+ &self,
+ ent_target: &Lrt::Target,
+ ) -> bool {
+ let ent_node = Lrt::project(ent_target);
+ // SAFETY: `ent_node` is convertible to a reference.
+ let v = unsafe {
+ (*Node::owner(ent_node)).load(atomic::Relaxed)
+ };
+ v == self.as_owner()
+ }
+
+ /// Create an immutable cursor at the first element.
+ ///
+ /// The new cursor will point to the first element. If the tree is empty,
+ /// the cursor points to no element at all.
+ pub fn cursor_first(&self) -> Cursor<'_, Lrt> {
+ // SAFETY: `rb_first()` requires a pointer to a valid root, pointing to
+ // valid nodes. This invariant is maintained by `Tree`.
+ let pos = if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_first(&self.root)
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus it is
+ // embedded in a `Node`.
+ CursorPos::At(unsafe { Node::from_rb(v) })
+ } else {
+ CursorPos::Empty
+ };
+
+ Cursor {
+ _tree: self,
+ pos,
+ }
+ }
+
+ /// Create an immutable cursor at the last element.
+ ///
+ /// The new cursor will point to the last element. If the tree is empty,
+ /// the cursor points to no element at all.
+ pub fn cursor_last(&self) -> Cursor<'_, Lrt> {
+ // SAFETY: `rb_last()` requires a pointer to a valid root, pointing to
+ // valid nodes. This invariant is maintained by `Tree`.
+ let pos = if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_last(&self.root)
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus it is
+ // embedded in a `Node`.
+ CursorPos::At(unsafe { Node::from_rb(v) })
+ } else {
+ CursorPos::Empty
+ };
+
+ Cursor {
+ _tree: self,
+ pos,
+ }
+ }
+
+ /// Create a mutable cursor at the first element.
+ ///
+ /// The new cursor will point to the first element. If the tree is empty,
+ /// the cursor points to no element at all.
+ pub fn cursor_mut_first(
+ mut self: Pin<&mut Self>,
+ ) -> CursorMut<'_, Lrt> {
+ // SAFETY: `rb_first()` requires a pointer to a valid root, pointing to
+ // valid nodes. This invariant is maintained by `Tree`.
+ let pos = if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_first(
+ self.as_mut().root_mut(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus it is
+ // embedded in a `Node`.
+ CursorPos::At(unsafe { Node::from_rb(v) })
+ } else {
+ CursorPos::Empty
+ };
+
+ CursorMut {
+ tree: self,
+ pos,
+ }
+ }
+
+ /// Create a mutable cursor at the last element.
+ ///
+ /// The new cursor will point to the last element. If the tree is empty,
+ /// the cursor points to no element at all.
+ pub fn cursor_mut_last(
+ mut self: Pin<&mut Self>,
+ ) -> CursorMut<'_, Lrt> {
+ // SAFETY: `rb_last()` requires a pointer to a valid root, pointing to
+ // valid nodes. This invariant is maintained by `Tree`.
+ let pos = if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_last(
+ self.as_mut().root_mut(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus it is
+ // embedded in a `Node`.
+ CursorPos::At(unsafe { Node::from_rb(v) })
+ } else {
+ CursorPos::Empty
+ };
+
+ CursorMut {
+ tree: self,
+ pos,
+ }
+ }
+
+ /// Try creating a mutable cursor at an explicit element.
+ ///
+ /// This tries to create a [`CursorMut`] at the given element. If the
+ /// element is not linked in this tree, this will return `None` instead.
+ pub fn try_cursor_mut_at(
+ mut self: Pin<&mut Self>,
+ ent_target: Pin<&Lrt::Target>,
+ ) -> Option<CursorMut<'_, Lrt>> {
+ let ent_node = Lrt::project(&ent_target);
+ // SAFETY: `end_node` points to a valid allocation.
+ let v = unsafe {
+ (*Node::owner(ent_node)).load(atomic::Relaxed)
+ };
+ if v == self.as_mut().as_owner() {
+ Some(CursorMut {
+ tree: self,
+ pos: CursorPos::At(ent_node),
+ })
+ } else {
+ None
+ }
+ }
+
+ /// Find a slot in the tree.
+ ///
+ /// Search through the tree with the given comparison function, looking for
+ /// a specific slot. Regardless whether the slot is occupied or not, this
+ /// will return a `Slot` object.
+ ///
+ /// This will perform a search through the binary tree from root to leaf,
+ /// using `cmp_fn` on each node. `cmp_fn` can be chosen freely, but should
+ /// preferably implement a partial order to ensure a coherent tree order.
+ pub fn find_slot_by<CmpFn>(
+ mut self: Pin<&mut Self>,
+ mut cmp_fn: CmpFn,
+ ) -> Slot<'_, Lrt>
+ where
+ CmpFn: FnMut(Pin<&Lrt::Target>) -> core::cmp::Ordering,
+ {
+ let mut anchor: *mut kernel::bindings::rb_node;
+ let mut slot: &mut *mut kernel::bindings::rb_node;
+
+ anchor = core::ptr::null_mut();
+ slot = &mut self.as_mut().root_mut().rb_node;
+
+ while let Some(mut ent_rb) = NonNull::new(*slot) {
+ // SAFETY: All rb-entriess in a tree always refer to a valid
+ // rb-entry within a valid node.
+ let ent_node = unsafe { Node::from_rb(ent_rb) };
+ // SAFETY: All nodes in a tree always refer to a valid node
+ // within a reference target.
+ let ent_target = unsafe { Lrt::borrow(ent_node) };
+
+ slot = match cmp_fn(ent_target) {
+ core::cmp::Ordering::Less => {
+ // SAFETY: `ent_rb` points to a valid node and no other
+ // references to it exist.
+ unsafe { &mut ent_rb.as_mut().rb_left }
+ },
+ core::cmp::Ordering::Greater => {
+ // SAFETY: `ent_rb` points to a valid node and no other
+ // references to it exist.
+ unsafe { &mut ent_rb.as_mut().rb_left }
+ },
+ core::cmp::Ordering::Equal => break,
+ };
+ anchor = ent_rb.as_ptr();
+ }
+
+ Slot {
+ anchor,
+ slot,
+ tree: self,
+ }
+ }
+
+ /// Remove all entries from a tree.
+ ///
+ /// Clear the entire tree and pass ownership of each entry by invoking
+ /// `clear_fn`.
+ ///
+ /// This will iterate the tree in postorder, without rebalancing. Hence,
+ /// this is significantly faster than clearing a tree via [`CursorMut`].
+ pub fn clear_with<ClearFn>(
+ mut self: Pin<&mut Self>,
+ mut clear_fn: ClearFn,
+ )
+ where
+ ClearFn: FnMut(Pin<Lrt::Ref>),
+ {
+ let mut anchor: *mut kernel::bindings::rb_node;
+
+ // SAFETY: `rb_first_postorder()` requires a pointer to a valid root,
+ // pointing to valid nodes. This invariant is maintained by `Tree`.
+ anchor = unsafe {
+ kernel::bindings::rb_first_postorder(
+ self.as_mut().root_mut(),
+ )
+ };
+
+ // Clear the tree, so it is considered empty. Since nodes do not
+ // contain pointers to the root, this cannot affect the postorder
+ // traversal below.
+ self.as_mut().root_mut().rb_node = core::ptr::null_mut();
+
+ while let Some(ent_rb) = NonNull::new(anchor) {
+ // SAFETY: Same as for `rb_first_postorder()` above, but only cares
+ // for elements following it, not any elements preceding it.
+ // Since we call it before calling `clear_fn`, it is safe.
+ anchor = unsafe {
+ kernel::bindings::rb_next_postorder(ent_rb.as_ptr())
+ };
+
+ // SAFETY: `ent_rb` is a valid rb-entry in a valid node.
+ let ent_node = unsafe { Node::from_rb(ent_rb) };
+ // SAFETY: `end_node` is a valid node.
+ unsafe { (*Node::owner(ent_node)).store(0, atomic::Release) };
+ // SAFETY: `end_node` is a valid node in a reference target.
+ let ent = unsafe { Lrt::release(ent_node) };
+ clear_fn(ent);
+ }
+ }
+}
+
+// Convenience helpers
+impl<Lrt> Tree<Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ /// Create a mutable cursor at an explicit element.
+ ///
+ /// Works like [`Tree::try_cursor_mut_at()`] but panics if the element is
+ /// not linked into this tree.
+ pub fn cursor_mut_at(
+ self: Pin<&mut Self>,
+ ent_target: Pin<&Lrt::Target>,
+ ) -> CursorMut<'_, Lrt> {
+ self.try_cursor_mut_at(ent_target).unwrap_or_else(
+ || Self::panic_claim(ent_target),
+ )
+ }
+
+ /// Try linking a new entry in this tree.
+ ///
+ /// This combines [`Tree::find_slot_by()`] with [`Slot::try_link()`].
+ ///
+ /// On success, the ownership of the new entry is transferred to the tree
+ /// and the dereferenced entry is returned as a borrow wrapped in `Ok`. If
+ /// there either already is a matching entry linked into the tree, or if
+ /// `ent` is already linked into any tree, this function will fail and
+ /// return ownership of the new entry to the caller wrapped in `Err`.
+ ///
+ /// If this function fails, the slot is immediately dropped. If a retry
+ /// attempt is desired, the slot should be retrieved via `find_slot_by()`
+ /// instead. This avoids repeated traversals.
+ ///
+ /// ## Comparator
+ ///
+ /// The comparator `cmp_fn` gets the dereferenced to-be-linked entry `ent`
+ /// as first argument and an existing entry in the tree as second argument.
+ /// It shall return an order describing the relationship of the first
+ /// argument compared to the second.
+ pub fn try_link_by<CmpFn>(
+ self: Pin<&mut Self>,
+ ent: Pin<Lrt::Ref>,
+ mut cmp_fn: CmpFn,
+ ) -> Result<Pin<&'_ Lrt::Target>, Pin<Lrt::Ref>>
+ where
+ CmpFn: FnMut(&Pin<Lrt::Ref>, Pin<&Lrt::Target>) -> core::cmp::Ordering,
+ {
+ self.find_slot_by(
+ |other| cmp_fn(&ent, other),
+ ).try_link(ent)
+ }
+
+ /// Try linking a new entry in this tree, after all duplicates.
+ ///
+ /// Works like [`Tree:try_link_by()`] but will link the entry even if there
+ /// are duplicates with the same key. The entry will be linked after all
+ /// duplicates.
+ pub fn try_link_last_by<CmpFn>(
+ self: Pin<&mut Self>,
+ ent: Pin<Lrt::Ref>,
+ mut cmp_fn: CmpFn,
+ ) -> Result<Pin<&'_ Lrt::Target>, Pin<Lrt::Ref>>
+ where
+ CmpFn: FnMut(&Pin<Lrt::Ref>, Pin<&Lrt::Target>) -> core::cmp::Ordering,
+ {
+ self.find_slot_by(|other| match cmp_fn(&ent, other) {
+ core::cmp::Ordering::Equal => core::cmp::Ordering::Greater,
+ v => v,
+ }).try_link(ent)
+ }
+
+ /// Try unlinking a specific element from the tree.
+ ///
+ /// This chains [`Tree::try_cursor_mut_at()`] and [`Slot::try_unlink()`].
+ ///
+ /// This returns the element if it was unlinked from the tree, or `None` if
+ /// the element was not linked into this tree.
+ pub fn try_unlink(
+ self: Pin<&mut Self>,
+ ent_target: Pin<&Lrt::Target>,
+ ) -> Option<Pin<Lrt::Ref>> {
+ self.try_cursor_mut_at(ent_target).and_then(|v| {
+ v.try_unlink()
+ })
+ }
+
+ /// Unlink a specific element from the tree.
+ ///
+ /// Works like [`Tree::try_unlink()`] but panics if the entry was not
+ /// linked into this tree.
+ pub fn unlink(
+ self: Pin<&mut Self>,
+ ent_target: Pin<&Lrt::Target>,
+ ) -> Pin<Lrt::Ref> {
+ self.try_unlink(ent_target).unwrap_or_else(
+ || core::panic!("attempting to unlink foreign entry"),
+ )
+ }
+
+ /// Remove all entries from a tree.
+ ///
+ /// Works like [`Tree::clear_with()`] but uses [`core::mem::drop()`] as
+ /// callback.
+ pub fn clear(self: Pin<&mut Self>) {
+ self.clear_with(|_| {});
+ }
+}
+
+// SAFETY: Trees have no interior mutability, nor do they otherwise care for
+// their calling CPU. They can be freely sent across CPUs, only limited by
+// the stored type.
+unsafe impl<Lrt> Send for Tree<Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+ Lrt::Ref: Send,
+{
+}
+
+// SAFETY: Trees have no interior mutability, nor do they otherwise care for
+// their calling CPU. They can be shared across CPUs, only limited by
+// the stored type.
+unsafe impl<Lrt> Sync for Tree<Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+ Lrt::Ref: Sync,
+{
+}
+
+impl<Lrt> core::default::Default for Tree<Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ /// Return a new empty tree.
+ ///
+ /// The new tree has no entries linked and is completely independent of
+ /// other trees.
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<Lrt> core::ops::Drop for Tree<Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ /// Clear a tree before dropping it.
+ ///
+ /// This drops all elements in the tree via [`Tree::clear()`], before
+ /// dropping the tree. This ensures that the elements of a tree are
+ /// not leaked.
+ fn drop(&mut self) {
+ // SAFETY: We treat `self` as pinned unconditionally.
+ let this = unsafe { Pin::new_unchecked(self) };
+ this.clear();
+ }
+}
+
+impl Node {
+ /// Create a new unlinked node.
+ ///
+ /// Note that unlinked nodes do not need to be pinned. However, to link a
+ /// node into a tree, it must be pinned. Therefore, you need to pin the
+ /// returned value in place, or move it into a pinned structure, to make
+ /// use of it.
+ pub fn new() -> Self {
+ Self {
+ owner: atomic::Atomic::new(0),
+ bindings: kernel::types::Opaque::new(
+ kernel::bindings::rb_node {
+ __rb_parent_color: 0,
+ rb_right: core::ptr::null_mut(),
+ rb_left: core::ptr::null_mut(),
+ },
+ ),
+ _pin: core::marker::PhantomPinned,
+ }
+ }
+
+ /// Return a pointer to the atomic owner tag of a node.
+ ///
+ /// The owner tag of a node can be accessed at any time, as long as the
+ /// allocation of the node does not get deallocated. That is, the owner tag
+ /// can even be accessed if another part holds a mutable reference to the
+ /// node. The transparent wrapper around `UnsafeCell` in an atomic
+ /// guarantee that such accesses are safe.
+ ///
+ /// ## Safety
+ ///
+ /// The node pointer must refer to a valid and initialized allocation of a
+ /// node.
+ unsafe fn owner(node: NonNull<Self>) -> *mut atomic::Atomic<usize> {
+ // SAFETY: Delegated to caller.
+ unsafe { &raw mut (*node.as_ptr()).owner }
+ }
+
+ /// Get a node pointer from an rb-entry pointer.
+ ///
+ /// ## Safety
+ ///
+ /// The rb-entry must refer to a valid allocation inside of a node. The
+ /// allocation does not have to be initialized.
+ unsafe fn from_rb(rb: NonNull<kernel::bindings::rb_node>) -> NonNull<Self> {
+ // SAFETY: Delegated to caller.
+ unsafe {
+ NonNull::new_unchecked(
+ kernel::container_of!(
+ kernel::types::Opaque::cast_from(rb.as_ptr()),
+ Self,
+ bindings
+ ).cast_mut(),
+ )
+ }
+ }
+
+ /// Check whether this node is linked into a tree.
+ ///
+ /// This returns `true` if this node is linked into any tree. It returns
+ /// `false` if the node is currently unlinked.
+ ///
+ /// Note that a node can be linked into a tree at any time. That is,
+ /// validity of the returned boolean can change spuriously, unless the
+ /// caller otherwise ensures exclusive access to the node.
+ pub fn is_linked(&self) -> bool {
+ // SAFETY: `self` trivially points to a valid allocation of a node.
+ let v = unsafe {
+ (*Self::owner(util::nonnull_from_ref(self))).load(atomic::Relaxed)
+ };
+ v != 0
+ }
+}
+
+// SAFETY: Nodes are only ever modified through their owning tree, or through
+// atomics. Hence, they can be freely sent across CPUs.
+unsafe impl Send for Node {
+}
+
+// SAFETY: Shared references to a node always use atomics for any data access.
+// They can be freely shared across CPUs.
+unsafe impl Sync for Node {
+}
+
+impl core::clone::Clone for Node {
+ /// Returns a clean and unlinked node.
+ ///
+ /// Cloning a node always yields a new node that is unlinked and in no way
+ /// tied to the original node.
+ fn clone(&self) -> Self {
+ Self::new()
+ }
+}
+
+impl core::default::Default for Node {
+ /// Return a clean and unlinked node.
+ ///
+ /// The default state for nodes is an unlinked state. Such nodes are in no
+ /// way tied to a tree or any other node.
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl core::ops::Drop for Node {
+ /// Drop a node and verify it is unlinked.
+ ///
+ /// No special cleanup is required when dropping nodes. However, linked
+ /// nodes are owned by their respective tree and as such must never be
+ /// dropped. In an owning tree, this cannot happen, but in non-owning trees
+ /// it is the responsibility of the caller to ensure nodes are unlinked
+ /// before they are dropped.
+ ///
+ /// Since this is an owning tree implementation, this drop handler is a
+ /// safety net to ensure a correct implementation.
+ ///
+ /// ## Background
+ ///
+ /// The drop handler could attempt to disassociate the node. However, this
+ /// only works if node and tree are owned by the same thread. Since
+ /// [`Node`] was designed with [`Send`], it can be dropped by another
+ /// thread (possibly in parallel with a drop of the tree). Any attempt to
+ /// unlink would thus race.
+ ///
+ /// In case of non-owning trees, neither tree nor node can ensure the other
+ /// is valid for even the shortest interval, and thus cannot attempt any
+ /// unlink operation. Instead, validity of nodes is an invariant that must
+ /// be upheld by the user, and is protected by this drop implementation. In
+ /// case of an owning tree, nodes are always valid while linked, and thus
+ /// this drop implementation will hopefully be a no-op.
+ fn drop(&mut self) {
+ // SAFETY: The allocation behind `self` is valid.
+ let owner = unsafe {
+ (*Node::owner(util::nonnull_from_mut(self))).load(atomic::Relaxed)
+ };
+ if owner != 0 {
+ core::panic!(
+ "attempting drop of a claimed node: {:?}",
+ core::ptr::from_ref(&*self),
+ );
+ }
+ }
+}
+
+impl<'tree, Lrt> Cursor<'tree, Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ /// Return the reference target of the current element.
+ ///
+ /// Returns `None` if the tree is empty, or if the cursor points before the
+ /// first or after the last element.
+ pub fn get(&self) -> Option<Pin<&Lrt::Target>> {
+ if let CursorPos::At(ent_node) = self.pos {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ Some(unsafe { Lrt::borrow(ent_node) })
+ } else {
+ None
+ }
+ }
+
+ /// Return a clone of a reference to the current element.
+ ///
+ /// Returns `None` if the tree is empty, or if the cursor points before the
+ /// first or after the last element.
+ pub fn get_clone(&self) -> Option<Pin<Lrt::Ref>>
+ where
+ Lrt::Ref: Clone,
+ {
+ if let CursorPos::At(ent_node) = self.pos {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ Some(unsafe { Lrt::borrow_clone(ent_node) })
+ } else {
+ None
+ }
+ }
+
+ /// Move to the next entry, if any.
+ ///
+ /// Move the cursor to point to the next entry. If the tree is empty, or if
+ /// the cursor points to the last element, this is a no-op.
+ pub fn move_next(&mut self) {
+ match self.pos {
+ CursorPos::Empty => {},
+ CursorPos::Front(ent_node) => {
+ self.pos = CursorPos::At(ent_node);
+ },
+ CursorPos::Back(_ent_node) => {},
+ CursorPos::At(ent_node) => {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_next(
+ ent_node.as_ref().bindings.get(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus
+ // it is embedded in a `Node`.
+ self.pos = CursorPos::At(unsafe { Node::from_rb(v) });
+ } else {
+ self.pos = CursorPos::Back(ent_node);
+ }
+ },
+ }
+ }
+
+ /// Move to the previous entry, if any.
+ ///
+ /// Move the cursor to point to the previous entry. If the tree is empty,
+ /// or if the cursor points to the first element, this is a no-op.
+ pub fn move_prev(&mut self) {
+ match self.pos {
+ CursorPos::Empty => {},
+ CursorPos::Front(_ent_node) => {},
+ CursorPos::Back(ent_node) => {
+ self.pos = CursorPos::At(ent_node);
+ },
+ CursorPos::At(ent_node) => {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_prev(
+ ent_node.as_ref().bindings.get(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus
+ // it is embedded in a `Node`.
+ self.pos = CursorPos::At(unsafe { Node::from_rb(v) });
+ } else {
+ self.pos = CursorPos::Front(ent_node);
+ }
+ },
+ }
+ }
+}
+
+impl<'tree, Lrt> CursorMut<'tree, Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ /// Unlink a specific node from the tree.
+ ///
+ /// ## Safety
+ ///
+ /// `ent_node` must point to a valid entry in `tree`.
+ unsafe fn unlink_at(
+ mut tree: Pin<&mut Tree<Lrt>>,
+ ent_node: NonNull<Node>,
+ ) -> Pin<Lrt::Ref> {
+ // SAFETY: `rb_erase` only reshuffles a tree. So it is enough to
+ // ensure it is passed a valid root with only valid nodes. This
+ // invariant is always maintained by `Tree`.
+ unsafe {
+ kernel::bindings::rb_erase(
+ ent_node.as_ref().bindings.get(),
+ tree.as_mut().root_mut(),
+ )
+ };
+
+ // SAFETY: `ent_node` refers to a valid entry.
+ unsafe {
+ (*Node::owner(ent_node)).store(0, atomic::Release);
+ }
+
+ // SAFETY: `ent_node` was removed from the tree, as such it is
+ // guaranteed to not be used any further.
+ unsafe { Lrt::release(ent_node) }
+ }
+
+ /// Return the reference target of the current element.
+ ///
+ /// Returns `None` if the tree is empty, or if the cursor points before the
+ /// first or after the last element.
+ pub fn get(&self) -> Option<Pin<&Lrt::Target>> {
+ if let CursorPos::At(ent_node) = self.pos {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ Some(unsafe { Lrt::borrow(ent_node) })
+ } else {
+ None
+ }
+ }
+
+ /// Return the mutable reference target of the current element.
+ ///
+ /// Returns `None` if the tree is empty, or if the cursor points before the
+ /// first or after the last element.
+ pub fn get_mut(&mut self) -> Option<Pin<&mut Lrt::Target>>
+ where
+ Lrt: util::intrusive::LinkMut<Node>,
+ {
+ if let CursorPos::At(ent_node) = self.pos {
+ // SAFETY: `ent_node` points to a valid entry in the tree and the
+ // cursor is mutably borrowed for the same lifetime.
+ Some(unsafe { Lrt::borrow_mut(ent_node) })
+ } else {
+ None
+ }
+ }
+
+ /// Return a clone of a reference to the current element.
+ ///
+ /// Returns `None` if the tree is empty, or if the cursor points before the
+ /// first or after the last element.
+ pub fn get_clone(&self) -> Option<Pin<Lrt::Ref>>
+ where
+ Lrt::Ref: Clone,
+ {
+ if let CursorPos::At(ent_node) = self.pos {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ Some(unsafe { Lrt::borrow_clone(ent_node) })
+ } else {
+ None
+ }
+ }
+
+ /// Move to the next entry, if any.
+ ///
+ /// Move the cursor to point to the next entry. If the tree is empty, or if
+ /// the cursor points to the last element, this is a no-op.
+ pub fn move_next(&mut self) {
+ match self.pos {
+ CursorPos::Empty => {},
+ CursorPos::Front(ent_node) => {
+ self.pos = CursorPos::At(ent_node);
+ },
+ CursorPos::Back(_ent_node) => {},
+ CursorPos::At(ent_node) => {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_next(
+ ent_node.as_ref().bindings.get(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus
+ // it is embedded in a `Node`.
+ self.pos = CursorPos::At(unsafe { Node::from_rb(v) });
+ } else {
+ self.pos = CursorPos::Back(ent_node);
+ }
+ },
+ }
+ }
+
+ /// Move to the previous entry, if any.
+ ///
+ /// Move the cursor to point to the previous entry. If the tree is empty,
+ /// or if the cursor points to the first element, this is a no-op.
+ pub fn move_prev(&mut self) {
+ match self.pos {
+ CursorPos::Empty => {},
+ CursorPos::Front(_ent_node) => {},
+ CursorPos::Back(ent_node) => {
+ self.pos = CursorPos::At(ent_node);
+ },
+ CursorPos::At(ent_node) => {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_prev(
+ ent_node.as_ref().bindings.get(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus
+ // it is embedded in a `Node`.
+ self.pos = CursorPos::At(unsafe { Node::from_rb(v) });
+ } else {
+ self.pos = CursorPos::Front(ent_node);
+ }
+ },
+ }
+ }
+
+ /// Move to the next entry, trying to unlink the current entry first.
+ ///
+ /// If the cursor points to an element, the element is unlinked and
+ /// returned. Otherwise, `None` is returned. In all cases, the cursor is
+ /// moved to point to the next element.
+ pub fn try_unlink_and_move_next(&mut self) -> Option<Pin<Lrt::Ref>> {
+ if let CursorPos::At(ent_node) = self.pos {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ self.pos = if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_next(
+ ent_node.as_ref().bindings.get(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus
+ // it is embedded in a `Node`.
+ CursorPos::At(unsafe { Node::from_rb(v) })
+ } else if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_prev(
+ ent_node.as_ref().bindings.get(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus
+ // it is embedded in a `Node`.
+ CursorPos::Back(unsafe { Node::from_rb(v) })
+ } else {
+ CursorPos::Empty
+ };
+
+ // SAFETY: `ent_node` is a valid entry in `tree` and no longer
+ // referenced by the cursor.
+ Some(unsafe { Self::unlink_at(self.tree.as_mut(), ent_node) })
+ } else {
+ self.move_next();
+ None
+ }
+ }
+
+ /// Move to the previous entry, trying to unlink the current entry first.
+ ///
+ /// If the cursor points to an element, the element is unlinked and
+ /// returned. Otherwise, `None` is returned. In all cases, the cursor is
+ /// moved to point to the previous element.
+ pub fn try_unlink_and_move_prev(&mut self) -> Option<Pin<Lrt::Ref>> {
+ if let CursorPos::At(ent_node) = self.pos {
+ // SAFETY: `ent_node` points to a valid entry in the tree.
+ self.pos = if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_prev(
+ ent_node.as_ref().bindings.get(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus
+ // it is embedded in a `Node`.
+ CursorPos::At(unsafe { Node::from_rb(v) })
+ } else if let Some(v) = NonNull::new(unsafe {
+ kernel::bindings::rb_next(
+ ent_node.as_ref().bindings.get(),
+ )
+ }) {
+ // SAFETY: `v` points to a valid entry in the tree, thus
+ // it is embedded in a `Node`.
+ CursorPos::Front(unsafe { Node::from_rb(v) })
+ } else {
+ CursorPos::Empty
+ };
+
+ // SAFETY: `ent_node` is a valid entry in `tree` and no longer
+ // referenced by the cursor.
+ Some(unsafe { Self::unlink_at(self.tree.as_mut(), ent_node) })
+ } else {
+ self.move_prev();
+ None
+ }
+ }
+
+ /// Try unlinking the current entry from the tree, consuming the cursor.
+ ///
+ /// This will unlink the current entry under the cursor from the tree. If
+ /// the cursor refers to an empty tree, this is a no-op and `None` is
+ /// returned. Otherwise, the removed entry is returned.
+ ///
+ /// This consumes the cursor. Use [`move_next_try_unlink()`] etc. to unlink
+ /// entries while retaining the cursor.
+ pub fn try_unlink(mut self) -> Option<Pin<Lrt::Ref>> {
+ let CursorPos::At(ent_node) = self.pos else {
+ return None;
+ };
+
+ // SAFETY: `ent_node` is a valid entry in `tree`.
+ Some(unsafe { Self::unlink_at(self.tree.as_mut(), ent_node) })
+ }
+}
+
+// Convenience helpers
+impl<'tree, Lrt> CursorMut<'tree, Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ /// Move to the next entry, unlinking the current entry first.
+ ///
+ /// Works like [`Self::try_unlink_and_move_next()`] but panics if the tree
+ /// is empty.
+ pub fn unlink_and_move_next(&mut self) -> Pin<Lrt::Ref> {
+ self.try_unlink_and_move_next().unwrap_or_else(
+ || core::panic!("attempting to unlink from an empty tree"),
+ )
+ }
+
+ /// Move to the previous entry, unlinking the current entry first.
+ ///
+ /// Works like [`Self::try_unlink_and_move_prev()`] but panics if the tree
+ /// is empty.
+ pub fn unlink_and_move_prev(&mut self) -> Pin<Lrt::Ref> {
+ self.try_unlink_and_move_prev().unwrap_or_else(
+ || core::panic!("attempting to unlink from an empty tree"),
+ )
+ }
+
+ /// Unlink the current entry from the tree, consuming the cursor.
+ ///
+ /// Works like [`Self::try_unlink()`] but panics if the tree is empty.
+ pub fn unlink(self) -> Pin<Lrt::Ref> {
+ self.try_unlink().unwrap_or_else(
+ || core::panic!("attempting to unlink from an empty tree"),
+ )
+ }
+}
+
+impl<'tree, Lrt> Slot<'tree, Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ /// Check whether the slot is available for insertion.
+ ///
+ /// If the slot was already occupied by another entry of the tree, or if
+ /// the slot was already consumed for an insertion, this will return
+ /// `false`. Otherwise, this will return `true`.
+ ///
+ /// If this returns `true`, a following attempt of [`Self::try_link()`] is
+ /// guaranteed to succeed for any unclaimed entry.
+ pub fn available(&self) -> bool {
+ if let Some(slot) = NonNull::new(self.slot) {
+ // SAFETY: If non-null, `slot` is a valid reference to some slot in
+ // this tree. If the slot is non-empty, this slot is already in
+ // use and not available for insertion.
+ unsafe { slot.as_ref().is_null() }
+ } else {
+ // The slot was already used for insertion and is now invalid.
+ false
+ }
+ }
+
+ fn entry_ptr(&self) -> Option<NonNull<Node>> {
+ let slot = NonNull::new(self.slot)?;
+
+ // SAFETY: `self.slot` is a valid tree entry.
+ let ent_rb = NonNull::new(unsafe { *slot.as_ref() })?;
+
+ // SAFETY: `ent_rb` refers to a valid rb-entry in the tree, and is thus
+ // embedded in a valid node.
+ Some(unsafe { Node::from_rb(ent_rb) })
+ }
+
+ /// Get a reference to the entry in this slot.
+ ///
+ /// If the slot is occupied, this will return a reference to the entry in
+ /// this slot. Otherwise, this will return `None`.
+ pub fn entry(&self) -> Option<Pin<&Lrt::Target>> {
+ match self.entry_ptr() {
+ None => None,
+ // SAFETY: `entry_ptr()` only returns valid entryies.
+ Some(v) => Some(unsafe { Lrt::borrow(v) }),
+ }
+ }
+
+ /// Get a copy of the reference to the entry in this slot.
+ ///
+ /// If the slot is occupied, this will return a copy of the reference to
+ /// the entry in this slot. Otherwise, this will return `None`.
+ ///
+ /// This is only available if the reference type implements
+ /// [`core::clone::Clone`].
+ pub fn entry_clone(&self) -> Option<Pin<Lrt::Ref>>
+ where
+ Lrt::Ref: Clone,
+ {
+ self.entry_ptr().map(|v| {
+ // SAFETY: `v` is a valid tree entry, and as such is from
+ // `acquire()`.
+ unsafe { Lrt::borrow_clone(v) }
+ })
+ }
+
+ /// Try linking a new entry in this slot.
+ ///
+ /// If the slot is occupied, was already used for linking, or if the passed
+ /// entry is already linked into a tree, this will return `Err`, moving
+ /// ownership of the new entry back to the caller.
+ ///
+ /// Otherwise, this will move ownership of the entry to the tree and link
+ /// the entry in this slot. A reference to the target is returned via `Ok`.
+ ///
+ /// Note that a slot can only be used once to link an entry. Once linked
+ /// successfully, the slot must be considered unavailable and should be
+ /// dropped. Neither [`Slot::entry()`], nor any other operations will be
+ /// available on a used slot. The only reason this does not consume the
+ /// slot, is to allow re-use of the slot in case the link failed.
+ pub fn try_link(
+ &mut self,
+ ent: Pin<Lrt::Ref>,
+ ) -> Result<Pin<&'tree Lrt::Target>, Pin<Lrt::Ref>> {
+ // If the entry was already occupied, or already used for insertion, it
+ // is no longer available. Refuse to attempt the link.
+ if !self.available() {
+ return Err(ent);
+ }
+
+ // Acquire the entry and get access to the node. This can be converted
+ // to a reference as long as we do not release it.
+ let ent_node = Lrt::acquire(ent);
+
+ let owner = self.tree.as_mut().as_owner();
+ // SAFETY: `ent_node` is a valid node.
+ let Ok(_) = (unsafe {
+ (*Node::owner(ent_node)).cmpxchg(0, owner, atomic::Acquire)
+ }) else {
+ // If the cmpxchg fails, the entry is already claimed (either by
+ // this tree or another tree). Refuse to use this entry, but return
+ // it fully to the caller so it can be reused.
+ //
+ // SAFETY: The pointer was just obtained from `acquire()`.
+ return Err(unsafe { Lrt::release(ent_node) });
+ };
+
+ // The entry was successfully claimed. Let `rb_link_node()` and
+ // `rb_insert_color()` do their work. Then clear `self.{anchor,slot}`
+ // as they are no longer valid for insertion.
+ // Note that preferably the function would consume `self`, but that
+ // would prevent the caller from re-using the slot on insertion
+ // failure.
+ //
+ // SAFETY: As long as `self.slot` is non-null it points to a valid slot
+ // for insertion with `self.anchor` as the chosen `parent` value.
+ // This was checked via `self.available()` just now.
+ // `ent_node` was just claimed and as such is uniquely owned by
+ // this tree now. Since `self.tree` has a mutable reference, we can
+ // freely link it into the tree.
+ unsafe {
+ kernel::bindings::rb_link_node(
+ ent_node.as_ref().bindings.get(),
+ self.anchor,
+ self.slot,
+ );
+ kernel::bindings::rb_insert_color(
+ ent_node.as_ref().bindings.get(),
+ self.tree.as_mut().root_mut(),
+ );
+ }
+
+ self.anchor = core::ptr::null_mut();
+ self.slot = core::ptr::null_mut();
+
+ // SAFETY: The pointer was just obtained from `acquire()`.
+ Ok(unsafe { Lrt::borrow(ent_node) })
+ }
+}
+
+// Convenience helpers
+impl<'tree, Lrt> Slot<'tree, Lrt>
+where
+ Lrt: util::intrusive::Link<Node>,
+{
+ /// Link a new entry into this slot.
+ ///
+ /// Works like [`Self::try_link()`] but panics if the entry cannot be
+ /// linked.
+ pub fn link(mut self, ent: Pin<Lrt::Ref>) -> Pin<&'tree Lrt::Target> {
+ if !self.available() {
+ core::panic!("attempting to link on a used slot");
+ }
+ match self.try_link(ent) {
+ Ok(v) => v,
+ Err(v) => Tree::<Lrt>::panic_acquire(v),
+ }
+ }
+}
+
+#[kunit_tests(bus1_util_rb)]
+mod test {
+ use super::*;
+
+ #[derive(Default)]
+ struct Entry {
+ key: u8,
+ rb: Node,
+ }
+
+ util::field::impl_pin_field!(Entry, rb, Node);
+
+ #[test]
+ fn test_basic() {
+ let e0 = core::pin::pin!(Entry { key: 0, ..Default::default() });
+ let e1 = core::pin::pin!(Entry { key: 1, ..Default::default() });
+
+ let tree_o: Tree<node_of!(&Entry, rb)> = Tree::new();
+ let mut tree: Pin<&mut Tree<_>> = core::pin::pin!(tree_o);
+
+ assert!(tree.as_mut().is_empty());
+ tree.as_mut().find_slot_by(|other| e0.key.cmp(&other.key))
+ .link(e0.into_ref());
+ assert!(!tree.as_mut().is_empty());
+ assert!(
+ !tree.as_mut().find_slot_by(|other| 0.cmp(&other.key))
+ .available()
+ );
+ tree.as_mut().find_slot_by(|other| e1.key.cmp(&other.key))
+ .link(e1.into_ref());
+ assert!(!tree.as_mut().is_empty());
+
+ tree.as_mut().clear();
+ assert!(tree.as_mut().is_empty());
+ }
+}
--
2.53.0
next prev parent reply other threads:[~2026-03-31 19:06 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-31 19:02 [RFC 00/16] bus1: Capability-based IPC for Linux David Rheinsberg
2026-03-31 19:02 ` [RFC 01/16] rust/sync: add LockedBy::access_mut_unchecked() David Rheinsberg
2026-03-31 19:29 ` Miguel Ojeda
2026-03-31 19:02 ` [RFC 02/16] rust/sync: add Arc::drop_unless_unique() David Rheinsberg
2026-03-31 19:02 ` [RFC 03/16] rust/alloc: add Vec::into_boxed_slice() David Rheinsberg
2026-03-31 19:28 ` Miguel Ojeda
2026-03-31 21:10 ` Gary Guo
2026-03-31 22:07 ` Danilo Krummrich
2026-04-01 9:28 ` David Rheinsberg
2026-03-31 19:02 ` [RFC 04/16] rust/error: add EXFULL, EBADRQC, EDQUOT, ENOTRECOVERABLE David Rheinsberg
2026-03-31 19:02 ` [RFC 05/16] bus1: add module scaffolding David Rheinsberg
2026-03-31 19:02 ` [RFC 06/16] bus1: add the user-space API David Rheinsberg
2026-03-31 19:02 ` [RFC 07/16] bus1: add man-page David Rheinsberg
2026-04-01 16:30 ` Jonathan Corbet
2026-04-01 18:01 ` David Rheinsberg
2026-04-01 18:06 ` David Rheinsberg
2026-04-04 15:30 ` Thomas Meyer
2026-03-31 19:03 ` [RFC 08/16] bus1/util: add basic utilities David Rheinsberg
2026-03-31 19:35 ` Miguel Ojeda
2026-04-01 11:05 ` David Rheinsberg
2026-04-01 11:25 ` Miguel Ojeda
2026-03-31 19:03 ` [RFC 09/16] bus1/util: add field projections David Rheinsberg
2026-03-31 19:38 ` Miguel Ojeda
2026-03-31 19:03 ` [RFC 10/16] bus1/util: add IntoDeref/FromDeref David Rheinsberg
2026-03-31 19:44 ` Miguel Ojeda
2026-03-31 19:03 ` [RFC 11/16] bus1/util: add intrusive data-type helpers David Rheinsberg
2026-03-31 19:03 ` [RFC 12/16] bus1/util: add intrusive single linked lists David Rheinsberg
2026-03-31 19:03 ` David Rheinsberg [this message]
2026-03-31 19:43 ` [RFC 13/16] bus1/util: add intrusive rb-tree Miguel Ojeda
2026-03-31 19:03 ` [RFC 14/16] bus1/acct: add resouce accounting David Rheinsberg
2026-03-31 19:03 ` [RFC 15/16] bus1: introduce peers, handles, and nodes David Rheinsberg
2026-03-31 19:03 ` [RFC 16/16] bus1: implement the uapi David Rheinsberg
2026-03-31 19:46 ` [RFC 00/16] bus1: Capability-based IPC for Linux Miguel Ojeda
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260331190308.141622-14-david@readahead.eu \
--to=david@readahead.eu \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=teg@jklm.no \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox