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 <[email protected]> > --- > 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 <[email protected]> > +M: Alexandre Courbot <[email protected]> > +L: [email protected] > +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 <[email protected]> Some nits below: > + > RXRPC SOCKETS (AF_RXRPC) > M: David Howells <[email protected]> > M: Marc Dionne <[email protected]> > 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 {}
