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))
+}
next prev 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®