Skip to main content

Module buddy

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§

AllocatedBlock
A buddy block paired with its owning AllocatedBlocks context.
AllocatedBlocks
Allocated blocks from the buddy allocator with automatic cleanup.
GpuBuddy
GPU buddy allocator instance.
GpuBuddyAllocFlags
Modifier flags for GPU buddy allocation.
GpuBuddyParams
Parameters for creating a GPU buddy allocator.

Enums§

GpuBuddyAllocFlag
Individual modifier flag for GPU buddy allocation.
GpuBuddyAllocMode
Allocation mode for the GPU buddy allocator.