[PATCH v2 10/32] gpu: nova-core: mm: add VramBlock

From: Zhi Wang

Date: Mon Sep 14 2026 - 03:58:43 EST


vGPU memory slots need allocations at fixed offsets within usable VRAM.
Add GpuMm::alloc_vram_range and VramBlock to own these exact allocations
and return their storage to the buddy allocator on drop.

Use the buddy allocator's range validation and retain the checks for
absolute physical address overflow and alignment.

Cc: Alistair Popple <apopple@xxxxxxxxxx>
Signed-off-by: Zhi Wang <zhiw@xxxxxxxxxx>
---
drivers/gpu/nova-core/mm.rs | 2 +
drivers/gpu/nova-core/mm/vram.rs | 78 ++++++++++++++++++++++++++++++++
2 files changed, 80 insertions(+)
create mode 100644 drivers/gpu/nova-core/mm/vram.rs

diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index 8a934c55169f..016d99608aeb 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -67,6 +67,8 @@ macro_rules! impl_pfn_bounded {
mod regs;
pub(super) mod tlb;
pub(super) mod vmm;
+#[expect(dead_code)]
+pub(crate) mod vram;

/// GPU Memory Manager - owns all core MM components.
///
diff --git a/drivers/gpu/nova-core/mm/vram.rs b/drivers/gpu/nova-core/mm/vram.rs
new file mode 100644
index 000000000000..743a2a163318
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/vram.rs
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! VRAM allocation.
+
+use core::ops::Range;
+
+use kernel::{
+ gpu::buddy::{
+ AllocatedBlocks,
+ GpuBuddyAllocFlags,
+ GpuBuddyAllocMode, //
+ },
+ prelude::*,
+ ptr::Alignment,
+ sync::Arc, //
+};
+
+use crate::num::IntoSafeCast;
+
+use super::{
+ GpuMm,
+ PAGE_SIZE, //
+};
+
+/// A physically contiguous VRAM allocation.
+pub(crate) struct VramBlock {
+ _blocks: Pin<KBox<AllocatedBlocks>>,
+ address: u64,
+ size: u64,
+}
+
+impl VramBlock {
+ pub(crate) const fn address(&self) -> u64 {
+ self.address
+ }
+}
+
+impl GpuMm<'_> {
+ /// Allocates an exact byte range relative to the buddy allocator's base.
+ pub(crate) fn alloc_vram_range(&self, range: Range<u64>, align: u64) -> Result<Arc<VramBlock>> {
+ let size = range.end.checked_sub(range.start).ok_or(EINVAL)?;
+ let align = align.max(PAGE_SIZE.into_safe_cast());
+ let min_block_size =
+ Alignment::new_checked(usize::try_from(align).map_err(|_| EOVERFLOW)?).ok_or(EINVAL)?;
+ let buddy = self.buddy();
+ let address = buddy
+ .base_offset()
+ .checked_add(range.start)
+ .ok_or(EOVERFLOW)?;
+ buddy
+ .base_offset()
+ .checked_add(range.end)
+ .ok_or(EOVERFLOW)?;
+ if !address.is_multiple_of(align) {
+ return Err(EINVAL);
+ }
+
+ let blocks = KBox::pin_init(
+ buddy.alloc_blocks(
+ GpuBuddyAllocMode::Range(range),
+ size,
+ min_block_size,
+ GpuBuddyAllocFlags::default(),
+ ),
+ GFP_KERNEL,
+ )?;
+
+ Ok(Arc::new(
+ VramBlock {
+ _blocks: blocks,
+ address,
+ size,
+ },
+ GFP_KERNEL,
+ )?)
+ }
+}