mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Zhi Wang <zhiw@nvidia.com>
To: <dakr@kernel.org>, <acourbot@nvidia.com>
Cc: <alex@shazbot.org>, <jgg@nvidia.com>, <yishaih@nvidia.com>,
	<skolothumtho@nvidia.com>, <kevin.tian@intel.com>,
	<airlied@gmail.com>, <simona@ffwll.ch>, <ojeda@kernel.org>,
	<alex.gaynor@gmail.com>, <boqun.feng@gmail.com>,
	<gary@garyguo.net>, <bjorn3_gh@protonmail.com>,
	<lossin@kernel.org>, <a.hindborg@kernel.org>,
	<aliceryhl@google.com>, <tmgross@umich.edu>,
	<jhubbard@nvidia.com>, <ecourtney@nvidia.com>, <cjia@nvidia.com>,
	<smitra@nvidia.com>, <kjaju@nvidia.com>, <alkumar@nvidia.com>,
	<ankita@nvidia.com>, <aniketa@nvidia.com>, <kwankhede@nvidia.com>,
	<targupta@nvidia.com>, <nova-gpu@lists.linux.dev>,
	<linux-kernel@vger.kernel.org>, <zhiwang@kernel.org>,
	Zhi Wang <zhiw@nvidia.com>
Subject: [PATCH v2 21/32] gpu: nova-core: vgpu: add instance create/destroy
Date: Mon, 14 Sep 2026 10:48:56 +0300	[thread overview]
Message-ID: <20260914074907.1399629-22-zhiw@nvidia.com> (raw)
In-Reply-To: <20260914074907.1399629-1-zhiw@nvidia.com>

A vGPU instance holds the resources used by a VF. The VF must have a
vGPU type assigned before instance creation. The instance lifetime is
intended to follow VFIO device use: create it on open and destroy it
on close.

Add an instance registry to VgpuManager with creation and destruction
operations. Creation reserves channel IDs and a VRAM slot according
to the assigned vGPU type; destruction releases those reservations.

Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
 drivers/gpu/nova-core/gpu.rs           |  12 +-
 drivers/gpu/nova-core/vgpu.rs          |  23 ++-
 drivers/gpu/nova-core/vgpu/commands.rs |   1 -
 drivers/gpu/nova-core/vgpu/instance.rs | 225 +++++++++++++++++++++++++
 4 files changed, 247 insertions(+), 14 deletions(-)
 create mode 100644 drivers/gpu/nova-core/vgpu/instance.rs

diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index ebf29b06a8e1..61adf412bae7 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -54,14 +54,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 = $value:expr),* $(,)* }) =>
@@ -313,7 +315,7 @@ struct GspResources<'gpu> {
 #[pin_data]
 pub(crate) struct Gpu<'gpu> {
     spec: Spec,
-    vgpu: Option<VgpuManager<'gpu>>,
+    vgpu: Option<Pin<KBox<VgpuManager<'gpu>>>>,
     /// GSP event interrupt registration.
     ///
     /// Declared before `gsp_resources` so it is dropped first: `free_irq` runs, waiting out any
@@ -444,7 +446,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,
@@ -453,7 +455,7 @@ pub(crate) fn new<'a>(
                         &info.fifo_engine_list,
                         info.vmmu_segment_size,
                         TOTAL_CHANNELS,
-                    )),
+                    ), GFP_KERNEL)?),
                 }
             },
 
diff --git a/drivers/gpu/nova-core/vgpu.rs b/drivers/gpu/nova-core/vgpu.rs
index 0f9f5be752b5..f3e40e6de6f0 100644
--- a/drivers/gpu/nova-core/vgpu.rs
+++ b/drivers/gpu/nova-core/vgpu.rs
@@ -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,9 +91,13 @@ 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: &'gpu ChannelIdPool,
     vmmu_segment_size: u64,
     total_channels: u32,
@@ -104,22 +111,22 @@ 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,
+        })
     }
 
     /// Returns the VMMU segment size in bytes, or zero if GSP-RM omitted it.
-    #[expect(dead_code)]
     const fn vmmu_segment_size(&self) -> u64 {
         self.vmmu_segment_size
     }
 
-    #[expect(dead_code)]
     const fn total_channels(&self) -> u32 {
         self.total_channels
     }
diff --git a/drivers/gpu/nova-core/vgpu/commands.rs b/drivers/gpu/nova-core/vgpu/commands.rs
index b5fa3575b1d7..bdedd7fc21ad 100644
--- a/drivers/gpu/nova-core/vgpu/commands.rs
+++ b/drivers/gpu/nova-core/vgpu/commands.rs
@@ -43,7 +43,6 @@ pub(super) fn query_assigned_vf_type(cmdq: &Cmdq<'_>, dbdf: Dbdf) -> Result<u32>
 }
 
 /// Query and decode the firmware properties of one vGPU type.
-#[expect(dead_code)]
 pub(super) fn query_vgpu_properties(cmdq: &Cmdq<'_>, type_id: u32) -> Result<KBox<VgpuProperties>> {
     let response = cmdq.send_gmc_and_receive(
         GMCAPI_CMD_QUERY_VGPU_PROPERTIES,
diff --git a/drivers/gpu/nova-core/vgpu/instance.rs b/drivers/gpu/nova-core/vgpu/instance.rs
new file mode 100644
index 000000000000..99a8f47e72d1
--- /dev/null
+++ b/drivers/gpu/nova-core/vgpu/instance.rs
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+use core::num::NonZeroUsize;
+
+use kernel::{
+    prelude::*,
+    ptr::Alignment,
+    sizes::SizeConstants, //
+};
+
+use crate::gsp::cmdq::Cmdq;
+
+use crate::{
+    gpu::ChannelIdReservation,
+    mm::GpuMm, //
+};
+
+use super::{
+    vram::{
+        VgpuVramLayout,
+        VgpuVramSlot,
+        VgpuVramSlotAllocator, //
+    },
+    VgpuManager, //
+};
+
+use super::commands::{
+    query_vgpu_properties,
+    Dbdf,
+    VgpuProperties, //
+};
+
+/// Guest Function ID. GFID 0 is reserved for the PF; VFs start at 1.
+#[repr(transparent)]
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub(super) struct Gfid(pub(super) u32);
+
+/// Resource requirements and device identity for one vGPU type.
+#[expect(dead_code)]
+pub(super) struct VgpuType {
+    vgpu_type_id: u32,
+    bar1_length: u64,
+    max_instance: u32,
+    pci_dev_id: u32,
+    pci_subsys_id: u32,
+    fb_length: u64,
+    gsp_heap_size: u64,
+}
+
+impl VgpuType {
+    fn from_properties(properties: &VgpuProperties) -> Self {
+        Self {
+            vgpu_type_id: properties.type_id,
+            bar1_length: properties.bar1_length,
+            max_instance: properties.max_instance,
+            pci_dev_id: properties.dev_id,
+            pci_subsys_id: properties.subsystem_id,
+            fb_length: properties.fb_length,
+            gsp_heap_size: properties.gsp_heap_size,
+        }
+    }
+}
+
+/// A vGPU instance and the resources reserved for it.
+#[expect(dead_code)]
+pub(super) struct VgpuInstance<'gpu> {
+    pub(super) gfid: Gfid,
+    dbdf: Dbdf,
+    vgpu_type: VgpuType,
+    vm_pid: u32,
+    chids: ChannelIdReservation<'gpu>,
+    vram_slot: VgpuVramSlot,
+}
+
+/// Identity and firmware profile used to allocate an instance.
+pub(super) struct InstanceInfo {
+    gfid: Gfid,
+    dbdf: Dbdf,
+    vgpu_type: VgpuType,
+    vm_pid: u32,
+}
+
+#[expect(dead_code)]
+impl InstanceInfo {
+    pub(super) const fn new(gfid: Gfid, dbdf: Dbdf, vgpu_type: VgpuType, vm_pid: u32) -> Self {
+        Self {
+            gfid,
+            dbdf,
+            vgpu_type,
+            vm_pid,
+        }
+    }
+}
+
+/// Registry of live vGPU instances.
+pub(super) struct VgpuInstances<'gpu> {
+    instances: KVec<VgpuInstance<'gpu>>,
+    vram_slots: Option<VgpuVramSlotAllocator>,
+}
+
+#[expect(dead_code)]
+impl<'gpu> VgpuInstances<'gpu> {
+    pub(super) const fn new() -> Self {
+        Self {
+            instances: KVec::new(),
+            vram_slots: None,
+        }
+    }
+
+    fn alloc_vram_slot(&mut self, mm: &GpuMm<'_>, layout: VgpuVramLayout) -> Result<VgpuVramSlot> {
+        let replace_empty_pool = match self.vram_slots.as_ref() {
+            Some(allocator) if allocator.is_empty() => !allocator.matches_layout(layout)?,
+            _ => false,
+        };
+        if replace_empty_pool {
+            self.vram_slots = None;
+        }
+
+        if let Some(allocator) = self.vram_slots.as_mut() {
+            return allocator.alloc(layout);
+        }
+
+        let mut allocator = VgpuVramSlotAllocator::new(mm, layout)?;
+        let slot = allocator.alloc(layout)?;
+        self.vram_slots = Some(allocator);
+        Ok(slot)
+    }
+
+    fn release_vram_slot(&mut self, slot: VgpuVramSlot) -> Result {
+        self.vram_slots.as_mut().ok_or(EIO)?.release(slot);
+        Ok(())
+    }
+
+    /// Allocate resources and register a new inactive vGPU instance.
+    pub(super) fn allocate_instance(
+        &mut self,
+        mm: &GpuMm<'_>,
+        vgpu: &VgpuManager<'gpu>,
+        info: InstanceInfo,
+    ) -> Result<Gfid> {
+        let InstanceInfo {
+            gfid,
+            dbdf,
+            vgpu_type,
+            vm_pid,
+        } = info;
+
+        if self
+            .instances
+            .iter()
+            .any(|instance| instance.gfid == gfid || instance.dbdf == dbdf)
+        {
+            return Err(EEXIST);
+        }
+        let profile_instances = self
+            .instances
+            .iter()
+            .filter(|instance| instance.vgpu_type.vgpu_type_id == vgpu_type.vgpu_type_id)
+            .count();
+        if vgpu_type.max_instance == 0
+            || profile_instances
+                >= usize::try_from(vgpu_type.max_instance).map_err(|_| EOVERFLOW)?
+        {
+            return Err(ENOSPC);
+        }
+        // Reserve registry capacity before acquiring resources so publishing
+        // the completed instance cannot fail due to memory pressure.
+        self.instances.reserve(1, GFP_KERNEL)?;
+
+        let num_chid = vgpu
+            .total_channels()
+            .checked_div(vgpu_type.max_instance)
+            .filter(|count| *count != 0)
+            .ok_or(EINVAL)?;
+        let chids = vgpu.chid_pool.reserve_ids(
+            NonZeroUsize::new(usize::try_from(num_chid).map_err(|_| EOVERFLOW)?).ok_or(EINVAL)?,
+            Alignment::SZ_1,
+        )?;
+        let layout = VgpuVramLayout {
+            type_id: vgpu_type.vgpu_type_id,
+            max_slots: vgpu_type.max_instance,
+            fb_size: vgpu_type.fb_length,
+            heap_size: vgpu_type.gsp_heap_size,
+            fb_align: vgpu.vmmu_segment_size(),
+        };
+        let vram_slot = self.alloc_vram_slot(mm, layout)?;
+
+        let instance = VgpuInstance {
+            gfid,
+            dbdf,
+            vgpu_type,
+            vm_pid,
+            chids,
+            vram_slot,
+        };
+        match self.instances.push_within_capacity(instance) {
+            Ok(()) => Ok(gfid),
+            Err(error) => {
+                let VgpuInstance { vram_slot, .. } = error.0;
+                self.release_vram_slot(vram_slot)?;
+                Err(EIO)
+            }
+        }
+    }
+
+    /// Remove an instance and release its channel and VRAM reservations.
+    pub(super) fn destroy_instance(&mut self, gfid: Gfid) -> Result {
+        let index = self
+            .instances
+            .iter()
+            .position(|instance| instance.gfid == gfid)
+            .ok_or(ENOENT)?;
+        let instance = self.instances.remove(index).map_err(|_| EIO)?;
+        let VgpuInstance { vram_slot, .. } = instance;
+        self.release_vram_slot(vram_slot)
+    }
+}
+
+/// Query and decode one vGPU type using the typed NVKV schema.
+#[expect(dead_code)]
+pub(super) fn query_vgpu_type(cmdq: &Cmdq<'_>, type_id: u32) -> Result<VgpuType> {
+    let properties = query_vgpu_properties(cmdq, type_id)?;
+    Ok(VgpuType::from_properties(&properties))
+}

  parent reply	other threads:[~2026-09-14  7:52 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-14  7:48 [PATCH v2 00/32] Introduce NVIDIA vGPU manager Zhi Wang
2026-09-14  7:48 ` [PATCH v2 01/32] gpu: nova-core: gsp: pass boot context through setup helpers Zhi Wang
2026-09-14  7:48 ` [PATCH v2 02/32] gpu: nova-core: gsp: decouple boot context from VgpuManager Zhi Wang
2026-09-14  7:48 ` [PATCH v2 03/32] gpu: nova-core: vgpu: detect boot state independently Zhi Wang
2026-09-14  7:48 ` [PATCH v2 04/32] gpu: nova-core: gpu: add a channel ID pool for vGPU Zhi Wang
2026-09-14  7:48 ` [PATCH v2 05/32] gpu: nova-core: gsp: expose the VMMU segment size Zhi Wang
2026-09-14  7:48 ` [PATCH v2 06/32] gpu: nova-core: gsp: decode the FIFO engine table Zhi Wang
2026-09-14  7:48 ` [PATCH v2 07/32] gpu: nova-core: vgpu: initialize runtime parameters after GSP boot Zhi Wang
2026-09-14  7:48 ` [PATCH v2 08/32] gpu: nova-core: vgpu: reserve the 48-VM WPR2 heap Zhi Wang
2026-09-14  7:48 ` [PATCH v2 09/32] gpu: nova-core: mm: borrow BarUser for temporary BAR1 access Zhi Wang
2026-09-14  7:48 ` [PATCH v2 10/32] gpu: nova-core: mm: add VramBlock Zhi Wang
2026-09-14  7:48 ` [PATCH v2 11/32] gpu: nova-core: mm: add VramRegion Zhi Wang
2026-09-14  7:48 ` [PATCH v2 12/32] gpu: nova-core: mm: add BarMapping Zhi Wang
2026-09-14  7:48 ` [PATCH v2 13/32] gpu: nova-core: gsp: dispatch RM RPC messages during GMC receive Zhi Wang
2026-09-14  7:48 ` [PATCH v2 14/32] gpu: nova-core: gsp: add synchronous GMC transactions Zhi Wang
2026-09-14  7:48 ` [PATCH v2 15/32] gpu: nova-core: gsp: add status-only " Zhi Wang
2026-09-14  7:48 ` [PATCH v2 16/32] gpu: nova-core: gsp: wait for GMC completion events Zhi Wang
2026-09-14  7:48 ` [PATCH v2 17/32] gpu: nova-core: vgpu: add r000 plugin bindings Zhi Wang
2026-09-14  7:48 ` [PATCH v2 18/32] gpu: nova-core: vgpu: add VRAM slot allocator Zhi Wang
2026-09-14  7:48 ` [PATCH v2 19/32] gpu: nova-core: gsp: factor out NVKV payload conversion Zhi Wang
2026-09-14  7:48 ` [PATCH v2 20/32] gpu: nova-core: vgpu: query VF assignments and properties Zhi Wang
2026-09-14  7:48 ` Zhi Wang [this message]
2026-09-14  7:48 ` [PATCH v2 22/32] gpu: nova-core: vgpu: encode vGPU boot requests Zhi Wang
2026-09-14  7:48 ` [PATCH v2 23/32] gpu: nova-core: vgpu: add GSP plugin communication buffers Zhi Wang
2026-09-14  7:48 ` [PATCH v2 24/32] gpu: nova-core: vgpu: add instance boot and shutdown Zhi Wang
2026-09-14  7:49 ` [PATCH v2 25/32] gpu: nova-core: vgpu: initialize GSP plugin RPC buffers Zhi Wang
2026-09-14  7:49 ` [PATCH v2 26/32] gpu: nova-core: vgpu: add GSP plugin RPC transactions Zhi Wang
2026-09-14  7:49 ` [PATCH v2 27/32] gpu: nova-core: vgpu: negotiate the GSP plugin RPC version Zhi Wang
2026-09-14  7:49 ` [PATCH v2 28/32] gpu: nova-core: vgpu: send GSP plugin configuration parameters Zhi Wang
2026-09-14  7:49 ` [PATCH v2 29/32] gpu: nova-core: vgpu: update the GSP plugin BME state Zhi Wang
2026-09-14  7:49 ` [PATCH v2 30/32] gpu: nova-core: vgpu: scrub guest VRAM with CeUtils Zhi Wang
2026-09-14  7:49 ` [PATCH v2 31/32] gpu: nova-core: add build ID headers to debugfs log buffer dumps Zhi Wang
2026-09-14  7:49 ` [PATCH v2 32/32] gpu: nova-core: vgpu: export plugin log buffers via debugfs Zhi Wang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260914074907.1399629-22-zhiw@nvidia.com \
    --to=zhiw@nvidia.com \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=airlied@gmail.com \
    --cc=alex.gaynor@gmail.com \
    --cc=alex@shazbot.org \
    --cc=aliceryhl@google.com \
    --cc=alkumar@nvidia.com \
    --cc=aniketa@nvidia.com \
    --cc=ankita@nvidia.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=cjia@nvidia.com \
    --cc=dakr@kernel.org \
    --cc=ecourtney@nvidia.com \
    --cc=gary@garyguo.net \
    --cc=jgg@nvidia.com \
    --cc=jhubbard@nvidia.com \
    --cc=kevin.tian@intel.com \
    --cc=kjaju@nvidia.com \
    --cc=kwankhede@nvidia.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=nova-gpu@lists.linux.dev \
    --cc=ojeda@kernel.org \
    --cc=simona@ffwll.ch \
    --cc=skolothumtho@nvidia.com \
    --cc=smitra@nvidia.com \
    --cc=targupta@nvidia.com \
    --cc=tmgross@umich.edu \
    --cc=yishaih@nvidia.com \
    --cc=zhiwang@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®