Add a new module `clist` for working with C's doubly circular linked
lists. Provide low-level iteration over list nodes.
Typed iteration over actual items is provided with a `clist_create`
macro to assist in creation of the `CList` type.
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
MAINTAINERS | 7 +
drivers/gpu/Kconfig | 7 +
rust/helpers/helpers.c | 1 +
rust/helpers/list.c | 21 +++
rust/kernel/clist.rs | 315 +++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 2 +
6 files changed, 353 insertions(+)
create mode 100644 rust/helpers/list.c
create mode 100644 rust/kernel/clist.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 900fc00b73e6..310bb479260c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23204,6 +23204,13 @@ S: Maintained
T: git https://github.com/Rust-for-Linux/linux.git rust-analyzer-next
F: scripts/generate_rust_analyzer.py
+RUST TO C LIST INTERFACES
+M: Joel Fernandes <joelagnelf@nvidia.com>
+M: Alexandre Courbot <acourbot@nvidia.com>
+L: rust-for-linux@vger.kernel.org
+S: Maintained
+F: rust/kernel/clist.rs
+
RXRPC SOCKETS (AF_RXRPC)
M: David Howells <dhowells@redhat.com>
M: Marc Dionne <marc.dionne@auristor.com>
diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig
index 22dd29cd50b5..2c3dec070645 100644
--- a/drivers/gpu/Kconfig
+++ b/drivers/gpu/Kconfig
@@ -1,7 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
+config RUST_CLIST
+ bool
+ depends on RUST
+ help
+ Rust abstraction for interfacing with C linked lists.
+
config GPU_BUDDY
bool
+ select RUST_CLIST if RUST
help
A page based buddy allocator for GPU memory.
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index a3c42e51f00a..724fcb8240ac 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -35,6 +35,7 @@
#include "io.c"
#include "jump_label.c"
#include "kunit.c"
+#include "list.c"
#include "maple_tree.c"
#include "mm.c"
#include "mutex.c"
diff --git a/rust/helpers/list.c b/rust/helpers/list.c
new file mode 100644
index 000000000000..3390b154fa36
--- /dev/null
+++ b/rust/helpers/list.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Helpers for C Circular doubly linked list implementation.
+ */
+
+#include <linux/list.h>
+
+#ifndef __rust_helper
+#define __rust_helper
+#endif
+
+__rust_helper void rust_helper_INIT_LIST_HEAD(struct list_head *list)
+{
+ INIT_LIST_HEAD(list);
+}
+
+__rust_helper void rust_helper_list_add_tail(struct list_head *new, struct list_head *head)
+{
+ list_add_tail(new, head);
+}
diff --git a/rust/kernel/clist.rs b/rust/kernel/clist.rs
new file mode 100644
index 000000000000..1f6d4db13c1d
--- /dev/null
+++ b/rust/kernel/clist.rs
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A C doubly circular intrusive linked list interface for rust code.
+//!
+//! # Examples
+//!
+//! ```
+//! use kernel::{
+//! bindings,
+//! clist_create,
+//! types::Opaque, //
+//! };
+//! # // Create test list with values (0, 10, 20) - normally done by C code but it is
+//! # // emulated here for doctests using the C bindings.
+//! # use core::mem::MaybeUninit;
+//! #
+//! # /// C struct with embedded `list_head` (typically will be allocated by C code).
+//! # #[repr(C)]
+//! # pub(crate) struct SampleItemC {
+//! # pub value: i32,
+//! # pub link: bindings::list_head,
+//! # }
+//! #
+//! # let mut head = MaybeUninit::<bindings::list_head>::uninit();
+//! #
+//! # let head = head.as_mut_ptr();
+//! # // SAFETY: head and all the items are test objects allocated in this scope.
+//! # unsafe { bindings::INIT_LIST_HEAD(head) };
+//! #
+//! # let mut items = [
+//! # MaybeUninit::<SampleItemC>::uninit(),
+//! # MaybeUninit::<SampleItemC>::uninit(),
+//! # MaybeUninit::<SampleItemC>::uninit(),
+//! # ];
+//! #
+//! # for (i, item) in items.iter_mut().enumerate() {
+//! # let ptr = item.as_mut_ptr();
+//! # // SAFETY: pointers are to allocated test objects with a list_head field.
+//! # unsafe {
+//! # (*ptr).value = i as i32 * 10;
+//! # // addr_of_mut!() computes address of link directly as link is uninitialized.
+//! # bindings::INIT_LIST_HEAD(core::ptr::addr_of_mut!((*ptr).link));
+//! # bindings::list_add_tail(&mut (*ptr).link, head);
+//! # }
+//! # }
+//!
+//! // Rust wrapper for the C struct.
+//! // The list item struct in this example is defined in C code as:
+//! // struct SampleItemC {
+//! // int value;
+//! // struct list_head link;
+//! // };
+//! //
+//! #[repr(transparent)]
+//! pub(crate) struct Item(Opaque<SampleItemC>);
+//!
+//! impl Item {
+//! pub(crate) fn value(&self) -> i32 {
+//! // SAFETY: [`Item`] has same layout as [`SampleItemC`].
+//! unsafe { (*self.0.get()).value }
+//! }
+//! }
+//!
+//! // Create typed [`CList`] from sentinel head.
+//! // SAFETY: head is valid, items are [`SampleItemC`] with embedded `link` field.
+//! let list = unsafe { clist_create!(head, Item, SampleItemC, link) };
+//!
+//! // Iterate directly over typed items.
+//! let mut found_0 = false;
+//! let mut found_10 = false;
+//! let mut found_20 = false;
+//!
+//! for item in list.iter() {
+//! let val = item.value();
+//! if val == 0 { found_0 = true; }
+//! if val == 10 { found_10 = true; }
+//! if val == 20 { found_20 = true; }
+//! }
+//!
+//! assert!(found_0 && found_10 && found_20);
+//! ```
+
+use core::{
+ iter::FusedIterator,
+ marker::PhantomData, //
+};
+
+use crate::{
+ bindings,
+ types::Opaque, //
+};
+
+use pin_init::PinInit;
+
+/// Wraps a `list_head` object for use in intrusive linked lists.
+///
+/// # Invariants
+///
+/// - [`CListHead`] represents an allocated and valid `list_head` structure.
+/// - Once a [`CListHead`] is created in Rust, it will not be modified by non-Rust code.
+/// - All `list_head` for individual items are not modified for the lifetime of [`CListHead`].
+#[repr(transparent)]
+pub(crate) struct CListHead(Opaque<bindings::list_head>);
+
+impl CListHead {
+ /// Create a `&CListHead` reference from a raw `list_head` pointer.
+ ///
+ /// # Safety
+ ///
+ /// - `ptr` must be a valid pointer to an allocated and initialized `list_head` structure.
+ /// - `ptr` must remain valid and unmodified for the lifetime `'a`.
+ #[inline]
+ pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self {
+ // SAFETY:
+ // - [`CListHead`] has same layout as `list_head`.
+ // - `ptr` is valid and unmodified for 'a.
+ unsafe { &*ptr.cast() }
+ }
+
+ /// Get the raw `list_head` pointer.
+ #[inline]
+ pub(crate) fn as_raw(&self) -> *mut bindings::list_head {
+ self.0.get()
+ }
+
+ /// Get the next [`CListHead`] in the list.
+ #[inline]
+ pub(crate) fn next(&self) -> &Self {
+ let raw = self.as_raw();
+ // SAFETY:
+ // - `self.as_raw()` is valid per type invariants.
+ // - The `next` pointer is guaranteed to be non-NULL.
+ unsafe { Self::from_raw((*raw).next) }
+ }
+
+ /// Check if this node is linked in a list (not isolated).
+ #[inline]
+ pub(crate) fn is_linked(&self) -> bool {
+ let raw = self.as_raw();
+ // SAFETY: self.as_raw() is valid per type invariants.
+ unsafe { (*raw).next != raw && (*raw).prev != raw }
+ }
+
+ /// Pin-initializer that initializes the list head.
+ pub(crate) fn new() -> impl PinInit<Self> {
+ // SAFETY: `INIT_LIST_HEAD` initializes `slot` to a valid empty list.
+ unsafe {
+ pin_init::pin_init_from_closure(move |slot: *mut Self| {
+ bindings::INIT_LIST_HEAD(slot.cast());
+ Ok(())
+ })
+ }
+ }
+}
+
+// SAFETY: [`CListHead`] can be sent to any thread.
+unsafe impl Send for CListHead {}
+
+// SAFETY: [`CListHead`] can be shared among threads as it is not modified
+// by non-Rust code per type invariants.
+unsafe impl Sync for CListHead {}
+
+impl PartialEq for CListHead {
+ fn eq(&self, other: &Self) -> bool {
+ core::ptr::eq(self, other)
+ }
+}
+
+impl Eq for CListHead {}
+
+/// Low-level iterator over `list_head` nodes.
+///
+/// An iterator used to iterate over a C intrusive linked list (`list_head`). Caller has to
+/// perform conversion of returned [`CListHead`] to an item (using `container_of` macro or similar).
+///
+/// # Invariants
+///
+/// [`CListHeadIter`] is iterating over an allocated, initialized and valid list.
+struct CListHeadIter<'a> {
+ /// Current position in the list.
+ current: &'a CListHead,
+ /// The sentinel head (used to detect end of iteration).
+ sentinel: &'a CListHead,
+}
+
+impl<'a> Iterator for CListHeadIter<'a> {
+ type Item = &'a CListHead;
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ // Check if we've reached the sentinel (end of list).
+ if core::ptr::eq(self.current, self.sentinel) {
+ return None;
+ }
+
+ let item = self.current;
+ self.current = item.next();
+ Some(item)
+ }
+}
+
+impl<'a> FusedIterator for CListHeadIter<'a> {}
+
+/// A typed C linked list with a sentinel head.
+///
+/// A sentinel head represents the entire linked list and can be used for
+/// iteration over items of type `T`, it is not associated with a specific item.
+///
+/// The const generic `OFFSET` specifies the byte offset of the `list_head` field within
+/// the struct that `T` wraps.
+///
+/// # Invariants
+///
+/// - The [`CListHead`] is an allocated and valid sentinel C `list_head` structure.
+/// - `OFFSET` is the byte offset of the `list_head` field within the struct that `T` wraps.
+/// - All the list's `list_head` nodes are allocated and have valid next/prev pointers.
+#[repr(transparent)]
+pub(crate) struct CList<T, const OFFSET: usize>(CListHead, PhantomData<T>);
+
+impl<T, const OFFSET: usize> CList<T, OFFSET> {
+ /// Create a typed [`CList`] reference from a raw sentinel `list_head` pointer.
+ ///
+ /// # Safety
+ ///
+ /// - `ptr` must be a valid pointer to an allocated and initialized `list_head` structure
+ /// representing a list sentinel.
+ /// - `ptr` must remain valid and unmodified for the lifetime `'a`.
+ /// - The list must contain items where the `list_head` field is at byte offset `OFFSET`.
+ /// - `T` must be `#[repr(transparent)]` over the C struct.
+ #[inline]
+ pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self {
+ // SAFETY:
+ // - [`CList`] has same layout as [`CListHead`] due to repr(transparent).
+ // - Caller guarantees `ptr` is a valid, sentinel `list_head` object.
+ unsafe { &*ptr.cast() }
+ }
+
+ /// Check if the list is empty.
+ #[inline]
+ #[expect(dead_code)]
+ pub(crate) fn is_empty(&self) -> bool {
+ !self.0.is_linked()
+ }
+
+ /// Create an iterator over typed items.
+ #[inline]
+ pub(crate) fn iter(&self) -> CListIter<'_, T, OFFSET> {
+ let head = &self.0;
+ CListIter {
+ head_iter: CListHeadIter {
+ current: head.next(),
+ sentinel: head,
+ },
+ _phantom: PhantomData,
+ }
+ }
+}
+
+/// High-level iterator over typed list items.
+pub(crate) struct CListIter<'a, T, const OFFSET: usize> {
+ head_iter: CListHeadIter<'a>,
+ _phantom: PhantomData<&'a T>,
+}
+
+impl<'a, T, const OFFSET: usize> Iterator for CListIter<'a, T, OFFSET> {
+ type Item = &'a T;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let head = self.head_iter.next()?;
+
+ // Convert to item using OFFSET.
+ // SAFETY: `item_ptr` calculation from `OFFSET` (calculated using offset_of!)
+ // is valid per invariants.
+ Some(unsafe { &*head.as_raw().byte_sub(OFFSET).cast::<T>() })
+ }
+}
+
+impl<'a, T, const OFFSET: usize> FusedIterator for CListIter<'a, T, OFFSET> {}
+
+/// Create a C doubly-circular linked list interface `CList` from a raw `list_head` pointer.
+///
+/// This macro creates a `CList<T, OFFSET>` that can iterate over items of type `$rust_type`
+/// linked via the `$field` field in the underlying C struct `$c_type`.
+///
+/// # Arguments
+///
+/// - `$head`: Raw pointer to the sentinel `list_head` object (`*mut bindings::list_head`).
+/// - `$rust_type`: Each item's rust wrapper type.
+/// - `$c_type`: Each item's C struct type that contains the embedded `list_head`.
+/// - `$field`: The name of the `list_head` field within the C struct.
+///
+/// # Safety
+///
+/// This is an unsafe macro. The caller must ensure:
+///
+/// - `$head` is a valid, initialized sentinel `list_head` pointing to a list that remains
+/// unmodified for the lifetime of the rust `CList`.
+/// - The list contains items of type `$c_type` linked via an embedded `$field`.
+/// - `$rust_type` is `#[repr(transparent)]` over `$c_type` or has compatible layout.
+///
+/// # Examples
+///
+/// Refer to the examples in this module's documentation.
+#[macro_export]
+macro_rules! clist_create {
+ ($head:expr, $rust_type:ty, $c_type:ty, $($field:tt).+) => {{
+ // Compile-time check that field path is a list_head.
+ let _: fn(*const $c_type) -> *const $crate::bindings::list_head =
+ |p| &raw const (*p).$($field).+;
+
+ // Calculate offset and create `CList`.
+ const OFFSET: usize = ::core::mem::offset_of!($c_type, $($field).+);
+ $crate::clist::CList::<$rust_type, OFFSET>::from_raw($head)
+ }};
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 3da92f18f4ee..8439c30f40b5 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -75,6 +75,8 @@
pub mod bug;
#[doc(hidden)]
pub mod build_assert;
+#[cfg(CONFIG_RUST_CLIST)]
+pub(crate) mod clist;
pub mod clk;
#[cfg(CONFIG_CONFIGFS_FS)]
pub mod configfs;
--
2.34.1
Hi Joel,
> On 5 Feb 2026, at 21:41, Joel Fernandes <joelagnelf@nvidia.com> wrote:
>
> Add a new module `clist` for working with C's doubly circular linked
> lists. Provide low-level iteration over list nodes.
>
> Typed iteration over actual items is provided with a `clist_create`
> macro to assist in creation of the `CList` type.
>
> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> ---
> MAINTAINERS | 7 +
> drivers/gpu/Kconfig | 7 +
> rust/helpers/helpers.c | 1 +
> rust/helpers/list.c | 21 +++
> rust/kernel/clist.rs | 315 +++++++++++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 2 +
> 6 files changed, 353 insertions(+)
> create mode 100644 rust/helpers/list.c
> create mode 100644 rust/kernel/clist.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 900fc00b73e6..310bb479260c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23204,6 +23204,13 @@ S: Maintained
> T: git https://github.com/Rust-for-Linux/linux.git rust-analyzer-next
> F: scripts/generate_rust_analyzer.py
>
> +RUST TO C LIST INTERFACES
> +M: Joel Fernandes <joelagnelf@nvidia.com>
> +M: Alexandre Courbot <acourbot@nvidia.com>
> +L: rust-for-linux@vger.kernel.org
> +S: Maintained
> +F: rust/kernel/clist.rs
> +
> RXRPC SOCKETS (AF_RXRPC)
> M: David Howells <dhowells@redhat.com>
> M: Marc Dionne <marc.dionne@auristor.com>
> diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig
> index 22dd29cd50b5..2c3dec070645 100644
> --- a/drivers/gpu/Kconfig
> +++ b/drivers/gpu/Kconfig
> @@ -1,7 +1,14 @@
> # SPDX-License-Identifier: GPL-2.0
>
> +config RUST_CLIST
> + bool
> + depends on RUST
> + help
> + Rust abstraction for interfacing with C linked lists.
> +
> config GPU_BUDDY
> bool
> + select RUST_CLIST if RUST
> help
> A page based buddy allocator for GPU memory.
>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index a3c42e51f00a..724fcb8240ac 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -35,6 +35,7 @@
> #include "io.c"
> #include "jump_label.c"
> #include "kunit.c"
> +#include "list.c"
> #include "maple_tree.c"
> #include "mm.c"
> #include "mutex.c"
> diff --git a/rust/helpers/list.c b/rust/helpers/list.c
> new file mode 100644
> index 000000000000..3390b154fa36
> --- /dev/null
> +++ b/rust/helpers/list.c
> @@ -0,0 +1,21 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Helpers for C Circular doubly linked list implementation.
> + */
> +
> +#include <linux/list.h>
> +
> +#ifndef __rust_helper
> +#define __rust_helper
> +#endif
> +
> +__rust_helper void rust_helper_INIT_LIST_HEAD(struct list_head *list)
> +{
> + INIT_LIST_HEAD(list);
> +}
> +
> +__rust_helper void rust_helper_list_add_tail(struct list_head *new, struct list_head *head)
> +{
> + list_add_tail(new, head);
> +}
> diff --git a/rust/kernel/clist.rs b/rust/kernel/clist.rs
> new file mode 100644
> index 000000000000..1f6d4db13c1d
> --- /dev/null
> +++ b/rust/kernel/clist.rs
> @@ -0,0 +1,315 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! A C doubly circular intrusive linked list interface for rust code.
> +//!
> +//! # Examples
> +//!
> +//! ```
> +//! use kernel::{
> +//! bindings,
> +//! clist_create,
> +//! types::Opaque, //
> +//! };
> +//! # // Create test list with values (0, 10, 20) - normally done by C code but it is
> +//! # // emulated here for doctests using the C bindings.
> +//! # use core::mem::MaybeUninit;
> +//! #
> +//! # /// C struct with embedded `list_head` (typically will be allocated by C code).
> +//! # #[repr(C)]
> +//! # pub(crate) struct SampleItemC {
> +//! # pub value: i32,
> +//! # pub link: bindings::list_head,
> +//! # }
> +//! #
> +//! # let mut head = MaybeUninit::<bindings::list_head>::uninit();
> +//! #
> +//! # let head = head.as_mut_ptr();
> +//! # // SAFETY: head and all the items are test objects allocated in this scope.
> +//! # unsafe { bindings::INIT_LIST_HEAD(head) };
> +//! #
> +//! # let mut items = [
> +//! # MaybeUninit::<SampleItemC>::uninit(),
> +//! # MaybeUninit::<SampleItemC>::uninit(),
> +//! # MaybeUninit::<SampleItemC>::uninit(),
> +//! # ];
> +//! #
> +//! # for (i, item) in items.iter_mut().enumerate() {
> +//! # let ptr = item.as_mut_ptr();
> +//! # // SAFETY: pointers are to allocated test objects with a list_head field.
> +//! # unsafe {
> +//! # (*ptr).value = i as i32 * 10;
> +//! # // addr_of_mut!() computes address of link directly as link is uninitialized.
> +//! # bindings::INIT_LIST_HEAD(core::ptr::addr_of_mut!((*ptr).link));
Shoudn’t this be &raw mut?
> +//! # bindings::list_add_tail(&mut (*ptr).link, head);
> +//! # }
> +//! # }
> +//!
> +//! // Rust wrapper for the C struct.
> +//! // The list item struct in this example is defined in C code as:
> +//! // struct SampleItemC {
> +//! // int value;
> +//! // struct list_head link;
> +//! // };
> +//! //
> +//! #[repr(transparent)]
> +//! pub(crate) struct Item(Opaque<SampleItemC>);
> +//!
> +//! impl Item {
> +//! pub(crate) fn value(&self) -> i32 {
> +//! // SAFETY: [`Item`] has same layout as [`SampleItemC`].
> +//! unsafe { (*self.0.get()).value }
> +//! }
> +//! }
> +//!
> +//! // Create typed [`CList`] from sentinel head.
> +//! // SAFETY: head is valid, items are [`SampleItemC`] with embedded `link` field.
> +//! let list = unsafe { clist_create!(head, Item, SampleItemC, link) };
> +//!
> +//! // Iterate directly over typed items.
> +//! let mut found_0 = false;
> +//! let mut found_10 = false;
> +//! let mut found_20 = false;
> +//!
> +//! for item in list.iter() {
> +//! let val = item.value();
> +//! if val == 0 { found_0 = true; }
> +//! if val == 10 { found_10 = true; }
> +//! if val == 20 { found_20 = true; }
> +//! }
> +//!
> +//! assert!(found_0 && found_10 && found_20);
> +//! ```
> +
> +use core::{
> + iter::FusedIterator,
> + marker::PhantomData, //
> +};
> +
> +use crate::{
> + bindings,
> + types::Opaque, //
> +};
> +
> +use pin_init::PinInit;
> +
> +/// Wraps a `list_head` object for use in intrusive linked lists.
> +///
> +/// # Invariants
> +///
> +/// - [`CListHead`] represents an allocated and valid `list_head` structure.
> +/// - Once a [`CListHead`] is created in Rust, it will not be modified by non-Rust code.
> +/// - All `list_head` for individual items are not modified for the lifetime of [`CListHead`].
Can you expand on the two points above?
> +#[repr(transparent)]
> +pub(crate) struct CListHead(Opaque<bindings::list_head>);
> +
> +impl CListHead {
> + /// Create a `&CListHead` reference from a raw `list_head` pointer.
> + ///
> + /// # Safety
> + ///
> + /// - `ptr` must be a valid pointer to an allocated and initialized `list_head` structure.
> + /// - `ptr` must remain valid and unmodified for the lifetime `'a`.
> + #[inline]
> + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self {
> + // SAFETY:
> + // - [`CListHead`] has same layout as `list_head`.
> + // - `ptr` is valid and unmodified for 'a.
> + unsafe { &*ptr.cast() }
> + }
> +
> + /// Get the raw `list_head` pointer.
> + #[inline]
> + pub(crate) fn as_raw(&self) -> *mut bindings::list_head {
> + self.0.get()
> + }
> +
> + /// Get the next [`CListHead`] in the list.
> + #[inline]
> + pub(crate) fn next(&self) -> &Self {
> + let raw = self.as_raw();
> + // SAFETY:
> + // - `self.as_raw()` is valid per type invariants.
> + // - The `next` pointer is guaranteed to be non-NULL.
> + unsafe { Self::from_raw((*raw).next) }
> + }
> +
> + /// Check if this node is linked in a list (not isolated).
> + #[inline]
> + pub(crate) fn is_linked(&self) -> bool {
> + let raw = self.as_raw();
> + // SAFETY: self.as_raw() is valid per type invariants.
> + unsafe { (*raw).next != raw && (*raw).prev != raw }
I wonder if this is duplicating some C helper?
> + }
> +
> + /// Pin-initializer that initializes the list head.
> + pub(crate) fn new() -> impl PinInit<Self> {
> + // SAFETY: `INIT_LIST_HEAD` initializes `slot` to a valid empty list.
> + unsafe {
> + pin_init::pin_init_from_closure(move |slot: *mut Self| {
> + bindings::INIT_LIST_HEAD(slot.cast());
> + Ok(())
> + })
> + }
> + }
> +}
> +
> +// SAFETY: [`CListHead`] can be sent to any thread.
> +unsafe impl Send for CListHead {}
> +
> +// SAFETY: [`CListHead`] can be shared among threads as it is not modified
> +// by non-Rust code per type invariants.
> +unsafe impl Sync for CListHead {}
> +
> +impl PartialEq for CListHead {
> + fn eq(&self, other: &Self) -> bool {
> + core::ptr::eq(self, other)
> + }
> +}
> +
> +impl Eq for CListHead {}
> +
> +/// Low-level iterator over `list_head` nodes.
> +///
> +/// An iterator used to iterate over a C intrusive linked list (`list_head`). Caller has to
> +/// perform conversion of returned [`CListHead`] to an item (using `container_of` macro or similar).
> +///
> +/// # Invariants
> +///
> +/// [`CListHeadIter`] is iterating over an allocated, initialized and valid list.
> +struct CListHeadIter<'a> {
> + /// Current position in the list.
> + current: &'a CListHead,
> + /// The sentinel head (used to detect end of iteration).
> + sentinel: &'a CListHead,
> +}
> +
> +impl<'a> Iterator for CListHeadIter<'a> {
> + type Item = &'a CListHead;
> +
> + #[inline]
> + fn next(&mut self) -> Option<Self::Item> {
> + // Check if we've reached the sentinel (end of list).
> + if core::ptr::eq(self.current, self.sentinel) {
> + return None;
> + }
I was under the impression that CListHeads implemented PartialEq/Eq?
> +
> + let item = self.current;
> + self.current = item.next();
> + Some(item)
> + }
> +}
> +
> +impl<'a> FusedIterator for CListHeadIter<'a> {}
> +
> +/// A typed C linked list with a sentinel head.
> +///
> +/// A sentinel head represents the entire linked list and can be used for
> +/// iteration over items of type `T`, it is not associated with a specific item.
> +///
> +/// The const generic `OFFSET` specifies the byte offset of the `list_head` field within
> +/// the struct that `T` wraps.
> +///
> +/// # Invariants
> +///
> +/// - The [`CListHead`] is an allocated and valid sentinel C `list_head` structure.
> +/// - `OFFSET` is the byte offset of the `list_head` field within the struct that `T` wraps.
> +/// - All the list's `list_head` nodes are allocated and have valid next/prev pointers.
> +#[repr(transparent)]
> +pub(crate) struct CList<T, const OFFSET: usize>(CListHead, PhantomData<T>);
> +
> +impl<T, const OFFSET: usize> CList<T, OFFSET> {
> + /// Create a typed [`CList`] reference from a raw sentinel `list_head` pointer.
> + ///
> + /// # Safety
> + ///
> + /// - `ptr` must be a valid pointer to an allocated and initialized `list_head` structure
> + /// representing a list sentinel.
> + /// - `ptr` must remain valid and unmodified for the lifetime `'a`.
> + /// - The list must contain items where the `list_head` field is at byte offset `OFFSET`.
> + /// - `T` must be `#[repr(transparent)]` over the C struct.
> + #[inline]
> + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self {
> + // SAFETY:
> + // - [`CList`] has same layout as [`CListHead`] due to repr(transparent).
> + // - Caller guarantees `ptr` is a valid, sentinel `list_head` object.
> + unsafe { &*ptr.cast() }
> + }
> +
> + /// Check if the list is empty.
> + #[inline]
> + #[expect(dead_code)]
> + pub(crate) fn is_empty(&self) -> bool {
Why can’t this be pub?
> + !self.0.is_linked()
> + }
> +
> + /// Create an iterator over typed items.
> + #[inline]
> + pub(crate) fn iter(&self) -> CListIter<'_, T, OFFSET> {
> + let head = &self.0;
> + CListIter {
> + head_iter: CListHeadIter {
> + current: head.next(),
> + sentinel: head,
> + },
> + _phantom: PhantomData,
> + }
> + }
> +}
> +
> +/// High-level iterator over typed list items.
> +pub(crate) struct CListIter<'a, T, const OFFSET: usize> {
> + head_iter: CListHeadIter<'a>,
> + _phantom: PhantomData<&'a T>,
> +}
> +
> +impl<'a, T, const OFFSET: usize> Iterator for CListIter<'a, T, OFFSET> {
> + type Item = &'a T;
> +
> + fn next(&mut self) -> Option<Self::Item> {
> + let head = self.head_iter.next()?;
> +
> + // Convert to item using OFFSET.
> + // SAFETY: `item_ptr` calculation from `OFFSET` (calculated using offset_of!)
> + // is valid per invariants.
> + Some(unsafe { &*head.as_raw().byte_sub(OFFSET).cast::<T>() })
> + }
> +}
> +
> +impl<'a, T, const OFFSET: usize> FusedIterator for CListIter<'a, T, OFFSET> {}
> +
> +/// Create a C doubly-circular linked list interface `CList` from a raw `list_head` pointer.
> +///
> +/// This macro creates a `CList<T, OFFSET>` that can iterate over items of type `$rust_type`
> +/// linked via the `$field` field in the underlying C struct `$c_type`.
> +///
> +/// # Arguments
> +///
> +/// - `$head`: Raw pointer to the sentinel `list_head` object (`*mut bindings::list_head`).
> +/// - `$rust_type`: Each item's rust wrapper type.
> +/// - `$c_type`: Each item's C struct type that contains the embedded `list_head`.
> +/// - `$field`: The name of the `list_head` field within the C struct.
> +///
> +/// # Safety
> +///
> +/// This is an unsafe macro. The caller must ensure:
> +///
> +/// - `$head` is a valid, initialized sentinel `list_head` pointing to a list that remains
> +/// unmodified for the lifetime of the rust `CList`.
> +/// - The list contains items of type `$c_type` linked via an embedded `$field`.
> +/// - `$rust_type` is `#[repr(transparent)]` over `$c_type` or has compatible layout.
> +///
> +/// # Examples
> +///
> +/// Refer to the examples in this module's documentation.
> +#[macro_export]
> +macro_rules! clist_create {
> + ($head:expr, $rust_type:ty, $c_type:ty, $($field:tt).+) => {{
> + // Compile-time check that field path is a list_head.
> + let _: fn(*const $c_type) -> *const $crate::bindings::list_head =
> + |p| &raw const (*p).$($field).+;
> +
> + // Calculate offset and create `CList`.
> + const OFFSET: usize = ::core::mem::offset_of!($c_type, $($field).+);
> + $crate::clist::CList::<$rust_type, OFFSET>::from_raw($head)
> + }};
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 3da92f18f4ee..8439c30f40b5 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -75,6 +75,8 @@
> pub mod bug;
> #[doc(hidden)]
> pub mod build_assert;
> +#[cfg(CONFIG_RUST_CLIST)]
> +pub(crate) mod clist;
> pub mod clk;
> #[cfg(CONFIG_CONFIGFS_FS)]
> pub mod configfs;
> --
> 2.34.1
>
>
Hi Daniel,
Hope you do not mind me replying piecemeal as I can reply more quickly. Thank
you for all the comments.
On 2/6/2026 12:49 PM, Daniel Almeida wrote:
>> +use crate::{
>> + bindings,
>> + types::Opaque, //
>> +};
>> +
>> +use pin_init::PinInit;
>> +
>> +/// Wraps a `list_head` object for use in intrusive linked lists.
>> +///
>> +/// # Invariants
>> +///
>> +/// - [`CListHead`] represents an allocated and valid `list_head` structure.
>> +/// - Once a [`CListHead`] is created in Rust, it will not be modified by non-Rust code.
>> +/// - All `list_head` for individual items are not modified for the lifetime of [`CListHead`].
>
> Can you expand on the two points above?
This is basically saying that a C `list_head` that is wrapped by a `CListHead`
is read-only for the lifetime of `ClistHead`. modifying the pointers anymore.
That is the invariant.
Or did I miss something?
--
Joel Fernandes
> On 6 Feb 2026, at 17:51, Joel Fernandes <joelagnelf@nvidia.com> wrote:
>
> Hi Daniel,
> Hope you do not mind me replying piecemeal as I can reply more quickly. Thank
> you for all the comments.
>
> On 2/6/2026 12:49 PM, Daniel Almeida wrote:
>>> +use crate::{
>>> + bindings,
>>> + types::Opaque, //
>>> +};
>>> +
>>> +use pin_init::PinInit;
>>> +
>>> +/// Wraps a `list_head` object for use in intrusive linked lists.
>>> +///
>>> +/// # Invariants
>>> +///
>>> +/// - [`CListHead`] represents an allocated and valid `list_head` structure.
>>> +/// - Once a [`CListHead`] is created in Rust, it will not be modified by non-Rust code.
>>> +/// - All `list_head` for individual items are not modified for the lifetime of [`CListHead`].
>>
>> Can you expand on the two points above?
>
> This is basically saying that a C `list_head` that is wrapped by a `CListHead`
> is read-only for the lifetime of `ClistHead`. modifying the pointers anymore.
> That is the invariant.
>
> Or did I miss something?
>
> --
> Joel Fernandes
>
>
Yeah, but my point being: is there a reason why the underlying list has to
remain read-only? Is this a safety requirement or an invariant that is established
by the code above?
— Daniel
On 2/6/2026 4:21 PM, Daniel Almeida wrote:
>
>
>> On 6 Feb 2026, at 17:51, Joel Fernandes <joelagnelf@nvidia.com> wrote:
>>
>> Hi Daniel,
>> Hope you do not mind me replying piecemeal as I can reply more quickly. Thank
>> you for all the comments.
>>
>> On 2/6/2026 12:49 PM, Daniel Almeida wrote:
>>>> +use crate::{
>>>> + bindings,
>>>> + types::Opaque, //
>>>> +};
>>>> +
>>>> +use pin_init::PinInit;
>>>> +
>>>> +/// Wraps a `list_head` object for use in intrusive linked lists.
>>>> +///
>>>> +/// # Invariants
>>>> +///
>>>> +/// - [`CListHead`] represents an allocated and valid `list_head` structure.
>>>> +/// - Once a [`CListHead`] is created in Rust, it will not be modified by non-Rust code.
>>>> +/// - All `list_head` for individual items are not modified for the lifetime of [`CListHead`].
>>>
>>> Can you expand on the two points above?
>>
>> This is basically saying that a C `list_head` that is wrapped by a `CListHead`
>> is read-only for the lifetime of `ClistHead`. modifying the pointers anymore.
>> That is the invariant.
>>
>> Or did I miss something?
>>
>> --
>> Joel Fernandes
>>
>>
>
>
> Yeah, but my point being: is there a reason why the underlying list has to
> remain read-only? Is this a safety requirement or an invariant that is established
> by the code above?
I'm not fully sure if it's an invariant or a safety requirement, but anyone
creating a C list head on the rust side must guarantee that it is not modified.
Since rust has no visibility on the C side, I believe it is a Rust invariant
here that the existence of CListHead assumes that the list cannot be modified
once Rust has access over it. That is up to the creator (user) of the CListHead
to guarantee. In the DRM buddy case, once the list is allocated and accessible
from Rust, C code will not modify it while the Rust object exists.
Does that make sense, or is there a better way to document this?
--
Joel Fernandes
>> >> >> Yeah, but my point being: is there a reason why the underlying list has to >> remain read-only? Is this a safety requirement or an invariant that is established >> by the code above? > I'm not fully sure if it's an invariant or a safety requirement, but anyone > creating a C list head on the rust side must guarantee that it is not modified. > Since rust has no visibility on the C side, I believe it is a Rust invariant > here that the existence of CListHead assumes that the list cannot be modified > once Rust has access over it. That is up to the creator (user) of the CListHead > to guarantee. In the DRM buddy case, once the list is allocated and accessible > from Rust, C code will not modify it while the Rust object exists. > > Does that make sense, or is there a better way to document this? > > -- > Joel Fernandes In which case, I recommend moving this to a safety requirement when creating the list. I assume the purpose of not modifying the list on the C side is to avoid corrupting the list in Rust somehow? — Daniel
On 2/6/2026 12:49 PM, Daniel Almeida wrote:
>> +#[repr(transparent)]
>> +pub(crate) struct CList<T, const OFFSET: usize>(CListHead, PhantomData<T>);
>> +
>> +impl<T, const OFFSET: usize> CList<T, OFFSET> {
>> + /// Create a typed [`CList`] reference from a raw sentinel `list_head` pointer.
>> + ///
>> + /// # Safety
>> + ///
>> + /// - `ptr` must be a valid pointer to an allocated and initialized `list_head` structure
>> + /// representing a list sentinel.
>> + /// - `ptr` must remain valid and unmodified for the lifetime `'a`.
>> + /// - The list must contain items where the `list_head` field is at byte offset `OFFSET`.
>> + /// - `T` must be `#[repr(transparent)]` over the C struct.
>> + #[inline]
>> + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self {
>> + // SAFETY:
>> + // - [`CList`] has same layout as [`CListHead`] due to repr(transparent).
>> + // - Caller guarantees `ptr` is a valid, sentinel `list_head` object.
>> + unsafe { &*ptr.cast() }
>> + }
>> +
>> + /// Check if the list is empty.
>> + #[inline]
>> + #[expect(dead_code)]
>> + pub(crate) fn is_empty(&self) -> bool {
>
> Why can’t this be pub?
I believe this was suggested by Gary. See the other thread where we are
discussing it (with Gary and Danilo) and let us discuss there.
--
Joel Fernandes
On Fri Feb 6, 2026 at 8:46 PM GMT, Joel Fernandes wrote:
> On 2/6/2026 12:49 PM, Daniel Almeida wrote:
>>> +#[repr(transparent)]
>>> +pub(crate) struct CList<T, const OFFSET: usize>(CListHead, PhantomData<T>);
>>> +
>>> +impl<T, const OFFSET: usize> CList<T, OFFSET> {
>>> + /// Create a typed [`CList`] reference from a raw sentinel `list_head` pointer.
>>> + ///
>>> + /// # Safety
>>> + ///
>>> + /// - `ptr` must be a valid pointer to an allocated and initialized `list_head` structure
>>> + /// representing a list sentinel.
>>> + /// - `ptr` must remain valid and unmodified for the lifetime `'a`.
>>> + /// - The list must contain items where the `list_head` field is at byte offset `OFFSET`.
>>> + /// - `T` must be `#[repr(transparent)]` over the C struct.
>>> + #[inline]
>>> + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self {
>>> + // SAFETY:
>>> + // - [`CList`] has same layout as [`CListHead`] due to repr(transparent).
>>> + // - Caller guarantees `ptr` is a valid, sentinel `list_head` object.
>>> + unsafe { &*ptr.cast() }
>>> + }
>>> +
>>> + /// Check if the list is empty.
>>> + #[inline]
>>> + #[expect(dead_code)]
>>> + pub(crate) fn is_empty(&self) -> bool {
>>
>> Why can’t this be pub?
>
> I believe this was suggested by Gary. See the other thread where we are
> discussing it (with Gary and Danilo) and let us discuss there.
I suggested the module to be `pub(crate)`. For the individual item it is not
necessary if the module itself already have limited visibility.
Best,
Gary
On 2/6/2026 3:51 PM, Gary Guo wrote:
> On Fri Feb 6, 2026 at 8:46 PM GMT, Joel Fernandes wrote:
>> On 2/6/2026 12:49 PM, Daniel Almeida wrote:
>>>> +#[repr(transparent)]
>>>> +pub(crate) struct CList<T, const OFFSET: usize>(CListHead, PhantomData<T>);
>>>> +
>>>> +impl<T, const OFFSET: usize> CList<T, OFFSET> {
>>>> + /// Create a typed [`CList`] reference from a raw sentinel `list_head` pointer.
>>>> + ///
>>>> + /// # Safety
>>>> + ///
>>>> + /// - `ptr` must be a valid pointer to an allocated and initialized `list_head` structure
>>>> + /// representing a list sentinel.
>>>> + /// - `ptr` must remain valid and unmodified for the lifetime `'a`.
>>>> + /// - The list must contain items where the `list_head` field is at byte offset `OFFSET`.
>>>> + /// - `T` must be `#[repr(transparent)]` over the C struct.
>>>> + #[inline]
>>>> + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self {
>>>> + // SAFETY:
>>>> + // - [`CList`] has same layout as [`CListHead`] due to repr(transparent).
>>>> + // - Caller guarantees `ptr` is a valid, sentinel `list_head` object.
>>>> + unsafe { &*ptr.cast() }
>>>> + }
>>>> +
>>>> + /// Check if the list is empty.
>>>> + #[inline]
>>>> + #[expect(dead_code)]
>>>> + pub(crate) fn is_empty(&self) -> bool {
>>>
>>> Why can’t this be pub?
>>
>> I believe this was suggested by Gary. See the other thread where we are
>> discussing it (with Gary and Danilo) and let us discuss there.
>
> I suggested the module to be `pub(crate)`. For the individual item it is not
> necessary if the module itself already have limited visibility.
>
Sure, I can change it to module-level pub then, and drop the pub(crate) if
everyone agrees.
--
Joel Fernandes
On Fri Feb 6, 2026 at 12:41 AM GMT, Joel Fernandes wrote:
> Add a new module `clist` for working with C's doubly circular linked
> lists. Provide low-level iteration over list nodes.
>
> Typed iteration over actual items is provided with a `clist_create`
> macro to assist in creation of the `CList` type.
>
> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> ---
> MAINTAINERS | 7 +
> drivers/gpu/Kconfig | 7 +
> rust/helpers/helpers.c | 1 +
> rust/helpers/list.c | 21 +++
> rust/kernel/clist.rs | 315 +++++++++++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 2 +
> 6 files changed, 353 insertions(+)
> create mode 100644 rust/helpers/list.c
> create mode 100644 rust/kernel/clist.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 900fc00b73e6..310bb479260c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23204,6 +23204,13 @@ S: Maintained
> T: git https://github.com/Rust-for-Linux/linux.git rust-analyzer-next
> F: scripts/generate_rust_analyzer.py
>
> +RUST TO C LIST INTERFACES
> +M: Joel Fernandes <joelagnelf@nvidia.com>
> +M: Alexandre Courbot <acourbot@nvidia.com>
> +L: rust-for-linux@vger.kernel.org
> +S: Maintained
> +F: rust/kernel/clist.rs
I still think we should try to work on a more powerful list infra that works for
both C and Rust, but I reckon that is a longer term effort, and shouldn't
prevent a simpler version from getting in and be used by abstractions that need
it. So
Acked-by: Gary Guo <gary@garyguo.net>
Some nits below:
> +
> RXRPC SOCKETS (AF_RXRPC)
> M: David Howells <dhowells@redhat.com>
> M: Marc Dionne <marc.dionne@auristor.com>
> diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig
> index 22dd29cd50b5..2c3dec070645 100644
> --- a/drivers/gpu/Kconfig
> +++ b/drivers/gpu/Kconfig
> @@ -1,7 +1,14 @@
> # SPDX-License-Identifier: GPL-2.0
>
> +config RUST_CLIST
> + bool
> + depends on RUST
> + help
> + Rust abstraction for interfacing with C linked lists.
I am not sure if we need extra config entry. This is fully generic so shouldn't
generate any code unless there is an user.
> +
> config GPU_BUDDY
> bool
> + select RUST_CLIST if RUST
> help
> A page based buddy allocator for GPU memory.
>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index a3c42e51f00a..724fcb8240ac 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -35,6 +35,7 @@
> #include "io.c"
> #include "jump_label.c"
> #include "kunit.c"
> +#include "list.c"
> #include "maple_tree.c"
> #include "mm.c"
> #include "mutex.c"
> diff --git a/rust/helpers/list.c b/rust/helpers/list.c
> new file mode 100644
> index 000000000000..3390b154fa36
> --- /dev/null
> +++ b/rust/helpers/list.c
> @@ -0,0 +1,21 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Helpers for C Circular doubly linked list implementation.
> + */
> +
> +#include <linux/list.h>
> +
> +#ifndef __rust_helper
> +#define __rust_helper
> +#endif
This shouldn't be needed.
> +
> +__rust_helper void rust_helper_INIT_LIST_HEAD(struct list_head *list)
> +{
> + INIT_LIST_HEAD(list);
> +}
> +
> +__rust_helper void rust_helper_list_add_tail(struct list_head *new, struct list_head *head)
> +{
> + list_add_tail(new, head);
> +}
> diff --git a/rust/kernel/clist.rs b/rust/kernel/clist.rs
> new file mode 100644
> index 000000000000..1f6d4db13c1d
> --- /dev/null
> +++ b/rust/kernel/clist.rs
> @@ -0,0 +1,315 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! A C doubly circular intrusive linked list interface for rust code.
> +//!
> +//! # Examples
> +//!
> +//! ```
> +//! use kernel::{
> +//! bindings,
> +//! clist_create,
> +//! types::Opaque, //
> +//! };
> +//! # // Create test list with values (0, 10, 20) - normally done by C code but it is
> +//! # // emulated here for doctests using the C bindings.
> +//! # use core::mem::MaybeUninit;
> +//! #
> +//! # /// C struct with embedded `list_head` (typically will be allocated by C code).
> +//! # #[repr(C)]
> +//! # pub(crate) struct SampleItemC {
> +//! # pub value: i32,
> +//! # pub link: bindings::list_head,
> +//! # }
> +//! #
> +//! # let mut head = MaybeUninit::<bindings::list_head>::uninit();
> +//! #
> +//! # let head = head.as_mut_ptr();
> +//! # // SAFETY: head and all the items are test objects allocated in this scope.
> +//! # unsafe { bindings::INIT_LIST_HEAD(head) };
> +//! #
> +//! # let mut items = [
> +//! # MaybeUninit::<SampleItemC>::uninit(),
> +//! # MaybeUninit::<SampleItemC>::uninit(),
> +//! # MaybeUninit::<SampleItemC>::uninit(),
> +//! # ];
> +//! #
> +//! # for (i, item) in items.iter_mut().enumerate() {
> +//! # let ptr = item.as_mut_ptr();
> +//! # // SAFETY: pointers are to allocated test objects with a list_head field.
> +//! # unsafe {
> +//! # (*ptr).value = i as i32 * 10;
> +//! # // addr_of_mut!() computes address of link directly as link is uninitialized.
> +//! # bindings::INIT_LIST_HEAD(core::ptr::addr_of_mut!((*ptr).link));
> +//! # bindings::list_add_tail(&mut (*ptr).link, head);
> +//! # }
> +//! # }
> +//!
> +//! // Rust wrapper for the C struct.
> +//! // The list item struct in this example is defined in C code as:
> +//! // struct SampleItemC {
> +//! // int value;
> +//! // struct list_head link;
> +//! // };
> +//! //
> +//! #[repr(transparent)]
> +//! pub(crate) struct Item(Opaque<SampleItemC>);
> +//!
> +//! impl Item {
> +//! pub(crate) fn value(&self) -> i32 {
> +//! // SAFETY: [`Item`] has same layout as [`SampleItemC`].
> +//! unsafe { (*self.0.get()).value }
> +//! }
> +//! }
> +//!
> +//! // Create typed [`CList`] from sentinel head.
> +//! // SAFETY: head is valid, items are [`SampleItemC`] with embedded `link` field.
> +//! let list = unsafe { clist_create!(head, Item, SampleItemC, link) };
> +//!
> +//! // Iterate directly over typed items.
> +//! let mut found_0 = false;
> +//! let mut found_10 = false;
> +//! let mut found_20 = false;
> +//!
> +//! for item in list.iter() {
> +//! let val = item.value();
> +//! if val == 0 { found_0 = true; }
> +//! if val == 10 { found_10 = true; }
> +//! if val == 20 { found_20 = true; }
> +//! }
> +//!
> +//! assert!(found_0 && found_10 && found_20);
> +//! ```
> +
> +use core::{
> + iter::FusedIterator,
> + marker::PhantomData, //
> +};
> +
> +use crate::{
> + bindings,
> + types::Opaque, //
> +};
> +
> +use pin_init::PinInit;
> +
> +/// Wraps a `list_head` object for use in intrusive linked lists.
> +///
> +/// # Invariants
> +///
> +/// - [`CListHead`] represents an allocated and valid `list_head` structure.
> +/// - Once a [`CListHead`] is created in Rust, it will not be modified by non-Rust code.
> +/// - All `list_head` for individual items are not modified for the lifetime of [`CListHead`].
> +#[repr(transparent)]
> +pub(crate) struct CListHead(Opaque<bindings::list_head>);
> +
> +impl CListHead {
> + /// Create a `&CListHead` reference from a raw `list_head` pointer.
> + ///
> + /// # Safety
> + ///
> + /// - `ptr` must be a valid pointer to an allocated and initialized `list_head` structure.
> + /// - `ptr` must remain valid and unmodified for the lifetime `'a`.
> + #[inline]
> + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::list_head) -> &'a Self {
> + // SAFETY:
> + // - [`CListHead`] has same layout as `list_head`.
> + // - `ptr` is valid and unmodified for 'a.
> + unsafe { &*ptr.cast() }
> + }
> +
> + /// Get the raw `list_head` pointer.
> + #[inline]
> + pub(crate) fn as_raw(&self) -> *mut bindings::list_head {
> + self.0.get()
> + }
> +
> + /// Get the next [`CListHead`] in the list.
> + #[inline]
> + pub(crate) fn next(&self) -> &Self {
> + let raw = self.as_raw();
> + // SAFETY:
> + // - `self.as_raw()` is valid per type invariants.
> + // - The `next` pointer is guaranteed to be non-NULL.
> + unsafe { Self::from_raw((*raw).next) }
> + }
> +
> + /// Check if this node is linked in a list (not isolated).
> + #[inline]
> + pub(crate) fn is_linked(&self) -> bool {
> + let raw = self.as_raw();
> + // SAFETY: self.as_raw() is valid per type invariants.
> + unsafe { (*raw).next != raw && (*raw).prev != raw }
> + }
> +
> + /// Pin-initializer that initializes the list head.
> + pub(crate) fn new() -> impl PinInit<Self> {
> + // SAFETY: `INIT_LIST_HEAD` initializes `slot` to a valid empty list.
> + unsafe {
> + pin_init::pin_init_from_closure(move |slot: *mut Self| {
pin_init::ffi_init should be used for this.
> + bindings::INIT_LIST_HEAD(slot.cast());
> + Ok(())
> + })
> + }
> + }
> +}
> +
> +// SAFETY: [`CListHead`] can be sent to any thread.
> +unsafe impl Send for CListHead {}
> +
> +// SAFETY: [`CListHead`] can be shared among threads as it is not modified
> +// by non-Rust code per type invariants.
> +unsafe impl Sync for CListHead {}
> +
> +impl PartialEq for CListHead {
#[inline]
> + fn eq(&self, other: &Self) -> bool {
> + core::ptr::eq(self, other)
> + }
> +}
> +
> +impl Eq for CListHead {}
On Fri Feb 6, 2026 at 4:25 PM CET, Gary Guo wrote: > On Fri Feb 6, 2026 at 12:41 AM GMT, Joel Fernandes wrote: >> diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig >> index 22dd29cd50b5..2c3dec070645 100644 >> --- a/drivers/gpu/Kconfig >> +++ b/drivers/gpu/Kconfig >> @@ -1,7 +1,14 @@ >> # SPDX-License-Identifier: GPL-2.0 >> >> +config RUST_CLIST >> + bool >> + depends on RUST >> + help >> + Rust abstraction for interfacing with C linked lists. > > I am not sure if we need extra config entry. This is fully generic so shouldn't > generate any code unless there is an user. I also don't think we need a Kconfig for this. In any case, it shouln't be in drivers/gpu/Kconfig. >> + >> config GPU_BUDDY >> bool >> + select RUST_CLIST if RUST If we will have a Kconfig for this, this belongs in the GPU buddy patch. >> help >> A page based buddy allocator for GPU memory.
On 2/6/2026 10:53 AM, Danilo Krummrich wrote: > On Fri Feb 6, 2026 at 4:25 PM CET, Gary Guo wrote: >> On Fri Feb 6, 2026 at 12:41 AM GMT, Joel Fernandes wrote: >>> diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig >>> index 22dd29cd50b5..2c3dec070645 100644 >>> --- a/drivers/gpu/Kconfig >>> +++ b/drivers/gpu/Kconfig >>> @@ -1,7 +1,14 @@ >>> # SPDX-License-Identifier: GPL-2.0 >>> >>> +config RUST_CLIST >>> + bool >>> + depends on RUST >>> + help >>> + Rust abstraction for interfacing with C linked lists. >> >> I am not sure if we need extra config entry. This is fully generic so shouldn't >> generate any code unless there is an user. > > I also don't think we need a Kconfig for this. > > In any case, it shouln't be in drivers/gpu/Kconfig. Fair point, I believe I was having trouble compiling this into the kernel crate without warnings (I believe if !GPU_BUDDY). I'll try to drop it and see if we can get rid of it. > >>> + >>> config GPU_BUDDY >>> bool >>> + select RUST_CLIST if RUST > > If we will have a Kconfig for this, this belongs in the GPU buddy patch. You mean, in the GPU buddy bindings patch right? If so, yes, I will move it there. Thanks. -- Joel Fernandes
On Fri Feb 6, 2026 at 4:05 PM GMT, Joel Fernandes wrote:
>
>
> On 2/6/2026 10:53 AM, Danilo Krummrich wrote:
>> On Fri Feb 6, 2026 at 4:25 PM CET, Gary Guo wrote:
>>> On Fri Feb 6, 2026 at 12:41 AM GMT, Joel Fernandes wrote:
>>>> diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig
>>>> index 22dd29cd50b5..2c3dec070645 100644
>>>> --- a/drivers/gpu/Kconfig
>>>> +++ b/drivers/gpu/Kconfig
>>>> @@ -1,7 +1,14 @@
>>>> # SPDX-License-Identifier: GPL-2.0
>>>>
>>>> +config RUST_CLIST
>>>> + bool
>>>> + depends on RUST
>>>> + help
>>>> + Rust abstraction for interfacing with C linked lists.
>>>
>>> I am not sure if we need extra config entry. This is fully generic so shouldn't
>>> generate any code unless there is an user.
>>
>> I also don't think we need a Kconfig for this.
>>
>> In any case, it shouln't be in drivers/gpu/Kconfig.
>
> Fair point, I believe I was having trouble compiling this into the kernel crate
> without warnings (I believe if !GPU_BUDDY). I'll try to drop it and see if we
> can get rid of it.
If you run into dead code warnings, I think it is fine to just
#[allow(dead_code, reason = "all users might be cfg-ed out")]
the overhead of just let rustc type-checking this module isn't worth the extra
Kconfig plumbing, I think.
Best,
Gary
>
>>
>>>> +
>>>> config GPU_BUDDY
>>>> bool
>>>> + select RUST_CLIST if RUST
>>
>> If we will have a Kconfig for this, this belongs in the GPU buddy patch.
>
> You mean, in the GPU buddy bindings patch right? If so, yes, I will move it there.
>
> Thanks.
On Fri Feb 6, 2026 at 5:13 PM CET, Gary Guo wrote: > On Fri Feb 6, 2026 at 4:05 PM GMT, Joel Fernandes wrote: >> >> >> On 2/6/2026 10:53 AM, Danilo Krummrich wrote: >>> On Fri Feb 6, 2026 at 4:25 PM CET, Gary Guo wrote: >>>> On Fri Feb 6, 2026 at 12:41 AM GMT, Joel Fernandes wrote: >>>>> diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig >>>>> index 22dd29cd50b5..2c3dec070645 100644 >>>>> --- a/drivers/gpu/Kconfig >>>>> +++ b/drivers/gpu/Kconfig >>>>> @@ -1,7 +1,14 @@ >>>>> # SPDX-License-Identifier: GPL-2.0 >>>>> >>>>> +config RUST_CLIST >>>>> + bool >>>>> + depends on RUST >>>>> + help >>>>> + Rust abstraction for interfacing with C linked lists. >>>> >>>> I am not sure if we need extra config entry. This is fully generic so shouldn't >>>> generate any code unless there is an user. >>> >>> I also don't think we need a Kconfig for this. >>> >>> In any case, it shouln't be in drivers/gpu/Kconfig. >> >> Fair point, I believe I was having trouble compiling this into the kernel crate >> without warnings (I believe if !GPU_BUDDY). I'll try to drop it and see if we >> can get rid of it. > > If you run into dead code warnings, I think it is fine to just > > #[allow(dead_code, reason = "all users might be cfg-ed out")] > > the overhead of just let rustc type-checking this module isn't worth the extra > Kconfig plumbing, I think. You mean because there are pub(crate) in clist.rs? I don't think the Kconfig would help with that, nothing prevents people from enabling RUST_CLIST, but none of the users. Besides that, once we have the new build system, the users of CList are likely in other crates anyways, so I think we should just change things to pub.
On 2/6/2026 12:13 PM, Danilo Krummrich wrote: > On Fri Feb 6, 2026 at 5:13 PM CET, Gary Guo wrote: >> On Fri Feb 6, 2026 at 4:05 PM GMT, Joel Fernandes wrote: >>> >>> >>> On 2/6/2026 10:53 AM, Danilo Krummrich wrote: >>>> On Fri Feb 6, 2026 at 4:25 PM CET, Gary Guo wrote: >>>>> On Fri Feb 6, 2026 at 12:41 AM GMT, Joel Fernandes wrote: >>>>>> diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig >>>>>> index 22dd29cd50b5..2c3dec070645 100644 >>>>>> --- a/drivers/gpu/Kconfig >>>>>> +++ b/drivers/gpu/Kconfig >>>>>> @@ -1,7 +1,14 @@ >>>>>> # SPDX-License-Identifier: GPL-2.0 >>>>>> >>>>>> +config RUST_CLIST >>>>>> + bool >>>>>> + depends on RUST >>>>>> + help >>>>>> + Rust abstraction for interfacing with C linked lists. >>>>> >>>>> I am not sure if we need extra config entry. This is fully generic so shouldn't >>>>> generate any code unless there is an user. >>>> >>>> I also don't think we need a Kconfig for this. >>>> >>>> In any case, it shouln't be in drivers/gpu/Kconfig. >>> >>> Fair point, I believe I was having trouble compiling this into the kernel crate >>> without warnings (I believe if !GPU_BUDDY). I'll try to drop it and see if we >>> can get rid of it. >> >> If you run into dead code warnings, I think it is fine to just >> >> #[allow(dead_code, reason = "all users might be cfg-ed out")] >> >> the overhead of just let rustc type-checking this module isn't worth the extra >> Kconfig plumbing, I think. > > You mean because there are pub(crate) in clist.rs? I don't think the Kconfig > would help with that, nothing prevents people from enabling RUST_CLIST, but none > of the users. I think he means add the alloc annotation to suppress deadcode warnings and get rid of the Kconfig? > Besides that, once we have the new build system, the users of CList are likely > in other crates anyways, so I think we should just change things to pub. I agree with both approaches. Perhaps changing to pub is better to avoid churn in the future when other crates use it. -- Joel Fernandes
On Fri Feb 6, 2026 at 5:13 PM GMT, Danilo Krummrich wrote: > On Fri Feb 6, 2026 at 5:13 PM CET, Gary Guo wrote: >> On Fri Feb 6, 2026 at 4:05 PM GMT, Joel Fernandes wrote: >>> >>> >>> On 2/6/2026 10:53 AM, Danilo Krummrich wrote: >>>> On Fri Feb 6, 2026 at 4:25 PM CET, Gary Guo wrote: >>>>> On Fri Feb 6, 2026 at 12:41 AM GMT, Joel Fernandes wrote: >>>>>> diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig >>>>>> index 22dd29cd50b5..2c3dec070645 100644 >>>>>> --- a/drivers/gpu/Kconfig >>>>>> +++ b/drivers/gpu/Kconfig >>>>>> @@ -1,7 +1,14 @@ >>>>>> # SPDX-License-Identifier: GPL-2.0 >>>>>> >>>>>> +config RUST_CLIST >>>>>> + bool >>>>>> + depends on RUST >>>>>> + help >>>>>> + Rust abstraction for interfacing with C linked lists. >>>>> >>>>> I am not sure if we need extra config entry. This is fully generic so shouldn't >>>>> generate any code unless there is an user. >>>> >>>> I also don't think we need a Kconfig for this. >>>> >>>> In any case, it shouln't be in drivers/gpu/Kconfig. >>> >>> Fair point, I believe I was having trouble compiling this into the kernel crate >>> without warnings (I believe if !GPU_BUDDY). I'll try to drop it and see if we >>> can get rid of it. >> >> If you run into dead code warnings, I think it is fine to just >> >> #[allow(dead_code, reason = "all users might be cfg-ed out")] >> >> the overhead of just let rustc type-checking this module isn't worth the extra >> Kconfig plumbing, I think. > > You mean because there are pub(crate) in clist.rs? I don't think the Kconfig > would help with that, nothing prevents people from enabling RUST_CLIST, but none > of the users. > > Besides that, once we have the new build system, the users of CList are likely > in other crates anyways, so I think we should just change things to pub. I asked for this to be changed to `pub(crate)` because I think this isn't something that should be used by drivers. As you said, tt might be tricky to enforce that with new build system when subsystems are inside different crates. But until then I think it's better to limit visibility. Best, Gary
On Fri Feb 6, 2026 at 6:20 PM CET, Gary Guo wrote: > I asked for this to be changed to `pub(crate)` because I think this isn't > something that should be used by drivers. > > As you said, tt might be tricky to enforce that with new build system when > subsystems are inside different crates. But until then I think it's better to > limit visibility. It should *usually* not be used by drivers, but there are exceptions. For instance, it is perfectly valid to be used by Rust drivers that interact with C drivers. Besides that, my take on this is that we know that the new build system will come and that pub(crate) won't work in the long term, so why bother. I'd only use it in cases where you want to keep things local to the subsystem.
> On 6 Feb 2026, at 14:27, Danilo Krummrich <dakr@kernel.org> wrote: > > On Fri Feb 6, 2026 at 6:20 PM CET, Gary Guo wrote: >> I asked for this to be changed to `pub(crate)` because I think this isn't >> something that should be used by drivers. >> >> As you said, tt might be tricky to enforce that with new build system when >> subsystems are inside different crates. But until then I think it's better to >> limit visibility. > > It should *usually* not be used by drivers, but there are exceptions. For > instance, it is perfectly valid to be used by Rust drivers that interact with C > drivers. I agree with what Danilo said here. I don’t see a reason to forbid drivers from using this. If the reason is the unsafe bits, then isn’t it the same pattern used by impl_has_work!() anyways? i.e.: a macro that implements an unsafe trait so long as the driver gives it the right Work field. Seems equivalent in spirit to the clist_create macro introduced by this patch. — Daniel
© 2016 - 2026 Red Hat, Inc.