Introduce NVIDIA vGPU manager
Subsystems (9)
v4 cover Introduce NVIDIA vGPU manager
Add the nova-core support needed to create and manage NVIDIA vGPU instances on the current whole-GPU SR-IOV path.
The overall control path is shown below. This posting contains the nova-core manager and PF-side lifecycle APIs. Typed PF-service publication and the VFIO variant driver follow separately.
+-----------------------+ +-----------------------+
| Linux guest | | Windows guest |
| NVIDIA guest driver | | NVIDIA guest driver |
+-----------+-----------+ +-----------+-----------+
| |
+-----------v-----------+ +-----------v-----------+
| QEMU (VFIO user) | | QEMU (VFIO user) |
+-----------+-----------+ +-----------+-----------+
+----------------+----------------+
|
+-------v-------+
| VFIO core |
+-------+-------+
|
+---------------------v---------------------+
| NVIDIA vGPU VFIO variant driver |
+---------------------+---------------------+
|
+---------------+----------------------+
| binds PCI VF | PF lifecycle API
| | open / close / reset
v v
+--------+--------+ +-------------+-------------+
| PCI VF(s) | | nova-core (PF driver) |
| (virtual GPU) | | vGPU Manager |
+--------+--------+ | VRAM / channels ... |
| +-------------+-------------+
| |
+-------------v--------------------------------------v----------------+
| NVIDIA physical GPU |
| VF resources PF / GSP / VRAM |
+---------------------------------------------------------------------+The manager queries the type assigned to a VF by GSP-RM, reserves its channel and VRAM resources, boots and configures the GSP plugin, and handles shutdown. It also scrubs guest VRAM before reuse and exposes per-instance plugin logs through debugfs.
Host resources remain owned until firmware teardown and BAR unmapping complete. Failed creation rolls back through the instance owner; if firmware ownership is uncertain or teardown fails, the manager retains the affected resources until device removal.
This continues the rework of the earlier vGPU RFC [2]: firmware-facing management belongs to nova-core, while the VFIO driver binds to the VF and uses the PF's services. The companion VFIO driver does not need its own copies of the RM firmware headers.
The patches are organized as follows:
1 Retain BAR1 reservations after an unmap failure. 2-8 Boot state, runtime parameters and the 48-VM WPR2 heap. 9-12 VRAM allocation, region views and BAR mappings. 13-14 Synchronous GMC transactions and completion events. 15-19 Plugin bindings, property queries and instance allocation. 20-28 Plugin boot, shutdown and RPC. 29-31 CeUtils scrubbing and plugin logs. 32 SR-IOV PF lifecycle APIs.
Changes since v3:
* Include the BAR1 unmap-failure fix as patch 1.
* Rebase onto r000 firmware stack and drop changes already supplied by the prerequisites.
* Adapt boot-state/VF information handling, firmware encoding and GMC transactions to the interfaces supplied by that newer stack.
Dependencies:
This series is based on drm-rust-next at 41871f4ddf710339863f957bcfcab5094c234771 plus the r000 GSP firmware and other integration prerequisites.
The complete integration tree, including the prerequisites and the companion VFIO driver, is available at:
https://github.com/alokk-nvidia/nova-core/tree/nova-vgpu-wip-20261105
Previous versions:
v3: [1]
v2: https://lore.kernel.org/nova-gpu/[email protected]/
v1: https://lore.kernel.org/nova-gpu/[email protected]/
[1] https://lore.kernel.org/nova-gpu/[email protected]/
[2] https://lore.kernel.org/kvm/[email protected]/
Alok Kumar (2):
gpu: nova-core: gsp: decode the FIFO engine table
gpu: nova-core: vgpu: add VRAM slot allocator
Zhi Wang (30):
gpu: nova-core: mm: retain BAR1 reservations after unmap failure
gpu: nova-core: gsp: pass boot context through setup helpers
gpu: nova-core: gsp: decouple boot context from VgpuManager
gpu: nova-core: vgpu: detect boot state independently
gpu: nova-core: gpu: add a channel ID pool for vGPU
gpu: nova-core: vgpu: initialize runtime parameters after GSP boot
gpu: nova-core: vgpu: reserve the 48-VM WPR2 heap
gpu: nova-core: mm: borrow BarUser for temporary BAR1 access
gpu: nova-core: mm: add VramBlock
gpu: nova-core: mm: add VramRegion
gpu: nova-core: mm: add BarMapping
gpu: nova-core: gsp: add synchronous GMC transactions
gpu: nova-core: gsp: wait for GMC completion events
gpu: nova-core: vgpu: add r000 plugin bindings
gpu: nova-core: gsp: factor out NVKV payload conversion
gpu: nova-core: vgpu: query VF assignments and properties
gpu: nova-core: vgpu: add instance create/destroy
gpu: nova-core: vgpu: encode vGPU boot requests
gpu: nova-core: vgpu: add GSP plugin communication buffers
gpu: nova-core: vgpu: add instance boot
gpu: nova-core: vgpu: add instance shutdown
gpu: nova-core: vgpu: initialize GSP plugin RPC buffers
gpu: nova-core: vgpu: add GSP plugin RPC transactions
gpu: nova-core: vgpu: negotiate the GSP plugin RPC version
gpu: nova-core: vgpu: send GSP plugin configuration parameters
gpu: nova-core: vgpu: update the GSP plugin BME state
gpu: nova-core: vgpu: add CeUtils commands
gpu: nova-core: vgpu: scrub guest VRAM with CeUtils
gpu: nova-core: vgpu: export plugin log buffers via debugfs
gpu: nova-core: vgpu: introduce SR-IOV PF APIs
Documentation/gpu/nova/core/interrupts.rst | 20 +- drivers/gpu/nova-core/fb.rs | 28 +- drivers/gpu/nova-core/gpu.rs | 111 ++- drivers/gpu/nova-core/gpu/channel.rs | 4 + drivers/gpu/nova-core/gsp.rs | 30 +- drivers/gpu/nova-core/gsp/boot.rs | 8 +- drivers/gpu/nova-core/gsp/cmdq.rs | 28 +- drivers/gpu/nova-core/gsp/cmdq/gmc.rs | 223 +++++- drivers/gpu/nova-core/gsp/commands.rs | 11 + drivers/gpu/nova-core/gsp/commands/gmc.rs | 34 +- drivers/gpu/nova-core/gsp/fw.rs | 44 +- drivers/gpu/nova-core/gsp/fw/commands.rs | 108 ++- .../gpu/nova-core/gsp/fw/r000_00/bindings.rs | 175 +++++ drivers/gpu/nova-core/gsp/hal/gh100.rs | 2 +- drivers/gpu/nova-core/gsp/hal/tu102.rs | 2 +- drivers/gpu/nova-core/gsp/nvkv.rs | 42 +- drivers/gpu/nova-core/gsp/nvkv/encode.rs | 12 + drivers/gpu/nova-core/mm.rs | 1 + drivers/gpu/nova-core/mm/bar_user.rs | 209 +++++- drivers/gpu/nova-core/mm/vmm.rs | 18 +- drivers/gpu/nova-core/mm/vram.rs | 131 ++++ drivers/gpu/nova-core/nova_core.rs | 6 + drivers/gpu/nova-core/regs.rs | 31 +- drivers/gpu/nova-core/vgpu.rs | 111 ++- drivers/gpu/nova-core/vgpu/commands.rs | 321 +++++++++ drivers/gpu/nova-core/vgpu/fw.rs | 70 ++ drivers/gpu/nova-core/vgpu/fw/commands.rs | 470 +++++++++++++ drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs | 496 ++++++++++++++ drivers/gpu/nova-core/vgpu/gsp_plugin_rpc.rs | 170 +++++ drivers/gpu/nova-core/vgpu/instance.rs | 642 ++++++++++++++++++ drivers/gpu/nova-core/vgpu/log.rs | 171 +++++ drivers/gpu/nova-core/vgpu/scrubber.rs | 173 +++++ drivers/gpu/nova-core/vgpu/vgpu_api.rs | 158 +++++ drivers/gpu/nova-core/vgpu/vram.rs | 179 +++++ 34 files changed, 4085 insertions(+), 154 deletions(-) create mode 100644 drivers/gpu/nova-core/mm/vram.rs create mode 100644 drivers/gpu/nova-core/vgpu/commands.rs create mode 100644 drivers/gpu/nova-core/vgpu/fw.rs create mode 100644 drivers/gpu/nova-core/vgpu/fw/commands.rs create mode 100644 drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs create mode 100644 drivers/gpu/nova-core/vgpu/gsp_plugin_rpc.rs create mode 100644 drivers/gpu/nova-core/vgpu/instance.rs create mode 100644 drivers/gpu/nova-core/vgpu/log.rs create mode 100644 drivers/gpu/nova-core/vgpu/scrubber.rs create mode 100644 drivers/gpu/nova-core/vgpu/vgpu_api.rs create mode 100644 drivers/gpu/nova-core/vgpu/vram.rs
base-commit: ccfd86490ddf6fd9c8f59a1a3171c652afb44c7f
Signature · 1 line
2.53.0
PATCH v4 1/32 gpu: nova-core: mm: retain BAR1 reservations after unmap failure
From: Zhi Wang <[email protected]>
Vmm::unmap_pages currently returns a virtual address range to its pool even when PTE invalidation or its TLB flush fails. Hardware can still use that mapping, so reusing the range can alias a live mapping.
Borrow MappedRange during unmap and release its VA reservation only after invalidation succeeds. Disarm its drop guard at that point and reject a second unmap of a range whose guard is already disarmed.
Keep BarUserAccess's mapped state until unmap succeeds, and update its callers to pass a borrowed range. A failed unmap leaves the VA reserved; callers must retain the backing storage while hardware may still access it.
Extend the MM self-test to unmap an old range after its virtual address has been reused. Require the stale token to fail without invalidating the new mapping.
--- drivers/gpu/nova-core/mm/bar_user.rs | 35 +++++++++++++++++++--------- drivers/gpu/nova-core/mm/vmm.rs | 18 +++++++------- 2 files changed, 34 insertions(+), 19 deletions(-)
drivers/gpu/nova-core/mm/bar_user.rs +24 -11
@@ -83,8 +83,7 @@ pub(crate) fn map(/// Borrows the [`BarUser`] managing the mapping for the lifetime of the access object. pub (crate )struct BarUserAccess <'a> { bar_user: &'aBarUser <'a>, -/// [`BarUserAccess::release`] [`Option::take`]s this; `Some` at -/// drop time means `release()` was never called. +/// Cleared only after PTE invalidation and its TLB flush succeed. mapped:Option <MappedRange >, }
@@ -92,16 +91,22 @@ pub(crate) struct BarUserAccess<'a> {impl BarUserAccess <'_> {/// Tear down the BAR1 mapping. pub (crate )fn release (mut self, mm: &mut GpuMm <'_>) ->Result { -let mapped = self.mapped.take ().ok_or (EINVAL )?; + self.unmap (mm) + } + +fn unmap (&mut self, mm: &mut GpuMm <'_>) ->Result { +let Some (mapped) = self.mapped.as_ref ()else { +return Ok (()); + };let mut vmm = self.bar_user.vmm.lock (); vmm.unmap_pages (mm, mapped)?; + self.mapped =None ;Ok (()) }/// Returns the active mapping. fn mapped (&self) -> &MappedRange { -// `mapped` is only `None` after `take()` in `release`; hence unwrap() -// cannot panic here. +// `release` consumes the access, so no accessor can run after successful release. self.mapped.as_ref ().unwrap () }
@@ -167,7 +172,7 @@ impl Drop for BarUserAccess<'_> {fn drop (&mut self) {if self.mapped.is_some () { kernel::pr_warn!( -"BarUserAccess dropped without calling release(). BarUser address space will leak.\n" +"BarUserAccess dropped with a live mapping; BAR1 VA remains reserved.\n" ); }// The inner `MappedRange`'s own `MustUnmapGuard` will also fire,
@@ -266,19 +271,27 @@ pub(crate) fn run_self_test( };// Cleanup - invalidate PTE. - vmm.unmap_pages (mm, mapped)?; + vmm.unmap_pages (mm, &mapped)?;// Test 2: Two-phase prepare/execute API. let prepared = vmm.prepare_map (mm,1 ,None )?;let mapped2 = vmm.execute_map (mm, prepared, &[test_pfn],true )?; +// An old token must not invalidate a new mapping that reused its address. +let repeated_unmap = vmm.unmap_pages (mm, &mapped);let readback = vmm.read_mapping (mm, mapped2.vfn_start)?; -let test2_passed =if readback ==Some (test_pfn) { +let test2_passed =if mapped2.vfn_start == test_vfn + && repeated_unmap ==Err (EINVAL ) + && readback ==Some (test_pfn) + {true }else { -dev_err! (dev,"MM: Test 2 FAILED - Two-phase map readback mismatch\n" ); +dev_err! ( + dev, +"MM: Test 2 FAILED - Remapped page or stale-token rejection mismatch\n" + );false }; - vmm.unmap_pages (mm, mapped2)?; + vmm.unmap_pages (mm, &mapped2)?;// Test 3: Range-constrained allocation with a hole — exercises block.size()-driven // BAR1 mapping. A 4K hole is punched at base+16K, then a single 32K allocation
@@ -370,7 +383,7 @@ pub(crate) fn run_self_test( } } - vmm.unmap_pages (mm, mapped)?; + vmm.unmap_pages (mm, &mapped)?; }// Verify aggregate: all returned block sizes must sum to allocation size.
drivers/gpu/nova-core/mm/vmm.rs +10 -8
@@ -324,23 +324,25 @@ pub(crate) fn map_pages( }/// Unmap all pages in a [`MappedRange`] with a single TLB flush. -pub (crate )fn unmap_pages (&mut self, mm: &mut GpuMm <'_>, range:MappedRange ) ->Result { -let result = self - .pt_map - .invalidate_ptes (mm, range.vfn_start, range.num_pages); +/// +/// A failed invalidation retains the VA reservation. The caller must keep +/// the mapped storage alive while hardware may still access it. +pub (crate )fn unmap_pages (&mut self, mm: &mut GpuMm <'_>, range: &MappedRange ) ->Result { +if !range._drop_guard.armed.get () { +return Err (EINVAL ); + } + self.pt_map + .invalidate_ptes (mm, range.vfn_start, range.num_pages)?;// TODO: Internal page table pages (PDE, PTE pages) are still kept around. // This is by design as repeated maps/unmaps will be fast. As a future TODO, // we can add a reclaimer here to reclaim if VRAM is short. For now, the PT // pages are dropped once the `Vmm` is dropped. -// Free the VA range regardless of PTE invalidation success, so that the VA -// range is recovered even on failure (PTEs may be stale, but that is better -// than leaking both PTEs and VA range). self.free_vfn (range.vfn_start);// Unmap complete, safe to drop `MappedRange`. range._drop_guard.disarm (); - result +Ok (()) } }
Signature · 1 line
2.53.0
PATCH v4 2/32 gpu: nova-core: gsp: pass boot context through setup helpers
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The GSP boot paths pass the device, chipset, BAR and vGPU state to the VRAM layout and GSP_INIT helpers as separate arguments. These values are already available in GspBootContext, and some are forwarded again to the WPR2 heap and VF topology helpers.
Pass the boot context through these helpers so each can read the fields it needs. This simplifies the helper signatures and their call sites.
No functional change is intended.
--- drivers/gpu/nova-core/fb.rs | 26 +++++++++----------- drivers/gpu/nova-core/gsp/boot.rs | 8 +++--- drivers/gpu/nova-core/gsp/commands/gmc.rs | 17 ++++--------- drivers/gpu/nova-core/gsp/fw/commands.rs | 30 ++++++++++------------- drivers/gpu/nova-core/gsp/hal/gh100.rs | 2 +- drivers/gpu/nova-core/gsp/hal/tu102.rs | 2 +- 6 files changed, 34 insertions(+), 51 deletions(-)
drivers/gpu/nova-core/fb.rs +11 -15
@@ -174,13 +174,9 @@ pub(crate) struct FbRanges {impl FbRanges {/// Computes concrete framebuffer ranges required on non-FSP booting architectures. -pub (crate )fn new ( - chipset:Chipset , - bar:Bar0 <'_>, - gsp_fw: &GspFirmware <'_>, - vgpu_state:VgpuState , - ) ->Result <Self > { -let hal = hal::fb_hal (chipset); +pub (crate )fn new (ctx: &gsp::GspBootContext <'_, '_>, gsp_fw: &GspFirmware <'_>) ->Result <Self > { +let bar = ctx.bar; +let hal = hal::fb_hal (ctx.chipset);let fb = {let fb_size = hal.vidmem_size (bar);
@@ -242,7 +238,7 @@ pub(crate) fn new(FbRange (fw_image_addr..fw_image_addr + fw_image_size) }; -let (vf_partition_count, wpr2_heap_size) =wpr2_heap_params (chipset, vgpu_state, fb.end)?; +let (vf_partition_count, wpr2_heap_size) =wpr2_heap_params (ctx, fb.end)?;let wpr2_heap = {const WPR2_HEAP_DOWN_ALIGN :Alignment =Alignment ::SZ_1M ;
@@ -298,11 +294,11 @@ pub(crate) fn wpr2_range(bar: Bar0<'_>) -> Option<Range<u64>> { } /// Computes the number of VF partitions and the WPR2 heap size from the vGPU state. -fn wpr2_heap_params(chipset: Chipset, vgpu_state: VgpuState, fb_size: u64) -> Result<(u8, u64)> { - Ok(match vgpu_state { +fn wpr2_heap_params(ctx: &gsp::GspBootContext<'_, '_>, fb_size: u64) -> Result<(u8, u64)> { + Ok(match ctx.vgpu.state() { VgpuState::Disabled => ( 0, - gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb_size)?, + gsp::LibosParams::from_chipset(ctx.chipset).wpr_heap_size(ctx.chipset, fb_size)?, ), VgpuState::Enabled { total_vfs } => ( u8::try_from(total_vfs.get()).map_err(|_| EINVAL)?,
@@ -328,10 +324,10 @@ pub(crate) struct FbSizes {impl FbSizes {/// Computes the framebuffer region sizes for GSP-FMC boot. -pub (crate )fn new (chipset:Chipset , bar:Bar0 <'_>, vgpu_state:VgpuState ) ->Result <Self > { -let hal = hal::fb_hal (chipset); -let fb_size = hal.vidmem_size (bar); -let (vf_partition_count, wpr2_heap_size) =wpr2_heap_params (chipset, vgpu_state, fb_size)?; +pub (crate )fn new (ctx: &gsp::GspBootContext <'_, '_>) ->Result <Self > { +let hal = hal::fb_hal (ctx.chipset); +let fb_size = hal.vidmem_size (ctx.bar); +let (vf_partition_count, wpr2_heap_size) =wpr2_heap_params (ctx, fb_size)?;Ok (Self { frts_size: hal.frts_size (),
drivers/gpu/nova-core/gsp/boot.rs +3 -5
@@ -526,11 +526,9 @@ pub(crate) fn boot( bootloader_app_version: gsp_fw.bootloader.app_version, libos_dma_handle: self.libos.dma_address(), }; -let static_info = self - .cmdq - .send_gmc_command (GspInit ::new (pdev, chipset, ctx.vgpu.state ())?, |event| { - load_exec.dispatch_gmc_boot_event (event) - })?; +let static_info = self.cmdq.send_gmc_command (GspInit ::new (ctx)?, |event| { + load_exec.dispatch_gmc_boot_event (event) + })?;Ok (super ::BootResult { unload_bundle: unload_guard.dismiss ().1 ,
drivers/gpu/nova-core/gsp/commands/gmc.rs +5 -12
@@ -6,14 +6,11 @@use core::convert::Infallible ;use kernel::{ - device, - pci, prelude::*, transmute::AsBytes ,// };use crate ::{ - gpu::Chipset , gsp::{ cmdq::{ gmc::{
@@ -38,9 +35,9 @@ Schema, UnknownKeyPolicy, // }, + GspBootContext, // }, - sbuffer::SBufferIter, - vgpu::VgpuState, // + sbuffer::SBufferIter, // };/// The `GSP_INIT` command, which carries the system information and registry keys to GSP-RM.
@@ -52,7 +49,7 @@ pub(crate) struct GspInit { }impl GspInit { -/// Builds the request for `pdev`. +/// Builds the request using the device, chipset and vGPU state from `ctx`. /// /// # Errors ///
@@ -60,13 +57,9 @@ impl GspInit {/// - `ENODEV` if vGPU mode is enabled but the SR-IOV capability is missing. /// /// Errors reading the PCI configuration or decoding the VF BAR layout are propagated as-is. -pub (crate )fn new ( - pdev: &pci::Device <device::Bound >, - chipset:Chipset , - vgpu_state:VgpuState , - ) ->Result <Self > { +pub (crate )fn new (ctx: &GspBootContext <'_, '_>) ->Result <Self > {let mut encoder =Encoder ::new (); -GspInitRequest ::new (pdev, chipset, vgpu_state)?.encode (&mut encoder)?; +GspInitRequest ::new (ctx)?.encode (&mut encoder)?;Ok (Self { payload: encoder.finish (),
drivers/gpu/nova-core/gsp/fw/commands.rs +13 -17
@@ -6,7 +6,6 @@use kernel::{ alloc::ArrayVec , bitfield, - device, pci, prelude::*, transmute::{
@@ -16,7 +15,6 @@ };use crate ::{ - gpu::Chipset , gsp::{ commands::GspStaticInfo , nvkv::{
@@ -29,6 +27,7 @@ Required, TruncatingArray, // }, + GspBootContext, // }, vgpu::VgpuState, // };
@@ -229,16 +228,14 @@ struct DomainBusDevice(u64) { ];impl GspInitRequest { -fn build_vf_info ( - dev: &pci::Device <device::Bound >, - vgpu_state:VgpuState , - ) ->Result <Option <VfInfo >> { -let total_vfs =match vgpu_state { +fn build_vf_info (ctx: &GspBootContext <'_, '_>) ->Result <Option <VfInfo >> { +let total_vfs =match ctx.vgpu.state () {VgpuState ::Disabled =>return Ok (None ),VgpuState ::Enabled { total_vfs } => total_vfs, }; -let sriov = dev +let sriov = ctx + .pdev .config_space_extended ()? .find_ext_capability ::<pci::ExtSriovRegs >()? .ok_or (ENODEV )?;
@@ -261,10 +258,10 @@ fn build_vf_info( })) } -/// Creates the request for `dev`. +/// Creates the request using the device, chipset and vGPU state from `ctx`. /// -/// The registry keys are [`REGISTRY_ENTRIES`], plus `RMSetSriovMode` when `vgpu_state` reports -/// that vGPU is enabled. +/// The registry keys are [`REGISTRY_ENTRIES`], plus `RMSetSriovMode` when the boot context +/// reports that vGPU is enabled. /// /// # Errors ///
@@ -272,11 +269,10 @@ fn build_vf_info(/// - `ENODEV` if vGPU mode is enabled but the SR-IOV capability is missing. /// /// Errors reading the PCI configuration or decoding the VF BAR layout are propagated as-is. -pub (crate )fn new ( - dev: &pci::Device <device::Bound >, - chipset:Chipset , - vgpu_state:VgpuState , - ) ->Result <Self > { +pub (crate )fn new (ctx: &GspBootContext <'_, '_>) ->Result <Self > { +let dev = ctx.pdev; +let chipset = ctx.chipset; +let vgpu_state = ctx.vgpu.state ();let mut regkeys =KVVec ::new ();for &(name, value)in REGISTRY_ENTRIES { regkeys.push (RegKey ::new (name, value),GFP_KERNEL )?;
@@ -304,7 +300,7 @@ pub(crate) fn new( host_arch: HostArch::host().into(), bus_device_func: u64::from(bus_device_func).into(), regkeys, - vf_info: Self::build_vf_info(dev, vgpu_state)?, + vf_info: Self::build_vf_info(ctx)?, }) } }
drivers/gpu/nova-core/gsp/hal/gh100.rs +1 -1
@@ -151,7 +151,7 @@ fn boot<'gpu>(let chipset = ctx.chipset;let gsp_falcon = ctx.gsp_falcon; -let fb_sizes =FbSizes ::new (chipset, ctx.bar, ctx.vgpu.state ())?; +let fb_sizes =FbSizes ::new (ctx)?;dev_dbg! (dev,"{:#x?}\n" , fb_sizes); let wpr_meta =
drivers/gpu/nova-core/gsp/hal/tu102.rs +1 -1
@@ -260,7 +260,7 @@ fn boot<'gpu>(let gsp_falcon = ctx.gsp_falcon;let sec2_falcon = ctx.sec2_falcon; -let fb_ranges =FbRanges ::new (chipset, bar, gsp_fw, ctx.vgpu.state ())?; +let fb_ranges =FbRanges ::new (ctx, gsp_fw)?;dev_dbg! (dev,"{:#x?}\n" , fb_ranges);// Declared before the unload guard so that if Booter fails while running, SEC2 is reset
Signature · 1 line
2.53.0
Re: [PATCH v4 02/32] gpu: nova-core: gsp: pass boot context through setup helpers
Quoted 7 lines · Alok Kumar
From: Zhi Wang <[email protected]>
The GSP boot paths pass the device, chipset, BAR and vGPU state to the VRAM layout and
GSP_INIThelpers as separate arguments. These values are already available in GspBootContext, and some are forwarded again to the WPR2 heap and VF topology helpers.
Quoted 8 lines
Pass the boot context through these helpers so each can read the fields it needs. This simplifies the helper signatures and their call sites.
No functional change is intended.
Cc Alexandre Courbot <[email protected]>Signed-off-by Zhi Wang <[email protected]>Signed-off-by Alok Kumar <[email protected]>
Makes sense as the GSP HAL methods already take GspBootContext anyway.
(I would have merged it directly, but this depends on the r000 series so marking as reviewed for now)
PATCH v4 3/32 gpu: nova-core: gsp: decouple boot context from VgpuManager
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
GSP boot uses the detected vGPU mode to size firmware resources and populate GSP_INIT. Carrying the whole manager in the boot and unload context couples these operations to its runtime interface.
The boot code only needs read access to the mode, and unloading does not use the manager. A short state borrow is sufficient for both context construction sites.
Make GspBootContext borrow VgpuState directly and read it in the VRAM and GSP_INIT helpers. Return a state reference from the manager without changing mode detection or the values passed to firmware.
No functional change is intended.
--- drivers/gpu/nova-core/fb.rs | 2 +- drivers/gpu/nova-core/gpu.rs | 4 ++-- drivers/gpu/nova-core/gsp.rs | 4 ++-- drivers/gpu/nova-core/gsp/fw/commands.rs | 4 ++-- drivers/gpu/nova-core/vgpu.rs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-)
drivers/gpu/nova-core/fb.rs +1 -1
@@ -295,7 +295,7 @@ pub(crate) fn wpr2_range(bar: Bar0<'_>) -> Option<Range<u64>> { /// Computes the number of VF partitions and the WPR2 heap size from the vGPU state. fn wpr2_heap_params(ctx: &gsp::GspBootContext<'_, '_>, fb_size: u64) -> Result<(u8, u64)> { - Ok(match ctx.vgpu.state() { + Ok(match *ctx.vgpu_state { VgpuState::Disabled => ( 0, gsp::LibosParams::from_chipset(ctx.chipset).wpr_heap_size(ctx.chipset, fb_size)?,
drivers/gpu/nova-core/gpu.rs +2 -2
@@ -447,7 +447,7 @@ fn drop(self: Pin<&mut Self>) { gsp_falcon: &*this.gsp_falcon, sec2_falcon: &*this.sec2_falcon, fsp: this.fsp.as_mut(), - vgpu: &*this.vgpu, + vgpu_state: this.vgpu.state(), }, bundle, )
@@ -522,7 +522,7 @@ pub(crate) fn new<'a>( gsp_falcon, sec2_falcon, fsp: fsp.as_mut(), - vgpu, + vgpu_state: vgpu.state(), })?, }),
drivers/gpu/nova-core/gsp.rs +2 -2
@@ -66,7 +66,7 @@ fw::GspArgumentsPadded, // }, num, - vgpu::VgpuManager, // + vgpu::VgpuState, // };pub (crate )const GSP_PAGE_SHIFT :usize =12 ;
@@ -85,7 +85,7 @@ pub(crate) struct GspBootContext<'ctx, 'gpu> { pub(crate) gsp_falcon: &'ctx Falcon<'gpu, GspFalcon>, pub(crate) sec2_falcon: &'ctx Falcon<'gpu, Sec2Falcon>, pub(crate) fsp: Option<&'ctx mut Fsp<'gpu>>, - pub(crate) vgpu: &'ctx VgpuManager, + pub(crate) vgpu_state: &'ctx VgpuState, }impl <'ctx, 'gpu>GspBootContext <'ctx, 'gpu> {
drivers/gpu/nova-core/gsp/fw/commands.rs +2 -2
@@ -229,7 +229,7 @@ struct DomainBusDevice(u64) {impl GspInitRequest {fn build_vf_info (ctx: &GspBootContext <'_, '_>) ->Result <Option <VfInfo >> { -let total_vfs =match ctx.vgpu.state () { +let total_vfs =match *ctx.vgpu_state {VgpuState ::Disabled =>return Ok (None ),VgpuState ::Enabled { total_vfs } => total_vfs, };
@@ -272,7 +272,7 @@ fn build_vf_info(ctx: &GspBootContext<'_, '_>) -> Result<Option<VfInfo>> {pub (crate )fn new (ctx: &GspBootContext <'_, '_>) ->Result <Self > {let dev = ctx.pdev;let chipset = ctx.chipset; -let vgpu_state = ctx.vgpu.state (); +let vgpu_state = *ctx.vgpu_state;let mut regkeys =KVVec ::new ();for &(name, value)in REGISTRY_ENTRIES { regkeys.push (RegKey ::new (name, value),GFP_KERNEL )?;
drivers/gpu/nova-core/vgpu.rs +2 -2
@@ -85,7 +85,7 @@ fn detect_state( }/// Returns the detected vGPU state for this boot. -pub (crate )fn state (&self) ->VgpuState { - self.state +pub (crate )fn state (&self) -> &VgpuState { + &self.state } }
Signature · 1 line
2.53.0
Alexandre Courbot
maintainerRe: [PATCH v4 03/32] gpu: nova-core: gsp: decouple boot context from VgpuManager
Quoted 10 lines · Alok Kumar
From: Zhi Wang <[email protected]>
GSP boot uses the detected vGPU mode to size firmware resources and populate
GSP_INIT. Carrying the whole manager in the boot and unload context couples these operations to its runtime interface.The boot code only needs read access to the mode, and unloading does not use the manager. A short state borrow is sufficient for both context construction sites.
Quoted 5 lines
Make GspBootContext borrow VgpuState directly and read it in the VRAM and
GSP_INIThelpers. Return a state reference from the manager without changing mode detection or the values passed to firmware.No functional change is intended.
This patch changes the signature of state right before it is removed by the next patch, which looks a bit like churn. Both patches are actually a single logical change and can be squashed together into a single one - the diff actually reads better that way imho.
<...>
Quoted 14 lines
drivers/gpu/nova-core/gsp.rs +1
-1@@ -66,7 +66,7 @@ fw::GspArgumentsPadded, // }, num, - vgpu::VgpuManager, // + vgpu::VgpuState, // };pub (crate )const GSP_PAGE_SHIFT :usize =12 ;
drivers/gpu/nova-core/gsp.rs pub(crate) struct GspBootContext<'ctx, 'gpu> { · line 88
Quoted 5 lines
drivers/gpu/nova-core/gsp.rs +1
-1@@ -85,7 +85,7 @@ pub(crate) struct GspBootContext<'ctx, 'gpu> { pub(crate) gsp_falcon: &'ctx Falcon<'gpu, GspFalcon>, pub(crate) sec2_falcon: &'ctx Falcon<'gpu, Sec2Falcon>, pub(crate) fsp: Option<&'ctx mut Fsp<'gpu>>, - pub(crate) vgpu: &'ctx VgpuManager, + pub(crate) vgpu_state: &'ctx VgpuState,
VgpuState is Copy, so let's store it by value. This will also remove the dereferences done through this patch.
PATCH v4 4/32 gpu: nova-core: vgpu: detect boot state independently
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The vGPU manager currently contains only the detected boot mode. Its construction couples mode detection to an object with no runtime management responsibilities.
Firmware setup needs the mode before allocating GSP resources, even when vGPU is disabled. Keeping that value with GspResources also makes it available throughout the firmware lifecycle without a manager.
Move detection to VgpuState and store its result in GspResources. Borrow it directly when constructing the boot and unload contexts, and remove the state-only manager wrapper. Preserve the FSP query order, existing diagnostics and disabled fallback.
No functional change is intended.
--- drivers/gpu/nova-core/gpu.rs | 11 +++++------ drivers/gpu/nova-core/vgpu.rs | 25 ++++++++----------------- 2 files changed, 13 insertions(+), 23 deletions(-)
drivers/gpu/nova-core/gpu.rs +5 -6
@@ -52,7 +52,7 @@ GpuMm, VramAddress, // }, - vgpu::VgpuManager, // + vgpu::VgpuState, // };#[cfg_attr(not( CONFIG_KUNIT = "y" ), expect(dead_code))]
@@ -337,8 +337,7 @@ struct GspResources<'gpu> {// TODO: use different resource types for each boot method, and make the relevant Gsp methods // generic against them. fsp: Option<Fsp<'gpu>>, - /// vGPU state detected before GSP boot. - vgpu: VgpuManager, + vgpu_state: VgpuState,/// GSP runtime data. #[pin] gsp: Gsp<'gpu>,
@@ -447,7 +446,7 @@ fn drop(self: Pin<&mut Self>) { gsp_falcon: &*this.gsp_falcon, sec2_falcon: &*this.sec2_falcon, fsp: this.fsp.as_mut(), - vgpu_state: this.vgpu.state(), + vgpu_state: this.vgpu_state, }, bundle, )
@@ -508,7 +507,7 @@ pub(crate) fn new<'a>( fsp: Fsp::try_new(dev, bar, spec.chipset)?, - vgpu: VgpuManager::new(pdev, spec.chipset, fsp.as_mut()), + vgpu_state: VgpuState::detect(pdev, spec.chipset, fsp.as_mut()), gsp <- Gsp::new(pdev, *spec, bar),
@@ -522,7 +521,7 @@ pub(crate) fn new<'a>( gsp_falcon, sec2_falcon, fsp: fsp.as_mut(), - vgpu_state: vgpu.state(), + vgpu_state, })?, }),
drivers/gpu/nova-core/vgpu.rs +8 -17
@@ -30,19 +30,16 @@ pub(crate) enum VgpuState { }, } -/// vGPU state manager. -pub(crate) struct VgpuManager { - state: VgpuState, -} - -impl VgpuManager { - /// Creates a vGPU manager by querying SR-IOV and the FSP PRC vGPU knob. - pub(crate) fn new( +impl VgpuState { + /// Detects the boot mode, falling back to disabled if querying the device fails. + /// + /// Call after creating the FSP and before allocating GSP firmware resources. + pub(crate) fn detect( pdev: &pci::Device<device::Core<'_>>, chipset: Chipset, fsp: Option<&mut Fsp<'_>>, ) -> Self { - let state = Self::detect_state(pdev, chipset, fsp).unwrap_or_else(|e| { + let state = Self::query_state(pdev, chipset, fsp).unwrap_or_else(|e| { dev_warn!( pdev, "vGPU state detection failed: {:?}; disabling vGPU\n",
@@ -51,12 +48,11 @@ pub(crate) fn new(VgpuState ::Disabled });dev_dbg! (pdev,"vGPU state: {:?}\n" , state); - -Self { state } + state }/// Detects the vGPU state from the chipset, SR-IOV capability and FSP PRC knob. -fn detect_state ( +fn query_state ( pdev: &pci::Device <device::Core <'_>>, chipset:Chipset , fsp: Option<&mut Fsp<'_>>,
@@ -83,9 +79,4 @@ fn detect_state( VgpuMode::Disabled => Ok(VgpuState::Disabled), } } - -/// Returns the detected vGPU state for this boot. -pub (crate )fn state (&self) -> &VgpuState { - &self.state - } }
Signature · 1 line
2.53.0
Alexandre Courbot
maintainerRe: [PATCH v4 04/32] gpu: nova-core: vgpu: detect boot state independently
On Fri Nov 6, 2026 at 2:39 PM JST, Alok Kumar wrote:
<...>
Quoted 11 lines
drivers/gpu/nova-core/vgpu.rs +0
-3@@ -30,19 +30,16 @@ pub(crate) enum VgpuState { }, } -/// vGPU state manager. -pub (crate )struct VgpuManager { - state:VgpuState ,
drivers/gpu/nova-core/vgpu.rs pub(crate) enum VgpuState { · line 37
Quoted 10 lines
drivers/gpu/nova-core/vgpu.rs +5
-5-} - -impl VgpuManager { -/// Creates a vGPU manager by querying SR-IOV and the FSP PRC vGPU knob. - pub(crate) fn new( +impl VgpuState { +/// Detects the boot mode, falling back to disabled if querying the device fails. +/// +/// Call after creating the FSP and before allocating GSP firmware resources. + pub(crate) fn detect(
I think new was a fine method name here as it is a constructor...
Quoted 11 lines
drivers/gpu/nova-core/vgpu.rs +1
-1pdev: &pci::Device<device::Core<'_>>, chipset: Chipset, fsp: Option<&mut Fsp<'_>>, ) -> Self { - let state = Self::detect_state(pdev, chipset, fsp).unwrap_or_else(|e| { + let state = Self::query_state(pdev, chipset, fsp).unwrap_or_else(|e| { dev_warn!( pdev, "vGPU state detection failed: {:?}; disabling vGPU\n",@@ -51,12 +48,11 @@ pub(crate) fn new(VgpuState ::Disabled
drivers/gpu/nova-core/vgpu.rs pub(crate) fn new( · line 55
Quoted 10 lines
drivers/gpu/nova-core/vgpu.rs +2
-3});dev_dbg! (pdev,"vGPU state: {:?}\n" , state); - -Self { state } + state }/// Detects the vGPU state from the chipset, SR-IOV capability and FSP PRC knob. - fn detect_state( + fn query_state(
Here as well, I don't see a benefit in renaming this method.
PATCH v4 5/32 gpu: nova-core: gpu: add a channel ID pool for vGPU
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Channel ID reservations need a GPU-owned pool shared by vGPU instances.
The pool must remain at a stable address while its reservations exist. Its storage also needs to remain available throughout GPU cleanup.
Create a private pinned ChannelIdPool with a single 2048-channel capacity constant.
--- drivers/gpu/nova-core/gpu.rs | 9 +++++++++ drivers/gpu/nova-core/gpu/channel.rs | 4 ++++ 2 files changed, 13 insertions(+)
drivers/gpu/nova-core/gpu.rs +9 -0
@@ -52,6 +52,7 @@ GpuMm, VramAddress, // }, + num, vgpu::VgpuState, // };
@@ -60,6 +61,10 @@mod hal;mod regs; +use self::channel::TOTAL_CHANNELS ; + +pub (crate )use self::channel::ChannelIdPool ; +macro_rules! define_chipset { ({ $($variant:ident),* $(,)* }) => {
@@ -410,6 +415,8 @@ pub(crate) struct Gpu<'gpu> {/// GSP and its resources. #[pin] gsp_resources: GspResources<'gpu>, +#[pin] + chid_pool: ChannelIdPool,/// System memory page required for flushing all pending GPU-side memory writes done through /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation). ///
@@ -494,6 +501,8 @@ pub(crate) fn new<'a>(// Initialize this early because `gsp_resources` depends on it. sysmem_flush: SysmemFlush::register(dev, bar, spec.chipset)?, + chid_pool <- ChannelIdPool::new(cv!(num::u32_as_usize(TOTAL_CHANNELS))), + gsp_resources <-try_pin_init! (GspResources { device: pdev,
drivers/gpu/nova-core/gpu/channel.rs +4 -0
@@ -21,6 +21,10 @@ }, // }; +// TODO: Query the channel capacity through GMCAPI once an equivalent of +// NV2080_CTRL_CMD_INTERNAL_FIFO_GET_NUM_CHANNELS is available. +pub (super )const TOTAL_CHANNELS :u32 =2048 ; +/// Pool for tracking reservations of channel IDs. #[pin_data] pub (crate )struct ChannelIdPool {
Signature · 1 line
2.53.0
PATCH v4 6/32 gpu: nova-core: gsp: decode the FIFO engine table
In reply to Alok KumarGSP_INIT reports the FIFO engine topology used to build the channel map for vGPU instances.
Decode the count, GMC IDs and flags into GspStaticInfo and expose host-driven engines in firmware FIFO order, retaining repeated IDs. Reject counts and indexed entries beyond the driver's 64-entry table capacity rather than silently accepting a prefix. An omitted count remains zero.
--- drivers/gpu/nova-core/gsp/commands.rs | 10 +++ drivers/gpu/nova-core/gsp/commands/gmc.rs | 4 +- drivers/gpu/nova-core/gsp/fw/commands.rs | 77 +++++++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-)
drivers/gpu/nova-core/gsp/commands.rs +10 -0
@@ -11,6 +11,8 @@use kernel::prelude::*; +use super ::fw::commands::FifoEngineList ; +/// The static GPU configuration, which GSP-RM reports in reply to [`GspInit`](gmc::GspInit). pub struct GspStaticInfo { gpu_name: [u8 ;Self ::MAX_GPU_NAME_LEN ],
@@ -23,6 +25,8 @@ pub struct GspStaticInfo { pub(crate) usable_fb_regions: KVec<Range<u64>>,/// Exclusive end of the FB physical address space. pub(crate) total_fb_end: u64, +/// Host-driven engines in firmware FIFO order. + fifo_engine_list: FifoEngineList, }/// Error type for [`GspStaticInfo::gpu_name`].
@@ -63,6 +67,12 @@ pub fn gpu_short_name(&self) -> core::result::Result<&str, GpuNameError> { .map_err(GpuNameError::InvalidUtf8) } +/// Returns the host-driven engines in their firmware FIFO order. +#[expect(dead_code)] +pub (crate )fn fifo_engine_list (&self) ->FifoEngineList { + self.fifo_engine_list + } +/// Returns the total usable VRAM size in bytes, i.e. the summed lengths of all usable FB /// regions. pub fn vram_size (&self) ->u64 {
drivers/gpu/nova-core/gsp/commands/gmc.rs +3 -1
@@ -145,7 +145,8 @@ impl MessageFromGsp for GspStaticInfo {/// # Errors /// /// - `EINVAL` if the payload is not a whole number of NVKV words, if the stream is malformed -/// or omits a required key, or if GSP-RM reported no framebuffer region. +/// or omits a required key, if GSP-RM reported no framebuffer region, or if the FIFO engine +/// count or indexed entries exceed the supported table capacity. /// - `ENOMEM` if the words or the decoded regions cannot be allocated. fn read (payload: &mut SBufferIter <core::array::IntoIter <&[u8 ],2 >>) ->Result <Self > {let bytes = payload.flush_into_kvec (GFP_KERNEL )?;
@@ -184,6 +185,7 @@ fn read(payload: &mut SBufferIter<core::array::IntoIter<&[u8], 2>>) -> Result<Se bar1_pde_base: reply.bar1_pde_base(), usable_fb_regions, total_fb_end: reply.total_fb_end().ok_or(EINVAL)?, + fifo_engine_list: reply.fifo_engine_list(), }) } }
drivers/gpu/nova-core/gsp/fw/commands.rs +77 -0
109 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 7/32 gpu: nova-core: vgpu: initialize runtime parameters after GSP boot
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The vGPU manager needs the VMMU segment size and FIFO engine topology returned by a successful GSP boot to divide instance resources.
Construct VgpuManager only for a vGPU-enabled boot. Retain the GPU-owned channel pool, the ordered host-driven engine
list and the VMMU segment size. Expose the existing decoded VMMU value from GspStaticInfo at its first consumer, preserving zero when firmware omits the key.
Keep the runtime values in the manager for its instance module, and drop the manager before the pool and memory resources it borrows.
--- drivers/gpu/nova-core/gpu.rs | 23 ++++++++++++++- drivers/gpu/nova-core/gsp/commands.rs | 5 ++-- drivers/gpu/nova-core/gsp/commands/gmc.rs | 1 + drivers/gpu/nova-core/gsp/fw/commands.rs | 2 +- drivers/gpu/nova-core/vgpu.rs | 36 ++++++++++++++++++++++- 5 files changed, 62 insertions(+), 5 deletions(-)
drivers/gpu/nova-core/gpu.rs +22 -1
@@ -53,7 +53,10 @@ VramAddress, // }, num, - vgpu::VgpuState, // + vgpu::{ +VgpuManager , +VgpuState ,// + }, };#[cfg_attr(not( CONFIG_KUNIT = "y" ), expect(dead_code))]
@@ -397,6 +400,7 @@ unsafe fn new(#[pin_data] pub (crate )struct Gpu <'gpu> {pub (crate ) spec:Spec , + vgpu:Option <VgpuManager <'gpu>>,/// GSP event interrupt registration, and the enable of its subtree. /// /// Must be kept declared *before* `gsp_resources`, so that the handler is unregistered, and
@@ -534,6 +538,23 @@ pub(crate) fn new<'a>( })?, }), + vgpu: { + let info = &gsp_resources.boot_result.static_info; +match gsp_resources.vgpu_state { +VgpuState ::Disabled =>None , +VgpuState ::Enabled { .. } =>Some (VgpuManager ::new ( +// SAFETY: `chid_pool` is initialized above at its final pinned address. +// The private manager and its pool borrow cannot escape this `Gpu`. +// Completed field drop order drops the manager before the pool; on failure, +// pin-init drops it before the earlier-initialized pool. +unsafe { &*core::ptr::from_ref (chid_pool.as_ref ().get_ref ()) }, + &info.fifo_engine_list (), + info.vmmu_segment_size, +TOTAL_CHANNELS , + )), + } + }, + _: { irq::gsp::quiesce (tree_ref, &gsp_resources.gsp_falcon); },
drivers/gpu/nova-core/gsp/commands.rs +3 -2
@@ -11,7 +11,7 @@use kernel::prelude::*; -use super ::fw::commands::FifoEngineList ; +pub (crate )use super ::fw::commands::FifoEngineList ;/// The static GPU configuration, which GSP-RM reports in reply to [`GspInit`](gmc::GspInit). pub struct GspStaticInfo {
@@ -25,6 +25,8 @@ pub struct GspStaticInfo { pub(crate) usable_fb_regions: KVec<Range<u64>>,/// Exclusive end of the FB physical address space. pub(crate) total_fb_end: u64, +/// VMMU segment size in bytes, or zero if GSP-RM omitted it. + pub(crate) vmmu_segment_size: u64,/// Host-driven engines in firmware FIFO order. fifo_engine_list: FifoEngineList, }
@@ -68,7 +70,6 @@ pub fn gpu_short_name(&self) -> core::result::Result<&str, GpuNameError> { }/// Returns the host-driven engines in their firmware FIFO order. -#[expect(dead_code)] pub (crate )fn fifo_engine_list (&self) ->FifoEngineList { self.fifo_engine_list }
drivers/gpu/nova-core/gsp/commands/gmc.rs +1 -0
@@ -186,6 +186,7 @@ fn read(payload: &mut SBufferIter<core::array::IntoIter<&[u8], 2>>) -> Result<Se usable_fb_regions, total_fb_end: reply.total_fb_end().ok_or(EINVAL)?, fifo_engine_list: reply.fifo_engine_list(), + vmmu_segment_size: reply.vmmu_segment_size, }) } }
drivers/gpu/nova-core/gsp/fw/commands.rs +1 -1
@@ -378,7 +378,7 @@ pub(crate) struct GspInitResponse { gpu_gid: ArrayVec<u8, { GspStaticInfo::GID_LEN }>, fb_regions: KVVec<FbRegion>, bar1_pde_base: u64, - vmmu_segment_size: u64, + pub(crate) vmmu_segment_size: u64, fifo_engine_count: FifoEngineCount, fifo_engine_gmc_ids: [u32; MAX_FIFO_ENGINES], fifo_engine_flags: [u32; MAX_FIFO_ENGINES],
drivers/gpu/nova-core/vgpu.rs +35 -1
@@ -13,7 +13,11 @@ Fsp, VgpuMode, // }, - gpu::Chipset, // + gpu::{ + ChannelIdPool, + Chipset, // + }, + gsp::commands::FifoEngineList, // };mod hal;
@@ -80,3 +84,33 @@ fn query_state( } } } + +/// Runtime resources for an enabled vGPU boot. +pub (crate )struct VgpuManager <'gpu> { +#[expect(dead_code)] + chid_pool: &'gpuChannelIdPool , +/// VMMU segment size in bytes, or zero if GSP-RM omitted it. +#[expect(dead_code)] + vmmu_segment_size:u64 , +#[expect(dead_code)] + total_channels:u32 , +#[expect(dead_code)] + fifo_engine_list:FifoEngineList , +} + +impl <'gpu>VgpuManager <'gpu> { +/// Retains runtime parameters from a completed vGPU-enabled GSP boot. +pub (crate )fn new ( + chid_pool: &'gpuChannelIdPool , + fifo_engine_list: &FifoEngineList , + vmmu_segment_size:u64 , + total_channels:u32 , + ) ->Self { +Self { + chid_pool, + vmmu_segment_size, + total_channels, + fifo_engine_list: *fifo_engine_list, + } + } +}
Signature · 1 line
2.53.0
PATCH v4 8/32 gpu: nova-core: vgpu: reserve the 48-VM WPR2 heap
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
GSP-RM needs the larger r000 vGPU WPR2 heap on devices that advertise more than 32 VFs. The default heap is too small for the 48-VF GB202 configuration and GSP-RM fails to start.
Select the firmware-defined 48-VM size from the detected VF count while retaining the default size for configurations with 2 through 32 VFs.
--- drivers/gpu/nova-core/fb.rs | 2 +- drivers/gpu/nova-core/gsp/fw.rs | 7 +++++-- drivers/gpu/nova-core/gsp/fw/r000_00/bindings.rs | 1 + 3 files changed, 7 insertions(+), 3 deletions(-)
drivers/gpu/nova-core/fb.rs +1 -1
@@ -302,7 +302,7 @@ fn wpr2_heap_params(ctx: &gsp::GspBootContext<'_, '_>, fb_size: u64) -> Result<( ), VgpuState::Enabled { total_vfs } => ( u8::try_from (total_vfs.get ()).map_err (|_|EINVAL )?, - gsp::LibosParams ::vgpu_wpr_heap_size (), + gsp::LibosParams ::vgpu_wpr_heap_size (total_vfs.get ()), ), }) }
drivers/gpu/nova-core/gsp/fw.rs +5 -2
@@ -138,8 +138,11 @@ pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams { }/// Returns the WPR heap size to reserve when vGPU is enabled. -pub (crate )fn vgpu_wpr_heap_size () ->u64 { - u64::from (bindings::GSP_FW_HEAP_SIZE_VGPU_DEFAULT ) +pub (crate )fn vgpu_wpr_heap_size (total_vfs:u16 ) ->u64 { + u64::from (match total_vfs { +2 ..=32 => bindings::GSP_FW_HEAP_SIZE_VGPU_DEFAULT , + _ => r000_00::GSP_FW_HEAP_SIZE_VGPU_48VMS , + }) }/// Returns the amount of memory (in bytes) to allocate for the WPR heap for a framebuffer size
drivers/gpu/nova-core/gsp/fw/r000_00/bindings.rs +1 -0
@@ -84,6 +84,7 @@ impl<T> ::core::cmp::Eq for __BindgenUnionField<T> {}pub const GSP_FW_HEAP_PARAM_SIZE_PER_GB :u32 =98304 ;pub const GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE :u32 =100663296 ;pub const GSP_FW_HEAP_SIZE_VGPU_DEFAULT :u32 =609222656 ; +pub const GSP_FW_HEAP_SIZE_VGPU_48VMS :u32 =1436549120 ;pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB :u32 =64 ;pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB :u32 =256 ;pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB :u32 =88 ;
Signature · 1 line
2.53.0
PATCH v4 9/32 gpu: nova-core: mm: borrow BarUser for temporary BAR1 access
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Temporary BAR1 access needs a borrow of the GPU-owned BarUser. Store BarUser inline in Gpu and borrow it in BarUserAccess, avoiding a separate allocation and reference counting for these accesses. Update the existing self-test entry points to accept the borrowed interface.
No functional change is intended.
--- drivers/gpu/nova-core/gpu.rs | 2 +- drivers/gpu/nova-core/mm/bar_user.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-)
drivers/gpu/nova-core/gpu.rs +1 -1
@@ -666,7 +666,7 @@ pub(crate) fn run_selftests(self: Pin<&mut Self>, pdev: &pci::Device<device::Bou dev, this.mm, &info.usable_fb_regions, - &this.bar_user, + this.bar_user.as_ref().get_ref(), info.bar1_pde_base, this.spec.chipset, ) {
drivers/gpu/nova-core/mm/bar_user.rs +8 -8
@@ -59,12 +59,12 @@ pub(crate) fn new( }/// Map physical pages to a contiguous BAR1 virtual range. -pub (crate )fn map ( - &self, +pub (crate )fn map <'access>( + &'access self, mm: &mut GpuMm <'_>, pfns: &[Pfn ], writable:bool , - ) ->Result <BarUserAccess <'_>> { + ) ->Result <BarUserAccess <'access, 'a>> {if pfns.is_empty () {return Err (EINVAL ); }
@@ -81,14 +81,14 @@ pub(crate) fn map(/// Access object for a mapped BAR1 region. /// /// Borrows the [`BarUser`] managing the mapping for the lifetime of the access object. -pub (crate )struct BarUserAccess <'a> { - bar_user: &'aBarUser <'a>, +pub (crate )struct BarUserAccess <'access, 'gpu> { + bar_user: &'accessBarUser <'gpu>,/// Cleared only after PTE invalidation and its TLB flush succeed. mapped:Option <MappedRange >, }#[expect(dead_code)] -impl BarUserAccess <'_> { +impl BarUserAccess <'_, '_> {/// Tear down the BAR1 mapping. pub (crate )fn release (mut self, mm: &mut GpuMm <'_>) ->Result { self.unmap (mm)
@@ -168,7 +168,7 @@ pub(crate) fn try_write64(&self, value: u64, offset: usize) -> Result { } } -impl Drop for BarUserAccess <'_> { +impl Drop for BarUserAccess <'_, '_> {fn drop (&mut self) {if self.mapped.is_some () { kernel::pr_warn!(
@@ -186,7 +186,7 @@ fn drop(&mut self) {/// address space. Uses the `GpuMm`'s buddy allocator to allocate page tables /// and test pages as needed. #[cfg( CONFIG_NOVA_CORE_SELFTESTS )] -pub (crate )fn run_self_test ( +pub (super )fn run_self_test ( dev: &device::Device <device::Bound >, mm: &mut GpuMm <'_>, bar_user: &BarUser<'_>,
Signature · 1 line
2.53.0
PATCH v4 10/32 gpu: nova-core: mm: add VramBlock
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
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.
--- drivers/gpu/nova-core/mm.rs | 2 + drivers/gpu/nova-core/mm/vram.rs | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 drivers/gpu/nova-core/mm/vram.rs
drivers/gpu/nova-core/mm.rs +2 -0
@@ -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. ///
drivers/gpu/nova-core/mm/vram.rs new file +75 -0
75 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 11/32 gpu: nova-core: mm: add VramRegion
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
BAR mappings do not necessarily cover an entire VRAM block. A region layer lets a mapping select a byte range while keeping the underlying allocation alive.
For vGPU, communication buffers occupy only part of the management heap and need a BAR mapping of that range.
Add VramRegion to describe ranges within a shared VramBlock and support bounds-checked subregions.
--- drivers/gpu/nova-core/mm/vram.rs | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)
drivers/gpu/nova-core/mm/vram.rs +56 -0
@@ -34,6 +34,62 @@ impl VramBlock {pub (crate )const fn address (&self) ->u64 { self.address } + +/// Creates a byte range relative to this allocation, retaining its backing storage. +pub (crate )fn region (self: &Arc <Self >, range:Range <u64 >) ->Result <VramRegion > { +VramRegion ::new (self.clone (), range) + } +} + +/// A byte range that keeps its VRAM allocation alive. +#[derive( Clone )] +pub (crate )struct VramRegion { + backing:Arc <VramBlock >, + address:u64 , + size:u64 , +} + +impl VramRegion { +fn new (backing:Arc <VramBlock >, range:Range <u64 >) ->Result <Self > { +if range.start >= range.end || range.end > backing.size { +return Err (EINVAL ); + } + +let size = range.end - range.start; +let address = backing.address.checked_add (range.start).ok_or (EOVERFLOW )?; + backing.address.checked_add (range.end).ok_or (EOVERFLOW )?; + +Ok (Self { + backing, + address, + size, + }) + } + +pub (crate )const fn address (&self) ->u64 { + self.address + } + +pub (crate )const fn size (&self) ->u64 { + self.size + } + +/// Creates a nonempty subregion relative to this view's start. +pub (crate )fn subregion (&self, range:Range <u64 >) ->Result <Self > { +if range.start >= range.end || range.end > self.size { +return Err (EINVAL ); + } + +let size = range.end - range.start; +let address = self.address.checked_add (range.start).ok_or (EOVERFLOW )?; + address.checked_add (size).ok_or (EOVERFLOW )?; + +Ok (Self { + backing: self.backing.clone (), + address, + size, + }) + } }impl GpuMm <'_> {
Signature · 1 line
2.53.0
PATCH v4 12/32 gpu: nova-core: mm: add BarMapping
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
vGPU communication buffers need CPU access to regions within their VramBlocks. Add BarMapping to map these regions through BAR1, including regions that start or end within a page.
Retain the backing region and borrow BarUser and the MM mutex for the mapping's lifetime. Restrict CPU accesses to the region's byte range, and unmap on drop using the retained MM dependency.
Provide an explicit unmap operation so owners can observe errors before releasing resources. Keep the mapping and backing region on failure, remember the first error and reject subsequent accesses. Owners must retain failed mappings until device teardown; drop does not retry an unmap that already failed.
--- drivers/gpu/nova-core/mm/bar_user.rs | 145 +++++++++++++++++++++++++++ 1 file changed, 145 insertions(+)
drivers/gpu/nova-core/mm/bar_user.rs +145 -0
@@ -7,6 +7,10 @@ io::Io, new_mutex, prelude::*, + ptr::{ + Alignable, + Alignment, // + }, sync::Mutex, // };
@@ -18,6 +22,7 @@ MappedRange, Vmm, // }, + vram::VramRegion, GpuMm, Pfn, Vfn,
@@ -180,6 +185,146 @@ fn drop(&mut self) { } } +/// A BAR mapping that retains its backing VRAM region. +#[must_use] +pub (crate )struct BarMapping <'map, 'gpu> { + access:BarUserAccess <'map, 'gpu>, + mm: &'mapMutex <GpuMm <'gpu>>, + region:VramRegion , + region_offset:usize , + region_size:usize , + unmap_error:Option <Error >, +} + +#[expect(dead_code)] +impl <'map, 'gpu>BarMapping <'map, 'gpu> { +/// Maps the containing pages while restricting CPU access to the requested byte range. +pub (crate )fn new ( + bar_user: &'mapBarUser <'gpu>, + mm: &'mapMutex <GpuMm <'gpu>>, + region:VramRegion , + writable:bool , + ) ->Result <Self > { +let page_size:u64 =PAGE_SIZE .into_safe_cast (); +let page_align =Alignment ::new ::<PAGE_SIZE >(); + +let region_start = region.address (); +let region_end = region_start.checked_add (region.size ()).ok_or (EOVERFLOW )?; + +let map_start = region_start.align_down (page_align); +let map_end = region_end.align_up (page_align).ok_or (EOVERFLOW )?; +let map_size = map_end.checked_sub (map_start).ok_or (EINVAL )?; +let num_pages = usize::try_from (map_size / page_size).map_err (|_|EOVERFLOW )?; +if num_pages ==0 { +return Err (EINVAL ); + } + +let region_offset = usize::try_from (region_start - map_start).map_err (|_|EOVERFLOW )?; +let region_size = usize::try_from (region.size ()).map_err (|_|EOVERFLOW )?; + +let mut pfns =KVec ::with_capacity (num_pages,GFP_KERNEL )?; +for pagein 0 ..num_pages { +let page = u64::try_from (page).map_err (|_|EOVERFLOW )?; +let byte_offset = page.checked_mul (page_size).ok_or (EOVERFLOW )?; +let address = map_start.checked_add (byte_offset).ok_or (EOVERFLOW )?; + pfns.push (Pfn ::from (VramAddress ::from_raw (address)),GFP_KERNEL )?; + } + +let access = bar_user.map (&mut mm.lock (), &pfns, writable)?; + +Ok (Self { + access, + mm, + region, + region_offset, + region_size, + unmap_error:None , + }) + } + +pub (crate )fn region (&self) -> &VramRegion { + &self.region + } + +pub (crate )fn gpu_va_addr (&self) ->Result <u64 > { + self.access ()? + .base () + .into_raw () + .checked_add (u64::try_from (self.region_offset).map_err (|_|EOVERFLOW )?) + .ok_or (EOVERFLOW ) + } + +pub (crate )const fn size (&self) ->usize { + self.region_size + } + +fn access (&self) ->Result <&BarUserAccess <'map, 'gpu>> { +if self.access.mapped.is_none () { +return Err (ENODEV ); + } +if let Some (error) = self.unmap_error { +return Err (error); + } +Ok (&self.access) + } + +fn access_offset (&self, offset:usize , width:usize ) ->Result <usize > { +let region_end = offset.checked_add (width).ok_or (EOVERFLOW )?; +if region_end > self.region_size { +return Err (EINVAL ); + } + +let access_offset = self.region_offset.checked_add (offset).ok_or (EOVERFLOW )?; +if !access_offset.is_multiple_of (width) { +return Err (EINVAL ); + } + +Ok (access_offset) + } + +pub (crate )fn try_read32 (&self, offset:usize ) ->Result <u32 > { + self.access ()? + .try_read32 (self.access_offset (offset,size_of ::<u32 >())?) + } + +pub (crate )fn try_write32 (&self, value:u32 , offset:usize ) ->Result { + self.access ()? + .try_write32 (value, self.access_offset (offset,size_of ::<u32 >())?) + } + +pub (crate )fn try_write64 (&self, value:u64 , offset:usize ) ->Result { + self.access ()? + .try_write64 (value, self.access_offset (offset,size_of ::<u64 >())?) + } + +/// Unmap while retaining the mapping and its backing storage on failure. +/// +/// The first error is retained; later calls do not retry the operation. +pub (crate )fn unmap (&mut self) ->Result { +if let Some (error) = self.unmap_error { +return Err (error); + } +let result = self.access.unmap (&mut self.mm.lock ()); +if let Err (error) = result { + self.unmap_error =Some (error); + } + result + } +} + +impl Drop for BarMapping <'_, '_> { +/// Drop unmaps the region and may sleep. Call [`Self::unmap`] to observe errors; +/// on failure the owner must retain this object until device teardown. +fn drop (&mut self) { +if self.unmap_error.is_some () { +return ; + } +if let Err (error) = self.unmap () { + kernel::pr_err!("failed to unmap BAR1 region: {:?}\n" , error); + } + } +} +/// Run MM subsystem self-tests during probe. /// /// Tests page table infrastructure and `BAR1` MMIO access using the `BAR1`
Signature · 1 line
2.53.0
PATCH v4 13/32 gpu: nova-core: gsp: add synchronous GMC transactions
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
vGPU management queries need an owned response payload after the receive queue releases its slot. Sending and receiving must share one queue guard so another receiver cannot consume the reply.
Match the response flag, command ID and request sequence, then copy the payload and return the raw firmware status. Support the default receive timeout and a caller-selected timeout without converting firmware rejection into a transport error.
Share the receive deadline and retry loop in CmdqInner with the existing GMC boot response wait. Preserve each caller's matching and status policy, consume every valid element once even when its callback fails, and keep a single deadline after sending. Document unmatched-message consumption and distinguish a zero response payload capacity from a command that sends no reply.
--- Documentation/gpu/nova/core/interrupts.rst | 19 ++- drivers/gpu/nova-core/gsp/cmdq.rs | 28 +++- drivers/gpu/nova-core/gsp/cmdq/gmc.rs | 162 ++++++++++++++++++++- drivers/gpu/nova-core/gsp/fw.rs | 26 +++- 4 files changed, 215 insertions(+), 20 deletions(-)
Documentation/gpu/nova/core/interrupts.rst +12 -7
@@ -580,9 +580,12 @@ command reply or an unsolicited event, and the two differ in the function code. records, lifecycle notices) need no action and get no line of their own, because the receive trace at debug level already records every message's arrival with its sequence number, function code, and length. -* A GMC message carries a command id in place of a function code. Only the - ``GSP_INIT`` wait during boot claims GMC messages, so one that arrives - anywhere else is logged at warning level and dropped. +* A GMC message carries a command id in place of a function code. The + ``GSP_INIT`` wait and synchronous GMC transactions claim responses with the + expected command id and sequence number. These waits consume interleaved RPC + messages as events. Unmatched GMC messages are handled by the wait's callback + or logged and consumed; a queue drain with no waiting caller logs unclaimed + responses and unknown commands at warning level and drops them. A command's reply must carry the RPC sequence number that nova-core wrote into the command, as well as its function code. A message with the awaited function
@@ -602,10 +605,12 @@ element stays at the queue head, and every later receive fails on it with The polling path and the IRQ thread both read the queue under the command-queue mutex. Replies and events share one queue and one read pointer, so one lock is held across the whole drain. A thread waiting for a reply logs each event that -arrives before the reply and keeps waiting. One deadline of 5 seconds applies to -the whole wait, rather than a fresh timeout after each message, and the thread -holds the mutex for the whole wait, so that no other caller consumes the message -that it waits for. +arrives before the reply and keeps waiting. One receive deadline applies to +all messages in the wait. The default timeout is 5 seconds; GMC transactions +can specify another timeout. For send-and-wait operations the receive deadline +starts after sending, so it does not bound waiting for the mutex or for +command-queue space. The thread holds the mutex from sending through receiving, +so no other caller consumes the message it waits for. With one lock, a drain waits for an in-flight command's receive to finish or time out. For log and error records that delay does not matter.
drivers/gpu/nova-core/gsp/cmdq.rs +23 -5
@@ -2,6 +2,7 @@// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. pub (super )mod gmc; +// No RPC command is sent since the switch to the r000 firmware: GSP_INIT and GSP_SUSPEND replaced // the RPC commands that the driver used during boot and shutdown. The RPC send and reply paths // stay for the commands that later series add, and are built as dead code until then.
@@ -67,6 +68,9 @@use super ::regs; +#[expect(unused_imports)] +pub (crate )use gmc::GmcResponse ; +/// Marker type representing the absence of a reply for a command. Commands using this as their /// reply type are sent using [`Cmdq::send_rpc_command_no_wait`]. pub (crate )struct NoReply ;
@@ -722,15 +726,29 @@ fn consume_element<R>(/// - Any error returned by `f` with the exception of `ENOMSG`. fn await_msg_with <R >(&mut self, f:impl Fn (GspParsedMessage <'_>) ->Result <R >) ->Result <R > {let deadline =Instant ::<Monotonic >::now () +Cmdq ::RECEIVE_TIMEOUT ; + self.await_msg_with_deadline (deadline, |message|match f (message) { +Ok (value) =>Ok (Some (value)), +Err (ENOMSG ) =>Ok (None ), +Err (error) =>Err (error), + }) + } + +/// Waits under the queue guard until `f` claims a message, using one receive deadline. +/// +/// Every valid element is consumed even if `f` fails. Unclaimed messages do not extend the +/// deadline. Framing errors stall the queue and are returned without consuming the element. +fn await_msg_with_deadline <R >( + &mut self, + deadline:Instant <Monotonic >, +mut f:impl FnMut (GspParsedMessage <'_>) ->Result <Option <R >>, + ) ->Result <R > {loop {let remaining = deadline -Instant ::<Monotonic >::now ();if remaining.is_negative () { -break Err (ETIMEDOUT ); +return Err (ETIMEDOUT ); } -match self.consume_element (remaining, &f)? { -Ok (m) =>break Ok (m), -Err (ENOMSG ) =>continue , -Err (e) =>break Err (e), +if let Some (value) = self.consume_element (remaining, &mut f)?? { +return Ok (value); } } }
drivers/gpu/nova-core/gsp/cmdq/gmc.rs +157 -5
187 lines not on this page · raw message
drivers/gpu/nova-core/gsp/fw.rs +23 -3
@@ -834,8 +834,8 @@ impl GmcApiHeader {/// Creates the header of a request for `command`, sent with sequence number `sequence` and /// followed by `payload_size` bytes of payload. /// -/// `max_reply_size` is the largest reply the sender accepts, and zero for a request that -/// GSP-RM does not answer. +/// `max_reply_size` is the response payload capacity in bytes. Zero permits a status-only +/// response; whether GSP-RM sends a reply is determined by the command protocol. /// /// # Errors ///
@@ -845,9 +845,22 @@ pub(crate) fn init( sequence: u64, payload_size: usize, max_reply_size: u32, + ) -> impl Init<Self, Error> { +// CAST: `GmcCommandId` is `repr(u32)`, so this cast is lossless. + Self::init_raw(command as u32, sequence, payload_size, max_reply_size) + } + +/// Creates a request header for a runtime command ID, masking off command flags. +/// +/// Returns `EOVERFLOW` if `payload_size` does not fit its 32-bit field. +pub (super )fn init_raw ( + command:u32 , + sequence:u64 , + payload_size:usize , + max_reply_size:u32 , ) ->impl Init <Self ,Error > { let init_inner = try_init!(bindings::GMCAPI_HEADER { - command: GmcCommandWord::zeroed().with_id(command).into(), + command: command & 0x00ff_ffff, size: payload_size.try_into().map_err(|_| EOVERFLOW)?, sequence, __bindgen_anon_1: bindings::GMCAPI_HEADER__bindgen_ty_1 {
@@ -863,6 +876,13 @@ pub(crate) fn init( }) } +/// Matches the response flag, low 24 command-ID bits, and full request sequence number. +pub (crate )fn is_response_to (&self, command_id:u32 , sequence:u32 ) ->bool { + self.command ().is_response () + &&GmcCommand ::from (self.command ()).0 == (command_id &0x00ff_ffff ) + && self.sequence () == u64::from (sequence) + } +/// Returns the `command` word of the header. pub (crate )fn command (&self) ->GmcCommandWord {GmcCommandWord ::from (self.inner.command)
Signature · 1 line
2.53.0
PATCH v4 14/32 gpu: nova-core: gsp: wait for GMC completion events
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
GSP plugin shutdown completes through a separate event carrying the GFID. Releasing the queue guard after sending would let another receiver consume that completion.
Keep the guard from sending through the matching event and use the shared Inner receive deadline. Offer GMC events to the completion predicate, pass unmatched events to the caller's handler, and consume interleaved responses and RPC messages. Represent completion with a value and all continuing cases with None.
Propagate callback errors after consuming the current valid element. Document callback non-reentrancy and that the receive timeout starts after sending, excluding mutex acquisition and command-queue space waits.
--- Documentation/gpu/nova/core/interrupts.rst | 9 +-- drivers/gpu/nova-core/gsp/cmdq/gmc.rs | 65 ++++++++++++++++++++++ drivers/gpu/nova-core/gsp/fw.rs | 9 +++ 3 files changed, 79 insertions(+), 4 deletions(-)
Documentation/gpu/nova/core/interrupts.rst +5 -4
@@ -582,7 +582,8 @@ command reply or an unsolicited event, and the two differ in the function code. arrival with its sequence number, function code, and length. * A GMC message carries a command id in place of a function code. The ``GSP_INIT`` wait and synchronous GMC transactions claim responses with the - expected command id and sequence number. These waits consume interleaved RPC + expected command id and sequence number. A GMC completion-event wait uses its + caller's predicate to select an event. These waits consume interleaved RPC messages as events. Unmatched GMC messages are handled by the wait's callback or logged and consumed; a queue drain with no waiting caller logs unclaimed responses and unknown commands at warning level and drops them.
@@ -607,9 +608,9 @@ mutex. Replies and events share one queue and one read pointer, so one lock is held across the whole drain. A thread waiting for a reply logs each event that arrives before the reply and keeps waiting. One receive deadline applies to all messages in the wait. The default timeout is 5 seconds; GMC transactions -can specify another timeout. For send-and-wait operations the receive deadline -starts after sending, so it does not bound waiting for the mutex or for -command-queue space. The thread holds the mutex from sending through receiving, +and completion-event waits can specify another timeout. For send-and-wait +operations the receive deadline starts after sending, so it does not bound +waiting for the mutex or for command-queue space. The thread holds the mutex from sending through receiving, so no other caller consumes the message it waits for. With one lock, a drain waits for an in-flight command's receive to finish or
drivers/gpu/nova-core/gsp/cmdq/gmc.rs +65 -0
71 lines not on this page · raw message
drivers/gpu/nova-core/gsp/fw.rs +9 -0
@@ -905,6 +905,15 @@ pub(crate) fn payload_size(&self) -> usize { num::u32_as_usize (self.inner.size) } +/// Returns the raw `max_resp_or_status` word carried by a GMC header. +/// +/// Its meaning for an event is defined by that event's command. +pub (crate )fn raw_status_word (&self) ->u32 { +// SAFETY: Both union views contain a `u32` at the same offset, so every bit pattern is +// valid regardless of whether the header represents a request, response, or event. +unsafe { self.inner.__bindgen_anon_1.request.max_response_size } + } +/// Returns the `NV_STATUS` carried by a response to a command. /// /// Returns `Some` with the `NV_STATUS` code if the `response` flag of [`Self::command`] is set;
Signature · 1 line
2.53.0
PATCH v4 15/32 gpu: nova-core: vgpu: add r000 plugin bindings
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The GSP plugin communication area and RPC protocol are defined by the r000 firmware interface. Keeping local copies of that ABI risks letting the vGPU manager drift from the firmware layout.
Extend the existing r000 binding set with the nova-core subset of dev_vgpu_gsp_shared.h. The bindings expose the control and response region types, message IDs, and firmware-defined region sizes. Keep the raw declarations private and expose only the symbols needed by the GSP plugin communication layer.
--- .../gpu/nova-core/gsp/fw/r000_00/bindings.rs | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+)
drivers/gpu/nova-core/gsp/fw/r000_00/bindings.rs +173 -0
@@ -1,4 +1,5 @@// SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. #[repr( C )] #[derive( Default )]
@@ -850,3 +851,175 @@ fn default() -> Self { } } } +pub const GSP_PLUGIN_BOOTLOADED :u32 =1315261039 ; +pub const VGPU_CPU_GSP_CTRL_BUFF_VERSION :u32 =2 ; +pub const VGPU_CPU_GSP_CTRL_BUFF_REGION_SIZE :u32 =4096 ; +pub const VGPU_CPU_GSP_RESPONSE_BUFF_REGION_SIZE :u32 =4096 ; +pub const VGPU_CPU_GSP_MESSAGE_BUFF_REGION_SIZE :u32 =4096 ; +pub const VGPU_CPU_GSP_MIGRATION_BUFF_REGION_SIZE :u32 =2097152 ; +pub const VGPU_CPU_GSP_ERROR_BUFF_REGION_SIZE :u32 =4096 ; +pub const VGPU_CPU_GSP_INIT_TASK_LOG_BUFF_REGION_SIZE :u32 =131072 ; +pub const VGPU_CPU_GSP_VGPU_TASK_LOG_BUFF_REGION_SIZE :u32 =262144 ; +pub const VGPU_CPU_GSP_KERNEL_TASK_LOG_BUFF_REGION_SIZE :u32 =65536 ; +pub const VGPU_CPU_GSP_GUEST_RPC_TRACE_BUFF_REGION_SIZE :u32 =65536 ; +pub const VGPU_CPU_GSP_COMMUNICATION_BUFF_TOTAL_SIZE :u32 =2637824 ; +pub type VGPU_CPU_GSP_BOOL =u32_ ; +#[repr( C )] +#[derive( Debug , Default , Copy , Clone , MaybeZeroable )] +pub struct VGPU_CPU_GSP_VGX_VERSION { +pub major_number:u32_ , +pub minor_number:u32_ , +} +#[repr( C )] +#[derive( Debug , Copy , Clone , MaybeZeroable )] +pub struct VGPU_CPU_GSP_GUEST_INFO { +pub vgx_version:VGPU_CPU_GSP_VGX_VERSION , +pub guest_driver_version_buffer_length:u32_ , +pub guest_version_buffer_length:u32_ , +pub guest_title_buffer_length:u32_ , +pub guest_changelist_number:u32_ , +pub guest_driver_version_buffer: [ffi::c_char ;256usize ], +pub guest_version_buffer: [ffi::c_char ;256usize ], +pub guest_title_buffer: [ffi::c_char ;256usize ], +pub guest_branch_buffer: [ffi::c_char ;256usize ], +} +impl Default for VGPU_CPU_GSP_GUEST_INFO { +fn default () ->Self { +let mut s = ::core::mem::MaybeUninit ::<Self >::uninit (); +unsafe { + ::core::ptr::write_bytes (s.as_mut_ptr (),0 ,1 ); + s.assume_init () + } + } +} +#[repr( C )] +#[derive( Copy , Clone , MaybeZeroable )] +pub union VGPU_CPU_GSP_CTRL_BUFF_REGION { +pub buf: [u8_ ;4096usize ], +pub __bindgen_anon_1:VGPU_CPU_GSP_CTRL_BUFF_REGION__bindgen_ty_1 , +} +#[repr( C )] +#[derive( Debug , Default , Copy , Clone , MaybeZeroable )] +pub struct VGPU_CPU_GSP_CTRL_BUFF_REGION__bindgen_ty_1 { +pub version:u32_ , +pub message_type:u32_ , +pub message_seq_num:u32_ , +pub __bindgen_padding_0: [u8 ;4usize ], +pub response_buff_offset:u64_ , +pub message_buff_offset:u64_ , +pub migration_buff_offset:u64_ , +pub error_buff_offset:u64_ , +pub guest_rpc_trace_buff_offset:u64_ , +pub migration_buf_cpu_access_offset:u32_ , +pub is_migration_in_progress:u8_ , +pub __bindgen_padding_1: [u8 ;3usize ], +pub error_buff_cpu_get_idx:u32_ , +pub guest_rpc_trace_buff_cpu_get_idx:u32_ , +pub attached_vgpu_count:u32_ , +pub is_gr_init_done:u8_ , +pub __bindgen_padding_2: [u8 ;3usize ], +pub host_info: [VGPU_CPU_GSP_CTRL_BUFF_REGION__bindgen_ty_1__bindgen_ty_1 ;16usize ], +} +#[repr( C )] +#[derive( Debug , Default , Copy , Clone , MaybeZeroable )] +pub struct VGPU_CPU_GSP_CTRL_BUFF_REGION__bindgen_ty_1__bindgen_ty_1 { +pub vgpu_type_id:u32_ , +pub host_gpu_pci_id:u32_ , +pub pci_dev_id:u32_ , +pub vgpu_uuid: [u8_ ;16usize ], +} +impl Default for VGPU_CPU_GSP_CTRL_BUFF_REGION { +fn default () ->Self { +let mut s = ::core::mem::MaybeUninit ::<Self >::uninit (); +unsafe { + ::core::ptr::write_bytes (s.as_mut_ptr (),0 ,1 ); + s.assume_init () + } + } +} +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_VERSION_NEGOTIATION :MESSAGE =1 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_SETUP_CONFIG_PARAMS_AND_INIT :MESSAGE =2 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_RESET :MESSAGE =3 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_MIGRATION_STOP_WORK :MESSAGE =4 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_MIGRATION_CANCEL_STOP :MESSAGE =5 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_MIGRATION_SAVE_STATE :MESSAGE =6 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_MIGRATION_CANCEL_SAVE :MESSAGE =7 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_MIGRATION_RESTORE_STATE :MESSAGE =8 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_MIGRATION_RESTORE_DEFERRED_STATE :MESSAGE =9 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_MIGRATION_RESUME_WORK :MESSAGE =10 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_CONSOLE_VNC_STATE :MESSAGE =11 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_VF_BAR0_REG_ACCESS :MESSAGE =12 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_UPDATE_BME_STATE :MESSAGE =13 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_RESET_MIGRATION_BUFFER_PTR :MESSAGE =14 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_RELEASE_CLIENT_DATABASE :MESSAGE =15 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_CHECK_IS_ALIVE :MESSAGE =16 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_SEND_STATIC_INFO :MESSAGE =17 ; +pub const MESSAGE_NV_VGPU_CPU_RPC_MSG_MAX :MESSAGE =18 ; +pub type MESSAGE = ffi::c_uint ; +#[repr( C )] +#[derive( Debug , Default , Copy , Clone , MaybeZeroable )] +pub struct VGPU_CPU_GSP_DISPLAYLESS_SURFACE { +pub sequence_update_start:u64_ , +pub sequence_update_end:u64_ , +pub effective_fb_page_size:u32_ , +pub rect_width:u32_ , +pub rect_height:u32_ , +pub surface_width:u32_ , +pub surface_height:u32_ , +pub surface_size:u32_ , +pub surface_offset:u32_ , +pub surface_format:u32_ , +pub surface_kind:u32_ , +pub surface_pitch:u32_ , +pub surface_type:u32_ , +pub surface_block_height:u8_ , +pub __bindgen_padding_0: [u8 ;3usize ], +pub is_blanking_enabled:VGPU_CPU_GSP_BOOL , +pub is_flip_pending:VGPU_CPU_GSP_BOOL , +pub is_free_pending:VGPU_CPU_GSP_BOOL , +pub is_memory_blocklinear:VGPU_CPU_GSP_BOOL , +} +#[repr( C )] +#[derive( Copy , Clone , MaybeZeroable )] +pub union VGPU_CPU_GSP_RESPONSE_BUFF_REGION { +pub buf: [u8_ ;4096usize ], +pub __bindgen_anon_1:VGPU_CPU_GSP_RESPONSE_BUFF_REGION__bindgen_ty_1 , +} +#[repr( C )] +#[derive( Debug , Copy , Clone , MaybeZeroable )] +pub struct VGPU_CPU_GSP_RESPONSE_BUFF_REGION__bindgen_ty_1 { +pub message_seq_num_received:u32_ , +pub message_seq_num_processed:u32_ , +pub result_code:u32_ , +pub guest_rpc_version:u32_ , +pub migration_buf_gsp_access_offset:u32_ , +pub migration_state_save_complete:u32_ , +pub is_migration_allowed:VGPU_CPU_GSP_BOOL , +pub __bindgen_padding_0: [u8 ;4usize ], +pub surface: [VGPU_CPU_GSP_DISPLAYLESS_SURFACE ;4usize ], +pub error_buff_gsp_put_idx:u32_ , +pub grid_license_state:u32_ , +pub guest_os_type:u32_ , +pub frl_config:u32_ , +pub guest_info:VGPU_CPU_GSP_GUEST_INFO , +pub is_guest_info_populated:VGPU_CPU_GSP_BOOL , +pub guest_rpc_trace_buff_gsp_put_idx:u32_ , +} +impl Default for VGPU_CPU_GSP_RESPONSE_BUFF_REGION__bindgen_ty_1 { +fn default () ->Self { +let mut s = ::core::mem::MaybeUninit ::<Self >::uninit (); +unsafe { + ::core::ptr::write_bytes (s.as_mut_ptr (),0 ,1 ); + s.assume_init () + } + } +} +impl Default for VGPU_CPU_GSP_RESPONSE_BUFF_REGION { +fn default () ->Self { +let mut s = ::core::mem::MaybeUninit ::<Self >::uninit (); +unsafe { + ::core::ptr::write_bytes (s.as_mut_ptr (),0 ,1 ); + s.assume_init () + } + } +}
Signature · 1 line
2.53.0
PATCH v4 16/32 gpu: nova-core: vgpu: add VRAM slot allocator
In reply to Alok KumarA vGPU type specifies the guest VRAM and GSP plugin management-heap sizes for each instance. Allocating these regions independently fragments VRAM and does not preserve a stable layout for the vGPU type.
Add a standalone slot allocator that reserves one exact VRAM range and tracks assignments in a shared, mutex-protected bitmap. Validate the vGPU type's layout and reject an allocation request whose layout differs from the active pool.
Lay out all guest VRAM slots first and all management-heap slots second, pairing them by bitmap index. Require the guest VRAM stride and pool base to satisfy the required VMMU-segment alignment.
Return an owned slot that clears its bitmap entry on drop. Keep the bitmap alive while slots exist and release the entry if constructing the VRAM regions fails. Require users to stop device accesses and remove mappings before dropping a slot.
--- drivers/gpu/nova-core/vgpu.rs | 1 + drivers/gpu/nova-core/vgpu/vram.rs | 181 +++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 drivers/gpu/nova-core/vgpu/vram.rs
drivers/gpu/nova-core/vgpu.rs +1 -0
@@ -21,6 +21,7 @@ };mod hal; +mod vram;/// vGPU state detected during GPU construction. #[derive( Debug , Clone , Copy )]
drivers/gpu/nova-core/vgpu/vram.rs new file +181 -0
181 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 17/32 gpu: nova-core: gsp: factor out NVKV payload conversion
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Both GSP initialization and vGPU property queries decode their replies as NVKV streams.
Move the byte-to-word conversion helper into the NVKV module and make that module accessible to the vGPU firmware interface.
No functional change is intended.
--- drivers/gpu/nova-core/gsp.rs | 2 +- drivers/gpu/nova-core/gsp/commands/gmc.rs | 12 ++------- drivers/gpu/nova-core/gsp/nvkv.rs | 31 ++++++++++++++++++++++- 3 files changed, 33 insertions(+), 12 deletions(-)
drivers/gpu/nova-core/gsp.rs +1 -1
@@ -28,7 +28,7 @@pub (crate )mod cmdq;pub (crate )mod commands;mod fw; -mod nvkv; +pub (crate )mod nvkv;mod regs;pub (crate )use fw::{
drivers/gpu/nova-core/gsp/commands/gmc.rs +2 -10
@@ -29,6 +29,7 @@ GmcCommandId, // }, nvkv::{ + nvkv_words,Decoder ,Encodable ,Encoder ,
@@ -151,16 +152,7 @@ impl MessageFromGsp for GspStaticInfo {fn read (payload: &mut SBufferIter <core::array::IntoIter <&[u8 ],2 >>) ->Result <Self > {let bytes = payload.flush_into_kvec (GFP_KERNEL )?; -let chunks = bytes.chunks_exact (size_of ::<u64 >()); -if !chunks.remainder ().is_empty () { -return Err (EINVAL ); - } -let mut words =KVVec ::with_capacity (chunks.len (),GFP_KERNEL )?; -for chunkin chunks { -// `chunks_exact` yields slices of exactly `size_of::<u64>()` bytes. -let word = chunk.try_into ().map_err (|_|EINVAL )?; - words.push (u64::from_le_bytes (word),GFP_KERNEL )?; - } +let words =nvkv_words (&bytes, &[])?;let decoder =Decoder ::new (&words,UnknownKeyPolicy ::Ignore );let mut schema =KBox ::init (GspInitResponseSchema ::init (),GFP_KERNEL )?;
drivers/gpu/nova-core/gsp/nvkv.rs +30 -1
@@ -20,11 +20,40 @@use kernel::{ alloc::ArrayVec , bitfield, - num::Bounded ,// + num::Bounded , + prelude::*,// };use crate ::bounded_enum; +use crate ::sbuffer::SBufferIter ; + +/// Joins the two halves of a wrapped payload into the `u64` words an NVKV stream is made of. +/// +/// # Errors +/// +/// - `EINVAL` if the combined length is not a whole number of words. +/// - `ENOMEM` if the buffer cannot be allocated. +pub (crate )fn nvkv_words (payload_0: &[u8 ], payload_1: &[u8 ]) ->Result <KVVec <u64 >> { +const WORD_SIZE :usize =size_of ::<u64 >(); + +// Each byte slice is at most `isize::MAX` bytes, so their sum fits in `usize`. +let len = payload_0.len () + payload_1.len (); +if len %WORD_SIZE !=0 { +return Err (EINVAL ); + } + +let mut words =KVVec ::with_capacity (len /WORD_SIZE ,GFP_KERNEL )?; +let mut bytes =SBufferIter ::new_reader ([payload_0, payload_1]); +for _in 0 ..len /WORD_SIZE { +let mut word = [0u8 ;WORD_SIZE ]; + bytes.read_exact (&mut word)?; + words.push (u64::from_le_bytes (word),GFP_KERNEL )?; + } + +Ok (words) +} +mod encode;pub (crate )use encode::*;
Signature · 1 line
2.53.0
PATCH v4 18/32 gpu: nova-core: vgpu: query VF assignments and properties
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Creating an instance requires its VF assignment and the corresponding vGPU properties before reserving resources.
Call Cmdq's synchronous GMC interface with each query's command ID, request payload and receive capacity. Check the matching reply's firmware status before decoding it, and report the command and raw NV_STATUS when firmware rejects the request.
Decode the VF assignment and NVKV property replies at their query entry points. Validate the returned type ID and nonzero instance limit, and initialize the decoded properties on the heap. Keep the raw queue interface available to firmware-control clients.
--- drivers/gpu/nova-core/gsp.rs | 1 + drivers/gpu/nova-core/gsp/cmdq/gmc.rs | 3 - drivers/gpu/nova-core/gsp/fw.rs | 2 +- drivers/gpu/nova-core/vgpu.rs | 2 + drivers/gpu/nova-core/vgpu/commands.rs | 92 ++++++++++++++++++++++ drivers/gpu/nova-core/vgpu/fw.rs | 17 ++++ drivers/gpu/nova-core/vgpu/fw/commands.rs | 95 +++++++++++++++++++++++ 7 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 drivers/gpu/nova-core/vgpu/commands.rs create mode 100644 drivers/gpu/nova-core/vgpu/fw.rs create mode 100644 drivers/gpu/nova-core/vgpu/fw/commands.rs
drivers/gpu/nova-core/gsp.rs +1 -0
@@ -32,6 +32,7 @@mod regs;pub (crate )use fw::{ + r000_00as bindings,GspFmcBootParams ,GspFwWprMeta ,LibosMemoryRegionInitArgument ,
drivers/gpu/nova-core/gsp/cmdq/gmc.rs +0 -3
@@ -302,10 +302,8 @@ pub(super) fn log(&self, dev: &device::Device) {/// Response from a GMC API command. pub (crate )struct GmcResponse {/// Response status (`NV_STATUS` code). Zero means success. -#[expect(dead_code)] pub (crate ) status:u32 ,/// Response payload copied out of the message queue. -#[expect(dead_code)] pub (crate ) payload:KVec <u8 >, }
@@ -356,7 +354,6 @@ impl Cmdq<'_> {/// # Errors /// /// Returns the same errors as [`Self::send_gmc_and_receive_timeout`]. -#[expect(dead_code)] pub (crate )fn send_gmc_and_receive ( &self, command_id: u32,
drivers/gpu/nova-core/gsp/fw.rs +1 -1
@@ -2,7 +2,7 @@// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. pub (crate )mod commands; -mod r000_00; +pub (crate )mod r000_00;// Alias to avoid repeating the version number with every use. use r000_00as bindings;
drivers/gpu/nova-core/vgpu.rs +2 -0
@@ -20,6 +20,8 @@ gsp::commands::FifoEngineList, // }; +mod commands; +mod fw;mod hal;mod vram;
drivers/gpu/nova-core/vgpu/commands.rs new file +92 -0
92 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/fw.rs new file +17 -0
@@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +//! vGPU firmware interface. +//! +//! Exposes command constants and communication buffer layout types from +//! raw firmware bindings. + +pub (super )mod commands; + +use crate ::gsp::bindings; + +pub (super )const GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE :u32 = + bindings::GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE ; + +pub (super )const GMCAPI_CMD_QUERY_VGPU_PROPERTIES :u32 = + bindings::GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_VGPU_PROPERTIES ;
drivers/gpu/nova-core/vgpu/fw/commands.rs new file +95 -0
95 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 19/32 gpu: nova-core: vgpu: add instance create/destroy
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
A vGPU instance owns the host resources reserved for a VF. Its assigned type determines the channel range and VRAM and management-heap reservation.
Add a manager-owned instance registry, reject duplicate GFID or DBDF entries and enforce the profile's instance limit. Reserve registry capacity before acquiring resources, then move the channel and VRAM reservations into the instance. Removing an instance or dropping the registry releases these host reservations through their owners.
--- drivers/gpu/nova-core/gpu.rs | 12 +- drivers/gpu/nova-core/vgpu.rs | 24 ++- drivers/gpu/nova-core/vgpu/instance.rs | 218 +++++++++++++++++++++++++ drivers/gpu/nova-core/vgpu/vram.rs | 1 - 4 files changed, 240 insertions(+), 15 deletions(-) create mode 100644 drivers/gpu/nova-core/vgpu/instance.rs
drivers/gpu/nova-core/gpu.rs +7 -5
@@ -59,14 +59,16 @@ }, }; -#[cfg_attr(not( CONFIG_KUNIT = "y" ), expect(dead_code))] mod channel;mod hal;mod regs;use self::channel::TOTAL_CHANNELS ; -pub (crate )use self::channel::ChannelIdPool ; +pub (crate )use self::channel::{ +ChannelIdPool , +ChannelIdReservation ,// +};macro_rules! define_chipset { ({ $($variant:ident),* $(,)* }) =>
@@ -400,7 +402,7 @@ unsafe fn new(#[pin_data] pub (crate )struct Gpu <'gpu> {pub (crate ) spec:Spec , - vgpu:Option <VgpuManager <'gpu>>, + vgpu:Option <Pin <KBox <VgpuManager <'gpu>>>>,/// GSP event interrupt registration, and the enable of its subtree. /// /// Must be kept declared *before* `gsp_resources`, so that the handler is unregistered, and
@@ -542,7 +544,7 @@ pub(crate) fn new<'a>(let info = &gsp_resources.boot_result.static_info; match gsp_resources.vgpu_state { VgpuState::Disabled => None, - VgpuState::Enabled { .. } => Some(VgpuManager::new( + VgpuState::Enabled { .. } => Some(KBox::pin_init(VgpuManager::new( // SAFETY: `chid_pool` is initialized above at its final pinned address. // The private manager and its pool borrow cannot escape this `Gpu`. // Completed field drop order drops the manager before the pool; on failure,
@@ -551,7 +553,7 @@ pub(crate) fn new<'a>( &info.fifo_engine_list(), info.vmmu_segment_size, TOTAL_CHANNELS, - )), + ), GFP_KERNEL)?), } },
drivers/gpu/nova-core/vgpu.rs +15 -9
@@ -4,8 +4,10 @@use kernel::{ device, + new_mutex, pci, - prelude::*,// + prelude::*, + sync::Mutex ,// };use crate ::{
@@ -23,6 +25,7 @@mod commands;mod fw;mod hal; +mod instance;mod vram;/// vGPU state detected during GPU construction.
@@ -88,16 +91,17 @@ fn query_state( } } +use self::instance::VgpuInstances ; +/// Runtime resources for an enabled vGPU boot. +#[pin_data] pub (crate )struct VgpuManager <'gpu> { -#[expect(dead_code)] +#[pin] + instances:Mutex <VgpuInstances <'gpu>>, chid_pool: &'gpuChannelIdPool ,/// VMMU segment size in bytes, or zero if GSP-RM omitted it. -#[expect(dead_code)] vmmu_segment_size:u64 , -#[expect(dead_code)] total_channels:u32 , -#[expect(dead_code)] fifo_engine_list:FifoEngineList , }
@@ -108,12 +112,14 @@ pub(crate) fn new( fifo_engine_list: &FifoEngineList, vmmu_segment_size: u64, total_channels: u32, - ) -> Self { - Self { + ) -> impl PinInit<Self> + use<'gpu> { +let fifo_engine_list = *fifo_engine_list; +pin_init! (Self { + instances <-new_mutex! (VgpuInstances ::new (),"nova-core::vgpu-instances" ), chid_pool, vmmu_segment_size, total_channels, - fifo_engine_list: *fifo_engine_list, - } + fifo_engine_list, + }) } }
drivers/gpu/nova-core/vgpu/instance.rs new file +218 -0
218 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/vram.rs +0 -1
@@ -116,7 +116,6 @@ pub(super) struct VgpuVramSlotAllocator { slot_bitmap: Arc<SlotBitmap>, } -#[expect(dead_code)] impl VgpuVramSlotAllocator {pub (super )fn new (mm: &GpuMm <'_>, layout:VgpuVramLayout ) ->Result <Self > {let layout = layout.validated ()?;
Signature · 1 line
2.53.0
PATCH v4 20/32 gpu: nova-core: vgpu: encode vGPU boot requests
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The boot request describes a vGPU's identity, channel map, VRAM, plugin heap and log buffers to GSP-RM.
Add the NVKV schema and encode it from a named boot description. Pass VRAM region views so each physical address stays paired with its length until encoding, and retain the control offset relative to the plugin heap. Represent channel-map engine and index fields in their 16-bit wire domains.
Describe one guest VRAM segment and the current whole-GPU, non-SMIG resource layout. Keep separate MIG-RM heap fields zero for that supported firmware path.
--- drivers/gpu/nova-core/gsp/nvkv.rs | 11 ++ drivers/gpu/nova-core/gsp/nvkv/encode.rs | 12 ++ drivers/gpu/nova-core/vgpu/fw/commands.rs | 168 +++++++++++++++++++++- 3 files changed, 188 insertions(+), 3 deletions(-)
drivers/gpu/nova-core/gsp/nvkv.rs +11 -0
@@ -131,6 +131,17 @@ pub(crate) struct Array<T, const N: usize, const KEY_ID: KeyId> { vec: ArrayVec<T, N>, } +impl <T :Default +Copy ,const N :usize ,const KEY_ID :KeyId >Array <T ,N ,KEY_ID > { +/// Creates an array filled from `values`. +/// +/// Fails with `EINVAL` if `values` is longer than `N`. +pub (crate )fn new (values: &[T ]) ->Result <Self > { +let mut vec =ArrayVec ::default (); + vec.extend_from_slice (values)?; +Ok (Self { vec }) + } +} +bitfield! {/// The op word that starts each NVKV operation. struct Op (u64 ) {
drivers/gpu/nova-core/gsp/nvkv/encode.rs +12 -0
@@ -7,6 +7,7 @@use zerocopy::Immutable ;use super ::{ +Array ,Index ,IndexedKey ,Key ,
@@ -130,6 +131,17 @@ fn encode(&self, encoder: &mut Encoder) -> Result { } } +impl <T ,const N :usize ,const KEY_ID :KeyId >Encodable for Array <T ,N ,KEY_ID > +where +T :Default +Copy , +for <'a>IndexedKey <&'a [T ],KEY_ID >:Encodable , +{ +#[inline(always)] +fn encode (&self, encoder: &mut Encoder ) ->Result { +IndexedKey ::<&[T ],KEY_ID >::new (Index ::new ::<0 >(), self.vec.as_slice ()).encode (encoder) + } +} +impl <T :Encodable >Encodable for Option <T > {fn encode (&self, encoder: &mut Encoder ) ->Result {if let Some (value) = self {
drivers/gpu/nova-core/vgpu/fw/commands.rs +165 -3
188 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 21/32 gpu: nova-core: vgpu: add GSP plugin communication buffers
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The GSP plugin keeps its boot-ready marker, control data and logs in a communication region within the instance's management heap.
Map that region through BAR1 and expose its firmware-defined subregions. Check fixed layout totals and the control structure size at build time while retaining runtime checks against the allocated heap and mapping bounds.
Own the BarMapping so dropping the communication region unmaps it, and retain its BarUser and MM borrows for that lifetime. Expose explicit unmap to report teardown failures without consuming the owner. Provide the ready-marker access needed to boot a reused instance slot.
--- drivers/gpu/nova-core/mm.rs | 1 - drivers/gpu/nova-core/vgpu.rs | 1 + drivers/gpu/nova-core/vgpu/fw.rs | 15 ++ drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs | 205 ++++++++++++++++++ 4 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs
drivers/gpu/nova-core/mm.rs +0 -1
@@ -67,7 +67,6 @@ 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.
drivers/gpu/nova-core/vgpu.rs +1 -0
@@ -24,6 +24,7 @@mod commands;mod fw; +mod gsp_plugin_comm;mod hal;mod instance;mod vram;
drivers/gpu/nova-core/vgpu/fw.rs +15 -0
@@ -10,6 +10,21 @@use crate ::gsp::bindings; +pub (super )use bindings::{ +GSP_PLUGIN_BOOTLOADED , +VGPU_CPU_GSP_COMMUNICATION_BUFF_TOTAL_SIZE , +VGPU_CPU_GSP_CTRL_BUFF_REGION as RawControlRegion , +VGPU_CPU_GSP_CTRL_BUFF_REGION_SIZE , +VGPU_CPU_GSP_ERROR_BUFF_REGION_SIZE , +VGPU_CPU_GSP_GUEST_RPC_TRACE_BUFF_REGION_SIZE , +VGPU_CPU_GSP_INIT_TASK_LOG_BUFF_REGION_SIZE , +VGPU_CPU_GSP_KERNEL_TASK_LOG_BUFF_REGION_SIZE , +VGPU_CPU_GSP_MESSAGE_BUFF_REGION_SIZE , +VGPU_CPU_GSP_MIGRATION_BUFF_REGION_SIZE , +VGPU_CPU_GSP_RESPONSE_BUFF_REGION_SIZE , +VGPU_CPU_GSP_VGPU_TASK_LOG_BUFF_REGION_SIZE ,// +}; +pub (super )const GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE :u32 = bindings::GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE ;
drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs new file +205 -0
205 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 22/32 gpu: nova-core: vgpu: add instance boot
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Start an allocated instance's GSP plugin with its channel map, VRAM, plugin heap and log regions. Clear the old ready marker before BOOTLOAD and wait for firmware to publish a new one.
Retain the command queue and memory dependencies needed by the instance, and map its communication buffers before boot.
--- drivers/gpu/nova-core/gpu.rs | 71 +++++--- drivers/gpu/nova-core/gsp/fw/commands.rs | 1 - drivers/gpu/nova-core/vgpu.rs | 30 +++- drivers/gpu/nova-core/vgpu/commands.rs | 21 ++- drivers/gpu/nova-core/vgpu/fw.rs | 3 + drivers/gpu/nova-core/vgpu/fw/commands.rs | 2 - drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs | 16 +- drivers/gpu/nova-core/vgpu/instance.rs | 152 ++++++++++++++++-- drivers/gpu/nova-core/vgpu/vram.rs | 1 - 9 files changed, 244 insertions(+), 53 deletions(-)
drivers/gpu/nova-core/gpu.rs +45 -26
@@ -8,6 +8,7 @@ fmt, gpu::buddy::GpuBuddyParams, io::Io, + new_mutex, num::Bounded, pci, prelude::*,
@@ -16,6 +17,7 @@ SizeConstants, SZ_4K, // }, + sync::Mutex, uapi, // };
@@ -402,6 +404,7 @@ unsafe fn new(#[pin_data] pub (crate )struct Gpu <'gpu> {pub (crate ) spec:Spec , +/// Drops before the MM, BAR1 mappings and GSP needed for instance teardown. vgpu:Option <Pin <KBox <VgpuManager <'gpu>>>>,/// GSP event interrupt registration, and the enable of its subtree. ///
@@ -414,7 +417,8 @@ pub(crate) struct Gpu<'gpu> {/// /// Must be kept declared *before* `gsp_resources`, so that its components are dropped while /// the GSP is still operational. - mm: GpuMm<'gpu>, +#[pin] + mm: Mutex<GpuMm<'gpu>>,/// BAR1 user interface for CPU access to GPU virtual memory. #[pin] bar_user: BarUser<'gpu>,
@@ -540,23 +544,6 @@ pub(crate) fn new<'a>( })?, }), - vgpu: { - let info = &gsp_resources.boot_result.static_info; -match gsp_resources.vgpu_state { -VgpuState ::Disabled =>None , -VgpuState ::Enabled { .. } =>Some (KBox ::pin_init (VgpuManager ::new ( -// SAFETY: `chid_pool` is initialized above at its final pinned address. -// The private manager and its pool borrow cannot escape this `Gpu`. -// Completed field drop order drops the manager before the pool; on failure, -// pin-init drops it before the earlier-initialized pool. -unsafe { &*core::ptr::from_ref (chid_pool.as_ref ().get_ref ()) }, - &info.fifo_engine_list (), - info.vmmu_segment_size, -TOTAL_CHANNELS , - ),GFP_KERNEL )?), - } - }, - _: { irq::gsp::quiesce (tree_ref, &gsp_resources.gsp_falcon); },
@@ -602,7 +589,7 @@ pub(crate) fn new<'a>( },// Create GPU memory manager owning memory management resources. - mm: { + mm <- {let info = gsp_resources.static_info ();let usable_vram = info.usable_fb_regions.first ().ok_or (ENODEV )?; let buddy_params = GpuBuddyParams {
@@ -611,12 +598,15 @@ pub(crate) fn new<'a>( chunk_size: Alignment::new::<SZ_4K>(), }; -GpuMm ::new ( - bar, - gsp_resources.spec.chipset, - buddy_params, -VramAddress ::from_raw (info.total_fb_end), - )? +new_mutex! ( +GpuMm ::new ( + bar, + gsp_resources.spec.chipset, + buddy_params, +VramAddress ::from_raw (info.total_fb_end), + )?, +"nova-core::gpu-mm" , + ) },// Create BAR1 user interface for CPU access to GPU virtual memory.
@@ -632,6 +622,34 @@ pub(crate) fn new<'a>( bar1, ) }, + + vgpu: { + match gsp_resources.vgpu_state { + VgpuState::Disabled => None, + VgpuState::Enabled { .. } => { +// SAFETY: These sibling fields are initialized at their final pinned +// addresses. The private manager cannot escape this `Gpu`, and is dropped +// before all its dependencies, both here on failure and on normal removal. +let (cmdq, bar_user, mm, chid_pool) =unsafe { + ( + &*core::ptr::from_ref (&gsp_resources.gsp.cmdq), + &*core::ptr::from_ref (bar_user.as_ref ().get_ref ()), + &*core::ptr::from_ref (mm.as_ref ().get_ref ()), + &*core::ptr::from_ref (chid_pool.as_ref ().get_ref ()), + ) + }; +Some (KBox ::pin_init (VgpuManager ::new ( + dev, + cmdq, + bar_user, + mm, + chid_pool, + gsp_resources.static_info (), +TOTAL_CHANNELS , + ),GFP_KERNEL )?) + } + } + }, }) }
@@ -663,10 +681,11 @@ pub(crate) fn run_selftests(self: Pin<&mut Self>, pdev: &pci::Device<device::Boulet this = self.project ();let dev = pdev.as_ref ();let info = this.gsp_resources.static_info (); +let mut mm = this.mm.lock (); if let Err(err) = crate::mm::selftest::run( dev, - this.mm, + &mut mm, &info.usable_fb_regions, this.bar_user.as_ref().get_ref(), info.bar1_pde_base,
drivers/gpu/nova-core/gsp/fw/commands.rs +0 -1
@@ -326,7 +326,6 @@ pub(crate) struct FifoEngineList { }impl FifoEngineList { -#[expect(dead_code)] pub (crate )fn gmc_ids (&self) -> &[u32 ] {// PANIC: The type invariant bounds `count` by the array capacity. &self.gmc_ids[..self.count]
drivers/gpu/nova-core/vgpu.rs +26 -4
@@ -19,7 +19,17 @@ ChannelIdPool, Chipset, // }, - gsp::commands::FifoEngineList, // + gsp::{ + cmdq::Cmdq, + commands::{ +FifoEngineList , +GspStaticInfo ,// + }, // + }, + mm::{ + bar_user::BarUser, + GpuMm, // + }, // };mod commands;
@@ -99,6 +109,10 @@ fn query_state(pub (crate )struct VgpuManager <'gpu> {#[pin] instances:Mutex <VgpuInstances <'gpu>>, + dev: &'gpu device::Device <device::Bound >, + cmdq: &'gpuCmdq <'gpu>, + bar_user: &'gpuBarUser <'gpu>, + mm: &'gpuMutex <GpuMm <'gpu>>, chid_pool: &'gpuChannelIdPool ,/// VMMU segment size in bytes, or zero if GSP-RM omitted it. vmmu_segment_size:u64 ,
@@ -109,14 +123,22 @@ pub(crate) struct VgpuManager<'gpu> {impl <'gpu>VgpuManager <'gpu> {/// Retains runtime parameters from a completed vGPU-enabled GSP boot. pub (crate )fn new ( + dev: &'gpu device::Device <device::Bound >, + cmdq: &'gpuCmdq <'gpu>, + bar_user: &'gpuBarUser <'gpu>, + mm: &'gpuMutex <GpuMm <'gpu>>, chid_pool: &'gpuChannelIdPool , - fifo_engine_list: &FifoEngineList , - vmmu_segment_size:u64 , + info: &GspStaticInfo , total_channels:u32 , ) ->impl PinInit <Self > +use <'gpu> { -let fifo_engine_list = *fifo_engine_list; +let fifo_engine_list = info.fifo_engine_list (); +let vmmu_segment_size = info.vmmu_segment_size;pin_init! (Self { instances <-new_mutex! (VgpuInstances ::new (),"nova-core::vgpu-instances" ), + dev, + cmdq, + bar_user, + mm, chid_pool, vmmu_segment_size, total_channels,
drivers/gpu/nova-core/vgpu/commands.rs +20 -1
@@ -8,7 +8,9 @@use kernel::{ device, - prelude::*,// + prelude::*, + time::Delta , + transmute::AsBytes ,// };use crate ::gsp::{
@@ -26,6 +28,7 @@ commands::{VgpuPropertiesSchema ,// }, + GMCAPI_CMD_BOOTLOAD_GSP_VGPU_PLUGIN_TASK, GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE, GMCAPI_CMD_QUERY_VGPU_PROPERTIES, // }, //
@@ -90,3 +93,19 @@ pub(super) fn query_vgpu_properties(Ok (properties) } } + +/// Send BOOTLOAD and check its firmware status. +pub (super )fn send_bootload ( + dev: &device::Device <device::Bound >, + cmdq: &Cmdq <'_>, + payload: &[u64 ], +) ->Result { +let command_id =GMCAPI_CMD_BOOTLOAD_GSP_VGPU_PLUGIN_TASK ; +let response = cmdq.send_gmc_and_receive_timeout ( + command_id, +AsBytes ::as_bytes (payload), +0 , +Delta ::from_secs (10 ), + )?; +check_status (dev, command_id, response.status) +}
drivers/gpu/nova-core/vgpu/fw.rs +3 -0
@@ -30,3 +30,6 @@pub (super )const GMCAPI_CMD_QUERY_VGPU_PROPERTIES :u32 = bindings::GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_VGPU_PROPERTIES ; + +pub (super )const GMCAPI_CMD_BOOTLOAD_GSP_VGPU_PLUGIN_TASK :u32 = + bindings::GMCAPI_COMMANDS_GMCAPI_CMD_BOOTLOAD_GSP_VGPU_PLUGIN_TASK ;
drivers/gpu/nova-core/vgpu/fw/commands.rs +0 -2
@@ -60,7 +60,6 @@ pub(crate) struct ChannelMapEntry(u64) {impl ChannelMapEntry {const KEY :KeyId =0x1001 ; -#[expect(dead_code)] pub (crate )fn new (engine_type:u16 , index:u16 , chid_offset:u32 ) ->Self {Self ::zeroed () .with_engine_type (engine_type)
@@ -158,7 +157,6 @@ pub(crate) struct BootloadInfo<'a> { }/// Encodes a `VGPU_BOOTLOAD` request using the typed NVKV schema. -#[expect(dead_code)] pub (crate )fn encode_vgpu_bootload (info:BootloadInfo <'_>) ->Result <KVVec <u64 >> {let request =VgpuBootloadRequest { dbdf: info.dbdf.into (),
drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs +14 -2
@@ -39,7 +39,6 @@size_of ::<RawControlRegion >() ==u32_as_usize (fw::VGPU_CPU_GSP_CTRL_BUFF_REGION_SIZE ) );/// Physical VRAM regions containing the vGPU plugin logs. -#[expect(dead_code)] pub (super )struct PluginLogRegions {pub (super ) init:VramRegion ,pub (super ) vgpu:VramRegion ,
@@ -86,7 +85,6 @@ pub(super) struct CommBufferRegion<'map, 'gpu> { kernel_log: VramRegion, } -#[expect(dead_code)] impl <'map, 'gpu>CommBufferRegion <'map, 'gpu> {/// Map the communication portion of a plugin management heap. pub(super) fn new(
@@ -188,6 +186,19 @@ pub(super) fn plugin_logs(&self) -> PluginLogRegions { } } +/// Clear a previous boot marker before starting the plugin. +pub (super )fn clear_plugin_ready (&self) ->Result { +let offset = self.io_offset ( + &self.control, + core::mem::offset_of!(RawControlRegion , __bindgen_anon_1.message_seq_num), +size_of ::<u32 >(), + )?; + self.map.try_write32 (0 , offset)?; +// Complete the posted clear before firmware can publish its new marker. + self.map.try_read32 (offset)?; +Ok (()) + } +/// Return whether firmware has published the plugin boot marker. pub (super )fn is_plugin_ready (&self) ->Result <bool > { let value = self.read_u32(
@@ -199,6 +210,7 @@ pub(super) fn is_plugin_ready(&self) -> Result<bool> { }/// Invalidate the PTEs and release the communication mapping. +#[expect(dead_code)] pub (super )fn unmap (&mut self) ->Result { self.map.unmap () }
drivers/gpu/nova-core/vgpu/instance.rs +136 -16
216 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/vram.rs +0 -1
@@ -102,7 +102,6 @@ fn drop(&mut self) {/// All device accesses and mappings of its regions must end before dropping the slot. /// Clearing the entry locks a sleeping mutex, so dropping the slot may sleep. #[must_use] -#[expect(dead_code)] pub (super )struct VgpuVramSlot {pub (super ) fbmem:VramRegion ,pub (super ) mgmt_heap:VramRegion ,
Signature · 1 line
2.53.0
PATCH v4 23/32 gpu: nova-core: vgpu: add instance shutdown
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Stop the GSP plugin, wait for SHUTDOWN completion, and release its firmware resources with CLEANUP before unmapping the communication buffers.
Add PendingInstance rollback and manager teardown. Register host resources before firmware work, and retain them until device removal if firmware state is uncertain or teardown fails. Keep the manager's dependencies alive until cleanup finishes.
--- drivers/gpu/nova-core/gsp/cmdq/gmc.rs | 1 - drivers/gpu/nova-core/vgpu.rs | 9 +- drivers/gpu/nova-core/vgpu/commands.rs | 55 +++++++- drivers/gpu/nova-core/vgpu/fw.rs | 9 ++ drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs | 1 - drivers/gpu/nova-core/vgpu/instance.rs | 118 +++++++++++++++++- 6 files changed, 182 insertions(+), 11 deletions(-)
drivers/gpu/nova-core/gsp/cmdq/gmc.rs +0 -1
@@ -469,7 +469,6 @@ pub(crate) fn send_gmc_and_receive_timeout(/// /// Errors from initializing the request headers and from either callback are propagated /// as-is. The current valid element is consumed before a callback error is returned. -#[expect(dead_code)] pub (crate )fn send_gmc_and_wait_event ( &self, command_id: u32,
drivers/gpu/nova-core/vgpu.rs +8 -1
@@ -105,7 +105,7 @@ fn query_state(use self::instance::VgpuInstances ;/// Runtime resources for an enabled vGPU boot. -#[pin_data] +#[pin_data( PinnedDrop )] pub (crate )struct VgpuManager <'gpu> {#[pin] instances:Mutex <VgpuInstances <'gpu>>,
@@ -146,3 +146,10 @@ pub(crate) fn new( }) } } + +#[pinned_drop] +impl PinnedDrop for VgpuManager <'_> { +fn drop (self:Pin <&mut Self >) { + self.instances.lock ().release_all (&self); + } +}
drivers/gpu/nova-core/vgpu/commands.rs +53 -2
65 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/fw.rs +9 -0
@@ -33,3 +33,12 @@pub (super )const GMCAPI_CMD_BOOTLOAD_GSP_VGPU_PLUGIN_TASK :u32 = bindings::GMCAPI_COMMANDS_GMCAPI_CMD_BOOTLOAD_GSP_VGPU_PLUGIN_TASK ; + +pub (super )const GMCAPI_CMD_SHUTDOWN_GSP_VGPU_PLUGIN_TASK :u32 = + bindings::GMCAPI_COMMANDS_GMCAPI_CMD_SHUTDOWN_GSP_VGPU_PLUGIN_TASK ; + +pub (super )const GMCAPI_CMD_SHUTDOWN_GSP_VGPU_PLUGIN_TASK_COMPLETE :u32 = + bindings::GMCAPI_COMMANDS_GMCAPI_CMD_SHUTDOWN_GSP_VGPU_PLUGIN_TASK_COMPLETE ; + +pub (super )const GMCAPI_CMD_CLEANUP_GSP_VGPU_PLUGIN_RESOURCES :u32 = + bindings::GMCAPI_COMMANDS_GMCAPI_CMD_CLEANUP_GSP_VGPU_PLUGIN_RESOURCES ;
drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs +0 -1
@@ -210,7 +210,6 @@ pub(super) fn is_plugin_ready(&self) -> Result<bool> { }/// Invalidate the PTEs and release the communication mapping. -#[expect(dead_code)] pub (super )fn unmap (&mut self) ->Result { self.map.unmap () }
drivers/gpu/nova-core/vgpu/instance.rs +112 -6
177 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 24/32 gpu: nova-core: vgpu: initialize GSP plugin RPC buffers
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The first plugin RPC needs region offsets, a protocol version and initialized control and response state in the shared communication buffer.
Add BAR1 accessors to initialize those fields and verify the fixed response structure size at build time. The control sequence field first carries the boot-ready marker, so RPC initialization must follow the plugin's ready indication.
--- drivers/gpu/nova-core/mm/bar_user.rs | 10 ++ drivers/gpu/nova-core/vgpu/fw.rs | 2 + drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs | 141 +++++++++++++++++- 3 files changed, 147 insertions(+), 6 deletions(-)
drivers/gpu/nova-core/mm/bar_user.rs +10 -0
@@ -154,6 +154,11 @@ pub(crate) fn try_read32(&self, offset: usize) -> Result<u32> { self.bar_user.bar1.try_read32 (off) } +fn try_write8 (&self, value:u8 , offset:usize ) ->Result { +let off = self.bar_offset (offset)?; + self.bar_user.bar1.try_write8 (value, off) + } +/// Write a 32-bit value at the given offset. pub (crate )fn try_write32 (&self, value:u32 , offset:usize ) ->Result {let off = self.bar_offset (offset)?;
@@ -287,6 +292,11 @@ pub(crate) fn try_read32(&self, offset: usize) -> Result<u32> { .try_read32(self.access_offset(offset, size_of::<u32>())?) } +pub (crate )fn try_write8 (&self, value:u8 , offset:usize ) ->Result { + self.access ()? + .try_write8 (value, self.access_offset (offset,size_of ::<u8 >())?) + } +pub (crate )fn try_write32 (&self, value:u32 , offset:usize ) ->Result { self.access ()? .try_write32 (value, self.access_offset (offset,size_of ::<u32 >())?)
drivers/gpu/nova-core/vgpu/fw.rs +2 -0
@@ -15,12 +15,14 @@ VGPU_CPU_GSP_COMMUNICATION_BUFF_TOTAL_SIZE, VGPU_CPU_GSP_CTRL_BUFF_REGION as RawControlRegion, VGPU_CPU_GSP_CTRL_BUFF_REGION_SIZE, + VGPU_CPU_GSP_CTRL_BUFF_VERSION, VGPU_CPU_GSP_ERROR_BUFF_REGION_SIZE, VGPU_CPU_GSP_GUEST_RPC_TRACE_BUFF_REGION_SIZE, VGPU_CPU_GSP_INIT_TASK_LOG_BUFF_REGION_SIZE, VGPU_CPU_GSP_KERNEL_TASK_LOG_BUFF_REGION_SIZE, VGPU_CPU_GSP_MESSAGE_BUFF_REGION_SIZE, VGPU_CPU_GSP_MIGRATION_BUFF_REGION_SIZE, + VGPU_CPU_GSP_RESPONSE_BUFF_REGION as RawResponseRegion, VGPU_CPU_GSP_RESPONSE_BUFF_REGION_SIZE, VGPU_CPU_GSP_VGPU_TASK_LOG_BUFF_REGION_SIZE, // };
drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs +135 -6
207 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 25/32 gpu: nova-core: vgpu: add GSP plugin RPC transactions
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
A GSP plugin consumes a shared-buffer request after its VF doorbell is rung and publishes completion in the response region.
Add PluginRpc to own the communication mapping together with the instance's stable BAR0 and validated GFID. Accept typed RPC message IDs, copy the payload before publishing its sequence, advance the local sequence, ring and read back the doorbell, then wait for a matching completion and check its firmware status.
Keep one sequence identity for each published request, including a request whose doorbell or wait later fails, and bound completion polling with the RPC timeout. Delegate explicit unmap to the owned communication region so teardown errors retain its owner; ordinary drop releases the mapping through BarMapping.
--- drivers/gpu/nova-core/regs.rs | 31 +++- drivers/gpu/nova-core/vgpu.rs | 1 + drivers/gpu/nova-core/vgpu/fw.rs | 13 ++ drivers/gpu/nova-core/vgpu/fw/commands.rs | 10 ++ drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs | 51 +++++- drivers/gpu/nova-core/vgpu/gsp_plugin_rpc.rs | 155 ++++++++++++++++++ drivers/gpu/nova-core/vgpu/instance.rs | 15 +- 7 files changed, 267 insertions(+), 9 deletions(-) create mode 100644 drivers/gpu/nova-core/vgpu/gsp_plugin_rpc.rs
drivers/gpu/nova-core/regs.rs +30 -1
@@ -7,13 +7,17 @@ Io, Mmio, // }, + prelude::*, sizes::SizeConstants, time, // };use pin_init::Zeroable ;use crate ::{ - driver::NovaRegisters , + driver::{ +Bar0 , +NovaRegisters ,// + }, falcon::{DmaTrfCmdSize ,FalconCoreRev ,
@@ -39,6 +43,31 @@ pub(crate) NV_PBUS_SW_SCRATCH(u32)[64] @ 0x00001400 {} } +// VIRTUAL_FUNCTION + +register! { + base:NovaRegisters ; + +// PF BAR0 exposes the virtual-function register window at 0x00b8_0000. +pub (crate )NV_VIRTUAL_FUNCTION_PRIV_DOORBELL (u32 ) @0x00b8_2200 { +31 :0 handle; + } +} + +impl NV_VIRTUAL_FUNCTION_PRIV_DOORBELL { +const DOORBELL_STRIDE :u32 =32 ; +const DOORBELL_VECTOR :u32 =17 ; + +/// Notify the GSP plugin for the given guest function and read back the doorbell. +pub (crate )fn ring_gsp_plugin (bar0:Bar0 <'_>, gfid:u16 ) ->Result { +// A `u16` GFID produces a handle of at most 0x1f_fff1, which fits in `u32`. +let value = u32::from (gfid) *Self ::DOORBELL_STRIDE +Self ::DOORBELL_VECTOR ; + bar0.try_write_reg (Self ::zeroed ().with_handle (value))?; + bar0.try_read (NV_VIRTUAL_FUNCTION_PRIV_DOORBELL )?; +Ok (()) + } +} +// PGC6 register space. // // `GC6` is a GPU low-power state where VRAM is in self-refresh and the GPU is powered down (except
drivers/gpu/nova-core/vgpu.rs +1 -0
@@ -35,6 +35,7 @@mod commands;mod fw;mod gsp_plugin_comm; +mod gsp_plugin_rpc;mod hal;mod instance;mod vram;
drivers/gpu/nova-core/vgpu/fw.rs +13 -0
@@ -10,6 +10,8 @@use crate ::gsp::bindings; +pub (super )use commands::RpcMessage ; +pub (super )use bindings::{GSP_PLUGIN_BOOTLOADED ,VGPU_CPU_GSP_COMMUNICATION_BUFF_TOTAL_SIZE ,
@@ -44,3 +46,14 @@pub (super )const GMCAPI_CMD_CLEANUP_GSP_VGPU_PLUGIN_RESOURCES :u32 = bindings::GMCAPI_COMMANDS_GMCAPI_CMD_CLEANUP_GSP_VGPU_PLUGIN_RESOURCES ; + +/// State observed in the response buffer for an expected RPC sequence. +pub (super )enum RpcResponse { +Pending { +/// Last sequence completed by firmware. + sequence:u32 , + }, +Complete { + status:u32 , + }, +}
drivers/gpu/nova-core/vgpu/fw/commands.rs +10 -0
@@ -27,6 +27,16 @@ mm::vram::VramRegion, // }; +use super ::bindings; + +/// Message types supported by the nova-core plugin RPC channel. +#[expect(dead_code)] +#[derive( Clone , Copy )] +#[repr( u32 )] +pub (crate )enum RpcMessage { +VersionNegotiation = bindings::MESSAGE_NV_VGPU_CPU_RPC_MSG_VERSION_NEGOTIATION , +} +bitfield! {pub (crate )struct Dbdf (u32 ) {2 :0 function;
drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs +49 -2
69 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/gsp_plugin_rpc.rs new file +155 -0
155 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/instance.rs +9 -6
@@ -20,6 +20,7 @@ };use crate ::{ + driver::Bar0 , gpu::ChannelIdReservation , gsp::{ cmdq::Cmdq ,
@@ -41,6 +42,7 @@ ChannelMapEntry, // }, gsp_plugin_comm::CommBufferRegion, + gsp_plugin_rpc::PluginRpc, vram::{VgpuVramLayout ,VgpuVramSlot ,
@@ -141,7 +143,7 @@ struct VgpuInstance<'gpu> { vgpu_type: VgpuType, vm_pid: u32, num_plugin_channels: u32, - comm: CommBufferRegion<'gpu, 'gpu>, + plugin_rpc: PluginRpc<'gpu, 'gpu>, // Unmap the communication region before returning its slot and channel IDs. vram_slot: VgpuVramSlot, chids: ChannelIdReservation<'gpu>,
@@ -170,7 +172,7 @@ fn teardown(&mut self, vgpu: &VgpuManager<'gpu>) -> Result {send_cleanup (vgpu.dev, vgpu.cmdq, self.gfid)?; self.needs_teardown =false ; } - self.comm.unmap () + self.plugin_rpc.unmap () })();if let Err (error) = result { self.failure =Some (error);
@@ -187,7 +189,7 @@ fn bootload( ) -> Result { let fb = &self.vram_slot.fbmem;let mgmt = &self.vram_slot.mgmt_heap; -let logs = self.comm.plugin_logs (); +let logs = self.plugin_rpc.comm ().plugin_logs (); let payload = encode_vgpu_bootload(BootloadInfo { dbdf: self.dbdf,
@@ -215,11 +217,11 @@ fn bootload( payload.len() * size_of::<u64>(), ); - self.comm.clear_plugin_ready ()?; + self.plugin_rpc.comm ().clear_plugin_ready ()?; self.needs_teardown =true ;send_bootload (dev, cmdq, &payload)?; -wait_plugin_ready (dev, &self.comm)?; +wait_plugin_ready (dev, self.plugin_rpc.comm ())?;dev_dbg! (dev,"bootload: gfid={} plugin ready\n" , self.gfid.get ());Ok (())
@@ -293,6 +295,7 @@ fn alloc_vram_slot(&mut self, mm: &GpuMm<'_>, layout: VgpuVramLayout) -> Result<fn allocate_instance <'a>( &'amut self, vgpu: &'aVgpuManager <'gpu>, + bar0:Bar0 <'gpu>, info:InstanceInfo , ) ->Result <PendingInstance <'a, 'gpu>> { let InstanceInfo {
@@ -351,7 +354,7 @@ fn allocate_instance<'a>( vgpu_type, vm_pid, num_plugin_channels: PLUGIN_CHANNELS_PER_ENGINE, - comm, + plugin_rpc: PluginRpc::new(comm, bar0, gfid), vram_slot, chids, needs_teardown: false,
Signature · 1 line
2.53.0
PATCH v4 26/32 gpu: nova-core: vgpu: negotiate the GSP plugin RPC version
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The host must negotiate the RPC protocol with the booted GSP plugin before sending instance configuration.
After observing the plugin's ready marker, initialize the shared RPC buffers and submit the existing typed version-negotiation request through the instance's PluginRpc channel.
--- drivers/gpu/nova-core/vgpu/commands.rs | 12 +++++++++++- drivers/gpu/nova-core/vgpu/fw.rs | 4 ++-- drivers/gpu/nova-core/vgpu/fw/commands.rs | 1 - drivers/gpu/nova-core/vgpu/gsp_plugin_rpc.rs | 2 -- drivers/gpu/nova-core/vgpu/instance.rs | 3 +++ 5 files changed, 16 insertions(+), 6 deletions(-)
drivers/gpu/nova-core/vgpu/commands.rs +11 -1
@@ -28,13 +28,15 @@ commands::{VgpuPropertiesSchema ,// }, + RpcMessage, // GMCAPI_CMD_BOOTLOAD_GSP_VGPU_PLUGIN_TASK, GMCAPI_CMD_CLEANUP_GSP_VGPU_PLUGIN_RESOURCES, GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE, GMCAPI_CMD_QUERY_VGPU_PROPERTIES, GMCAPI_CMD_SHUTDOWN_GSP_VGPU_PLUGIN_TASK, - GMCAPI_CMD_SHUTDOWN_GSP_VGPU_PLUGIN_TASK_COMPLETE, // + GMCAPI_CMD_SHUTDOWN_GSP_VGPU_PLUGIN_TASK_COMPLETE, }, + gsp_plugin_rpc::PluginRpc, instance::Gfid, // };
@@ -160,3 +162,11 @@ pub(super) fn send_cleanup(dev_dbg! (dev,"cleanup: gfid={} done\n" , gfid.get ());Ok (()) } + +/// Negotiate the host protocol with a bootloaded GSP plugin. +pub (super )fn negotiate_plugin_version ( + dev: &device::Device <device::Bound >, + rpc: &mut PluginRpc <'_, '_>, +) ->Result { + rpc.rpc_call (dev,RpcMessage ::VersionNegotiation , &[]) +}
drivers/gpu/nova-core/vgpu/fw.rs +2 -2
@@ -10,8 +10,6 @@use crate ::gsp::bindings; -pub (super )use commands::RpcMessage ; -pub (super )use bindings::{GSP_PLUGIN_BOOTLOADED ,VGPU_CPU_GSP_COMMUNICATION_BUFF_TOTAL_SIZE ,
@@ -29,6 +27,8 @@ VGPU_CPU_GSP_VGPU_TASK_LOG_BUFF_REGION_SIZE, // }; +pub (super )use commands::RpcMessage ; +pub (super )const GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE :u32 = bindings::GMCAPI_COMMANDS_GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE ;
drivers/gpu/nova-core/vgpu/fw/commands.rs +0 -1
@@ -30,7 +30,6 @@use super ::bindings;/// Message types supported by the nova-core plugin RPC channel. -#[expect(dead_code)] #[derive( Clone , Copy )] #[repr( u32 )] pub (crate )enum RpcMessage {
drivers/gpu/nova-core/vgpu/gsp_plugin_rpc.rs +0 -2
@@ -65,7 +65,6 @@ pub(super) fn comm(&self) -> &CommBufferRegion<'map, 'gpu> { }/// Initialize the control and response buffers for the first RPC. -#[expect(dead_code)] pub (super )fn init_rpc (&mut self) ->Result { self.comm.initialize ()?; self.message_sequence =0 ;
@@ -82,7 +81,6 @@ fn next_sequence(&self) -> u32 { }/// Write one RPC message, ring the VF doorbell, and wait for its response. -#[expect(dead_code)] pub (super )fn rpc_call ( &mut self, dev: &device::Device<device::Bound>,
drivers/gpu/nova-core/vgpu/instance.rs +3 -0
@@ -31,6 +31,7 @@use super ::{ commands::{ + negotiate_plugin_version, send_bootload, send_cleanup, send_shutdown,
@@ -156,6 +157,8 @@ impl<'gpu> VgpuInstance<'gpu> {fn activate (&mut self, vgpu: &VgpuManager <'gpu>) ->Result {let dev = vgpu.dev; self.bootload (dev, vgpu.cmdq, &vgpu.fifo_engine_list)?; + self.plugin_rpc.init_rpc ()?; +negotiate_plugin_version (dev, &mut self.plugin_rpc)?;Ok (()) }
Signature · 1 line
2.53.0
PATCH v4 27/32 gpu: nova-core: vgpu: send GSP plugin configuration parameters
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The GSP plugin needs instance identity, channel allocation and host parameters before it can serve guest requests.
Encode the configuration as an NVKV stream, prefix it with its word count and send SETUP_CONFIG_PARAMS_AND_INIT after protocol negotiation through the instance's PluginRpc channel.
Derive the host's opaque device identity from the VF DBDF within the supported 16-bit PCI segment namespace. This identifies the local device without imposing a migration-persistent identity.
Describe the supported whole-GPU configuration and distinguish the optional VMM capability bitmap from the separately reported migration features. Encode the host page size with the kernel's infallible conversion.
--- drivers/gpu/nova-core/vgpu/commands.rs | 9 ++ drivers/gpu/nova-core/vgpu/fw/commands.rs | 135 +++++++++++++++++++ drivers/gpu/nova-core/vgpu/gsp_plugin_rpc.rs | 17 +++ drivers/gpu/nova-core/vgpu/instance.rs | 16 +++ 4 files changed, 177 insertions(+)
drivers/gpu/nova-core/vgpu/commands.rs +9 -0
@@ -170,3 +170,12 @@ pub(super) fn negotiate_plugin_version( ) -> Result { rpc.rpc_call (dev,RpcMessage ::VersionNegotiation , &[]) } + +/// Send an instance's encoded configuration to the GSP plugin. +pub (super )fn send_plugin_config ( + dev: &device::Device <device::Bound >, + rpc: &mut PluginRpc <'_, '_>, + config: &[u64 ], +) ->Result { + rpc.rpc_call_nvkv (dev,RpcMessage ::SetupConfigParamsAndInit , config) +}
drivers/gpu/nova-core/vgpu/fw/commands.rs +135 -0
150 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/gsp_plugin_rpc.rs +17 -0
@@ -19,6 +19,7 @@use kernel::{ device, + num::casts::usize_as_u64, prelude::*, time::{ delay::fsleep,
@@ -26,6 +27,7 @@ Instant, Monotonic, // }, + transmute::AsBytes, // };use crate ::{
@@ -105,6 +107,21 @@ pub(super) fn rpc_call( self.wait_response (dev, sequence) } +/// Send an NVKV stream prefixed by its word count. +pub (super )fn rpc_call_nvkv ( + &mut self, + dev: &device::Device <device::Bound >, + message_type:RpcMessage , + encoded: &[u64 ], + ) ->Result { +let word_count =usize_as_u64 (encoded.len ()); +let mut payload =KVec ::new (); + payload.extend_from_slice (&word_count.to_le_bytes (),GFP_KERNEL )?; + payload.extend_from_slice (AsBytes ::as_bytes (encoded),GFP_KERNEL )?; + + self.rpc_call (dev, message_type, &payload) + } +fn wait_response (&self, dev: &device::Device <device::Bound >, expected_sequence:u32 ) ->Result {let start =Instant ::<Monotonic >::now ();let timeout =Delta ::from_secs (120 );
drivers/gpu/nova-core/vgpu/instance.rs +16 -0
@@ -34,10 +34,12 @@ negotiate_plugin_version, send_bootload, send_cleanup, + send_plugin_config, send_shutdown, Dbdf, // }, fw::commands::{ + encode_plugin_config_params, encode_vgpu_bootload,BootloadInfo ,ChannelMapEntry ,//
@@ -159,6 +161,7 @@ fn activate(&mut self, vgpu: &VgpuManager<'gpu>) -> Result { self.bootload (dev, vgpu.cmdq, &vgpu.fifo_engine_list)?; self.plugin_rpc.init_rpc ()?;negotiate_plugin_version (dev, &mut self.plugin_rpc)?; + self.configure_plugin (dev)?;Ok (()) }
@@ -230,6 +233,19 @@ fn bootload(Ok (()) } +fn configure_plugin (&mut self, dev: &device::Device <device::Bound >) ->Result { +let config =encode_plugin_config_params ( + [0 ;16 ], + self.dbdf, + self.vgpu_type.vgpu_type_id, + self.vm_pid, + u32::try_from (self.chids.len ()).map_err (|_|EOVERFLOW )?, + self.num_plugin_channels, + )?; + +send_plugin_config (dev, &mut self.plugin_rpc, &config) + } +/// Stop the plugin when firmware may own instance resources. fn shutdown (&mut self, dev: &device::Device <device::Bound >, cmdq: &Cmdq <'_>) ->Result {if self.needs_teardown {
Signature · 1 line
2.53.0
PATCH v4 28/32 gpu: nova-core: vgpu: update the GSP plugin BME state
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
The vGPU manager reports the enabled bus-mastering state to the GSP plugin after its initial configuration. The guest can then use the vGPU from now on.
Add the BME state request and NVKV encoder, and send the enabled state after configuration succeeds.
--- drivers/gpu/nova-core/vgpu/commands.rs | 11 +++++++++++ drivers/gpu/nova-core/vgpu/fw/commands.rs | 22 ++++++++++++++++++++++ drivers/gpu/nova-core/vgpu/instance.rs | 2 ++ 3 files changed, 35 insertions(+)
drivers/gpu/nova-core/vgpu/commands.rs +11 -0
@@ -26,6 +26,7 @@use super ::{ fw::{ commands::{ + encode_plugin_set_bme,VgpuPropertiesSchema ,// },RpcMessage ,//
@@ -179,3 +180,13 @@ pub(super) fn send_plugin_config( ) -> Result { rpc.rpc_call_nvkv (dev,RpcMessage ::SetupConfigParamsAndInit , config) } + +/// Update the bus-mastering state reported to the GSP plugin. +pub (super )fn set_plugin_bme ( + dev: &device::Device <device::Bound >, + rpc: &mut PluginRpc <'_, '_>, + enable:bool , +) ->Result { +let bme =encode_plugin_set_bme (enable)?; + rpc.rpc_call_nvkv (dev,RpcMessage ::UpdateBmeState , &bme) +}
drivers/gpu/nova-core/vgpu/fw/commands.rs +22 -0
@@ -36,6 +36,7 @@pub (crate )enum RpcMessage {VersionNegotiation = bindings::MESSAGE_NV_VGPU_CPU_RPC_MSG_VERSION_NEGOTIATION ,SetupConfigParamsAndInit = bindings::MESSAGE_NV_VGPU_CPU_RPC_MSG_SETUP_CONFIG_PARAMS_AND_INIT , +UpdateBmeState = bindings::MESSAGE_NV_VGPU_CPU_RPC_MSG_UPDATE_BME_STATE , }bitfield! {
@@ -397,3 +398,24 @@ pub(crate) fn encode_plugin_config_params( request.encode (&mut encoder)?;Ok (encoder.finish ()) } + +nvkv_encode! { +struct PluginSetBmeRequest { + bme_enable:Key <bool , {Self ::BME_ENABLE_KEY },u32 >, + } +} + +impl PluginSetBmeRequest { +const BME_ENABLE_KEY :KeyId =0x0100 ; +} + +/// Encodes a plugin BME state update using the typed NVKV schema. +pub (crate )fn encode_plugin_set_bme (enable:bool ) ->Result <KVVec <u64 >> { +let request =PluginSetBmeRequest { + bme_enable: enable.into (), + }; + +let mut encoder =Encoder ::new (); + request.encode (&mut encoder)?; +Ok (encoder.finish ()) +}
drivers/gpu/nova-core/vgpu/instance.rs +2 -0
@@ -36,6 +36,7 @@ send_cleanup, send_plugin_config, send_shutdown, + set_plugin_bme, Dbdf, // }, fw::commands::{
@@ -162,6 +163,7 @@ fn activate(&mut self, vgpu: &VgpuManager<'gpu>) -> Result { self.plugin_rpc.init_rpc ()?;negotiate_plugin_version (dev, &mut self.plugin_rpc)?; self.configure_plugin (dev)?; +set_plugin_bme (dev, &mut self.plugin_rpc,true )?;Ok (()) }
Signature · 1 line
2.53.0
PATCH v4 29/32 gpu: nova-core: vgpu: add CeUtils commands
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Add Cmdq operations to allocate and free a CeUtils channel and submit guest VRAM scrub requests. Decode the allocation and scrub replies, and validate the returned semaphore address and aperture.
Distinguish an explicit allocation rejection from failures that may leave firmware owning the channel, so callers can decide whether to retain its host resources.
--- .../gpu/nova-core/gsp/fw/r000_00/bindings.rs | 1 + drivers/gpu/nova-core/vgpu/commands.rs | 145 ++++++++++++++++-- drivers/gpu/nova-core/vgpu/fw.rs | 11 ++ drivers/gpu/nova-core/vgpu/fw/commands.rs | 52 ++++++- 4 files changed, 198 insertions(+), 11 deletions(-)
drivers/gpu/nova-core/gsp/fw/r000_00/bindings.rs +1 -0
@@ -851,6 +851,7 @@ fn default() -> Self { } } } +pub const NV_ADDR_FBMEM :u32 =2 ;pub const GSP_PLUGIN_BOOTLOADED :u32 =1315261039 ;pub const VGPU_CPU_GSP_CTRL_BUFF_VERSION :u32 =2 ;pub const VGPU_CPU_GSP_CTRL_BUFF_REGION_SIZE :u32 =4096 ;
drivers/gpu/nova-core/vgpu/commands.rs +136 -9
173 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/fw.rs +11 -0
@@ -27,6 +27,8 @@ VGPU_CPU_GSP_VGPU_TASK_LOG_BUFF_REGION_SIZE, // }; +pub (super )use bindings::NV_ADDR_FBMEM ; +pub (super )use commands::RpcMessage ; pub(super) const GMCAPI_CMD_QUERY_ASSIGNED_VF_VGPU_TYPE: u32 =
@@ -47,6 +49,15 @@pub (super )const GMCAPI_CMD_CLEANUP_GSP_VGPU_PLUGIN_RESOURCES :u32 = bindings::GMCAPI_COMMANDS_GMCAPI_CMD_CLEANUP_GSP_VGPU_PLUGIN_RESOURCES ; +pub (super )const GMCAPI_CMD_VGPU_MGR_ALLOC_GSP_CEUTILS :u32 = + bindings::GMCAPI_COMMANDS_GMCAPI_CMD_VGPU_MGR_ALLOC_GSP_CEUTILS ; + +pub (super )const GMCAPI_CMD_VGPU_MGR_FREE_GSP_CEUTILS :u32 = + bindings::GMCAPI_COMMANDS_GMCAPI_CMD_VGPU_MGR_FREE_GSP_CEUTILS ; + +pub (super )const GMCAPI_CMD_VGPU_MGR_SCRUB_GUEST_FB :u32 = + bindings::GMCAPI_COMMANDS_GMCAPI_CMD_VGPU_MGR_SCRUB_GUEST_FB ; +/// State observed in the response buffer for an expected RPC sequence. pub (super )enum RpcResponse {Pending {
drivers/gpu/nova-core/vgpu/fw/commands.rs +50 -2
66 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 30/32 gpu: nova-core: vgpu: scrub guest VRAM with CeUtils
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Scrub guest VRAM before plugin boot and after plugin shutdown, before its slot can be reused. Reserve the last channel ID in each instance range for CeUtils and give the remaining channels to the plugin.
Submit scrubs in chunks and wait for completion through a temporary BAR1 mapping of the CeUtils semaphore. Release the mapping on both success and failure, preserving a polling error if unmapping also fails.
Keep allocation, scrubbing and release within the instance lifecycle. Retain host resources until device removal when firmware ownership is uncertain or a scrub fails, and do not retry a recorded teardown failure.
--- drivers/gpu/nova-core/vgpu.rs | 1 + drivers/gpu/nova-core/vgpu/commands.rs | 4 - drivers/gpu/nova-core/vgpu/instance.rs | 51 +++++++- drivers/gpu/nova-core/vgpu/scrubber.rs | 173 +++++++++++++++++++++++++ 4 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 drivers/gpu/nova-core/vgpu/scrubber.rs
drivers/gpu/nova-core/vgpu.rs +1 -0
@@ -38,6 +38,7 @@mod gsp_plugin_rpc;mod hal;mod instance; +mod scrubber;mod vram;/// vGPU state detected during GPU construction.
drivers/gpu/nova-core/vgpu/commands.rs +0 -4
@@ -208,7 +208,6 @@ pub(super) fn set_plugin_bme( }/// Whether a failed allocation may still have transferred CHID ownership to firmware. -#[expect(dead_code)] pub (super )enum CeUtilsAllocError {/// A matching firmware response explicitly rejected the allocation. NotOwned (Error ),
@@ -217,7 +216,6 @@ pub(super) enum CeUtilsAllocError { }/// Allocate a CeUtils channel and validate its semaphore description. -#[expect(dead_code)] pub (super )fn alloc_ceutils ( dev: &device::Device <device::Bound >, cmdq: &Cmdq<'_>,
@@ -266,7 +264,6 @@ pub(super) fn alloc_ceutils( }/// Release a CeUtils allocation, including one whose allocation reply was lost. -#[expect(dead_code)] pub (super )fn free_ceutils ( dev: &device::Device <device::Bound >, cmdq: &Cmdq<'_>,
@@ -283,7 +280,6 @@ pub(super) fn free_ceutils( }/// Submit an asynchronous guest FB scrub and return its work identifier. -#[expect(dead_code)] pub (super )fn submit_ceutils_scrub ( dev: &device::Device <device::Bound >, cmdq: &Cmdq<'_>,
drivers/gpu/nova-core/vgpu/instance.rs +49 -2
111 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/scrubber.rs new file +173 -0
173 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 31/32 gpu: nova-core: vgpu: export plugin log buffers via debugfs
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Expose each instance's init, vgpu and kernel plugin logs through debugfs for nvlog_decoder.
Read the management-heap log buffers through BAR1 and reuse the existing GSP log header when a firmware build ID is available. Use the INIT, VGPU and KRNL task names expected by the decoder, and derive the instance directory name from the typed DBDF fields.
Retain the GPU identity and firmware build ID in the manager for per-instance log headers.
Retain the mapping while the files are accessible and revoke the debugfs scope before explicitly unmapping it. Place the scope before the RPC mapping in the instance's field order so device removal also drains readers before the mapping is dropped. Preserve partial-read accounting when a user copy fails.
--- drivers/gpu/nova-core/gpu.rs | 9 +- drivers/gpu/nova-core/gsp.rs | 23 +++ drivers/gpu/nova-core/mm/bar_user.rs | 5 +- drivers/gpu/nova-core/vgpu.rs | 11 +- drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs | 116 +++++++++++- drivers/gpu/nova-core/vgpu/instance.rs | 60 +++++- drivers/gpu/nova-core/vgpu/log.rs | 171 ++++++++++++++++++ 7 files changed, 382 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/nova-core/vgpu/log.rs
drivers/gpu/nova-core/gpu.rs +6 -3
@@ -630,9 +630,10 @@ pub(crate) fn new<'a>(// SAFETY: These sibling fields are initialized at their final pinned // addresses. The private manager cannot escape this `Gpu`, and is dropped // before all its dependencies, both here on failure and on normal removal. -let (cmdq, bar_user, mm, chid_pool) =unsafe { +// The GSP build ID is not modified after initialization. +let (gsp, bar_user, mm, chid_pool) =unsafe { ( - &*core::ptr::from_ref (&gsp_resources.gsp.cmdq), + &*core::ptr::from_ref (&gsp_resources.gsp), &*core::ptr::from_ref (bar_user.as_ref ().get_ref ()), &*core::ptr::from_ref (mm.as_ref ().get_ref ()), &*core::ptr::from_ref(chid_pool.as_ref().get_ref()),
@@ -640,7 +641,9 @@ pub(crate) fn new<'a>( }; Some(KBox::pin_init(VgpuManager::new( dev, - cmdq, + &gsp.cmdq, + gsp_resources.spec, + gsp.build_id(), bar_user, mm, chid_pool,
drivers/gpu/nova-core/gsp.rs +23 -0
@@ -180,6 +180,21 @@ fn new(spec: Spec, build_id: &BuildId, task_prefix: &str) -> Self { } } +/// Size of the header prepended to debugfs log buffer dumps. +pub (crate )const LOG_BUFFER_HEADER_SIZE :usize =size_of ::<LogBufferHeader >(); + +/// Builds a log header using the GPU implementation reported by the hardware. +pub (crate )fn build_log_buffer_header ( + spec:Spec , + build_id: &BuildId , + task_prefix: &str , +) -> [u8 ;LOG_BUFFER_HEADER_SIZE ] { +let header =LogBufferHeader ::new (spec, build_id, task_prefix); +let mut bytes = [0 ;LOG_BUFFER_HEADER_SIZE ]; + bytes.copy_from_slice (header.as_bytes ()); + bytes +} +/// The logging buffers are byte queues that contain encoded printf-like /// messages from GSP-RM. They need to be decoded by a special application /// that can parse the buffers.
@@ -368,6 +383,8 @@ fn register_debugfs<'data>(&'data self, dir: &debugfs::ScopedDir<'data, '_>) {pub (crate )struct Gsp <'gsp> {/// The GSP firmware's TLV. gsp_tlv: firmware::Firmware , +/// Build identifier of the firmware whose log buffers are exposed. + build_id:Option <BuildId >,/// Libos arguments. pub (crate ) libos:Coherent <'gsp, [LibosMemoryRegionInitArgument ]>,/// Log buffers, optionally exposed via debugfs.
@@ -383,6 +400,11 @@ pub(crate) struct Gsp<'gsp> { }impl <'gsp>Gsp <'gsp> { +/// Returns the GSP firmware build identifier, when available. +pub (crate )fn build_id (&self) ->Option <&BuildId > { + self.build_id.as_ref () + } +// Creates an in-place initializer for a `Gsp` manager for `pdev`. pub (crate )fn new ( pdev: &'gsp pci::Device<device::Bound>,
@@ -405,6 +427,7 @@ pub(crate) fn new( Ok(try_pin_init!(Self { gsp_tlv, + build_id, cmdq <- Cmdq::new(dev, bar), rm_state_monitor: Coherent::zeroed(dev, GFP_KERNEL)?, rmargs: Coherent::init(
drivers/gpu/nova-core/mm/bar_user.rs +4 -1
@@ -201,7 +201,6 @@ pub(crate) struct BarMapping<'map, 'gpu> { unmap_error: Option<Error>, } -#[expect(dead_code)] impl <'map, 'gpu>BarMapping <'map, 'gpu> {/// Maps the containing pages while restricting CPU access to the requested byte range. pub(crate) fn new(
@@ -247,6 +246,10 @@ pub(crate) fn new( }) } +pub (crate )fn bar1 (&self) ->Bar1 <'gpu> { + self.access.bar_user.bar1 + } +pub (crate )fn region (&self) -> &VramRegion { &self.region }
drivers/gpu/nova-core/vgpu.rs +10 -1
@@ -11,13 +11,15 @@ };use crate ::{ + firmware::gsp::BuildId , fsp::{Fsp ,VgpuMode ,// }, gpu::{ChannelIdPool , -Chipset ,// +Chipset , +Spec ,// }, gsp::{ cmdq::Cmdq ,
@@ -38,6 +40,7 @@mod gsp_plugin_rpc;mod hal;mod instance; +mod log;mod scrubber;mod vram;
@@ -113,6 +116,8 @@ pub(crate) struct VgpuManager<'gpu> { instances: Mutex<VgpuInstances<'gpu>>, dev: &'gpu device::Device<device::Bound>, cmdq: &'gpu Cmdq<'gpu>, + spec: Spec, + build_id: Option<&'gpu BuildId>, bar_user: &'gpu BarUser<'gpu>, mm: &'gpu Mutex<GpuMm<'gpu>>, chid_pool: &'gpu ChannelIdPool,
@@ -127,6 +132,8 @@ impl<'gpu> VgpuManager<'gpu> {pub (crate )fn new ( dev: &'gpu device::Device <device::Bound >, cmdq: &'gpuCmdq <'gpu>, + spec:Spec , + build_id:Option <&'gpuBuildId >, bar_user: &'gpuBarUser <'gpu>, mm: &'gpuMutex <GpuMm <'gpu>>, chid_pool: &'gpu ChannelIdPool,
@@ -139,6 +146,8 @@ pub(crate) fn new( instances <- new_mutex!(VgpuInstances::new(), "nova-core::vgpu-instances"), dev, cmdq, + spec, + build_id, bar_user, mm, chid_pool,
drivers/gpu/nova-core/vgpu/gsp_plugin_comm.rs +110 -6
140 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/instance.rs +58 -2
113 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/log.rs new file +171 -0
171 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v4 32/32 gpu: nova-core: vgpu: introduce SR-IOV PF APIs
In reply to Alok KumarFrom: Zhi Wang <[email protected]>
Provide PF operations for querying vGPU availability and creating, closing and resetting VF instances.
Validate VF identifiers against the PF's VF count and route lifecycle operations through the manager. Retain the GPU context needed to create plugin RPC channels and return guest PCI properties after activation.
--- drivers/gpu/nova-core/gpu.rs | 24 +++- drivers/gpu/nova-core/nova_core.rs | 6 + drivers/gpu/nova-core/vgpu.rs | 2 + drivers/gpu/nova-core/vgpu/commands.rs | 10 +- drivers/gpu/nova-core/vgpu/fw/commands.rs | 1 + drivers/gpu/nova-core/vgpu/instance.rs | 93 +++++++++++-- drivers/gpu/nova-core/vgpu/vgpu_api.rs | 158 ++++++++++++++++++++++ 7 files changed, 280 insertions(+), 14 deletions(-) create mode 100644 drivers/gpu/nova-core/vgpu/vgpu_api.rs
drivers/gpu/nova-core/gpu.rs +23 -1
@@ -1,6 +1,9 @@// SPDX-License-Identifier: GPL-2.0 -use core::ops::Range ; +use core::{ + num::NonZero , + ops::Range ,// +};use kernel::{ device,
@@ -472,6 +475,25 @@ fn drop(self: Pin<&mut Self>) { }impl <'gpu>Gpu <'gpu> { +pub (crate )fn cmdq (&self) -> &Cmdq <'gpu> { + &self.gsp_resources.gsp.cmdq + } + +pub (crate )fn vgpu_manager (&self) ->Option <&VgpuManager <'gpu>> { + self.vgpu.as_ref ().map (|vgpu| vgpu.as_ref ().get_ref ()) + } + +pub (crate )fn vgpu_total_vfs (&self) ->Option <NonZero <u16 >> { +match self.gsp_resources.vgpu_state { +VgpuState ::Disabled =>None , +VgpuState ::Enabled { total_vfs } =>Some (total_vfs), + } + } + +pub (crate )fn bar0 (&self) ->Bar0 <'gpu> { + self.gsp_resources.bar + } +pub (crate )fn new <'a>( pdev: &'gpu pci::Device <device::Core <'a>>, bar: Bar0<'gpu>,
drivers/gpu/nova-core/nova_core.rs +6 -0
@@ -30,6 +30,12 @@mod vbios;mod vgpu; +#[cfg( CONFIG_PCI_IOV )] +pub use vgpu::vgpu_api::{ +NovaCoreVfApi , +VgpuTypeInfo ,// +}; +pub (crate )const MODULE_NAME : &core::ffi::CStr = <LocalModule as kernel::ModuleMetadata >::NAME ;// TODO: Move this into per-module data once that exists.
drivers/gpu/nova-core/vgpu.rs +2 -0
@@ -42,6 +42,8 @@mod instance;mod log;mod scrubber; +#[cfg_attr(not( CONFIG_PCI_IOV ), expect(dead_code, unreachable_pub))] +pub (crate )mod vgpu_api;mod vram;/// vGPU state detected during GPU construction.
drivers/gpu/nova-core/vgpu/commands.rs +8 -2
@@ -78,7 +78,6 @@ fn check_status(dev: &device::Device<device::Bound>, command_id: u32, status: u3 }/// Query the vGPU type assigned to a VF by its DBDF. -#[expect(dead_code)] pub (super )fn query_assigned_vf_type ( dev: &device::Device <device::Bound >, cmdq: &Cmdq<'_>,
@@ -95,7 +94,6 @@ pub(super) fn query_assigned_vf_type( }/// Query and decode the firmware properties of one vGPU type. -#[expect(dead_code)] pub (super )fn query_vgpu_properties ( dev: &device::Device <device::Bound >, cmdq: &Cmdq<'_>,
@@ -207,6 +205,14 @@ pub(super) fn set_plugin_bme( rpc.rpc_call_nvkv (dev,RpcMessage ::UpdateBmeState , &bme) } +/// Reset an active GSP plugin. +pub (super )fn reset_plugin ( + dev: &device::Device <device::Bound >, + rpc: &mut PluginRpc <'_, '_>, +) ->Result { + rpc.rpc_call (dev,RpcMessage ::Reset , &[]) +} +/// Whether a failed allocation may still have transferred CHID ownership to firmware. pub (super )enum CeUtilsAllocError {/// A matching firmware response explicitly rejected the allocation.
drivers/gpu/nova-core/vgpu/fw/commands.rs +1 -0
@@ -36,6 +36,7 @@pub (crate )enum RpcMessage {VersionNegotiation = bindings::MESSAGE_NV_VGPU_CPU_RPC_MSG_VERSION_NEGOTIATION ,SetupConfigParamsAndInit = bindings::MESSAGE_NV_VGPU_CPU_RPC_MSG_SETUP_CONFIG_PARAMS_AND_INIT , +Reset = bindings::MESSAGE_NV_VGPU_CPU_RPC_MSG_RESET ,UpdateBmeState = bindings::MESSAGE_NV_VGPU_CPU_RPC_MSG_UPDATE_BME_STATE , }
drivers/gpu/nova-core/vgpu/instance.rs +82 -11
167 lines not on this page · raw message
drivers/gpu/nova-core/vgpu/vgpu_api.rs new file +158 -0
158 lines not on this page · raw message
Signature · 1 line
2.53.0