[PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings

Joel Fernandes posted 8 patches 1 month, 1 week ago
[PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Joel Fernandes 1 month, 1 week ago
Add safe Rust abstractions over the Linux kernel's GPU buddy
allocator for physical memory management. The GPU buddy allocator
implements a binary buddy system useful for GPU physical memory
allocation. nova-core will use it for physical memory allocation.

Cc: Nikola Djukic <ndjukic@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
 rust/bindings/bindings_helper.h |  11 +
 rust/helpers/gpu.c              |  23 ++
 rust/helpers/helpers.c          |   1 +
 rust/kernel/gpu/buddy.rs        | 537 ++++++++++++++++++++++++++++++++
 rust/kernel/gpu/mod.rs          |   5 +
 rust/kernel/lib.rs              |   2 +
 6 files changed, 579 insertions(+)
 create mode 100644 rust/helpers/gpu.c
 create mode 100644 rust/kernel/gpu/buddy.rs
 create mode 100644 rust/kernel/gpu/mod.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 083cc44aa952..dbb765a9fdbd 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -29,6 +29,7 @@
 #include <linux/hrtimer_types.h>
 
 #include <linux/acpi.h>
+#include <linux/gpu_buddy.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_file.h>
@@ -146,6 +147,16 @@ const vm_flags_t RUST_CONST_HELPER_VM_MIXEDMAP = VM_MIXEDMAP;
 const vm_flags_t RUST_CONST_HELPER_VM_HUGEPAGE = VM_HUGEPAGE;
 const vm_flags_t RUST_CONST_HELPER_VM_NOHUGEPAGE = VM_NOHUGEPAGE;
 
+#if IS_ENABLED(CONFIG_GPU_BUDDY)
+const unsigned long RUST_CONST_HELPER_GPU_BUDDY_RANGE_ALLOCATION = GPU_BUDDY_RANGE_ALLOCATION;
+const unsigned long RUST_CONST_HELPER_GPU_BUDDY_TOPDOWN_ALLOCATION = GPU_BUDDY_TOPDOWN_ALLOCATION;
+const unsigned long RUST_CONST_HELPER_GPU_BUDDY_CONTIGUOUS_ALLOCATION =
+								GPU_BUDDY_CONTIGUOUS_ALLOCATION;
+const unsigned long RUST_CONST_HELPER_GPU_BUDDY_CLEAR_ALLOCATION = GPU_BUDDY_CLEAR_ALLOCATION;
+const unsigned long RUST_CONST_HELPER_GPU_BUDDY_CLEARED = GPU_BUDDY_CLEARED;
+const unsigned long RUST_CONST_HELPER_GPU_BUDDY_TRIM_DISABLE = GPU_BUDDY_TRIM_DISABLE;
+#endif
+
 #if IS_ENABLED(CONFIG_ANDROID_BINDER_IPC_RUST)
 #include "../../drivers/android/binder/rust_binder.h"
 #include "../../drivers/android/binder/rust_binder_events.h"
diff --git a/rust/helpers/gpu.c b/rust/helpers/gpu.c
new file mode 100644
index 000000000000..38b1a4e6bef8
--- /dev/null
+++ b/rust/helpers/gpu.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/gpu_buddy.h>
+
+#ifdef CONFIG_GPU_BUDDY
+
+__rust_helper u64 rust_helper_gpu_buddy_block_offset(const struct gpu_buddy_block *block)
+{
+	return gpu_buddy_block_offset(block);
+}
+
+__rust_helper unsigned int rust_helper_gpu_buddy_block_order(struct gpu_buddy_block *block)
+{
+	return gpu_buddy_block_order(block);
+}
+
+__rust_helper u64 rust_helper_gpu_buddy_block_size(struct gpu_buddy *mm,
+						   struct gpu_buddy_block *block)
+{
+	return gpu_buddy_block_size(mm, block);
+}
+
+#endif /* CONFIG_GPU_BUDDY */
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 724fcb8240ac..a53929ce52a3 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -32,6 +32,7 @@
 #include "err.c"
 #include "irq.c"
 #include "fs.c"
+#include "gpu.c"
 #include "io.c"
 #include "jump_label.c"
 #include "kunit.c"
diff --git a/rust/kernel/gpu/buddy.rs b/rust/kernel/gpu/buddy.rs
new file mode 100644
index 000000000000..5df7a2199671
--- /dev/null
+++ b/rust/kernel/gpu/buddy.rs
@@ -0,0 +1,537 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! GPU buddy allocator bindings.
+//!
+//! C header: [`include/linux/gpu_buddy.h`](srctree/include/linux/gpu_buddy.h)
+//!
+//! This module provides Rust abstractions over the Linux kernel's GPU buddy
+//! allocator, which implements a binary buddy memory allocator.
+//!
+//! The buddy allocator manages a contiguous address space and allocates blocks
+//! in power-of-two sizes, useful for GPU physical memory management.
+//!
+//! # Examples
+//!
+//! ```
+//! use kernel::{
+//!     gpu::buddy::{BuddyFlags, GpuBuddy, GpuBuddyAllocParams, GpuBuddyParams},
+//!     prelude::*,
+//!     sizes::*, //
+//! };
+//!
+//! // Create a 1GB buddy allocator with 4KB minimum chunk size.
+//! let buddy = GpuBuddy::new(GpuBuddyParams {
+//!     base_offset_bytes: 0,
+//!     physical_memory_size_bytes: SZ_1G as u64,
+//!     chunk_size_bytes: SZ_4K as u64,
+//! })?;
+//!
+//! // Verify initial state.
+//! assert_eq!(buddy.size(), SZ_1G as u64);
+//! assert_eq!(buddy.chunk_size(), SZ_4K as u64);
+//! let initial_free = buddy.free_memory_bytes();
+//!
+//! // Base allocation params - mutated between calls for field overrides.
+//! let mut params = GpuBuddyAllocParams {
+//!     start_range_address: 0,
+//!     end_range_address: 0,   // Entire range.
+//!     size_bytes: SZ_16M as u64,
+//!     min_block_size_bytes: SZ_16M as u64,
+//!     buddy_flags: BuddyFlags::try_new(BuddyFlags::RANGE_ALLOCATION)?,
+//! };
+//!
+//! // Test top-down allocation (allocates from highest addresses).
+//! params.buddy_flags = BuddyFlags::try_new(BuddyFlags::TOPDOWN_ALLOCATION)?;
+//! let topdown = KBox::pin_init(buddy.alloc_blocks(&params), GFP_KERNEL)?;
+//! assert_eq!(buddy.free_memory_bytes(), initial_free - SZ_16M as u64);
+//!
+//! for block in topdown.iter() {
+//!     assert_eq!(block.offset(), (SZ_1G - SZ_16M) as u64);
+//!     assert_eq!(block.order(), 12); // 2^12 pages
+//!     assert_eq!(block.size(), SZ_16M as u64);
+//! }
+//! drop(topdown);
+//! assert_eq!(buddy.free_memory_bytes(), initial_free);
+//!
+//! // Allocate 16MB - should result in a single 16MB block at offset 0.
+//! params.buddy_flags = BuddyFlags::try_new(BuddyFlags::RANGE_ALLOCATION)?;
+//! let allocated = KBox::pin_init(buddy.alloc_blocks(&params), GFP_KERNEL)?;
+//! assert_eq!(buddy.free_memory_bytes(), initial_free - SZ_16M as u64);
+//!
+//! for block in allocated.iter() {
+//!     assert_eq!(block.offset(), 0);
+//!     assert_eq!(block.order(), 12); // 2^12 pages
+//!     assert_eq!(block.size(), SZ_16M as u64);
+//! }
+//! drop(allocated);
+//! assert_eq!(buddy.free_memory_bytes(), initial_free);
+//!
+//! // Test non-contiguous allocation with fragmented memory.
+//! // Create fragmentation by allocating 4MB blocks at [0,4M) and [8M,12M).
+//! params.end_range_address = SZ_4M as u64;
+//! params.size_bytes = SZ_4M as u64;
+//! params.min_block_size_bytes = SZ_4M as u64;
+//! let frag1 = KBox::pin_init(buddy.alloc_blocks(&params), GFP_KERNEL)?;
+//! assert_eq!(buddy.free_memory_bytes(), initial_free - SZ_4M as u64);
+//!
+//! params.start_range_address = SZ_8M as u64;
+//! params.end_range_address = (SZ_8M + SZ_4M) as u64;
+//! let frag2 = KBox::pin_init(buddy.alloc_blocks(&params), GFP_KERNEL)?;
+//! assert_eq!(buddy.free_memory_bytes(), initial_free - SZ_8M as u64);
+//!
+//! // Allocate 8MB without CONTIGUOUS - should return 2 blocks from the holes.
+//! params.start_range_address = 0;
+//! params.end_range_address = SZ_16M as u64;
+//! params.size_bytes = SZ_8M as u64;
+//! let fragmented = KBox::pin_init(buddy.alloc_blocks(&params), GFP_KERNEL)?;
+//! assert_eq!(buddy.free_memory_bytes(), initial_free - (SZ_16M) as u64);
+//!
+//! let (mut count, mut total) = (0u32, 0u64);
+//! for block in fragmented.iter() {
+//!     // The 8MB allocation should return 2 blocks, each 4MB.
+//!     assert_eq!(block.size(), SZ_4M as u64);
+//!     total += block.size();
+//!     count += 1;
+//! }
+//! assert_eq!(total, SZ_8M as u64);
+//! assert_eq!(count, 2);
+//! drop(fragmented);
+//! drop(frag2);
+//! drop(frag1);
+//! assert_eq!(buddy.free_memory_bytes(), initial_free);
+//!
+//! // Test CONTIGUOUS failure when only fragmented space available.
+//! // Create a small buddy allocator with only 16MB of memory.
+//! let small = GpuBuddy::new(GpuBuddyParams {
+//!     base_offset_bytes: 0,
+//!     physical_memory_size_bytes: SZ_16M as u64,
+//!     chunk_size_bytes: SZ_4K as u64,
+//! })?;
+//!
+//! // Allocate 4MB blocks at [0,4M) and [8M,12M) to create fragmented memory.
+//! params.start_range_address = 0;
+//! params.end_range_address = SZ_4M as u64;
+//! params.size_bytes = SZ_4M as u64;
+//! let hole1 = KBox::pin_init(small.alloc_blocks(&params), GFP_KERNEL)?;
+//!
+//! params.start_range_address = SZ_8M as u64;
+//! params.end_range_address = (SZ_8M + SZ_4M) as u64;
+//! let hole2 = KBox::pin_init(small.alloc_blocks(&params), GFP_KERNEL)?;
+//!
+//! // 8MB contiguous should fail - only two non-contiguous 4MB holes exist.
+//! params.start_range_address = 0;
+//! params.end_range_address = 0;
+//! params.size_bytes = SZ_8M as u64;
+//! params.buddy_flags = BuddyFlags::try_new(BuddyFlags::CONTIGUOUS_ALLOCATION)?;
+//! let result = KBox::pin_init(small.alloc_blocks(&params), GFP_KERNEL);
+//! assert!(result.is_err());
+//! drop(hole2);
+//! drop(hole1);
+//!
+//! # Ok::<(), Error>(())
+//! ```
+
+use crate::{
+    bindings,
+    clist_create,
+    error::to_result,
+    ffi::clist::CListHead,
+    new_mutex,
+    prelude::*,
+    sync::{
+        lock::mutex::MutexGuard,
+        Arc,
+        Mutex, //
+    },
+    types::Opaque,
+};
+
+/// Flags for GPU buddy allocator operations.
+///
+/// These flags control the allocation behavior of the buddy allocator.
+#[derive(Clone, Copy, Default, PartialEq, Eq)]
+pub struct BuddyFlags(usize);
+
+impl BuddyFlags {
+    /// Range-based allocation from start to end addresses.
+    pub const RANGE_ALLOCATION: usize = bindings::GPU_BUDDY_RANGE_ALLOCATION;
+
+    /// Allocate from top of address space downward.
+    pub const TOPDOWN_ALLOCATION: usize = bindings::GPU_BUDDY_TOPDOWN_ALLOCATION;
+
+    /// Allocate physically contiguous blocks.
+    pub const CONTIGUOUS_ALLOCATION: usize = bindings::GPU_BUDDY_CONTIGUOUS_ALLOCATION;
+
+    /// Request allocation from the cleared (zeroed) memory. The zero'ing is not
+    /// done by the allocator, but by the caller before freeing old blocks.
+    pub const CLEAR_ALLOCATION: usize = bindings::GPU_BUDDY_CLEAR_ALLOCATION;
+
+    /// Disable trimming of partially used blocks.
+    pub const TRIM_DISABLE: usize = bindings::GPU_BUDDY_TRIM_DISABLE;
+
+    /// Mark blocks as cleared (zeroed) when freeing. When set during free,
+    /// indicates that the caller has already zeroed the memory.
+    pub const CLEARED: usize = bindings::GPU_BUDDY_CLEARED;
+
+    /// Create [`BuddyFlags`] from a raw value with validation.
+    ///
+    /// Use `|` operator to combine flags if needed, before calling this method.
+    pub fn try_new(flags: usize) -> Result<Self> {
+        // Flags must not exceed u32::MAX to satisfy the GPU buddy allocator C API.
+        if flags > u32::MAX as usize {
+            return Err(EINVAL);
+        }
+
+        // `TOPDOWN_ALLOCATION` only works without `RANGE_ALLOCATION`. When both are
+        // set, `TOPDOWN_ALLOCATION` is silently ignored by the allocator. Reject this.
+        if (flags & Self::RANGE_ALLOCATION) != 0 && (flags & Self::TOPDOWN_ALLOCATION) != 0 {
+            return Err(EINVAL);
+        }
+
+        Ok(Self(flags))
+    }
+
+    /// Get raw value of the flags.
+    pub(crate) fn as_raw(self) -> usize {
+        self.0
+    }
+}
+
+/// Parameters for creating a GPU buddy allocator.
+pub struct GpuBuddyParams {
+    /// Base offset in bytes where the managed memory region starts.
+    /// Allocations will be offset by this value.
+    pub base_offset_bytes: u64,
+    /// Total physical memory size managed by the allocator in bytes.
+    pub physical_memory_size_bytes: u64,
+    /// Minimum allocation unit / chunk size in bytes, must be >= 4KB.
+    pub chunk_size_bytes: u64,
+}
+
+/// Parameters for allocating blocks from a GPU buddy allocator.
+pub struct GpuBuddyAllocParams {
+    /// Start of allocation range in bytes. Use 0 for beginning.
+    pub start_range_address: u64,
+    /// End of allocation range in bytes. Use 0 for entire range.
+    pub end_range_address: u64,
+    /// Total size to allocate in bytes.
+    pub size_bytes: u64,
+    /// Minimum block size for fragmented allocations in bytes.
+    pub min_block_size_bytes: u64,
+    /// Buddy allocator behavior flags.
+    pub buddy_flags: BuddyFlags,
+}
+
+/// Inner structure holding the actual buddy allocator.
+///
+/// # Synchronization
+///
+/// The C `gpu_buddy` API requires synchronization (see `include/linux/gpu_buddy.h`).
+/// The internal [`GpuBuddyGuard`] ensures that the lock is held for all
+/// allocator and free operations, preventing races between concurrent allocations
+/// and the freeing that occurs when [`AllocatedBlocks`] is dropped.
+///
+/// # Invariants
+///
+/// The inner [`Opaque`] contains a valid, initialized buddy allocator.
+#[pin_data(PinnedDrop)]
+struct GpuBuddyInner {
+    #[pin]
+    inner: Opaque<bindings::gpu_buddy>,
+    // TODO: Replace `Mutex<()>` with `Mutex<Opaque<..>>` once `Mutex::new()`
+    // accepts `impl PinInit<T>`.
+    #[pin]
+    lock: Mutex<()>,
+    /// Base offset for all allocations (does not change after init).
+    base_offset: u64,
+    /// Cached chunk size (does not change after init).
+    chunk_size: u64,
+    /// Cached total size (does not change after init).
+    size: u64,
+}
+
+impl GpuBuddyInner {
+    /// Create a pin-initializer for the buddy allocator.
+    fn new(params: GpuBuddyParams) -> impl PinInit<Self, Error> {
+        let base_offset = params.base_offset_bytes;
+        let size = params.physical_memory_size_bytes;
+        let chunk_size = params.chunk_size_bytes;
+
+        try_pin_init!(Self {
+            inner <- Opaque::try_ffi_init(|ptr| {
+                // SAFETY: ptr points to valid uninitialized memory from the pin-init
+                // infrastructure. gpu_buddy_init will initialize the structure.
+                to_result(unsafe { bindings::gpu_buddy_init(ptr, size, chunk_size) })
+            }),
+            lock <- new_mutex!(()),
+            base_offset: base_offset,
+            chunk_size: chunk_size,
+            size: size,
+        })
+    }
+
+    /// Lock the mutex and return a guard for accessing the allocator.
+    fn lock(&self) -> GpuBuddyGuard<'_> {
+        GpuBuddyGuard {
+            inner: self,
+            _guard: self.lock.lock(),
+        }
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for GpuBuddyInner {
+    fn drop(self: Pin<&mut Self>) {
+        let guard = self.lock();
+
+        // SAFETY: guard provides exclusive access to the allocator.
+        unsafe {
+            bindings::gpu_buddy_fini(guard.as_raw());
+        }
+    }
+}
+
+// SAFETY: [`GpuBuddyInner`] can be sent between threads.
+unsafe impl Send for GpuBuddyInner {}
+
+// SAFETY: [`GpuBuddyInner`] is `Sync` because the internal [`GpuBuddyGuard`]
+// serializes all access to the C allocator, preventing data races.
+unsafe impl Sync for GpuBuddyInner {}
+
+/// Guard that proves the lock is held, enabling access to the allocator.
+///
+/// # Invariants
+///
+/// The inner `_guard` holds the lock for the duration of this guard's lifetime.
+pub(crate) struct GpuBuddyGuard<'a> {
+    inner: &'a GpuBuddyInner,
+    _guard: MutexGuard<'a, ()>,
+}
+
+impl GpuBuddyGuard<'_> {
+    /// Get a raw pointer to the underlying C `gpu_buddy` structure.
+    fn as_raw(&self) -> *mut bindings::gpu_buddy {
+        self.inner.inner.get()
+    }
+}
+
+/// GPU buddy allocator instance.
+///
+/// This structure wraps the C `gpu_buddy` allocator using reference counting.
+/// The allocator is automatically cleaned up when all references are dropped.
+///
+/// # Invariants
+///
+/// The inner [`Arc`] points to a valid, initialized GPU buddy allocator.
+pub struct GpuBuddy(Arc<GpuBuddyInner>);
+
+impl GpuBuddy {
+    /// Create a new buddy allocator.
+    ///
+    /// Creates a buddy allocator that manages a contiguous address space of the given
+    /// size, with the specified minimum allocation unit (chunk_size must be at least 4KB).
+    pub fn new(params: GpuBuddyParams) -> Result<Self> {
+        Ok(Self(Arc::pin_init(GpuBuddyInner::new(params), GFP_KERNEL)?))
+    }
+
+    /// Get the base offset for allocations.
+    pub fn base_offset(&self) -> u64 {
+        self.0.base_offset
+    }
+
+    /// Get the chunk size (minimum allocation unit).
+    pub fn chunk_size(&self) -> u64 {
+        self.0.chunk_size
+    }
+
+    /// Get the total managed size.
+    pub fn size(&self) -> u64 {
+        self.0.size
+    }
+
+    /// Get the available (free) memory in bytes.
+    pub fn free_memory_bytes(&self) -> u64 {
+        let guard = self.0.lock();
+        // SAFETY: guard provides exclusive access to the allocator.
+        unsafe { (*guard.as_raw()).avail }
+    }
+
+    /// Allocate blocks from the buddy allocator.
+    ///
+    /// Returns a pin-initializer for [`AllocatedBlocks`].
+    ///
+    /// Takes `&self` instead of `&mut self` because the internal [`Mutex`] provides
+    /// synchronization - no external `&mut` exclusivity needed.
+    pub fn alloc_blocks(
+        &self,
+        params: &GpuBuddyAllocParams,
+    ) -> impl PinInit<AllocatedBlocks, Error> {
+        let buddy_arc = Arc::clone(&self.0);
+        let start = params.start_range_address;
+        let end = params.end_range_address;
+        let size = params.size_bytes;
+        let min_block_size = params.min_block_size_bytes;
+        let flags = params.buddy_flags;
+
+        // Create pin-initializer that initializes list and allocates blocks.
+        try_pin_init!(AllocatedBlocks {
+            buddy: buddy_arc,
+            list <- CListHead::new(),
+            flags: flags,
+            _: {
+                // Lock while allocating to serialize with concurrent frees.
+                let guard = buddy.lock();
+
+                // SAFETY: `guard` provides exclusive access to the buddy allocator.
+                to_result(unsafe {
+                    bindings::gpu_buddy_alloc_blocks(
+                        guard.as_raw(),
+                        start,
+                        end,
+                        size,
+                        min_block_size,
+                        list.as_raw(),
+                        flags.as_raw(),
+                    )
+                })?
+            }
+        })
+    }
+}
+
+/// Allocated blocks from the buddy allocator with automatic cleanup.
+///
+/// This structure owns a list of allocated blocks and ensures they are
+/// automatically freed when dropped. Use `iter()` to iterate over all
+/// allocated [`Block`] structures.
+///
+/// # Invariants
+///
+/// - `list` is an initialized, valid list head containing allocated blocks.
+/// - `buddy` references a valid [`GpuBuddyInner`].
+#[pin_data(PinnedDrop)]
+pub struct AllocatedBlocks {
+    #[pin]
+    list: CListHead,
+    buddy: Arc<GpuBuddyInner>,
+    flags: BuddyFlags,
+}
+
+impl AllocatedBlocks {
+    /// Check if the block list is empty.
+    pub fn is_empty(&self) -> bool {
+        // An empty list head points to itself.
+        !self.list.is_linked()
+    }
+
+    /// Iterate over allocated blocks.
+    ///
+    /// Returns an iterator yielding [`AllocatedBlock`] values. Each [`AllocatedBlock`]
+    /// borrows `self` and is only valid for the duration of that borrow.
+    pub fn iter(&self) -> impl Iterator<Item = AllocatedBlock<'_>> + '_ {
+        // SAFETY: list contains gpu_buddy_block items linked via __bindgen_anon_1.link.
+        let clist = unsafe {
+            clist_create!(
+                self.list.as_raw(),
+                Block,
+                bindings::gpu_buddy_block,
+                __bindgen_anon_1.link
+            )
+        };
+
+        clist
+            .iter()
+            .map(|block| AllocatedBlock { block, alloc: self })
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for AllocatedBlocks {
+    fn drop(self: Pin<&mut Self>) {
+        let guard = self.buddy.lock();
+
+        // SAFETY:
+        // - list is valid per the type's invariants.
+        // - guard provides exclusive access to the allocator.
+        // CAST: BuddyFlags were validated to fit in u32 at construction.
+        unsafe {
+            bindings::gpu_buddy_free_list(
+                guard.as_raw(),
+                self.list.as_raw(),
+                self.flags.as_raw() as u32,
+            );
+        }
+    }
+}
+
+/// A GPU buddy block.
+///
+/// Transparent wrapper over C `gpu_buddy_block` structure. This type is returned
+/// as references from [`CListIter`] during iteration over [`AllocatedBlocks`].
+///
+/// # Invariants
+///
+/// The inner [`Opaque`] contains a valid, allocated `gpu_buddy_block`.
+#[repr(transparent)]
+pub struct Block(Opaque<bindings::gpu_buddy_block>);
+
+impl Block {
+    /// Get a raw pointer to the underlying C block.
+    fn as_raw(&self) -> *mut bindings::gpu_buddy_block {
+        self.0.get()
+    }
+
+    /// Get the block's offset in the address space.
+    pub(crate) fn offset(&self) -> u64 {
+        // SAFETY: self.as_raw() is valid per the type's invariants.
+        unsafe { bindings::gpu_buddy_block_offset(self.as_raw()) }
+    }
+
+    /// Get the block order.
+    pub(crate) fn order(&self) -> u32 {
+        // SAFETY: self.as_raw() is valid per the type's invariants.
+        unsafe { bindings::gpu_buddy_block_order(self.as_raw()) }
+    }
+}
+
+// SAFETY: `Block` is is not modified after allocation for the lifetime
+// of `AllocatedBlock`.
+unsafe impl Send for Block {}
+
+// SAFETY: `Block` is is not modified after allocation for the lifetime
+// of `AllocatedBlock`.
+unsafe impl Sync for Block {}
+
+/// An allocated block with access to the GPU buddy allocator.
+///
+/// It is returned by [`AllocatedBlocks::iter()`] and provides access to the
+/// GPU buddy allocator required for some accessors.
+///
+/// # Invariants
+///
+/// - `block` is a valid reference to an allocated [`Block`].
+/// - `alloc` is a valid reference to the [`AllocatedBlocks`] that owns this block.
+pub struct AllocatedBlock<'a> {
+    block: &'a Block,
+    alloc: &'a AllocatedBlocks,
+}
+
+impl AllocatedBlock<'_> {
+    /// Get the block's offset in the address space.
+    ///
+    /// Returns the absolute offset including the allocator's base offset.
+    /// This is the actual address to use for accessing the allocated memory.
+    pub fn offset(&self) -> u64 {
+        self.alloc.buddy.base_offset + self.block.offset()
+    }
+
+    /// Get the block order (size = chunk_size << order).
+    pub fn order(&self) -> u32 {
+        self.block.order()
+    }
+
+    /// Get the block's size in bytes.
+    pub fn size(&self) -> u64 {
+        self.alloc.buddy.chunk_size << self.block.order()
+    }
+}
diff --git a/rust/kernel/gpu/mod.rs b/rust/kernel/gpu/mod.rs
new file mode 100644
index 000000000000..8f25e6367edc
--- /dev/null
+++ b/rust/kernel/gpu/mod.rs
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! GPU subsystem abstractions.
+
+pub mod buddy;
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 0a77b4c0ffeb..1cd6feff4f02 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -96,6 +96,8 @@
 pub mod firmware;
 pub mod fmt;
 pub mod fs;
+#[cfg(CONFIG_GPU_BUDDY)]
+pub mod gpu;
 #[cfg(CONFIG_I2C = "y")]
 pub mod i2c;
 pub mod id_pool;
-- 
2.34.1
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Eliot Courtney 1 month, 1 week ago
On Thu Feb 19, 2026 at 5:55 AM JST, Joel Fernandes wrote:
> +__rust_helper u64 rust_helper_gpu_buddy_block_size(struct gpu_buddy *mm,
> +						   struct gpu_buddy_block *block)
> +{
> +	return gpu_buddy_block_size(mm, block);
> +}
> +

Will `rust_helper_gpu_buddy_block_size` be used in the future? It
doesn't appear to be used in buddy.rs.
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Joel Fernandes 1 month, 1 week ago

On 2/20/2026 3:22 AM, Eliot Courtney wrote:
> On Thu Feb 19, 2026 at 5:55 AM JST, Joel Fernandes wrote:
>> +__rust_helper u64 rust_helper_gpu_buddy_block_size(struct gpu_buddy *mm,
>> +						   struct gpu_buddy_block *block)
>> +{
>> +	return gpu_buddy_block_size(mm, block);
>> +}
>> +
> 
> Will `rust_helper_gpu_buddy_block_size` be used in the future? It
> doesn't appear to be used in buddy.rs.

I think it is worth keeping because it is a pretty basic API the underlying
infrastructure. Finding the size of a block can be important in the future
IMO. It is only few lines, no?

Thanks,

--
Joel Fernandes
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Danilo Krummrich 1 month, 1 week ago
On Fri Feb 20, 2026 at 3:54 PM CET, Joel Fernandes wrote:
>
>
> On 2/20/2026 3:22 AM, Eliot Courtney wrote:
>> On Thu Feb 19, 2026 at 5:55 AM JST, Joel Fernandes wrote:
>>> +__rust_helper u64 rust_helper_gpu_buddy_block_size(struct gpu_buddy *mm,
>>> +						   struct gpu_buddy_block *block)
>>> +{
>>> +	return gpu_buddy_block_size(mm, block);
>>> +}
>>> +
>> 
>> Will `rust_helper_gpu_buddy_block_size` be used in the future? It
>> doesn't appear to be used in buddy.rs.
>
> I think it is worth keeping because it is a pretty basic API the underlying
> infrastructure. Finding the size of a block can be important in the future
> IMO. It is only few lines, no?

The helper should be added with the code using it.
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Joel Fernandes 1 month, 1 week ago

On 2/20/2026 10:53 AM, Danilo Krummrich wrote:
> On Fri Feb 20, 2026 at 3:54 PM CET, Joel Fernandes wrote:
>>
>>
>> On 2/20/2026 3:22 AM, Eliot Courtney wrote:
>>> On Thu Feb 19, 2026 at 5:55 AM JST, Joel Fernandes wrote:
>>>> +__rust_helper u64 rust_helper_gpu_buddy_block_size(struct gpu_buddy *mm,
>>>> +						   struct gpu_buddy_block *block)
>>>> +{
>>>> +	return gpu_buddy_block_size(mm, block);
>>>> +}
>>>> +
>>>
>>> Will `rust_helper_gpu_buddy_block_size` be used in the future? It
>>> doesn't appear to be used in buddy.rs.
>>
>> I think it is worth keeping because it is a pretty basic API the underlying
>> infrastructure. Finding the size of a block can be important in the future
>> IMO. It is only few lines, no?
> 
> The helper should be added with the code using it.

I will add this as a test case to exercise it and include it in that patch.

thanks,

-- 
Joel Fernandes
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Danilo Krummrich 1 month, 1 week ago
On Fri Feb 20, 2026 at 10:20 PM CET, Joel Fernandes wrote:
>
>
> On 2/20/2026 10:53 AM, Danilo Krummrich wrote:
>> On Fri Feb 20, 2026 at 3:54 PM CET, Joel Fernandes wrote:
>>>
>>>
>>> On 2/20/2026 3:22 AM, Eliot Courtney wrote:
>>>> On Thu Feb 19, 2026 at 5:55 AM JST, Joel Fernandes wrote:
>>>>> +__rust_helper u64 rust_helper_gpu_buddy_block_size(struct gpu_buddy *mm,
>>>>> +						   struct gpu_buddy_block *block)
>>>>> +{
>>>>> +	return gpu_buddy_block_size(mm, block);
>>>>> +}
>>>>> +
>>>>
>>>> Will `rust_helper_gpu_buddy_block_size` be used in the future? It
>>>> doesn't appear to be used in buddy.rs.
>>>
>>> I think it is worth keeping because it is a pretty basic API the underlying
>>> infrastructure. Finding the size of a block can be important in the future
>>> IMO. It is only few lines, no?
>> 
>> The helper should be added with the code using it.
>
> I will add this as a test case to exercise it and include it in that patch.

A test case for a helper? Or do you mean you will add the actual abstraction?
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Joel Fernandes 1 month, 1 week ago

On 2/20/2026 6:43 PM, Danilo Krummrich wrote:
> On Fri Feb 20, 2026 at 10:20 PM CET, Joel Fernandes wrote:
>>
>>
>> On 2/20/2026 10:53 AM, Danilo Krummrich wrote:
>>> On Fri Feb 20, 2026 at 3:54 PM CET, Joel Fernandes wrote:
>>>>
>>>>
>>>> On 2/20/2026 3:22 AM, Eliot Courtney wrote:
>>>>> On Thu Feb 19, 2026 at 5:55 AM JST, Joel Fernandes wrote:
>>>>>> +__rust_helper u64 rust_helper_gpu_buddy_block_size(struct gpu_buddy *mm,
>>>>>> +						   struct gpu_buddy_block *block)
>>>>>> +{
>>>>>> +	return gpu_buddy_block_size(mm, block);
>>>>>> +}
>>>>>> +
>>>>>
>>>>> Will `rust_helper_gpu_buddy_block_size` be used in the future? It
>>>>> doesn't appear to be used in buddy.rs.
>>>>
>>>> I think it is worth keeping because it is a pretty basic API the underlying
>>>> infrastructure. Finding the size of a block can be important in the future
>>>> IMO. It is only few lines, no?
>>>
>>> The helper should be added with the code using it.
>>
>> I will add this as a test case to exercise it and include it in that patch.
> 
> A test case for a helper? Or do you mean you will add the actual abstraction?

Actual abstraction.

thanks,

--
Joel Fernandes
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Joel Fernandes 1 month, 1 week ago

On 2/20/2026 9:54 AM, Joel Fernandes wrote:
> 
> 
> On 2/20/2026 3:22 AM, Eliot Courtney wrote:
>> On Thu Feb 19, 2026 at 5:55 AM JST, Joel Fernandes wrote:
>>> +__rust_helper u64 rust_helper_gpu_buddy_block_size(struct gpu_buddy *mm,
>>> +						   struct gpu_buddy_block *block)
>>> +{
>>> +	return gpu_buddy_block_size(mm, block);
>>> +}
>>> +
>>
>> Will `rust_helper_gpu_buddy_block_size` be used in the future? It
>> doesn't appear to be used in buddy.rs.
> 
> I think it is worth keeping because it is a pretty basic API the underlying
> infrastructure. Finding the size of a block can be important in the future
> IMO. It is only few lines, no?

By the way, this can become important for non-contiguous physical memory
allocations where an allocation is split across different blocks. In that
case, we would the information size of each individual block, not just the
whole allocation. I could probably add a test case for that.

--
Joel Fernandes
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Danilo Krummrich 1 month, 1 week ago
(Cc: Arun, Christian)

On Wed Feb 18, 2026 at 9:55 PM CET, Joel Fernandes wrote:
> Add safe Rust abstractions over the Linux kernel's GPU buddy
> allocator for physical memory management. The GPU buddy allocator
> implements a binary buddy system useful for GPU physical memory
> allocation. nova-core will use it for physical memory allocation.
>
> Cc: Nikola Djukic <ndjukic@nvidia.com>
> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>

The patch should also update the MAINTAINERS file accordingly.

(I will go through the code later on.)
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Joel Fernandes 1 month, 1 week ago
On Thu, Feb 19, 2026 at 02:18:31PM +0100, Danilo Krummrich wrote:
> The patch should also update the MAINTAINERS file accordingly.

Will add the MAINTAINERS update for GPU buddy bindings.

Thanks,
Joel
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Alexandre Courbot 1 month, 1 week ago
Just a few things caught when building.

On Thu Feb 19, 2026 at 5:55 AM JST, Joel Fernandes wrote:
<snip>
> +use crate::{
> +    bindings,
> +    clist_create,
> +    error::to_result,
> +    ffi::clist::CListHead,
> +    new_mutex,
> +    prelude::*,
> +    sync::{
> +        lock::mutex::MutexGuard,
> +        Arc,
> +        Mutex, //
> +    },
> +    types::Opaque,

Need a `//` or `rustfmt` will reformat.

<snip>
> +#[pinned_drop]
> +impl PinnedDrop for GpuBuddyInner {
> +    fn drop(self: Pin<&mut Self>) {
> +        let guard = self.lock();
> +
> +        // SAFETY: guard provides exclusive access to the allocator.
> +        unsafe {
> +            bindings::gpu_buddy_fini(guard.as_raw());
> +        }
> +    }
> +}
> +
> +// SAFETY: [`GpuBuddyInner`] can be sent between threads.

No need to link on non-doccomments.

> +unsafe impl Send for GpuBuddyInner {}
> +
> +// SAFETY: [`GpuBuddyInner`] is `Sync` because the internal [`GpuBuddyGuard`]
> +// serializes all access to the C allocator, preventing data races.

Here as well.

<snip>
> +/// Allocated blocks from the buddy allocator with automatic cleanup.
> +///
> +/// This structure owns a list of allocated blocks and ensures they are
> +/// automatically freed when dropped. Use `iter()` to iterate over all
> +/// allocated [`Block`] structures.
> +///
> +/// # Invariants
> +///
> +/// - `list` is an initialized, valid list head containing allocated blocks.
> +/// - `buddy` references a valid [`GpuBuddyInner`].

rustdoc complains that this links to a private item in a public doc - we
should not mention `GpuBuddyInner` here.

> +#[pin_data(PinnedDrop)]
> +pub struct AllocatedBlocks {
> +    #[pin]
> +    list: CListHead,
> +    buddy: Arc<GpuBuddyInner>,
> +    flags: BuddyFlags,
> +}
> +
> +impl AllocatedBlocks {
> +    /// Check if the block list is empty.
> +    pub fn is_empty(&self) -> bool {
> +        // An empty list head points to itself.
> +        !self.list.is_linked()
> +    }
> +
> +    /// Iterate over allocated blocks.
> +    ///
> +    /// Returns an iterator yielding [`AllocatedBlock`] values. Each [`AllocatedBlock`]
> +    /// borrows `self` and is only valid for the duration of that borrow.
> +    pub fn iter(&self) -> impl Iterator<Item = AllocatedBlock<'_>> + '_ {
> +        // SAFETY: list contains gpu_buddy_block items linked via __bindgen_anon_1.link.
> +        let clist = unsafe {
> +            clist_create!(
> +                self.list.as_raw(),
> +                Block,
> +                bindings::gpu_buddy_block,
> +                __bindgen_anon_1.link
> +            )
> +        };
> +
> +        clist
> +            .iter()
> +            .map(|block| AllocatedBlock { block, alloc: self })
> +    }
> +}
> +
> +#[pinned_drop]
> +impl PinnedDrop for AllocatedBlocks {
> +    fn drop(self: Pin<&mut Self>) {
> +        let guard = self.buddy.lock();
> +
> +        // SAFETY:
> +        // - list is valid per the type's invariants.
> +        // - guard provides exclusive access to the allocator.
> +        // CAST: BuddyFlags were validated to fit in u32 at construction.
> +        unsafe {
> +            bindings::gpu_buddy_free_list(
> +                guard.as_raw(),
> +                self.list.as_raw(),
> +                self.flags.as_raw() as u32,
> +            );
> +        }
> +    }
> +}
> +
> +/// A GPU buddy block.
> +///
> +/// Transparent wrapper over C `gpu_buddy_block` structure. This type is returned
> +/// as references from [`CListIter`] during iteration over [`AllocatedBlocks`].

Link should be [`CListIter`](kernel::ffi::clist::CListIter) to resolve.
But maybe we don't need to share that detail in the public
documentation?
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Joel Fernandes 1 month, 1 week ago
On Thu, Feb 19, 2026 at 02:13:37PM +0900, Alexandre Courbot wrote:
> > +    types::Opaque,
>
> Need a `//` or `rustfmt` will reformat.

Fixed, thanks.

> > +// SAFETY: [`GpuBuddyInner`] can be sent between threads.
>
> No need to link on non-doccomments.

Fixed. Removed the brackets from SAFETY comments for GpuBuddyInner
and GpuBuddyGuard.

> > +/// - `buddy` references a valid [`GpuBuddyInner`].
>
> rustdoc complains that this links to a private item in a public doc - we
> should not mention `GpuBuddyInner` here.

Per Miguel's reply, I've kept the mention but removed the square
brackets so it doesn't try to create a link. This way it's still
mentioned for practical reference without triggering the rustdoc
warning.

> > /// as references from [`CListIter`] during iteration over [`AllocatedBlocks`].
>
> Link should be [`CListIter`](kernel::ffi::clist::CListIter) to resolve.
> But maybe we don't need to share that detail in the public
> documentation?

Agreed, removed the CListIter reference from the public doc. It now
just says "as references during iteration over AllocatedBlocks".

Thanks,
Joel
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Alexandre Courbot 1 month, 1 week ago
On Fri Feb 20, 2026 at 12:31 AM JST, Joel Fernandes wrote:
> On Thu, Feb 19, 2026 at 02:13:37PM +0900, Alexandre Courbot wrote:
>> > +    types::Opaque,
>>
>> Need a `//` or `rustfmt` will reformat.
>
> Fixed, thanks.
>
>> > +// SAFETY: [`GpuBuddyInner`] can be sent between threads.
>>
>> No need to link on non-doccomments.
>
> Fixed. Removed the brackets from SAFETY comments for GpuBuddyInner
> and GpuBuddyGuard.
>
>> > +/// - `buddy` references a valid [`GpuBuddyInner`].
>>
>> rustdoc complains that this links to a private item in a public doc - we
>> should not mention `GpuBuddyInner` here.
>
> Per Miguel's reply, I've kept the mention but removed the square
> brackets so it doesn't try to create a link. This way it's still
> mentioned for practical reference without triggering the rustdoc
> warning.

Won't it be confusing for readers of the public documentation? If a type
is private, it shouldn't be needed to folks who read the HTML.

This sounds like we instead want a regular `//` comment somewhere to
guide those who brave the code.
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Joel Fernandes 1 month, 1 week ago

On 2/19/2026 8:56 PM, Alexandre Courbot wrote:
> On Fri Feb 20, 2026 at 12:31 AM JST, Joel Fernandes wrote:
>> On Thu, Feb 19, 2026 at 02:13:37PM +0900, Alexandre Courbot wrote:
>>>> +    types::Opaque,
>>>
>>> Need a `//` or `rustfmt` will reformat.
>>
>> Fixed, thanks.
>>
>>>> +// SAFETY: [`GpuBuddyInner`] can be sent between threads.
>>>
>>> No need to link on non-doccomments.
>>
>> Fixed. Removed the brackets from SAFETY comments for GpuBuddyInner
>> and GpuBuddyGuard.
>>
>>>> +/// - `buddy` references a valid [`GpuBuddyInner`].
>>>
>>> rustdoc complains that this links to a private item in a public doc - we
>>> should not mention `GpuBuddyInner` here.
>>
>> Per Miguel's reply, I've kept the mention but removed the square
>> brackets so it doesn't try to create a link. This way it's still
>> mentioned for practical reference without triggering the rustdoc
>> warning.
> 
> Won't it be confusing for readers of the public documentation? If a type
> is private, it shouldn't be needed to folks who read the HTML.
> 
> This sounds like we instead want a regular `//` comment somewhere to
> guide those who brave the code.

You are right about the audience of the docs perhaps not requiring this
information. I will remove it then.

thanks,

--
Joel Fernandes
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Miguel Ojeda 1 month, 1 week ago
On Thu, Feb 19, 2026 at 6:13 AM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> rustdoc complains that this links to a private item in a public doc - we
> should not mention `GpuBuddyInner` here.

If you all think something should be mentioned for practical reasons,
then please don't let `rustdoc` force you to not mention it, i.e.
please feel free to remove the square brackets if needed.

In other words, I don't want the intra-doc links convention we have to
make it harder for you to write certain things exceptionally.

I hope that helps.

Cheers,
Miguel
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Joel Fernandes 1 month, 1 week ago
On Wed, Feb 19, 2026 at 09:54:27AM +0100, Miguel Ojeda wrote:
> If you all think something should be mentioned for practical reasons,
> then please don't let `rustdoc` force you to not mention it, i.e.
> please feel free to remove the square brackets if needed.
>
> In other words, I don't want the intra-doc links convention we have to
> make it harder for you to write certain things exceptionally.

Thanks Miguel, that's helpful! I've kept the GpuBuddyInner mention in
the invariants but removed the square brackets to avoid the rustdoc
warning while still providing the reference for readers.

Joel
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Gary Guo 1 month ago
On Thu Feb 19, 2026 at 3:31 PM GMT, Joel Fernandes wrote:
> On Wed, Feb 19, 2026 at 09:54:27AM +0100, Miguel Ojeda wrote:
>> If you all think something should be mentioned for practical reasons,
>> then please don't let `rustdoc` force you to not mention it, i.e.
>> please feel free to remove the square brackets if needed.
>>
>> In other words, I don't want the intra-doc links convention we have to
>> make it harder for you to write certain things exceptionally.
>
> Thanks Miguel, that's helpful! I've kept the GpuBuddyInner mention in
> the invariants but removed the square brackets to avoid the rustdoc
> warning while still providing the reference for readers.
>
> Joel

I started to think that the way we document invariants is problematic. For most
of the types, the invariants mentioned does not make sense to end up in public
facing docs.

Perhaps we should:
- Document invariants on specific fields as doc comments on the fields. So they
  don't show up in the doc unless document-private-items is enabled.
- Invariants across multiple fields perhaps should either be documented as
  normal (non-doc) comments, or we do something like:

struct Foo {
    field_a: X,
    field_b: Y,
    // Put invariants here
    _invariant: (),
}

This has an additional benefit where when you're constructing the type, you're
forced to write

Foo {
    ...,
    _invariant: (),
}

where you're reminded that invariants exist on the type and cannot forget to
write an invariant comment.

Best,
Gary
Re: [PATCH v10 6/8] rust: gpu: Add GPU buddy allocator bindings
Posted by Miguel Ojeda 1 month ago
On Sun, Mar 1, 2026 at 2:23 PM Gary Guo <gary@garyguo.net> wrote:
>
> I started to think that the way we document invariants is problematic. For most
> of the types, the invariants mentioned does not make sense to end up in public
> facing docs.

Yeah, it isn't ideal.

To give some context in case it helps, so far we said that the `#
Invariants` section is a special case where mentioning private fields
is OK-ish if needed/helpful for a reader, even if technically it may
not be "proper", i.e. leak details.

One reason was that sometimes private invariants may make the type
easier to understand, even if technically it is a private one. We also
discussed at some point splitting invariants into public and private
ones, and perhaps have `rustdoc` know about that and only render the
private ones when one toggles the private items rendering (runtime
toggle, which is another feature I requested -- if it is compile-time
as it is the normal flag, then reading the private docs becomes too
hard).

Another reason was to be able to have them use doc comments and get a
nice rendering output, and your suggestion of using a dummy field
would work for that. I like the fact that it makes one remember to
write the invariant "natively". On the other hand, it seems like
something we could somehow do with changes in tooling, i.e. in
`rustdoc` or Clippy (and perhaps it could be enforced rather than just
be a reminder).

Another bit was that sometimes the invariant, even if it applies to a
private field, the type may be exposing e.g. a copy of the field
through a method, and thus it is useful to know about the invariant
anyway. Those cases could/should "properly" be a guarantee on the
return value of that method, keeping the invariant private, though.

In short, the current approach makes it easy to write for everyone,
but it does have downsides and ideally we would have something better
now that everyone is accustomed to writing them.

Cheers,
Miguel