mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
rust: gpu: Add GPU buddy allocator bindings
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> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://patch.msgid.link/20260320045711.43494-2-joelagnelf@nvidia.com [ * Use doc-comments for GpuBuddyAllocMode methods and GpuBuddyGuard, * Fix comma splice in GpuBuddyParams::chunk_size doc-comment, * Remove redundant summary in GpuBuddy::new doc-comment, * Drop Rust helper for gpu_buddy_block_size(). - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
parent
f9f0b4a1f3
commit
b9616d9721
|
|
@ -8505,7 +8505,10 @@ T: git https://gitlab.freedesktop.org/drm/rust/kernel.git
|
|||
F: drivers/gpu/drm/nova/
|
||||
F: drivers/gpu/drm/tyr/
|
||||
F: drivers/gpu/nova-core/
|
||||
F: rust/helpers/gpu.c
|
||||
F: rust/kernel/drm/
|
||||
F: rust/kernel/gpu.rs
|
||||
F: rust/kernel/gpu/
|
||||
|
||||
DRM DRIVERS FOR ALLWINNER A10
|
||||
M: Chen-Yu Tsai <wens@kernel.org>
|
||||
|
|
@ -8926,6 +8929,9 @@ F: drivers/gpu/drm/drm_buddy.c
|
|||
F: drivers/gpu/tests/gpu_buddy_test.c
|
||||
F: include/drm/drm_buddy.h
|
||||
F: include/linux/gpu_buddy.h
|
||||
F: rust/helpers/gpu.c
|
||||
F: rust/kernel/gpu.rs
|
||||
F: rust/kernel/gpu/
|
||||
|
||||
DRM AUTOMATED TESTING
|
||||
M: Helen Koike <helen.fornazier@gmail.com>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
17
rust/helpers/gpu.c
Normal file
17
rust/helpers/gpu.c
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// 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);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_GPU_BUDDY */
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
6
rust/kernel/gpu.rs
Normal file
6
rust/kernel/gpu.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! GPU subsystem abstractions.
|
||||
|
||||
#[cfg(CONFIG_GPU_BUDDY = "y")]
|
||||
pub mod buddy;
|
||||
614
rust/kernel/gpu/buddy.rs
Normal file
614
rust/kernel/gpu/buddy.rs
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
// 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
|
||||
//!
|
||||
//! Create a buddy allocator and perform a basic range allocation:
|
||||
//!
|
||||
//! ```
|
||||
//! use kernel::{
|
||||
//! gpu::buddy::{
|
||||
//! GpuBuddy,
|
||||
//! GpuBuddyAllocFlags,
|
||||
//! GpuBuddyAllocMode,
|
||||
//! GpuBuddyParams, //
|
||||
//! },
|
||||
//! prelude::*,
|
||||
//! ptr::Alignment,
|
||||
//! sizes::*, //
|
||||
//! };
|
||||
//!
|
||||
//! // Create a 1GB buddy allocator with 4KB minimum chunk size.
|
||||
//! let buddy = GpuBuddy::new(GpuBuddyParams {
|
||||
//! base_offset: 0,
|
||||
//! size: SZ_1G as u64,
|
||||
//! chunk_size: Alignment::new::<SZ_4K>(),
|
||||
//! })?;
|
||||
//!
|
||||
//! assert_eq!(buddy.size(), SZ_1G as u64);
|
||||
//! assert_eq!(buddy.chunk_size(), Alignment::new::<SZ_4K>());
|
||||
//! let initial_free = buddy.avail();
|
||||
//!
|
||||
//! // Allocate 16MB. Block lands at the top of the address range.
|
||||
//! let allocated = KBox::pin_init(
|
||||
//! buddy.alloc_blocks(
|
||||
//! GpuBuddyAllocMode::Simple,
|
||||
//! SZ_16M as u64,
|
||||
//! Alignment::new::<SZ_16M>(),
|
||||
//! GpuBuddyAllocFlags::default(),
|
||||
//! ),
|
||||
//! GFP_KERNEL,
|
||||
//! )?;
|
||||
//! assert_eq!(buddy.avail(), initial_free - SZ_16M as u64);
|
||||
//!
|
||||
//! let block = allocated.iter().next().expect("expected one block");
|
||||
//! assert_eq!(block.offset(), (SZ_1G - SZ_16M) as u64);
|
||||
//! assert_eq!(block.order(), 12); // 2^12 pages = 16MB
|
||||
//! assert_eq!(block.size(), SZ_16M as u64);
|
||||
//! assert_eq!(allocated.iter().count(), 1);
|
||||
//!
|
||||
//! // Dropping the allocation returns the range to the buddy allocator.
|
||||
//! drop(allocated);
|
||||
//! assert_eq!(buddy.avail(), initial_free);
|
||||
//! # Ok::<(), Error>(())
|
||||
//! ```
|
||||
//!
|
||||
//! Top-down allocation allocates from the highest addresses:
|
||||
//!
|
||||
//! ```
|
||||
//! # use kernel::{
|
||||
//! # gpu::buddy::{GpuBuddy, GpuBuddyAllocMode, GpuBuddyAllocFlags, GpuBuddyParams},
|
||||
//! # prelude::*,
|
||||
//! # ptr::Alignment,
|
||||
//! # sizes::*, //
|
||||
//! # };
|
||||
//! # let buddy = GpuBuddy::new(GpuBuddyParams {
|
||||
//! # base_offset: 0,
|
||||
//! # size: SZ_1G as u64,
|
||||
//! # chunk_size: Alignment::new::<SZ_4K>(),
|
||||
//! # })?;
|
||||
//! # let initial_free = buddy.avail();
|
||||
//! let topdown = KBox::pin_init(
|
||||
//! buddy.alloc_blocks(
|
||||
//! GpuBuddyAllocMode::TopDown,
|
||||
//! SZ_16M as u64,
|
||||
//! Alignment::new::<SZ_16M>(),
|
||||
//! GpuBuddyAllocFlags::default(),
|
||||
//! ),
|
||||
//! GFP_KERNEL,
|
||||
//! )?;
|
||||
//! assert_eq!(buddy.avail(), initial_free - SZ_16M as u64);
|
||||
//!
|
||||
//! let block = topdown.iter().next().expect("expected one block");
|
||||
//! assert_eq!(block.offset(), (SZ_1G - SZ_16M) as u64);
|
||||
//! assert_eq!(block.order(), 12);
|
||||
//! assert_eq!(block.size(), SZ_16M as u64);
|
||||
//!
|
||||
//! // Dropping the allocation returns the range to the buddy allocator.
|
||||
//! drop(topdown);
|
||||
//! assert_eq!(buddy.avail(), initial_free);
|
||||
//! # Ok::<(), Error>(())
|
||||
//! ```
|
||||
//!
|
||||
//! Non-contiguous allocation can fill fragmented memory by returning multiple
|
||||
//! blocks:
|
||||
//!
|
||||
//! ```
|
||||
//! # use kernel::{
|
||||
//! # gpu::buddy::{
|
||||
//! # GpuBuddy, GpuBuddyAllocFlags, GpuBuddyAllocMode, GpuBuddyParams,
|
||||
//! # },
|
||||
//! # prelude::*,
|
||||
//! # ptr::Alignment,
|
||||
//! # sizes::*, //
|
||||
//! # };
|
||||
//! # let buddy = GpuBuddy::new(GpuBuddyParams {
|
||||
//! # base_offset: 0,
|
||||
//! # size: SZ_1G as u64,
|
||||
//! # chunk_size: Alignment::new::<SZ_4K>(),
|
||||
//! # })?;
|
||||
//! # let initial_free = buddy.avail();
|
||||
//! // Create fragmentation by allocating 4MB blocks at [0,4M) and [8M,12M).
|
||||
//! let frag1 = KBox::pin_init(
|
||||
//! buddy.alloc_blocks(
|
||||
//! GpuBuddyAllocMode::Range(0..SZ_4M as u64),
|
||||
//! SZ_4M as u64,
|
||||
//! Alignment::new::<SZ_4M>(),
|
||||
//! GpuBuddyAllocFlags::default(),
|
||||
//! ),
|
||||
//! GFP_KERNEL,
|
||||
//! )?;
|
||||
//! assert_eq!(buddy.avail(), initial_free - SZ_4M as u64);
|
||||
//!
|
||||
//! let frag2 = KBox::pin_init(
|
||||
//! buddy.alloc_blocks(
|
||||
//! GpuBuddyAllocMode::Range(SZ_8M as u64..(SZ_8M + SZ_4M) as u64),
|
||||
//! SZ_4M as u64,
|
||||
//! Alignment::new::<SZ_4M>(),
|
||||
//! GpuBuddyAllocFlags::default(),
|
||||
//! ),
|
||||
//! GFP_KERNEL,
|
||||
//! )?;
|
||||
//! assert_eq!(buddy.avail(), initial_free - SZ_8M as u64);
|
||||
//!
|
||||
//! // Allocate 8MB, this returns 2 blocks from the holes.
|
||||
//! let fragmented = KBox::pin_init(
|
||||
//! buddy.alloc_blocks(
|
||||
//! GpuBuddyAllocMode::Range(0..SZ_16M as u64),
|
||||
//! SZ_8M as u64,
|
||||
//! Alignment::new::<SZ_4M>(),
|
||||
//! GpuBuddyAllocFlags::default(),
|
||||
//! ),
|
||||
//! GFP_KERNEL,
|
||||
//! )?;
|
||||
//! assert_eq!(buddy.avail(), initial_free - SZ_16M as u64);
|
||||
//!
|
||||
//! let (mut count, mut total) = (0u32, 0u64);
|
||||
//! for block in fragmented.iter() {
|
||||
//! assert_eq!(block.size(), SZ_4M as u64);
|
||||
//! total += block.size();
|
||||
//! count += 1;
|
||||
//! }
|
||||
//! assert_eq!(total, SZ_8M as u64);
|
||||
//! assert_eq!(count, 2);
|
||||
//! # Ok::<(), Error>(())
|
||||
//! ```
|
||||
//!
|
||||
//! Contiguous allocation fails when only fragmented space is available:
|
||||
//!
|
||||
//! ```
|
||||
//! # use kernel::{
|
||||
//! # gpu::buddy::{
|
||||
//! # GpuBuddy, GpuBuddyAllocFlag, GpuBuddyAllocFlags, GpuBuddyAllocMode, GpuBuddyParams,
|
||||
//! # },
|
||||
//! # prelude::*,
|
||||
//! # ptr::Alignment,
|
||||
//! # sizes::*, //
|
||||
//! # };
|
||||
//! // Create a small 16MB buddy allocator with fragmented memory.
|
||||
//! let small = GpuBuddy::new(GpuBuddyParams {
|
||||
//! base_offset: 0,
|
||||
//! size: SZ_16M as u64,
|
||||
//! chunk_size: Alignment::new::<SZ_4K>(),
|
||||
//! })?;
|
||||
//!
|
||||
//! let _hole1 = KBox::pin_init(
|
||||
//! small.alloc_blocks(
|
||||
//! GpuBuddyAllocMode::Range(0..SZ_4M as u64),
|
||||
//! SZ_4M as u64,
|
||||
//! Alignment::new::<SZ_4M>(),
|
||||
//! GpuBuddyAllocFlags::default(),
|
||||
//! ),
|
||||
//! GFP_KERNEL,
|
||||
//! )?;
|
||||
//!
|
||||
//! let _hole2 = KBox::pin_init(
|
||||
//! small.alloc_blocks(
|
||||
//! GpuBuddyAllocMode::Range(SZ_8M as u64..(SZ_8M + SZ_4M) as u64),
|
||||
//! SZ_4M as u64,
|
||||
//! Alignment::new::<SZ_4M>(),
|
||||
//! GpuBuddyAllocFlags::default(),
|
||||
//! ),
|
||||
//! GFP_KERNEL,
|
||||
//! )?;
|
||||
//!
|
||||
//! // 8MB contiguous should fail, only two non-contiguous 4MB holes exist.
|
||||
//! let result = KBox::pin_init(
|
||||
//! small.alloc_blocks(
|
||||
//! GpuBuddyAllocMode::Simple,
|
||||
//! SZ_8M as u64,
|
||||
//! Alignment::new::<SZ_4M>(),
|
||||
//! GpuBuddyAllocFlag::Contiguous,
|
||||
//! ),
|
||||
//! GFP_KERNEL,
|
||||
//! );
|
||||
//! assert!(result.is_err());
|
||||
//! # Ok::<(), Error>(())
|
||||
//! ```
|
||||
|
||||
use core::ops::Range;
|
||||
|
||||
use crate::{
|
||||
bindings,
|
||||
clist_create,
|
||||
error::to_result,
|
||||
interop::list::CListHead,
|
||||
new_mutex,
|
||||
prelude::*,
|
||||
ptr::Alignment,
|
||||
sync::{
|
||||
lock::mutex::MutexGuard,
|
||||
Arc,
|
||||
Mutex, //
|
||||
},
|
||||
types::Opaque, //
|
||||
};
|
||||
|
||||
/// Allocation mode for the GPU buddy allocator.
|
||||
///
|
||||
/// The mode determines the primary allocation strategy. Modes are mutually
|
||||
/// exclusive: an allocation is either simple, range-constrained, or top-down.
|
||||
///
|
||||
/// Orthogonal modifier flags (e.g., contiguous, clear) are specified separately
|
||||
/// via [`GpuBuddyAllocFlags`].
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum GpuBuddyAllocMode {
|
||||
/// Simple allocation without constraints.
|
||||
Simple,
|
||||
/// Range-based allocation within the given address range.
|
||||
Range(Range<u64>),
|
||||
/// Allocate from top of address space downward.
|
||||
TopDown,
|
||||
}
|
||||
|
||||
impl GpuBuddyAllocMode {
|
||||
/// Returns the C flags corresponding to the allocation mode.
|
||||
fn as_flags(&self) -> usize {
|
||||
match self {
|
||||
Self::Simple => 0,
|
||||
Self::Range(_) => bindings::GPU_BUDDY_RANGE_ALLOCATION,
|
||||
Self::TopDown => bindings::GPU_BUDDY_TOPDOWN_ALLOCATION,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the range start/end, defaulting to `(0, 0)` for non-range modes.
|
||||
fn range(&self) -> (u64, u64) {
|
||||
match self {
|
||||
Self::Range(range) => (range.start, range.end),
|
||||
_ => (0, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::impl_flags!(
|
||||
/// Modifier flags for GPU buddy allocation.
|
||||
///
|
||||
/// These flags can be combined with any [`GpuBuddyAllocMode`] to control
|
||||
/// additional allocation behavior.
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct GpuBuddyAllocFlags(usize);
|
||||
|
||||
/// Individual modifier flag for GPU buddy allocation.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GpuBuddyAllocFlag {
|
||||
/// Allocate physically contiguous blocks.
|
||||
Contiguous = bindings::GPU_BUDDY_CONTIGUOUS_ALLOCATION,
|
||||
|
||||
/// Request allocation from cleared (zeroed) memory.
|
||||
Clear = bindings::GPU_BUDDY_CLEAR_ALLOCATION,
|
||||
|
||||
/// Disable trimming of partially used blocks.
|
||||
TrimDisable = bindings::GPU_BUDDY_TRIM_DISABLE,
|
||||
}
|
||||
);
|
||||
|
||||
/// 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: u64,
|
||||
/// Total size (in bytes) of the address space managed by the allocator.
|
||||
pub size: u64,
|
||||
/// Minimum allocation unit / chunk size; must be >= 4KB.
|
||||
pub chunk_size: Alignment,
|
||||
}
|
||||
|
||||
/// Inner structure holding the actual buddy allocator.
|
||||
///
|
||||
/// # Synchronization
|
||||
///
|
||||
/// The C `gpu_buddy` API requires synchronization (see `include/linux/gpu_buddy.h`).
|
||||
/// Internal locking ensures all allocator and free operations are properly
|
||||
/// synchronized, preventing races between concurrent allocations and the
|
||||
/// freeing that occurs when [`AllocatedBlocks`] is dropped.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// The inner [`Opaque`] contains an 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<()>,
|
||||
/// Cached creation parameters (do not change after init).
|
||||
params: GpuBuddyParams,
|
||||
}
|
||||
|
||||
impl GpuBuddyInner {
|
||||
/// Create a pin-initializer for the buddy allocator.
|
||||
fn new(params: GpuBuddyParams) -> impl PinInit<Self, Error> {
|
||||
let size = params.size;
|
||||
let chunk_size = params.chunk_size;
|
||||
|
||||
// INVARIANT: `gpu_buddy_init` returns 0 on success, at which point the
|
||||
// `gpu_buddy` structure is initialized and ready for use with all
|
||||
// `gpu_buddy_*` APIs. `try_pin_init!` only completes if all fields succeed,
|
||||
// so the invariant holds when construction finishes.
|
||||
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.as_usize() as u64)
|
||||
})
|
||||
}),
|
||||
lock <- new_mutex!(()),
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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: Per the type invariant, `inner` contains an initialized
|
||||
// allocator. `guard` provides exclusive access.
|
||||
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 `GpuBuddyInner::lock`
|
||||
// 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.
|
||||
///
|
||||
/// The `_guard` holds the lock for the duration of this guard's lifetime.
|
||||
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.
|
||||
///
|
||||
/// Refer to the module-level documentation for usage examples.
|
||||
pub struct GpuBuddy(Arc<GpuBuddyInner>);
|
||||
|
||||
impl GpuBuddy {
|
||||
/// Create a new buddy allocator.
|
||||
///
|
||||
/// The allocator 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> {
|
||||
Arc::pin_init(GpuBuddyInner::new(params), GFP_KERNEL).map(Self)
|
||||
}
|
||||
|
||||
/// Get the base offset for allocations.
|
||||
pub fn base_offset(&self) -> u64 {
|
||||
self.0.params.base_offset
|
||||
}
|
||||
|
||||
/// Get the chunk size (minimum allocation unit).
|
||||
pub fn chunk_size(&self) -> Alignment {
|
||||
self.0.params.chunk_size
|
||||
}
|
||||
|
||||
/// Get the total managed size.
|
||||
pub fn size(&self) -> u64 {
|
||||
self.0.params.size
|
||||
}
|
||||
|
||||
/// Get the available (free) memory in bytes.
|
||||
pub fn avail(&self) -> u64 {
|
||||
let guard = self.0.lock();
|
||||
|
||||
// SAFETY: Per the type invariant, `inner` contains an initialized allocator.
|
||||
// `guard` provides exclusive access.
|
||||
unsafe { (*guard.as_raw()).avail }
|
||||
}
|
||||
|
||||
/// Allocate blocks from the buddy allocator.
|
||||
///
|
||||
/// Returns a pin-initializer for [`AllocatedBlocks`].
|
||||
pub fn alloc_blocks(
|
||||
&self,
|
||||
mode: GpuBuddyAllocMode,
|
||||
size: u64,
|
||||
min_block_size: Alignment,
|
||||
flags: impl Into<GpuBuddyAllocFlags>,
|
||||
) -> impl PinInit<AllocatedBlocks, Error> {
|
||||
let buddy_arc = Arc::clone(&self.0);
|
||||
let (start, end) = mode.range();
|
||||
let mode_flags = mode.as_flags();
|
||||
let modifier_flags = flags.into();
|
||||
|
||||
// Create pin-initializer that initializes list and allocates blocks.
|
||||
try_pin_init!(AllocatedBlocks {
|
||||
buddy: buddy_arc,
|
||||
list <- CListHead::new(),
|
||||
_: {
|
||||
// Reject zero-sized or inverted ranges.
|
||||
if let GpuBuddyAllocMode::Range(range) = &mode {
|
||||
if range.is_empty() {
|
||||
Err::<(), Error>(EINVAL)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock while allocating to serialize with concurrent frees.
|
||||
let guard = buddy.lock();
|
||||
|
||||
// SAFETY: Per the type invariant, `inner` contains an initialized
|
||||
// allocator. `guard` provides exclusive access.
|
||||
to_result(unsafe {
|
||||
bindings::gpu_buddy_alloc_blocks(
|
||||
guard.as_raw(),
|
||||
start,
|
||||
end,
|
||||
size,
|
||||
min_block_size.as_usize() as u64,
|
||||
list.as_raw(),
|
||||
mode_flags | usize::from(modifier_flags),
|
||||
)
|
||||
})?
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 blocks.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// - `list` is an initialized, valid list head containing allocated blocks.
|
||||
#[pin_data(PinnedDrop)]
|
||||
pub struct AllocatedBlocks {
|
||||
#[pin]
|
||||
list: CListHead,
|
||||
buddy: Arc<GpuBuddyInner>,
|
||||
}
|
||||
|
||||
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<'_>> + '_ {
|
||||
let head = self.list.as_raw();
|
||||
// SAFETY: Per the type invariant, `list` is an initialized sentinel `list_head`
|
||||
// and is not concurrently modified (we hold a `&self` borrow). The list contains
|
||||
// `gpu_buddy_block` items linked via `__bindgen_anon_1.link`. `Block` is
|
||||
// `#[repr(transparent)]` over `gpu_buddy_block`.
|
||||
let clist = unsafe {
|
||||
clist_create!(
|
||||
head,
|
||||
Block,
|
||||
bindings::gpu_buddy_block,
|
||||
__bindgen_anon_1.link
|
||||
)
|
||||
};
|
||||
|
||||
clist
|
||||
.iter()
|
||||
.map(|this| AllocatedBlock { this, blocks: 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.
|
||||
unsafe {
|
||||
bindings::gpu_buddy_free_list(guard.as_raw(), self.list.as_raw(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A GPU buddy block.
|
||||
///
|
||||
/// Transparent wrapper over C `gpu_buddy_block` structure. This type is returned
|
||||
/// as references during iteration over [`AllocatedBlocks`].
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// The inner [`Opaque`] contains a valid, allocated `gpu_buddy_block`.
|
||||
#[repr(transparent)]
|
||||
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 raw offset in the buddy address space (without base offset).
|
||||
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.
|
||||
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 a wrapper around `gpu_buddy_block` which can be
|
||||
// sent across threads safely.
|
||||
unsafe impl Send for Block {}
|
||||
|
||||
// SAFETY: `Block` is only accessed through shared references after
|
||||
// allocation, and thus safe to access concurrently across threads.
|
||||
unsafe impl Sync for Block {}
|
||||
|
||||
/// A buddy block paired with its owning [`AllocatedBlocks`] context.
|
||||
///
|
||||
/// Unlike a raw block, which only knows its offset within the buddy address
|
||||
/// space, an [`AllocatedBlock`] also has access to the allocator's `base_offset`
|
||||
/// and `chunk_size`, enabling it to compute absolute offsets and byte sizes.
|
||||
///
|
||||
/// Returned by [`AllocatedBlocks::iter()`].
|
||||
pub struct AllocatedBlock<'a> {
|
||||
this: &'a Block,
|
||||
blocks: &'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.blocks.buddy.params.base_offset + self.this.offset()
|
||||
}
|
||||
|
||||
/// Get the block order (size = chunk_size << order).
|
||||
pub fn order(&self) -> u32 {
|
||||
self.this.order()
|
||||
}
|
||||
|
||||
/// Get the block's size in bytes.
|
||||
pub fn size(&self) -> u64 {
|
||||
(self.blocks.buddy.params.chunk_size.as_usize() as u64) << self.this.order()
|
||||
}
|
||||
}
|
||||
|
|
@ -105,6 +105,8 @@
|
|||
pub mod firmware;
|
||||
pub mod fmt;
|
||||
pub mod fs;
|
||||
#[cfg(CONFIG_GPU_BUDDY = "y")]
|
||||
pub mod gpu;
|
||||
#[cfg(CONFIG_I2C = "y")]
|
||||
pub mod i2c;
|
||||
pub mod id_pool;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user