kernel::gpu

Module buddy

Source
Expand description

GPU buddy allocator bindings.

C header: 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);

Top-down allocation allocates from the highest addresses:

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);

Non-contiguous allocation can fill fragmented memory by returning multiple blocks:

// 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);

Contiguous allocation fails when only fragmented space is available:

// 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());

Structs§

Enums§