Hi Dave,

On Mon Oct 26, 2026 at 1:24 PM JST, Dave Airlie via B4 Relay wrote:
> From: Dave Airlie <[email protected]>
>
> These aren't used on systems with no framebuffer memory.
>
> Signed-off-by: Dave Airlie <[email protected]>
> ---
>  drivers/gpu/nova-core/gpu.rs               | 15 +++++++------
>  drivers/gpu/nova-core/mm.rs                | 34 
> +++++++++++++++---------------
>  drivers/gpu/nova-core/mm/bar_user.rs       | 14 ++++++------
>  drivers/gpu/nova-core/mm/pagetable/map.rs  | 10 ++++-----
>  drivers/gpu/nova-core/mm/pagetable/walk.rs |  2 +-
>  5 files changed, 38 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
> index 4e59072cab50..5d53fde2cdc1 100644
> --- a/drivers/gpu/nova-core/gpu.rs
> +++ b/drivers/gpu/nova-core/gpu.rs
> @@ -564,18 +564,19 @@ pub(crate) fn new<'a>(
>  
>              // Create GPU memory manager owning memory management resources.
>              mm: {
> -                let usable_vram = 
> gsp_static_info.usable_fb_regions.first().ok_or(ENODEV)?;
> -                let buddy_params = GpuBuddyParams {
> -                    base_offset: usable_vram.start,
> -                    size: usable_vram.end - usable_vram.start,
> -                    chunk_size: Alignment::new::<SZ_4K>(),
> -                };
> +                let buddy_params = 
> gsp_static_info.usable_fb_regions.first().map(|usable_vram| {
> +                    GpuBuddyParams {
> +                        base_offset: usable_vram.start,
> +                        size: usable_vram.end - usable_vram.start,
> +                        chunk_size: Alignment::new::<SZ_4K>(),
> +                    }
> +                });
>  
>                  GpuMm::new(
>                      bar,
>                      gsp_resources.spec.chipset,
>                      buddy_params,
> -                    VramAddress::from_raw(gsp_static_info.total_fb_end),
> +                    
> Some(VramAddress::from_raw(gsp_static_info.total_fb_end)),
>                  )?
>              },
>  
> diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
> index 8a934c55169f..9a2c73c978be 100644
> --- a/drivers/gpu/nova-core/mm.rs
> +++ b/drivers/gpu/nova-core/mm.rs
> @@ -71,12 +71,12 @@ macro_rules! impl_pfn_bounded {
>  /// GPU Memory Manager - owns all core MM components.
>  ///
>  /// Provides centralized ownership of memory management resources:
> -/// - [`GpuBuddy`] allocator for VRAM page table allocation.
> -/// - [`pramin::Pramin`] for direct VRAM access.
> +/// - Optional [`GpuBuddy`] allocator for VRAM page table allocation.
> +/// - Optional [`pramin::Pramin`] for direct VRAM access.
>  /// - [`Tlb`] manager for translation buffer flush operations.
>  pub(crate) struct GpuMm<'gpu> {
> -    buddy: GpuBuddy,
> -    pramin: pramin::Pramin<'gpu>,
> +    buddy: Option<GpuBuddy>,
> +    pramin: Option<pramin::Pramin<'gpu>>,
>      tlb: Pin<KBox<Tlb<'gpu>>>,
>  }
>  
> @@ -85,28 +85,28 @@ impl<'gpu> GpuMm<'gpu> {
>      pub(crate) fn new(
>          bar: Bar0<'gpu>,
>          chipset: Chipset,
> -        buddy_params: GpuBuddyParams,
> -        total_fb_end: VramAddress,
> +        buddy_params: Option<GpuBuddyParams>,
> +        total_fb_end: Option<VramAddress>,

This is related to the comment made by Sashiko, but in which cases are
we expecting `total_fb_end` to be `None` here? Is this the same set of
systems for which `buddy_params` will be `None`? If so both arguments
should be merged into a new type to remove the ability to represent
invalid state where one is `Some` and the other is `None`.

>      ) -> Result<Self> {
> -        // PRAMIN covers all physical VRAM (including GSP-reserved areas
> -        // above the usable region, e.g. the BAR1 page directory).
> -        let vram_region = VramAddress::ZERO..total_fb_end;
> -
>          Ok(Self {
> -            buddy: GpuBuddy::new(buddy_params)?,
> -            pramin: pramin::Pramin::new(bar, chipset, vram_region)?,
> +            buddy: buddy_params.map(GpuBuddy::new).transpose()?,
> +            // PRAMIN covers all physical VRAM, including GSP-reserved areas 
> above
> +            // the usable region, such as the BAR1 page directory.
> +            pramin: total_fb_end
> +                .map(|end| pramin::Pramin::new(bar, chipset, 
> VramAddress::ZERO..end))
> +                .transpose()?,
>              tlb: KBox::pin_init(Tlb::new(bar), GFP_KERNEL)?,
>          })
>      }
>  
>      /// Access the [`GpuBuddy`] allocator.
> -    pub(crate) fn buddy(&self) -> &GpuBuddy {
> -        &self.buddy
> +    pub(crate) fn buddy(&self) -> Result<&GpuBuddy> {
> +        self.buddy.as_ref().ok_or(ENODEV)
>      }

For this kind of accessor to an `Option` it is considered more idiomatic
to just return an `Option` and let the caller decide how to handle the
error, but in this case it just seems to be a private convenience method
used only by submodules, so doing that would defeat its purpose.
Instead, can we make it private?

<...>
> diff --git a/drivers/gpu/nova-core/mm/pagetable/map.rs 
> b/drivers/gpu/nova-core/mm/pagetable/map.rs
> index 77431c509a89..fd404b0734bb 100644
> --- a/drivers/gpu/nova-core/mm/pagetable/map.rs
> +++ b/drivers/gpu/nova-core/mm/pagetable/map.rs
> @@ -81,7 +81,7 @@ pub(super) fn new(pdb_addr: VramAddress) -> Self {
>      /// Allocate and zero a physical page table page.
>      fn alloc_and_zero_page(mm: &mut GpuMm<'_>, level: PageTableLevel) -> 
> Result<PreparedPtPage> {
>          let blocks = KBox::pin_init(
> -            mm.buddy().alloc_blocks(
> +            mm.buddy()?.alloc_blocks(
>                  GpuBuddyAllocMode::Simple,
>                  SZ_4K.into_safe_cast(),
>                  Alignment::new::<SZ_4K>(),
> @@ -94,7 +94,7 @@ fn alloc_and_zero_page(mm: &mut GpuMm<'_>, level: 
> PageTableLevel) -> Result<Prep
>  
>          // Zero via PRAMIN.
>          let window = mm
> -            .pramin_mut()
> +            .pramin_mut()?
>              .window_at::<[u64; PAGE_SIZE / 8]>(page_vram)?;
>          for i in 0..PAGE_SIZE / 8 {
>              io_write!(window.view(), [build: i], 0);
> @@ -120,7 +120,7 @@ fn ensure_single_pte_path(
>          for _ in 0..max_iter {
>              let result = self
>                  .walker
> -                .walk_pde_levels(mm.pramin_mut(), vfn, |install_addr| {
> +                .walk_pde_levels(mm.pramin_mut()?, vfn, |install_addr| {
>                      pt_pages.get(&install_addr).and_then(|p| {
>                          p.alloc
>                              .iter()
> @@ -197,7 +197,7 @@ pub(super) fn install_mappings(
>          writable: bool,
>      ) -> Result {
>          {
> -            let pramin = mm.pramin_mut();
> +            let pramin = mm.pramin_mut()?;
>  
>              // Drain prepared PT pages, install all pending PDEs.
>              let mut cursor = pt_pages.cursor_front_mut();
> @@ -255,7 +255,7 @@ pub(super) fn invalidate_ptes(
>          let invalid_pte = M::Pte::invalid();
>  
>          {
> -            let pramin = mm.pramin_mut();
> +            let pramin = mm.pramin_mut()?;

I'll let Eliot comment on this in more depth as I am not very familiar
with this module, but I'd like to avoid these runtime checks if
possible. On systems without a framebuffer, we should not even be able
to call these methods, so I'd like to enforce it at that level.

Ideally, we would consolidate all the state requiring a framebuffer into
a new type (i.e. my first comment, but pushed further), and put it
behind a single `Option` that solely tells us whether the system has a
framebuffer or not. Then methods requiring a framebuffer can be passed a
reference to that type and work without runtime checking its presence.

Reply via email to