Re: [PATCH] rust: add a ring buffer implementation

From: Alice Ryhl

Date: Mon Feb 16 2026 - 07:25:54 EST


On Sun, Feb 15, 2026 at 09:24:59PM +0100, Andreas Hindborg wrote:
> Add a fixed-capacity FIFO ring buffer. The implementation uses a circular
> buffer with head and tail pointers, providing constant-time push and pop
> operations.
>
> The module includes a few tests covering basic operations, wrap-around
> behavior, interleaved push/pop sequences, and edge cases such as
> single-capacity buffers.
>
> Signed-off-by: Andreas Hindborg <a.hindborg@xxxxxxxxxx>

Why call this ringbuffer instead of matching the stdlib name for the
same collection? VecDeque.

And a more general question .. is there any chance we could avoid
rolling our own for this? Is there an impl in the kernel we could take?
Or could we vendor code from stdlib?

> rust/kernel/lib.rs | 1 +
> rust/kernel/ringbuffer.rs | 321 ++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 322 insertions(+)
>
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index f812cf1200428..d6555ccceb32f 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -133,6 +133,7 @@
> pub mod rbtree;
> pub mod regulator;
> pub mod revocable;
> +pub mod ringbuffer;
> pub mod scatterlist;
> pub mod security;
> pub mod seq_file;
> diff --git a/rust/kernel/ringbuffer.rs b/rust/kernel/ringbuffer.rs
> new file mode 100644
> index 0000000000000..9a66ebf1bb390
> --- /dev/null
> +++ b/rust/kernel/ringbuffer.rs

This should probably be in rust/kernel/alloc/ next to Vec?

> @@ -0,0 +1,321 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! A fixed-capacity FIFO ring buffer.
> +//!
> +//! This module provides [`RingBuffer`], a circular buffer implementation that
> +//! supports efficient push and pop operations at opposite ends of the buffer.
> +
> +use kernel::prelude::*;
> +
> +/// A fixed-capacity FIFO ring buffer.
> +///
> +/// `RingBuffer` is a circular buffer that allows pushing elements to the head
> +/// and popping elements from the tail in constant time. The buffer has a fixed
> +/// capacity specified at construction time and will return an error if a push
> +/// is attempted when full.
> +///
> +/// # Invariants
> +///
> +/// - `self.head` points at the next empty slot.
> +/// - `self.tail` points at the last full slot, except if the buffer is empty.
> +/// - The buffer is empty when `self.head == self.tail`.
> +/// - The buffer will always have at least one empty slot, even when full.
> +pub struct RingBuffer<T> {
> + nodes: KVec<Option<T>>,

This is quite inefficient storage for any type T that does not contain a
non-nullable pointer.

> + size: usize,

This size is just the vector's capacity. Field is redundant.

> + pub fn new(capacity: usize) -> Result<Self> {
> + let mut this = Self {
> + nodes: KVec::with_capacity(capacity + 1, GFP_KERNEL)?,

Should return ENOMEM on capacity == usize::MAX instead of panic.

> + /// Returns the number of available slots in the buffer.
> + ///
> + /// This is the number of elements that can be pushed before the buffer
> + /// becomes full.
> + pub fn free_count(&self) -> usize {
> + (if self.head >= self.tail {
> + self.size - (self.head - self.tail)
> + } else {
> + (self.size - self.tail) + self.head
> + } - 1)

The else branch should just be `self.tail - self.head`.

> +impl<T> Drop for RingBuffer<T> {
> + fn drop(&mut self) {
> + while !self.empty() {
> + drop(self.pop_tail().expect("Not empty"));
> + }

The destructor of KVec already drops the items.

Alice