| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | |
| 3 | //! GPU buddy allocator bindings. |
| 4 | //! |
| 5 | //! C header: [`include/linux/gpu_buddy.h`](srctree/include/linux/gpu_buddy.h) |
| 6 | //! |
| 7 | //! This module provides Rust abstractions over the Linux kernel's GPU buddy |
| 8 | //! allocator, which implements a binary buddy memory allocator. |
| 9 | //! |
| 10 | //! The buddy allocator manages a contiguous address space and allocates blocks |
| 11 | //! in power-of-two sizes, useful for GPU physical memory management. |
| 12 | //! |
| 13 | //! # Examples |
| 14 | //! |
| 15 | //! Create a buddy allocator and perform a basic range allocation: |
| 16 | //! |
| 17 | //! ``` |
| 18 | //! use kernel::{ |
| 19 | //! gpu::buddy::{ |
| 20 | //! GpuBuddy, |
| 21 | //! GpuBuddyAllocFlags, |
| 22 | //! GpuBuddyAllocMode, |
| 23 | //! GpuBuddyParams, // |
| 24 | //! }, |
| 25 | //! prelude::*, |
| 26 | //! ptr::Alignment, |
| 27 | //! sizes::*, // |
| 28 | //! }; |
| 29 | //! |
| 30 | //! // Create a 1GB buddy allocator with 4KB minimum chunk size. |
| 31 | //! let buddy = GpuBuddy::new(GpuBuddyParams { |
| 32 | //! base_offset: 0, |
| 33 | //! size: SZ_1G as u64, |
| 34 | //! chunk_size: Alignment::new::<SZ_4K>(), |
| 35 | //! })?; |
| 36 | //! |
| 37 | //! assert_eq!(buddy.size(), SZ_1G as u64); |
| 38 | //! assert_eq!(buddy.chunk_size(), Alignment::new::<SZ_4K>()); |
| 39 | //! let initial_free = buddy.avail(); |
| 40 | //! |
| 41 | //! // Allocate 16MB. Block lands at the top of the address range. |
| 42 | //! let allocated = KBox::pin_init( |
| 43 | //! buddy.alloc_blocks( |
| 44 | //! GpuBuddyAllocMode::Simple, |
| 45 | //! SZ_16M as u64, |
| 46 | //! Alignment::new::<SZ_16M>(), |
| 47 | //! GpuBuddyAllocFlags::default(), |
| 48 | //! ), |
| 49 | //! GFP_KERNEL, |
| 50 | //! )?; |
| 51 | //! assert_eq!(buddy.avail(), initial_free - SZ_16M as u64); |
| 52 | //! |
| 53 | //! let block = allocated.iter().next().expect("expected one block"); |
| 54 | //! assert_eq!(block.offset(), (SZ_1G - SZ_16M) as u64); |
| 55 | //! assert_eq!(block.order(), 12); // 2^12 pages = 16MB |
| 56 | //! assert_eq!(block.size(), SZ_16M as u64); |
| 57 | //! assert_eq!(allocated.iter().count(), 1); |
| 58 | //! |
| 59 | //! // Dropping the allocation returns the range to the buddy allocator. |
| 60 | //! drop(allocated); |
| 61 | //! assert_eq!(buddy.avail(), initial_free); |
| 62 | //! # Ok::<(), Error>(()) |
| 63 | //! ``` |
| 64 | //! |
| 65 | //! Top-down allocation allocates from the highest addresses: |
| 66 | //! |
| 67 | //! ``` |
| 68 | //! # use kernel::{ |
| 69 | //! # gpu::buddy::{GpuBuddy, GpuBuddyAllocMode, GpuBuddyAllocFlags, GpuBuddyParams}, |
| 70 | //! # prelude::*, |
| 71 | //! # ptr::Alignment, |
| 72 | //! # sizes::*, // |
| 73 | //! # }; |
| 74 | //! # let buddy = GpuBuddy::new(GpuBuddyParams { |
| 75 | //! # base_offset: 0, |
| 76 | //! # size: SZ_1G as u64, |
| 77 | //! # chunk_size: Alignment::new::<SZ_4K>(), |
| 78 | //! # })?; |
| 79 | //! # let initial_free = buddy.avail(); |
| 80 | //! let topdown = KBox::pin_init( |
| 81 | //! buddy.alloc_blocks( |
| 82 | //! GpuBuddyAllocMode::TopDown, |
| 83 | //! SZ_16M as u64, |
| 84 | //! Alignment::new::<SZ_16M>(), |
| 85 | //! GpuBuddyAllocFlags::default(), |
| 86 | //! ), |
| 87 | //! GFP_KERNEL, |
| 88 | //! )?; |
| 89 | //! assert_eq!(buddy.avail(), initial_free - SZ_16M as u64); |
| 90 | //! |
| 91 | //! let block = topdown.iter().next().expect("expected one block"); |
| 92 | //! assert_eq!(block.offset(), (SZ_1G - SZ_16M) as u64); |
| 93 | //! assert_eq!(block.order(), 12); |
| 94 | //! assert_eq!(block.size(), SZ_16M as u64); |
| 95 | //! |
| 96 | //! // Dropping the allocation returns the range to the buddy allocator. |
| 97 | //! drop(topdown); |
| 98 | //! assert_eq!(buddy.avail(), initial_free); |
| 99 | //! # Ok::<(), Error>(()) |
| 100 | //! ``` |
| 101 | //! |
| 102 | //! Non-contiguous allocation can fill fragmented memory by returning multiple |
| 103 | //! blocks: |
| 104 | //! |
| 105 | //! ``` |
| 106 | //! # use kernel::{ |
| 107 | //! # gpu::buddy::{ |
| 108 | //! # GpuBuddy, GpuBuddyAllocFlags, GpuBuddyAllocMode, GpuBuddyParams, |
| 109 | //! # }, |
| 110 | //! # prelude::*, |
| 111 | //! # ptr::Alignment, |
| 112 | //! # sizes::*, // |
| 113 | //! # }; |
| 114 | //! # let buddy = GpuBuddy::new(GpuBuddyParams { |
| 115 | //! # base_offset: 0, |
| 116 | //! # size: SZ_1G as u64, |
| 117 | //! # chunk_size: Alignment::new::<SZ_4K>(), |
| 118 | //! # })?; |
| 119 | //! # let initial_free = buddy.avail(); |
| 120 | //! // Create fragmentation by allocating 4MB blocks at [0,4M) and [8M,12M). |
| 121 | //! let frag1 = KBox::pin_init( |
| 122 | //! buddy.alloc_blocks( |
| 123 | //! GpuBuddyAllocMode::Range(0..SZ_4M as u64), |
| 124 | //! SZ_4M as u64, |
| 125 | //! Alignment::new::<SZ_4M>(), |
| 126 | //! GpuBuddyAllocFlags::default(), |
| 127 | //! ), |
| 128 | //! GFP_KERNEL, |
| 129 | //! )?; |
| 130 | //! assert_eq!(buddy.avail(), initial_free - SZ_4M as u64); |
| 131 | //! |
| 132 | //! let frag2 = KBox::pin_init( |
| 133 | //! buddy.alloc_blocks( |
| 134 | //! GpuBuddyAllocMode::Range(SZ_8M as u64..(SZ_8M + SZ_4M) as u64), |
| 135 | //! SZ_4M as u64, |
| 136 | //! Alignment::new::<SZ_4M>(), |
| 137 | //! GpuBuddyAllocFlags::default(), |
| 138 | //! ), |
| 139 | //! GFP_KERNEL, |
| 140 | //! )?; |
| 141 | //! assert_eq!(buddy.avail(), initial_free - SZ_8M as u64); |
| 142 | //! |
| 143 | //! // Allocate 8MB, this returns 2 blocks from the holes. |
| 144 | //! let fragmented = KBox::pin_init( |
| 145 | //! buddy.alloc_blocks( |
| 146 | //! GpuBuddyAllocMode::Range(0..SZ_16M as u64), |
| 147 | //! SZ_8M as u64, |
| 148 | //! Alignment::new::<SZ_4M>(), |
| 149 | //! GpuBuddyAllocFlags::default(), |
| 150 | //! ), |
| 151 | //! GFP_KERNEL, |
| 152 | //! )?; |
| 153 | //! assert_eq!(buddy.avail(), initial_free - SZ_16M as u64); |
| 154 | //! |
| 155 | //! let (mut count, mut total) = (0u32, 0u64); |
| 156 | //! for block in fragmented.iter() { |
| 157 | //! assert_eq!(block.size(), SZ_4M as u64); |
| 158 | //! total += block.size(); |
| 159 | //! count += 1; |
| 160 | //! } |
| 161 | //! assert_eq!(total, SZ_8M as u64); |
| 162 | //! assert_eq!(count, 2); |
| 163 | //! # Ok::<(), Error>(()) |
| 164 | //! ``` |
| 165 | //! |
| 166 | //! Contiguous allocation fails when only fragmented space is available: |
| 167 | //! |
| 168 | //! ``` |
| 169 | //! # use kernel::{ |
| 170 | //! # gpu::buddy::{ |
| 171 | //! # GpuBuddy, GpuBuddyAllocFlag, GpuBuddyAllocFlags, GpuBuddyAllocMode, GpuBuddyParams, |
| 172 | //! # }, |
| 173 | //! # prelude::*, |
| 174 | //! # ptr::Alignment, |
| 175 | //! # sizes::*, // |
| 176 | //! # }; |
| 177 | //! // Create a small 16MB buddy allocator with fragmented memory. |
| 178 | //! let small = GpuBuddy::new(GpuBuddyParams { |
| 179 | //! base_offset: 0, |
| 180 | //! size: SZ_16M as u64, |
| 181 | //! chunk_size: Alignment::new::<SZ_4K>(), |
| 182 | //! })?; |
| 183 | //! |
| 184 | //! let _hole1 = KBox::pin_init( |
| 185 | //! small.alloc_blocks( |
| 186 | //! GpuBuddyAllocMode::Range(0..SZ_4M as u64), |
| 187 | //! SZ_4M as u64, |
| 188 | //! Alignment::new::<SZ_4M>(), |
| 189 | //! GpuBuddyAllocFlags::default(), |
| 190 | //! ), |
| 191 | //! GFP_KERNEL, |
| 192 | //! )?; |
| 193 | //! |
| 194 | //! let _hole2 = KBox::pin_init( |
| 195 | //! small.alloc_blocks( |
| 196 | //! GpuBuddyAllocMode::Range(SZ_8M as u64..(SZ_8M + SZ_4M) as u64), |
| 197 | //! SZ_4M as u64, |
| 198 | //! Alignment::new::<SZ_4M>(), |
| 199 | //! GpuBuddyAllocFlags::default(), |
| 200 | //! ), |
| 201 | //! GFP_KERNEL, |
| 202 | //! )?; |
| 203 | //! |
| 204 | //! // 8MB contiguous should fail, only two non-contiguous 4MB holes exist. |
| 205 | //! let result = KBox::pin_init( |
| 206 | //! small.alloc_blocks( |
| 207 | //! GpuBuddyAllocMode::Simple, |
| 208 | //! SZ_8M as u64, |
| 209 | //! Alignment::new::<SZ_4M>(), |
| 210 | //! GpuBuddyAllocFlag::Contiguous, |
| 211 | //! ), |
| 212 | //! GFP_KERNEL, |
| 213 | //! ); |
| 214 | //! assert!(result.is_err()); |
| 215 | //! # Ok::<(), Error>(()) |
| 216 | //! ``` |
| 217 | |
| 218 | use Range; |
| 219 | |
| 220 | use crate:: |
| 221 | bindings, |
| 222 | clist_create, |
| 223 | to_result, |
| 224 | CListHead, |
| 225 | new_mutex, |
| 226 | *, |
| 227 | Alignment, |
| 228 | |
| 229 | MutexGuard, |
| 230 | Arc, |
| 231 | Mutex, // |
| 232 | , |
| 233 | Opaque, // |
| 234 | ; |
| 235 | |
| 236 | /// Allocation mode for the GPU buddy allocator. |
| 237 | /// |
| 238 | /// The mode determines the primary allocation strategy. Modes are mutually |
| 239 | /// exclusive: an allocation is either simple, range-constrained, or top-down. |
| 240 | /// |
| 241 | /// Orthogonal modifier flags (e.g., contiguous, clear) are specified separately |
| 242 | /// via [`GpuBuddyAllocFlags`]. |
| 243 | |
| 244 | |
| 245 | /// Simple allocation without constraints. |
| 246 | Simple, |
| 247 | /// Range-based allocation within the given address range. |
| 248 | Range, |
| 249 | /// Allocate from top of address space downward. |
| 250 | TopDown, |
| 251 | |
| 252 | |
| 253 | |
| 254 | /// Returns the C flags corresponding to the allocation mode. |
| 255 | |
| 256 | match self |
| 257 | SelfSimple => 0, |
| 258 | SelfRange => GPU_BUDDY_RANGE_ALLOCATION, |
| 259 | SelfTopDown => GPU_BUDDY_TOPDOWN_ALLOCATION, |
| 260 | |
| 261 | |
| 262 | |
| 263 | /// Extracts the range start/end, defaulting to `(0, 0)` for non-range modes. |
| 264 | |
| 265 | match self |
| 266 | SelfRange => , |
| 267 | _ => , |
| 268 | |
| 269 | |
| 270 | |
| 271 | |
| 272 | crateimpl_flags! |
| 273 | /// Modifier flags for GPU buddy allocation. |
| 274 | /// |
| 275 | /// These flags can be combined with any [`GpuBuddyAllocMode`] to control |
| 276 | /// additional allocation behavior. |
| 277 | |
| 278 | ; |
| 279 | |
| 280 | /// Individual modifier flag for GPU buddy allocation. |
| 281 | |
| 282 | |
| 283 | /// Allocate physically contiguous blocks. |
| 284 | Contiguous = GPU_BUDDY_CONTIGUOUS_ALLOCATION, |
| 285 | |
| 286 | /// Request allocation from cleared (zeroed) memory. |
| 287 | Clear = GPU_BUDDY_CLEAR_ALLOCATION, |
| 288 | |
| 289 | /// Disable trimming of partially used blocks. |
| 290 | TrimDisable = GPU_BUDDY_TRIM_DISABLE, |
| 291 | |
| 292 | ; |
| 293 | |
| 294 | /// Parameters for creating a GPU buddy allocator. |
| 295 | |
| 296 | /// Base offset (in bytes) where the managed memory region starts. |
| 297 | /// Allocations will be offset by this value. |
| 298 | pub base_offset: u64, |
| 299 | /// Total size (in bytes) of the address space managed by the allocator. |
| 300 | pub size: u64, |
| 301 | /// Minimum allocation unit / chunk size; must be >= 4KB. |
| 302 | pub chunk_size: Alignment, |
| 303 | |
| 304 | |
| 305 | /// Inner structure holding the actual buddy allocator. |
| 306 | /// |
| 307 | /// # Synchronization |
| 308 | /// |
| 309 | /// The C `gpu_buddy` API requires synchronization (see `include/linux/gpu_buddy.h`). |
| 310 | /// Internal locking ensures all allocator and free operations are properly |
| 311 | /// synchronized, preventing races between concurrent allocations and the |
| 312 | /// freeing that occurs when [`AllocatedBlocks`] is dropped. |
| 313 | /// |
| 314 | /// # Invariants |
| 315 | /// |
| 316 | /// The inner [`Opaque`] contains an initialized buddy allocator. |
| 317 | |
| 318 | |
| 319 | #[pin] |
| 320 | inner: , |
| 321 | |
| 322 | // TODO: Replace `Mutex<()>` with `Mutex<Opaque<..>>` once `Mutex::new()` |
| 323 | // accepts `impl PinInit<T>`. |
| 324 | #[pin] |
| 325 | lock: , |
| 326 | /// Cached creation parameters (do not change after init). |
| 327 | params: GpuBuddyParams, |
| 328 | |
| 329 | |
| 330 | |
| 331 | /// Create a pin-initializer for the buddy allocator. |
| 332 | |
| 333 | let size = params.size; |
| 334 | let chunk_size = params.chunk_size; |
| 335 | |
| 336 | // INVARIANT: `gpu_buddy_init` returns 0 on success, at which point the |
| 337 | // `gpu_buddy` structure is initialized and ready for use with all |
| 338 | // `gpu_buddy_*` APIs. `try_pin_init!` only completes if all fields succeed, |
| 339 | // so the invariant holds when construction finishes. |
| 340 | try_pin_init! |
| 341 | inner <- try_ffi_init |
| 342 | // SAFETY: `ptr` points to valid uninitialized memory from the pin-init |
| 343 | // infrastructure. `gpu_buddy_init` will initialize the structure. |
| 344 | to_result |
| 345 | gpu_buddy_init |
| 346 | |
| 347 | , |
| 348 | lock <- new_mutex!, |
| 349 | params, |
| 350 | |
| 351 | |
| 352 | |
| 353 | /// Lock the mutex and return a guard for accessing the allocator. |
| 354 | |
| 355 | GpuBuddyGuard |
| 356 | inner: self, |
| 357 | _guard: self.lock.lock, |
| 358 | |
| 359 | |
| 360 | |
| 361 | |
| 362 | |
| 363 | |
| 364 | |
| 365 | let guard = self.lock; |
| 366 | |
| 367 | // SAFETY: Per the type invariant, `inner` contains an initialized |
| 368 | // allocator. `guard` provides exclusive access. |
| 369 | unsafe ; |
| 370 | |
| 371 | |
| 372 | |
| 373 | // SAFETY: `GpuBuddyInner` can be sent between threads. |
| 374 | unsafe |
| 375 | |
| 376 | // SAFETY: `GpuBuddyInner` is `Sync` because `GpuBuddyInner::lock` |
| 377 | // serializes all access to the C allocator, preventing data races. |
| 378 | unsafe |
| 379 | |
| 380 | /// Guard that proves the lock is held, enabling access to the allocator. |
| 381 | /// |
| 382 | /// The `_guard` holds the lock for the duration of this guard's lifetime. |
| 383 | |
| 384 | inner: &'a GpuBuddyInner, |
| 385 | _guard: , |
| 386 | |
| 387 | |
| 388 | |
| 389 | /// Get a raw pointer to the underlying C `gpu_buddy` structure. |
| 390 | *mut gpu_buddy |
| 391 | self.inner.inner.get |
| 392 | |
| 393 | |
| 394 | |
| 395 | /// GPU buddy allocator instance. |
| 396 | /// |
| 397 | /// This structure wraps the C `gpu_buddy` allocator using reference counting. |
| 398 | /// The allocator is automatically cleaned up when all references are dropped. |
| 399 | /// |
| 400 | /// Refer to the module-level documentation for usage examples. |
| 401 | ; |
| 402 | |
| 403 | |
| 404 | /// Create a new buddy allocator. |
| 405 | /// |
| 406 | /// The allocator manages a contiguous address space of the given size, with the |
| 407 | /// specified minimum allocation unit (chunk_size must be at least 4KB). |
| 408 | |
| 409 | pin_init.map |
| 410 | |
| 411 | |
| 412 | /// Get the base offset for allocations. |
| 413 | |
| 414 | self.0.params.base_offset |
| 415 | |
| 416 | |
| 417 | /// Get the chunk size (minimum allocation unit). |
| 418 | |
| 419 | self.0.params.chunk_size |
| 420 | |
| 421 | |
| 422 | /// Get the total managed size. |
| 423 | |
| 424 | self.0.params.size |
| 425 | |
| 426 | |
| 427 | /// Get the available (free) memory in bytes. |
| 428 | |
| 429 | let guard = self.0.lock; |
| 430 | |
| 431 | // SAFETY: Per the type invariant, `inner` contains an initialized allocator. |
| 432 | // `guard` provides exclusive access. |
| 433 | unsafe |
| 434 | |
| 435 | |
| 436 | /// Allocate blocks from the buddy allocator. |
| 437 | /// |
| 438 | /// Returns a pin-initializer for [`AllocatedBlocks`]. |
| 439 | |
| 440 | &self, |
| 441 | mode: GpuBuddyAllocMode, |
| 442 | size: u64, |
| 443 | min_block_size: Alignment, |
| 444 | flags: impl , |
| 445 | |
| 446 | let buddy_arc = clone; |
| 447 | let = mode.range; |
| 448 | let mode_flags = mode.as_flags; |
| 449 | let modifier_flags = flags.into; |
| 450 | |
| 451 | // Create pin-initializer that initializes list and allocates blocks. |
| 452 | try_pin_init! |
| 453 | buddy: buddy_arc, |
| 454 | list <- new, |
| 455 | _: |
| 456 | // Reject zero-sized or inverted ranges. |
| 457 | if let Range = &mode |
| 458 | if range.is_empty |
| 459 | Err::?; |
| 460 | |
| 461 | |
| 462 | |
| 463 | // Lock while allocating to serialize with concurrent frees. |
| 464 | let guard = buddy.lock; |
| 465 | |
| 466 | // SAFETY: Per the type invariant, `inner` contains an initialized |
| 467 | // allocator. `guard` provides exclusive access. |
| 468 | to_result |
| 469 | gpu_buddy_alloc_blocks |
| 470 | guard.as_raw, |
| 471 | start, |
| 472 | end, |
| 473 | size, |
| 474 | min_block_size.as_usize as u64, |
| 475 | list.as_raw, |
| 476 | mode_flags | usizefrom, |
| 477 | |
| 478 | ? |
| 479 | |
| 480 | |
| 481 | |
| 482 | |
| 483 | |
| 484 | /// Allocated blocks from the buddy allocator with automatic cleanup. |
| 485 | /// |
| 486 | /// This structure owns a list of allocated blocks and ensures they are |
| 487 | /// automatically freed when dropped. Use `iter()` to iterate over all |
| 488 | /// allocated blocks. |
| 489 | /// |
| 490 | /// # Invariants |
| 491 | /// |
| 492 | /// - `list` is an initialized, valid list head containing allocated blocks. |
| 493 | |
| 494 | |
| 495 | #[pin] |
| 496 | list: CListHead, |
| 497 | buddy: , |
| 498 | |
| 499 | |
| 500 | |
| 501 | /// Check if the block list is empty. |
| 502 | |
| 503 | // An empty list head points to itself. |
| 504 | !self.list.is_linked |
| 505 | |
| 506 | |
| 507 | /// Iterate over allocated blocks. |
| 508 | /// |
| 509 | /// Returns an iterator yielding [`AllocatedBlock`] values. Each [`AllocatedBlock`] |
| 510 | /// borrows `self` and is only valid for the duration of that borrow. |
| 511 | + '_ |
| 512 | let head = self.list.as_raw; |
| 513 | // SAFETY: Per the type invariant, `list` is an initialized sentinel `list_head` |
| 514 | // and is not concurrently modified (we hold a `&self` borrow). The list contains |
| 515 | // `gpu_buddy_block` items linked via `__bindgen_anon_1.link`. `Block` is |
| 516 | // `#[repr(transparent)]` over `gpu_buddy_block`. |
| 517 | let clist = unsafe |
| 518 | clist_create! |
| 519 | head, |
| 520 | Block, |
| 521 | gpu_buddy_block, |
| 522 | __bindgen_anon_1.link |
| 523 | |
| 524 | ; |
| 525 | |
| 526 | clist |
| 527 | .iter |
| 528 | .map |
| 529 | |
| 530 | |
| 531 | |
| 532 | |
| 533 | |
| 534 | |
| 535 | let guard = self.buddy.lock; |
| 536 | |
| 537 | // SAFETY: |
| 538 | // - list is valid per the type's invariants. |
| 539 | // - guard provides exclusive access to the allocator. |
| 540 | unsafe |
| 541 | gpu_buddy_free_list; |
| 542 | |
| 543 | |
| 544 | |
| 545 | |
| 546 | /// A GPU buddy block. |
| 547 | /// |
| 548 | /// Transparent wrapper over C `gpu_buddy_block` structure. This type is returned |
| 549 | /// as references during iteration over [`AllocatedBlocks`]. |
| 550 | /// |
| 551 | /// # Invariants |
| 552 | /// |
| 553 | /// The inner [`Opaque`] contains a valid, allocated `gpu_buddy_block`. |
| 554 | |
| 555 | ; |
| 556 | |
| 557 | |
| 558 | /// Get a raw pointer to the underlying C block. |
| 559 | *mut gpu_buddy_block |
| 560 | self.0.get |
| 561 | |
| 562 | |
| 563 | /// Get the block's raw offset in the buddy address space (without base offset). |
| 564 | |
| 565 | // SAFETY: `self.as_raw()` is valid per the type's invariants. |
| 566 | unsafe |
| 567 | |
| 568 | |
| 569 | /// Get the block order. |
| 570 | |
| 571 | // SAFETY: `self.as_raw()` is valid per the type's invariants. |
| 572 | unsafe |
| 573 | |
| 574 | |
| 575 | |
| 576 | // SAFETY: `Block` is a wrapper around `gpu_buddy_block` which can be |
| 577 | // sent across threads safely. |
| 578 | unsafe |
| 579 | |
| 580 | // SAFETY: `Block` is only accessed through shared references after |
| 581 | // allocation, and thus safe to access concurrently across threads. |
| 582 | unsafe |
| 583 | |
| 584 | /// A buddy block paired with its owning [`AllocatedBlocks`] context. |
| 585 | /// |
| 586 | /// Unlike a raw block, which only knows its offset within the buddy address |
| 587 | /// space, an [`AllocatedBlock`] also has access to the allocator's `base_offset` |
| 588 | /// and `chunk_size`, enabling it to compute absolute offsets and byte sizes. |
| 589 | /// |
| 590 | /// Returned by [`AllocatedBlocks::iter()`]. |
| 591 | |
| 592 | this: &'a Block, |
| 593 | blocks: &'a AllocatedBlocks, |
| 594 | |
| 595 | |
| 596 | |
| 597 | /// Get the block's offset in the address space. |
| 598 | /// |
| 599 | /// Returns the absolute offset including the allocator's base offset. |
| 600 | /// This is the actual address to use for accessing the allocated memory. |
| 601 | |
| 602 | self.blocks.buddy.params.base_offset + self.this.offset |
| 603 | |
| 604 | |
| 605 | /// Get the block order (size = chunk_size << order). |
| 606 | |
| 607 | self.this.order |
| 608 | |
| 609 | |
| 610 | /// Get the block's size in bytes. |
| 611 | |
| 612 | << self.this.order |
| 613 | |
| 614 |