Skip to main content

RegionInit

Trait RegionInit 

Source
pub unsafe trait RegionInit {
    // Required methods
    unsafe fn try_init(
        &self,
        phys_base: usize,
        span_len: usize,
        usable: &[PhysRange],
    ) -> Result<(), InitError>;
    unsafe fn add_usable(&self, base: usize, len: usize);

    // Provided methods
    unsafe fn init(
        &self,
        phys_base: usize,
        span_len: usize,
        usable: &[PhysRange],
    ) { ... }
    unsafe fn try_init_region(
        &self,
        phys_base: usize,
        len: usize,
    ) -> Result<(), InitError> { ... }
    unsafe fn init_region(&self, phys_base: usize, len: usize) { ... }
}
Expand description

Memory-map-aware initialisation interface for physical frame allocators.

try_init configures the allocator’s metadata over the whole span and frees only the usable ranges; holes between them stay reserved (never handed out).

§Contract guarantees

  • On Err, the allocator is untouched - it remains in the valid empty state it had before the call, and a corrected retry is permitted.
  • At most one successful call. A returned Ok(()) consumes the one-time initialisation; a returned Err(_) does not, so a caller may retry with corrected arguments after any failure.

§Safety

These preconditions are unverifiable from the arguments and so remain the caller’s responsibility (violating any of them is undefined behaviour, not an InitError):

  • Backends reach the managed memory through a Provenance strategy of their own, obtaining pointers for the physical addresses they are given; the caller supplies no mapping pointer. Each backend’s Provenance # Safety contract states what it requires of that mapping.
  • Each usable range’s memory must be exclusively owned and not aliased while registered.
  • Must be called single-threaded, and the allocator must be published to other threads with a happens-before edge (thread spawn, mutex, or a Release store / Acquire load of a ready flag) before any concurrent use.

§Errors

The checkable preconditions are reported rather than assumed. See InitError for the full list; in summary try_init returns:

An empty usable slice is permitted by this interface, but some implementations may reject it with InitError::MetadataWontFit: an allocator that keeps its metadata inside the managed pool needs enough usable RAM to host that metadata.

Required Methods§

Source

unsafe fn try_init( &self, phys_base: usize, span_len: usize, usable: &[PhysRange], ) -> Result<(), InitError>

Configure metadata over [phys_base, phys_base + span_len) and mark only the usable ranges free. Holes stay reserved.

On success the one-time initialisation is consumed; on failure the allocator is untouched and the call may be retried. See the trait-level documentation for the full contract, the # Errors conditions, and the # Safety preconditions.

§Errors

See the trait-level # Errors documentation.

§Safety

See the trait-level safety documentation.

Source

unsafe fn add_usable(&self, base: usize, len: usize)

Transition a reserved (never-freed) in-span range to free. Repeatable after try_init. base/len must satisfy the same per-range constraints as a usable entry.

§Safety

try_init must have succeeded first. [base, base + len) must be base-frame aligned, lie within the span, be currently reserved (not already free), exclusively owned, and not aliased while registered.

Provided Methods§

Source

unsafe fn init(&self, phys_base: usize, span_len: usize, usable: &[PhysRange])

As try_init but panic on error.

§Safety

See the trait-level safety documentation.

§Panics

Panics if try_init returns an InitError.

Examples found in repository?
examples/basic.rs (line 117)
91fn main() {
92    // A boot memory map with holes:
93    //
94    // The backing pool spans the whole window; we then declare only the *usable*
95    // sub-ranges. Two holes stay reserved and are never handed out:
96    //
97    //   [  0 .. 100)  usable   <- the buddy carves its in-pool bitmap from here
98    //   [100 .. 104)  RESERVED  (kernel image)
99    //   [104 .. 200)  usable
100    //   [200 .. 205)  RESERVED  (MMIO window)
101    //   [205 .. 256)  usable
102    let pool = Region::new(SPAN_FRAMES * BASE.bytes(), MAX_BLOCK);
103    let phys_base = pool.addr(); // the "physical" origin
104
105    let frame = BASE.bytes();
106    let range = |lo: usize, hi: usize| PhysRange {
107        base: phys_base + lo * frame,
108        len: (hi - lo) * frame,
109    };
110    let usable = [range(0, 100), range(104, 200), range(205, 256)];
111
112    // SAFETY: single-threaded, called once before any allocation. `phys_base` is
113    // MAX_BLOCK-aligned (Region honours the requested alignment); the bitmap host
114    // range is exclusively owned and reachable through `IdentityProv::create`; the
115    // usable ranges are sorted, non-overlapping, base-frame-aligned, and within
116    // the span.
117    unsafe { PHYS.init(phys_base, SPAN_FRAMES * frame, &usable) };
118
119    println!("Initialised SummaryBuddy over a 1 MiB span with two reserved holes.");
120    print_stats("after init", &PHYS);
121
122    // A few allocate / deallocate cycles:
123    //
124    // One single base frame, then a 4-frame (order-2) contiguous block. Every
125    // address is physical and aligned to the request size.
126    let single = PHYS
127        .allocate_physical(BASE, n(1))
128        .expect("single-frame alloc");
129    let block = PHYS
130        .allocate_physical(BASE, n(4))
131        .expect("4-frame contiguous alloc");
132    println!("\nallocate_physical(1 frame)  -> {single:#x}");
133    println!("allocate_physical(4 frames) -> {block:#x}");
134    print_stats("with 5 frames out", &PHYS);
135
136    // SAFETY: each address came from `allocate_physical` with the same page size
137    // and count and is not used afterwards.
138    unsafe {
139        PHYS.deallocate_physical(BASE, n(1), single);
140        PHYS.deallocate_physical(BASE, n(4), block);
141    }
142    println!("\nfreed both — buddies merge back.");
143    print_stats("after free", &PHYS);
144
145    // Composition: a per-CPU magazine + shared depot over a fresh SummaryBuddy:
146    compose_with_depot();
147}
Source

unsafe fn try_init_region( &self, phys_base: usize, len: usize, ) -> Result<(), InitError>

Convenience: initialise from a single fully-usable contiguous region [phys_base, phys_base + len).

§Errors

See the trait-level # Errors documentation.

§Safety

See the trait-level safety documentation; the whole region is usable.

Source

unsafe fn init_region(&self, phys_base: usize, len: usize)

As try_init_region but panic on error.

§Safety

See the trait-level safety documentation; the whole region is usable.

§Panics

Panics if initialisation returns an InitError.

Examples found in repository?
examples/basic.rs (line 168)
150fn compose_with_depot() {
151    /// Uniprocessor selector: every CPU maps to slot 0. A real kernel returns an
152    /// APIC id / `TPIDR_EL1` here.
153    struct OneCpu;
154    impl CpuId for OneCpu {
155        fn current_cpu() -> usize {
156            0
157        }
158    }
159    const SLOTS: usize = 8; // magazines (>= CPUs you want disjoint)
160
161    // Same const-new composability: the whole stack is one `static`-able value.
162    let mag: DepotAllocator<SummaryBuddyAllocator<ORDERS, IdentityProv>, OneCpu, SLOTS> =
163        DepotAllocator::new(BASE, SummaryBuddyAllocator::new(BASE));
164
165    let pool = Region::new(SPAN_FRAMES * BASE.bytes(), MAX_BLOCK);
166    // SAFETY: as above; here the whole region is usable, so the single-range
167    // convenience applies.
168    unsafe { mag.init_region(pool.addr(), SPAN_FRAMES * BASE.bytes()) };
169
170    println!("\n── DepotAllocator<SummaryBuddyAllocator> ──");
171    let a = mag.allocate_physical(BASE, n(1)).expect("mag alloc");
172    // SAFETY: `a` came from this allocator; freed once, then not reused by us.
173    unsafe { mag.deallocate_physical(BASE, n(1), a) };
174    let b = mag.allocate_physical(BASE, n(1)).expect("mag re-alloc");
175    println!("alloc {a:#x} -> free -> alloc {b:#x}");
176    assert_eq!(a, b, "the magazine should return the just-freed frame");
177    println!("re-alloc returned the cached frame (no backend round-trip).");
178    // SAFETY: final free of a live frame.
179    unsafe { mag.deallocate_physical(BASE, n(1), b) };
180}

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl<A: RegionInit, S, const SLOTS: usize, const CAP: usize, const DEPOT_CAP: usize, I: InterruptControl> RegionInit for DepotAllocator<A, S, SLOTS, CAP, DEPOT_CAP, I>

Source§

impl<const ORDERS: usize, P: Provenance, G: GateConfig> RegionInit for SummaryBuddyAllocator<ORDERS, P, G>