Demonstrates usage of the clist module for iterating over C-managed linked lists. C code creates and populates the list, Rust code performs safe iteration using the clist abstraction.
Signed-off-by: Joel Fernandes <[email protected]> --- samples/rust/Kconfig | 11 +++ samples/rust/Makefile | 2 + samples/rust/rust_clist_c.c | 54 +++++++++++++ samples/rust/rust_clist_main.rs | 138 ++++++++++++++++++++++++++++++++ 4 files changed, 205 insertions(+) create mode 100644 samples/rust/rust_clist_c.c create mode 100644 samples/rust/rust_clist_main.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index c1cc787a9add..b45631e2593c 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -10,6 +10,17 @@ menuconfig SAMPLES_RUST if SAMPLES_RUST +config SAMPLE_RUST_CLIST + tristate "C Linked List sample" + help + This option builds the Rust CList sample demonstrating + the clist module for working with C list_head structures. + + To compile this as a module, choose M here: + the module will be called rust_clist. + + If unsure, say N. + config SAMPLE_RUST_CONFIGFS tristate "Configfs sample" depends on CONFIGFS_FS diff --git a/samples/rust/Makefile b/samples/rust/Makefile index cf8422f8f219..f8899c0df762 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 ccflags-y += -I$(src) # needed for trace events +obj-$(CONFIG_SAMPLE_RUST_CLIST) += rust_clist.o obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o @@ -14,6 +15,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) += rust_driver_faux.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY) += rust_driver_auxiliary.o obj-$(CONFIG_SAMPLE_RUST_CONFIGFS) += rust_configfs.o +rust_clist-y := rust_clist_main.o rust_clist_c.o rust_print-y := rust_print_main.o rust_print_events.o subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs diff --git a/samples/rust/rust_clist_c.c b/samples/rust/rust_clist_c.c new file mode 100644 index 000000000000..7a8f5e6c642a --- /dev/null +++ b/samples/rust/rust_clist_c.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/list.h> +#include <linux/slab.h> + +/* Sample item with embedded C list_head */ +struct sample_item_c { + int value; + struct list_head link; +}; + +/* Create a list_head and populate it with items */ +struct list_head *clist_sample_create(int count) +{ + struct list_head *head; + int i; + + head = kmalloc(sizeof(*head), GFP_KERNEL); + if (!head) + return NULL; + + INIT_LIST_HEAD(head); + + /* Populate with items */ + for (i = 0; i < count; i++) { + struct sample_item_c *item = kmalloc(sizeof(*item), GFP_KERNEL); + + if (!item) + continue; + + item->value = i * 10; + INIT_LIST_HEAD(&item->link); + list_add_tail(&item->link, head); + } + + return head; +} + +/* Free the list_head and all items */ +void clist_sample_free(struct list_head *head) +{ + struct sample_item_c *item, *tmp; + + if (!head) + return; + + /* Free all items in the list */ + list_for_each_entry_safe(item, tmp, head, link) { + list_del(&item->link); + kfree(item); + } + + kfree(head); +} diff --git a/samples/rust/rust_clist_main.rs b/samples/rust/rust_clist_main.rs new file mode 100644 index 000000000000..6600b0c79558 --- /dev/null +++ b/samples/rust/rust_clist_main.rs @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Sample for Rust code interfacing with C linked lists (list_head). +//! +//! This sample demonstrates iteration of a C-managed linked list using the [`clist`] module. +//! C code creates and populates the list, Rust code performs iteration. + +use core::ptr::NonNull; +use kernel::{ + bindings, + clist, + container_of, + prelude::*, // +}; + +module! { + type: RustClistModule, + name: "rust_clist", + authors: ["Joel Fernandes"], + description: "Rust Clist sample", + license: "GPL", +} + +// FFI declarations for C functions +extern "C" { + fn clist_sample_create(count: i32) -> *mut bindings::list_head; + fn clist_sample_free(head: *mut bindings::list_head); +} + +/// Sample item with embedded C list_head (This would typically be a C struct). +#[repr(C)] +struct SampleItemC { + value: i32, + link: bindings::list_head, +} + +/// Rust wrapper for SampleItemC object pointer allocated on the C side. +/// +/// # Invariants +/// +/// `ptr` points to a valid [`SampleItemC`] with an initialized embedded `list_head`. +struct SampleItem { + ptr: NonNull<SampleItemC>, +} + +impl clist::FromListHead for SampleItem { + unsafe fn from_list_head(link: *const bindings::list_head) -> Self { + // SAFETY: Caller ensures link points to a valid, initialized list_head. + unsafe { + let item_ptr = container_of!(link, SampleItemC, link) as *mut _; + SampleItem { + ptr: NonNull::new_unchecked(item_ptr), + } + } + } +} + +impl SampleItem { + /// Get the value from the sample item. + fn value(&self) -> i32 { + // SAFETY: self.ptr is valid as per the SampleItem invariants. + unsafe { (*self.ptr.as_ptr()).value } + } +} + +/// Clist struct - holds the C list_head and manages its lifecycle. +#[repr(C)] +struct RustClist { + list_head: NonNull<bindings::list_head>, +} + +// SAFETY: RustClist can be sent between threads since it only holds a pointer +// which can be accessed from any thread with proper synchronization. +unsafe impl Send for RustClist {} + +impl RustClist { + /// Create a container for a pointer to a C-allocated list_head. + fn new(list_head: *mut bindings::list_head) -> Self { + // SAFETY: Caller ensures list_head is a valid, non-null pointer. + Self { + list_head: unsafe { NonNull::new_unchecked(list_head) }, + } + } + + /// Demonstrate iteration over the list. + fn do_iteration(&self) { + // Wrap the C list_head with a Rust [`Clist`]. + // SAFETY: list_head is a valid, initialized, populated list_head. + let list = unsafe { clist::Clist::<SampleItem>::new(self.list_head.as_ptr()) }; + pr_info!("Created C list with items, is_empty: {}\n", list.is_empty()); + + // Iterate over the list. + pr_info!("Iterating over C list from Rust:\n"); + for item in list.iter() { + pr_info!(" Item value: {}\n", item.value()); + } + + pr_info!("Iteration complete\n"); + } +} + +impl Drop for RustClist { + fn drop(&mut self) { + // Free the list and all items using C FFI. + // SAFETY: list_head was allocated by clist_sample_create. + // C side handles freeing both the list_head and all items. + unsafe { + clist_sample_free(self.list_head.as_ptr()); + } + } +} + +struct RustClistModule; + +impl kernel::Module for RustClistModule { + fn init(_module: &'static ThisModule) -> Result<Self> { + pr_info!("Rust Clist sample (init)\n"); + + // C creates and populates a list_head with items. + // SAFETY: clist_sample_create allocates, initializes, and populates a list. + let c_list_head = unsafe { clist_sample_create(6) }; + if c_list_head.is_null() { + pr_err!("Failed to create and populate C list\n"); + return Err(ENOMEM); + } + + // Create the list container (separate from module). + let rust_clist = RustClist::new(c_list_head); + + // Demonstrate list operations. + rust_clist.do_iteration(); + + // rust_clist is dropped here, which frees the C list via Drop impl. + pr_info!("Rust Clist sample (exit)\n"); + + Ok(RustClistModule {}) + } +} -- 2.34.1
