On Tue, 20 Jan 2026 15:42:51 -0500 Joel Fernandes <[email protected]> wrote:
> Add TLB (Translation Lookaside Buffer) flush support for GPU MMU. > The same concern as in PATCH 5, guess we need to think of concurrency for TLB flush. > After modifying page table entries, the GPU's TLB must be invalidated > to ensure the new mappings take effect. The Tlb struct provides flush > functionality through BAR0 registers. > > The flush operation writes the page directory base address and triggers > an invalidation, polling for completion with a 2 second timeout matching > the Nouveau driver. > > Signed-off-by: Joel Fernandes <[email protected]> > --- > drivers/gpu/nova-core/mm/mod.rs | 1 + > drivers/gpu/nova-core/mm/tlb.rs | 79 +++++++++++++++++++++++++++++++++ > drivers/gpu/nova-core/regs.rs | 33 ++++++++++++++ > 3 files changed, 113 insertions(+) > create mode 100644 drivers/gpu/nova-core/mm/tlb.rs > > diff --git a/drivers/gpu/nova-core/mm/mod.rs > b/drivers/gpu/nova-core/mm/mod.rs index 6015fc8753bc..39635f2d0156 100644 > --- a/drivers/gpu/nova-core/mm/mod.rs > +++ b/drivers/gpu/nova-core/mm/mod.rs > @@ -6,6 +6,7 @@ > > pub(crate) mod pagetable; > pub(crate) mod pramin; > +pub(crate) mod tlb; > > use kernel::sizes::SZ_4K; > > diff --git a/drivers/gpu/nova-core/mm/tlb.rs > b/drivers/gpu/nova-core/mm/tlb.rs new file mode 100644 > index 000000000000..8b2ee620da18 > --- /dev/null > +++ b/drivers/gpu/nova-core/mm/tlb.rs > @@ -0,0 +1,79 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! TLB (Translation Lookaside Buffer) flush support for GPU MMU. > +//! > +//! After modifying page table entries, the GPU's TLB must be flushed to > +//! ensure the new mappings take effect. This module provides TLB flush > +//! functionality for virtual memory managers. > +//! > +//! # Example > +//! > +//! ```ignore > +//! use crate::mm::tlb::Tlb; > +//! > +//! fn page_table_update(tlb: &Tlb, pdb_addr: VramAddress) -> > Result<()> { +//! // ... modify page tables ... > +//! > +//! // Flush TLB to make changes visible (polls for completion). > +//! tlb.flush(pdb_addr)?; > +//! > +//! Ok(()) > +//! } > +//! ``` > + > +#![allow(dead_code)] > + > +use kernel::{ > + devres::Devres, > + io::poll::read_poll_timeout, > + prelude::*, > + sync::Arc, > + time::Delta, // > +}; > + > +use crate::{ > + driver::Bar0, > + mm::VramAddress, > + regs, // > +}; > + > +/// TLB manager for GPU translation buffer operations. > +pub(crate) struct Tlb { > + bar: Arc<Devres<Bar0>>, > +} > + > +impl Tlb { > + /// Create a new TLB manager. > + pub(super) fn new(bar: Arc<Devres<Bar0>>) -> Self { > + Self { bar } > + } > + > + /// Flush the GPU TLB for a specific page directory base. > + /// > + /// This invalidates all TLB entries associated with the given PDB > address. > + /// Must be called after modifying page table entries to ensure the > GPU sees > + /// the updated mappings. > + pub(crate) fn flush(&self, pdb_addr: VramAddress) -> Result { > + let bar = self.bar.try_access().ok_or(ENODEV)?; > + > + // Write PDB address. > + > regs::NV_TLB_FLUSH_PDB_LO::from_pdb_addr(pdb_addr.raw_u64()).write(&*bar); > + > regs::NV_TLB_FLUSH_PDB_HI::from_pdb_addr(pdb_addr.raw_u64()).write(&*bar); > + > + // Trigger flush: invalidate all pages and enable. > + regs::NV_TLB_FLUSH_CTRL::default() > + .set_page_all(true) > + .set_enable(true) > + .write(&*bar); > + > + // Poll for completion - enable bit clears when flush is done. > + read_poll_timeout( > + || Ok(regs::NV_TLB_FLUSH_CTRL::read(&*bar)), > + |ctrl| !ctrl.enable(), > + Delta::ZERO, > + Delta::from_secs(2), > + )?; > + > + Ok(()) > + } > +} > diff --git a/drivers/gpu/nova-core/regs.rs > b/drivers/gpu/nova-core/regs.rs index c8b8fbdcf608..e722ef837e11 100644 > --- a/drivers/gpu/nova-core/regs.rs > +++ b/drivers/gpu/nova-core/regs.rs > @@ -414,3 +414,36 @@ pub(crate) mod ga100 { > 0:0 display_disabled as bool; > }); > } > + > +// MMU TLB > + > +register!(NV_TLB_FLUSH_PDB_LO @ 0x00b830a0, "TLB flush register: PDB > address bits [39:8]" { > + 31:0 pdb_lo as u32, "PDB address bits [39:8]"; > +}); > + > +impl NV_TLB_FLUSH_PDB_LO { > + /// Create a register value from a PDB address. > + /// > + /// Extracts bits [39:8] of the address and shifts it right by 8 > bits. > + pub(crate) fn from_pdb_addr(addr: u64) -> Self { > + Self::default().set_pdb_lo(((addr >> 8) & 0xFFFF_FFFF) as u32) > + } > +} > + > +register!(NV_TLB_FLUSH_PDB_HI @ 0x00b830a4, "TLB flush register: PDB > address bits [47:40]" { > + 7:0 pdb_hi as u8, "PDB address bits [47:40]"; > +}); > + > +impl NV_TLB_FLUSH_PDB_HI { > + /// Create a register value from a PDB address. > + /// > + /// Extracts bits [47:40] of the address and shifts it right by 40 > bits. > + pub(crate) fn from_pdb_addr(addr: u64) -> Self { > + Self::default().set_pdb_hi(((addr >> 40) & 0xFF) as u8) > + } > +} > + > +register!(NV_TLB_FLUSH_CTRL @ 0x00b830b0, "TLB flush control register" { > + 0:0 page_all as bool, "Invalidate all pages"; > + 31:31 enable as bool, "Enable/trigger flush (clears when flush > completes)"; +});
