From: Benno Lossin <benno.lossin@proton.me>
To: "Matt Gilbride" <mattgilbride@google.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Wedson Almeida Filho" <wedsonaf@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Andreas Hindborg" <a.hindborg@samsung.com>,
"Alice Ryhl" <aliceryhl@google.com>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Arve Hjønnevåg" <arve@android.com>,
"Todd Kjos" <tkjos@android.com>,
"Martijn Coenen" <maco@android.com>,
"Joel Fernandes" <joel@joelfernandes.org>,
"Carlos Llamas" <cmllamas@google.com>,
"Suren Baghdasaryan" <surenb@google.com>,
"Christian Brauner" <brauner@kernel.org>
Cc: Rob Landley <rob@landley.net>,
Davidlohr Bueso <dave@stgolabs.net>,
Michel Lespinasse <michel@lespinasse.org>,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v8 2/6] rust: rbtree: add red-black tree implementation backed by the C version
Date: Mon, 05 Aug 2024 19:02:28 +0000 [thread overview]
Message-ID: <242d0107-8e2b-4856-8f4c-1d5351fdaad8@proton.me> (raw)
In-Reply-To: <20240727-b4-rbtree-v8-2-951600ada434@google.com>
On 27.07.24 22:30, Matt Gilbride wrote:
> diff --git a/rust/kernel/rbtree.rs b/rust/kernel/rbtree.rs
> new file mode 100644
> index 000000000000..74c53e4d5c00
> --- /dev/null
> +++ b/rust/kernel/rbtree.rs
> @@ -0,0 +1,432 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Red-black trees.
> +//!
> +//! C header: [`include/linux/rbtree.h`](srctree/include/linux/rbtree.h)
> +//!
> +//! Reference: <https://www.kernel.org/doc/html/latest/core-api/rbtree.html>
> +
> +use crate::{alloc::Flags, bindings, container_of, error::Result, prelude::*};
> +use alloc::boxed::Box;
> +use core::{
> + cmp::{Ord, Ordering},
> + marker::PhantomData,
> + mem::MaybeUninit,
> + ptr::{addr_of_mut, NonNull},
> +};
> +
> +/// A red-black tree with owned nodes.
> +///
> +/// It is backed by the kernel C red-black trees.
> +///
> +/// # Invariants
> +///
> +/// Non-null parent/children pointers stored in instances of the `rb_node` C struct are always
> +/// valid, and pointing to a field of our internal representation of a node.
I think we should standardize that `Invariants` and `Safety` sections
are put last. This is because people reading the HTML version are
probably not interested in the inner workings. This also makes it
possible to have the invariants and the struct definition on the same
screen.
> +///
> +/// # Examples
[...]
> +/// ```
> +pub struct RBTree<K, V> {
> + root: bindings::rb_root,
It has been a while, so I might have already asked this, but do we need
`Opaque` here? We would need it, if C changes anything inside `root`
while Rust holds a `&RBTree` or `&mut RBTree`.
I don't think that this is the case (ie we don't need `Opaque`), but I
am not sure.
> + _p: PhantomData<Node<K, V>>,
> +}
> +
[...]
> + /// Inserts a new node into the tree.
> + ///
> + /// It overwrites a node if one already exists with the same key and returns it (containing the
> + /// key/value pair). Returns [`None`] if a node with the same key didn't already exist.
> + ///
> + /// This function always succeeds.
> + pub fn insert(&mut self, RBTreeNode { node }: RBTreeNode<K, V>) -> Option<RBTreeNode<K, V>> {
> + let node = Box::into_raw(node);
> + // SAFETY: `node` is valid at least until we call `Box::from_raw`, which only happens when
> + // the node is removed or replaced.
> + let node_links = unsafe { addr_of_mut!((*node).links) };
> +
> + // The parameters of `rb_link_node` are as follows:
Can you write `bindings::rb_link_node`?
> + // - `node`: A pointer to an uninitialized node being inserted.
> + // - `parent`: A pointer to an existing node in the tree. One of its child pointers must be
> + // null, and `node` will become a child of `parent` by replacing that child pointer
> + // with a pointer to `node`.
> + // - `rb_link`: A pointer to either the left-child or right-child field of `parent`. This
> + // specifies which child of `parent` should hold `node` after this call. The
> + // value of `*rb_link` must be null before the call to `rb_link_node`. If the
> + // red/black tree is empty, then it’s also possible for `parent` to be null. In
> + // this case, `rb_link` is a pointer to the `root` field of the red/black tree.
> + //
> + // We will traverse the tree looking for a node that has a null pointer as its child,
> + // representing an empty subtree where we can insert our new node. We need to make sure
> + // that we preserve the ordering of the nodes in the tree. In each iteration of the loop
> + // we store `parent` and `child_field_of_parent`, and the new `node` will go somewhere
> + // in the subtree of `parent` that `child_field_of_parent` points at. Once
> + // we find an empty subtree, we can insert the new node using `rb_link_node`.
> + let mut parent = core::ptr::null_mut();
> + let mut child_field_of_parent: &mut *mut bindings::rb_node = &mut self.root.rb_node;
> + while !child_field_of_parent.is_null() {
> + parent = *child_field_of_parent;
> +
> + // We need to determine whether `node` should be the left or right child of `parent`,
> + // so we will compare with the `key` field of `parent` a.k.a. `this` below.
> + //
> + // SAFETY: By the type invariant of `Self`, all non-null `rb_node` pointers stored in `self`
> + // point to the links field of `Node<K, V>` objects.
> + let this = unsafe { container_of!(parent, Node<K, V>, links) };
> +
> + // SAFETY: `this` is a non-null node so it is valid by the type invariants. `node` is
> + // valid until the node is removed.
> + match unsafe { (*node).key.cmp(&(*this).key) } {
> + // We would like `node` to be the left child of `parent`. Move to this child to check
> + // whether we can use it, or continue searching, at the next iteration.
> + //
> + // SAFETY: `parent` is a non-null node so it is valid by the type invariants.
> + Ordering::Less => child_field_of_parent = unsafe { &mut (*parent).rb_left },
> + // We would like `node` to be the right child of `parent`. Move to this child to check
> + // whether we can use it, or continue searching, at the next iteration.
> + //
> + // SAFETY: `parent` is a non-null node so it is valid by the type invariants.
> + Ordering::Greater => child_field_of_parent = unsafe { &mut (*parent).rb_right },
> + Ordering::Equal => {
> + // There is an existing node in the tree with this key, and that node is
> + // parent. Thus, we are replacing parent with a new node.
Missing `` around "parent", double space after '.'.
> + //
> + // INVARIANT: We are replacing an existing node with a new one, which is valid.
> + // It remains valid because we "forgot" it with `Box::into_raw`.
> + // SAFETY: All pointers are non-null and valid.
> + unsafe { bindings::rb_replace_node(parent, node_links, &mut self.root) };
> +
> + // INVARIANT: The node is being returned and the caller may free it, however,
> + // it was removed from the tree. So the invariants still hold.
> + return Some(RBTreeNode {
> + // SAFETY: `this` was a node in the tree, so it is valid.
> + node: unsafe { Box::from_raw(this.cast_mut()) },
> + });
> + }
> + }
> + }
[...]
> +struct Node<K, V> {
> + links: bindings::rb_node,
Same question as with `rb_root`, do we need `Opaque`?
Otherwise the patch looks good.
---
Cheers,
Benno
> + key: K,
> + value: V,
> +}
>
> --
> 2.46.0.rc1.232.g9752f9e123-goog
>
next prev parent reply other threads:[~2024-08-05 19:02 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-27 20:30 [PATCH v8 0/6] Red-black tree abstraction needed by Rust Binder Matt Gilbride
2024-07-27 20:30 ` [PATCH v8 1/6] rust: kernel: add `drop_contents` to `BoxExt` Matt Gilbride
2024-08-01 9:00 ` Alice Ryhl
2024-08-01 9:02 ` Alice Ryhl
2024-07-27 20:30 ` [PATCH v8 2/6] rust: rbtree: add red-black tree implementation backed by the C version Matt Gilbride
2024-07-30 21:54 ` Boqun Feng
2024-07-30 21:56 ` Boqun Feng
2024-08-05 19:02 ` Benno Lossin [this message]
2024-08-06 8:41 ` Alice Ryhl
2024-08-06 8:51 ` Benno Lossin
2024-07-27 20:30 ` [PATCH v8 3/6] rust: rbtree: add iterator Matt Gilbride
2024-08-05 19:08 ` Benno Lossin
2024-07-27 20:30 ` [PATCH v8 4/6] rust: rbtree: add mutable iterator Matt Gilbride
2024-08-05 19:22 ` Benno Lossin
2024-08-06 8:30 ` Alice Ryhl
2024-08-06 9:23 ` Benno Lossin
2024-07-27 20:30 ` [PATCH v8 5/6] rust: rbtree: add `RBTreeCursor` Matt Gilbride
2024-08-05 19:35 ` Benno Lossin
2024-08-06 8:24 ` Alice Ryhl
2024-08-06 9:01 ` Benno Lossin
2024-08-06 9:04 ` Alice Ryhl
2024-08-06 9:27 ` Benno Lossin
2024-07-27 20:30 ` [PATCH v8 6/6] rust: rbtree: add `RBTree::entry` Matt Gilbride
2024-08-05 20:02 ` Benno Lossin
2024-08-06 8:39 ` Alice Ryhl
2024-07-30 21:57 ` [PATCH v8 0/6] Red-black tree abstraction needed by Rust Binder Boqun Feng
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=242d0107-8e2b-4856-8f4c-1d5351fdaad8@proton.me \
--to=benno.lossin@proton.me \
--cc=a.hindborg@samsung.com \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=arve@android.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=brauner@kernel.org \
--cc=cmllamas@google.com \
--cc=dave@stgolabs.net \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=joel@joelfernandes.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maco@android.com \
--cc=mattgilbride@google.com \
--cc=michel@lespinasse.org \
--cc=ojeda@kernel.org \
--cc=rob@landley.net \
--cc=rust-for-linux@vger.kernel.org \
--cc=surenb@google.com \
--cc=tkjos@android.com \
--cc=wedsonaf@gmail.com \
/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;
as well as URLs for NNTP newsgroup(s).