Rust VFIO PCI support and NVIDIA vGPU driver
Subsystems (10)
cover Rust VFIO PCI support and NVIDIA vGPU driver
This series adds Rust support for VFIO PCI variant drivers and an NVIDIA vGPU driver using Nova's typed SR-IOV PF services.
It follows the discussion of the original C NVIDIA vGPU VFIO variant [1] and builds on Danilo Krummrich's Rust vGPU/VFIO proof of concept [2], including his PCI helpers, VFIO abstractions and NVIDIA variant driver. Keeping the variant in Rust preserves the ownership and lifetime relationships with nova-core across the PF/VF interface, and allows registration and first-open/last-close resources to use Rust's RAII model.
The VFIO core prerequisite is the work of Simon Song and Alex Williamson, discussed in [3]. VFIO core previously required PCI driver data to point directly to a vfio_pci_core_device, conflicting with Rust's typed private data. Their changes let each driver's PM, AER and VGA callbacks recover the core device from its own private data and pass it to shared VFIO helpers. This addresses the concern raised in the earlier thread: the Rust driver can retain its typed PCI private data without adding a separate VFIO pointer to struct pci_dev.
The main changes from the PoC are:
- Separate common VFIO device and callback-data handling from the PCI wrapper. Require covariant registration data when restoring erased lifetimes, keep callback borrows distinct from data lifetimes, and extend callback-scoped access to open, write and mmap.
- Add a VFIO PCI adapter for PM, AER, reset and VGA callbacks. Register VFIO after publishing initialized PCI private data, and unregister before releasing that data.
- Connect the typed PF instance handle to the existing Nova manager's implemented open, close and reset operations. Make PF service access fallible and reject PCI segments that do not fit the firmware's DBDF format.
- Match all NVIDIA device IDs and classes through an override-only entry, with PCI passthrough for devices without Nova services. Wire up IOMMUFD PASID callbacks and passthrough BAR DMA-buffer operations.
- Enforce the assigned BAR1 aperture for read, write and mmap as well as region-info queries. Set the guest device ID in VFIO's virtual configuration space and retain the subsystem-ID read override.
Integration branch:
https://github.com/zhiwang-nvidia/nova-core/tree/zhi/nova-vgpu-20260930
[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/nova-gpu/[email protected]/
[3] https://lore.kernel.org/all/[email protected]/
Danilo Krummrich (5):
rust: pci: add VFIO override device IDs
rust: pci: expose the VF index within its PF
rust: pci: allow drivers to manage DMA ownership
rust: vfio: add PCI variant driver abstractions vfio/nvidia-vgpu: add the Rust VFIO variant driver
Zhi Wang (2):
rust: vfio: separate common device handling from PCI
gpu: nova-core: publish typed SR-IOV PF APIs
drivers/gpu/Makefile | 15 +- drivers/gpu/nova-core/driver.rs | 47 + drivers/gpu/nova-core/nova_core.rs | 2 + drivers/gpu/nova-core/vgpu/vgpu_api.rs | 82 +- drivers/vfio/pci/Kconfig | 2 + drivers/vfio/pci/Makefile | 2 + drivers/vfio/pci/nvidia-vgpu/Kconfig | 20 + drivers/vfio/pci/nvidia-vgpu/Makefile | 3 + drivers/vfio/pci/nvidia-vgpu/nvidia_vgpu.rs | 366 +++++++ rust/bindings/bindings_helper.h | 4 + rust/kernel/lib.rs | 2 + rust/kernel/pci.rs | 11 + rust/kernel/pci/iov.rs | 8 + rust/kernel/vfio.rs | 290 ++++++ rust/kernel/vfio/pci.rs | 1038 +++++++++++++++++++ 15 files changed, 1888 insertions(+), 4 deletions(-) create mode 100644 drivers/vfio/pci/nvidia-vgpu/Kconfig create mode 100644 drivers/vfio/pci/nvidia-vgpu/Makefile create mode 100644 drivers/vfio/pci/nvidia-vgpu/nvidia_vgpu.rs create mode 100644 rust/kernel/vfio.rs create mode 100644 rust/kernel/vfio/pci.rs
base-commit: 808dda6545c8775ea0abce0490308ddfeb5b8178
Signature · 1 line
2.53.0
PATCH 1/7 rust: pci: add VFIO override device IDs
From: Danilo Krummrich <[email protected]>
VFIO variant drivers need override-only IDs and VFIO-specific module aliases so userspace can select them for PCI assignment.
Add a DeviceId constructor equivalent to PCI_DRIVER_OVERRIDE_DEVICE_VFIO, matching a vendor and device without restricting the PCI class.
--- rust/kernel/pci.rs | 7 +++++++ 1 file changed, 7 insertions(+)
rust/kernel/pci.rs +7 -0
@@ -206,6 +206,13 @@ macro_rules! module_pci_driver {impl DeviceId {const PCI_ANY_ID :u32 = !0 ; +/// Equivalent to C's `PCI_DRIVER_OVERRIDE_DEVICE_VFIO` macro. +pub const fn from_id_vfio_override (vendor:Vendor , device:u32 ) ->Self { +let mut id =Self ::from_id (vendor, device); + id.0 .override_only = bindings::PCI_ID_F_VFIO_DRIVER_OVERRIDE ; + id + } +/// Equivalent to C's `PCI_DEVICE` macro. /// /// Create a new `pci::DeviceId` from a vendor and device ID.
Signature · 1 line
2.53.0
PATCH 2/7 rust: pci: expose the VF index within its PF
In reply to Zhi WangFrom: Danilo Krummrich <[email protected]>
Nova identifies a vGPU instance by the VF's index within its PF. Expose pci_iov_vf_id() through Device::vf_id() so a Rust VF driver can obtain that index, with an error for devices that are not VFs.
--- rust/kernel/pci/iov.rs | 8 ++++++++ 1 file changed, 8 insertions(+)
rust/kernel/pci/iov.rs +8 -0
@@ -40,6 +40,14 @@ pub fn is_virtfn(&self) -> bool {// SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`. unsafe { (*self.as_raw ()).is_virtfn () !=0 } } + +/// Return the zero-based VF index within its PF, or an error for a non-VF device. +pub fn vf_id (&self) ->Result <u32 > { +// SAFETY: `self.as_raw()` points to a live PCI device; the helper checks VF membership. +let id =unsafe { bindings::pci_iov_vf_id (self.as_raw ()) }; +to_result (id)?; +Ok (idas u32 ) + } }impl Device <device::Core <'_>> {
Signature · 1 line
2.53.0
Danilo Krummrich
maintainerRe: [PATCH 2/7] rust: pci: expose the VF index within its PF
rust/kernel/pci/iov.rs pub fn is_virtfn(&self) -> bool { · line 50
Quoted 12 lines · Zhi Wang
rust/kernel/pci/iov.rs +8
-0@@ -40,6 +40,14 @@ pub fn is_virtfn(&self) -> bool {// SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`. unsafe { (*self.as_raw ()).is_virtfn () !=0 } } + +/// Return the zero-based VF index within its PF, or an error for a non-VF device. +pub fn vf_id (&self) ->Result <u32 > { +// SAFETY: `self.as_raw()` points to a live PCI device; the helper checks VF membership. +let id =unsafe { bindings::pci_iov_vf_id (self.as_raw ()) }; +to_result (id)?; +Ok (idas u32 ) + }
In my PoC I had this implemented for Device<Bound>, but here it is implemented for just Device I think? It's not visible from the context, but IIRC you moved is_virtfn() in an impl Device block (which is correct).
So, if this is correct, please move it under Device<Bound>, as offset and stride are not guaranteed to not be concurrently modified otherwise.
PATCH 3/7 rust: pci: allow drivers to manage DMA ownership
In reply to Zhi WangFrom: Danilo Krummrich <[email protected]>
VFIO manages DMA ownership through the userspace IOMMU interface. Its PCI variant drivers must opt out of the driver's default DMA domain.
Add Driver::DRIVER_MANAGED_DMA and pass it to the PCI core. Existing Rust PCI drivers keep the default setting.
--- rust/kernel/pci.rs | 4 ++++ 1 file changed, 4 insertions(+)
rust/kernel/pci.rs +4 -0
@@ -108,6 +108,7 @@ unsafe fn register( (*pdrv.get ()).probe =Some (Self ::probe_callback); (*pdrv.get ()).remove =Some (Self ::remove_callback); (*pdrv.get ()).id_table =T ::ID_TABLE .as_ptr (); + (*pdrv.get ()).driver_managed_dma =T ::DRIVER_MANAGED_DMA ;#[cfg( CONFIG_PCI_IOV )] if T ::HAS_SRIOV_ENABLE { (*pdrv.get ()).sriov_configure =Some (Self ::sriov_configure_callback);
@@ -345,6 +346,9 @@ pub trait Driver {/// The table of device ids supported by the driver. const ID_TABLE :IdTable <Self ::IdInfo >; +/// Whether the driver manages DMA ownership instead of using the default DMA domain. +const DRIVER_MANAGED_DMA :bool =false ; +/// PCI driver probe. /// /// Called when a new pci device is added or discovered. Implementers should
Signature · 1 line
2.53.0
PATCH 4/7 rust: vfio: add PCI variant driver abstractions
In reply to Zhi WangFrom: Danilo Krummrich <[email protected]>
Add Rust abstractions for PCI drivers that extend vfio-pci-core with device-specific operations.
Provide Operations, Device and Registration with separate registration and first-open/last-close data. Allow open data to borrow registration data, undo PCI enablement if open fails, and drop open data before closing the core device.
Use a PCI adapter to register VFIO after publishing initialized driver data and unregister it before unbind and data destruction. Route runtime PM, AER, reset and VGA callbacks through the driver's typed registration.
Restrict core I/O helpers to their corresponding callback contexts. Require covariant registration data so callback borrows can safely shorten its erased binding lifetime. Add bounded user-buffer and region mapping helpers, and wire up VFIO core support for device features, matching, IOMMUFD and PASID. Allow passthrough devices to export physical BARs as DMA buffers.
--- rust/bindings/bindings_helper.h | 4 + rust/kernel/lib.rs | 2 + rust/kernel/vfio.rs | 100 +++ rust/kernel/vfio/pci.rs | 1109 +++++++++++++++++++++++++++++++ 4 files changed, 1215 insertions(+) create mode 100644 rust/kernel/vfio.rs create mode 100644 rust/kernel/vfio/pci.rs
rust/bindings/bindings_helper.h +4 -0
@@ -93,6 +93,8 @@#include <linux/task_work.h> #include <linux/tracepoint.h> #include <linux/usb.h> +#include <linux/vfio.h> +#include <linux/vfio_pci_core.h> #include <linux/wait.h> #include <linux/workqueue.h> #include <linux/xarray.h>
@@ -115,6 +117,8 @@const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN =ARCH_SLAB_MINALIGN ;const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN =ARCH_KMALLOC_MINALIGN ;const size_t RUST_CONST_HELPER_PAGE_SIZE =PAGE_SIZE ; +const unsigned int RUST_CONST_HELPER_VFIO_DEVICE_RESET =VFIO_DEVICE_RESET ; +const unsigned int RUST_CONST_HELPER_VFIO_DEVICE_PCI_HOT_RESET =VFIO_DEVICE_PCI_HOT_RESET ;const size_t RUST_CONST_HELPER_GENLMSG_DEFAULT_SIZE =GENLMSG_DEFAULT_SIZE ;const gfp_t RUST_CONST_HELPER_GFP_ATOMIC =GFP_ATOMIC ;const gfp_t RUST_CONST_HELPER_GFP_KERNEL =GFP_KERNEL ;
rust/kernel/lib.rs +2 -0
@@ -146,6 +146,8 @@pub mod uaccess;#[cfg( CONFIG_USB = "y" )] pub mod usb; +#[cfg( CONFIG_VFIO )] +pub mod vfio;pub mod workqueue;pub mod xarray;
rust/kernel/vfio.rs new file +100 -0
100 lines not on this page · raw message
rust/kernel/vfio/pci.rs new file +1109 -0
1,109 lines not on this page · raw message
Signature · 1 line
2.53.0
Re: [PATCH 4/7] rust: vfio: add PCI variant driver abstractions
rust/kernel/vfio/pci.rs line 1086
Quoted 4 lines · Zhi Wang
rust/kernel/vfio/pci.rs new file +4
-0+ bindings::vfio_pci_core_register_device ( + self.dev.core_device (), +Some (PciCallbacks ::<D >::vga_set_decode), + )
Hi Zhi,
When rebase this series onto v3 of my VFIO core drvdata patch, please modify this helper with only self.dev.core_device(), the vfio core now select the VGA callback internally.
See the V3 changes here:
https://lore.kernel.org/kvm/[email protected]/
rust/kernel/vfio/pci.rs line 914
Quoted 2 lines
rust/kernel/vfio/pci.rs new file +2
-0+// SAFETY: The registered device owns its error notification state. +unsafe { bindings::vfio_pci_core_aer_err_detected (reg.dev.core_device (), state) }
The AER helper now takes only a single argument which is core_device(), please drop the second argument here and in reset(done)
rust/kernel/vfio/pci.rs line 961
Quoted 5 lines
rust/kernel/vfio/pci.rs new file +5
-0+const PM_OPS : bindings::dev_pm_ops = bindings::dev_pm_ops { + runtime_suspend:Some (Self ::runtime_suspend), + runtime_resume:Some (Self ::runtime_resume), + ..pin_init::zeroed () + };
please also guard the PM functions with CONFIG_PM to match VFIO core helpers
Thanks,
Simon
PATCH 5/7 rust: vfio: separate common device handling from PCI
In reply to Zhi WangA VFIO PCI device contains a VFIO device. Expose that relationship in Rust so common device handling is independent of PCI resources.
Add vfio::Device and have vfio::pci::Device borrow its embedded device through AsRef, preserving the callback context. Share reference counting and callback-data lifetime handling in vfio. Keep driver operations, allocation, registration and the PCI enable/close sequence in vfio::pci.
No functional change is intended.
--- rust/kernel/vfio.rs | 200 +++++++++++++++++++++++++++++++++++++- rust/kernel/vfio/pci.rs | 207 +++++++++++++--------------------------- 2 files changed, 263 insertions(+), 144 deletions(-)
rust/kernel/vfio.rs +195 -5
@@ -1,14 +1,29 @@// SPDX-License-Identifier: GPL-2.0 -//! Virtual Function I/O (VFIO) abstractions. +//! Virtual Function I/O (VFIO) devices and callback data. +//! +//! Bus-specific device types borrow their embedded VFIO device through [`AsRef`]. +//! C header: [`include/linux/vfio.h`](srctree/include/linux/vfio.h) use crate ::{ bindings, prelude::*, + sync::aref::AlwaysRefCounted , + types::{ +CovariantForLt , +ForLt , +NotThreadSafe , +Opaque ,// + }, uaccess::{UserPtr ,UserSlice ,// - }, + },// +}; +use core::{ + cell::UnsafeCell , + marker::PhantomData , + ptr::NonNull ,// };#[cfg( CONFIG_VFIO_PCI_CORE )]
@@ -17,12 +32,187 @@/// Reset the VFIO device. pub const DEVICE_RESET :u32 = bindings::VFIO_DEVICE_RESET ; -/// Reset the PCI slot or bus containing a VFIO device. -pub const DEVICE_PCI_HOT_RESET :u32 = bindings::VFIO_DEVICE_PCI_HOT_RESET ; +/// The context in which a VFIO device reference is valid. +/// +/// Callback views can only be borrowed from the corresponding VFIO trampoline. +/// They cannot be refcounted or shared across threads. +pub trait DeviceContext : private::Sealed {} + +/// An ordinary device reference, without callback-specific operations. +pub struct Normal ; + +/// The device is undergoing its exclusive first-open callback. +pub struct Open ; + +/// The device is borrowed for a VFIO ioctl callback on the current thread. +pub struct Ioctl ; + +/// The device is borrowed for a VFIO read callback on the current thread. +pub struct Read ; + +/// The device is borrowed for a VFIO write callback on the current thread. +pub struct Write ; + +/// The device is borrowed for a VFIO mmap callback on the current thread. +pub struct Mmap ; + +/// The device is borrowed for a VFIO region-info callback on the current thread. +pub struct GetRegionInfo ; + +mod private { +pub trait Sealed {} + +impl Sealed for super ::Normal {} +impl Sealed for super ::Open {} +impl Sealed for super ::Write {} +impl Sealed for super ::Mmap {} +impl Sealed for super ::Ioctl {} +impl Sealed for super ::Read {} +impl Sealed for super ::GetRegionInfo {} +} + +impl DeviceContext for Normal {} +impl DeviceContext for Open {} +impl DeviceContext for Write {} +impl DeviceContext for Mmap {} +impl DeviceContext for Ioctl {} +impl DeviceContext for Read {} +impl DeviceContext for GetRegionInfo {} + +/// A VFIO device, independent of its bus-specific wrapper. +/// +/// # Invariants +/// +/// The underlying `vfio_device` is initialized and remains alive while borrowed. +/// Callback contexts are borrowed only for the corresponding callback and thread; +/// only [`Normal`] references can be reference-counted. +#[repr(transparent)] +pub struct Device <Ctx :DeviceContext =Normal > { + raw:Opaque <bindings::vfio_device >, + _context:PhantomData <(Ctx ,NotThreadSafe )>, +} + +impl <Ctx :DeviceContext >Device <Ctx > { +/// # Safety +/// +/// `raw` must point to an initialized `vfio_device` that remains alive for +/// `'a`. The caller must provide the guarantees of `Ctx` throughout the borrow. +#[cfg_attr(not( CONFIG_VFIO_PCI_CORE ), expect(dead_code))] +unsafe fn from_raw <'a>(raw: *mut bindings::vfio_device ) -> &'aSelf { +// SAFETY: Self is a transparent wrapper; the caller guarantees validity and context. +unsafe { &*raw.cast () } + } + +fn as_raw (&self) -> *mut bindings::vfio_device { + self.raw.get () + } +} + +// SAFETY: VFIO uses the embedded class device's refcount to own the full allocation. +unsafe impl AlwaysRefCounted for Device { +fn inc_ref (&self) { +// SAFETY: A shared reference keeps the initialized VFIO device alive. +unsafe { bindings::get_device (&raw mut (*self.as_raw ()).device) }; + } + +unsafe fn dec_ref (obj:NonNull <Self >) { +// SAFETY: The caller owns the VFIO reference being released. +unsafe { bindings::put_device (&raw mut (*obj.as_ref ().as_raw ()).device) }; + } +} + +// SAFETY: Ordinary VFIO references expose no unsynchronized callback operations. +unsafe impl Send for Device {} +// SAFETY: Shared access only changes the embedded class device's reference count. +unsafe impl Sync for Device {} + +/// Callback data stored after the bus-specific C device structure. +/// +/// # Invariants +/// +/// `reg_data` is dangling when unpublished; otherwise it borrows pinned data kept +/// alive until callback access has ended. `open_data` is null outside a successful +/// first open, otherwise owns a pinned allocation until the last close. +/// The bus-specific layer drains callbacks before releasing either allocation. +struct CallbackData <R :CovariantForLt ,O :ForLt > { + reg_data:UnsafeCell <NonNull <R ::Of <'static>>>, + open_data:UnsafeCell <*mut O ::Of <'static>>, +} + +#[cfg_attr(not( CONFIG_VFIO_PCI_CORE ), expect(dead_code))] +impl <R :CovariantForLt ,O :ForLt >CallbackData <R ,O > { +fn new () ->Self { +Self { + reg_data:UnsafeCell ::new (NonNull ::dangling ()), + open_data:UnsafeCell ::new (core::ptr::null_mut ()), + } + } + +/// Access registration data without allowing its erased lifetime to escape. +/// +/// # Safety +/// +/// Registration data must be published and remain valid through this call. +/// The caller must exclude concurrent changes to `reg_data`. +unsafe fn registration_data_with <V >(&self, f:impl for <'a>FnOnce (&'aR ::Of <'a>) ->V ) ->V { +// SAFETY: The caller keeps registration data alive and excludes mutation. +// Covariance permits shortening its erased lifetime to this borrow. +let reg_data =unsafe { (*self.reg_data.get ()).cast ::<R ::Of <'_>>().as_ref () }; +f (reg_data) + } + +/// # Safety +/// +/// The caller must be an I/O callback while VFIO keeps the device open. +/// Registration and open data must remain valid, with their pointer slots +/// unchanged throughout the call. +unsafe fn callback_data_with <V >( + &self, + f:impl for <'borrow, 'data>FnOnce (&'borrowR ::Of <'data>,Pin <&'borrowO ::Of <'data>>) ->V , + ) ->V { +// SAFETY: The caller excludes open/close and unregister while borrowing the data. +unsafe { + self.registration_data_with (|reg_data| { +let ptr = (*self.open_data.get ()).cast ::<O ::Of <'_>>(); +// The data lifetime stays independent of the callback borrow, +// so borrowed OpenData fields cannot be stored back into OpenData. +f (reg_data,Pin ::new_unchecked (&*ptr)) + }) + } + } + +/// # Safety +/// +/// Call only during exclusive first open, with no existing open data. +/// All resources borrowed for `'a` must remain valid until `close()` returns. +unsafe fn open <'a,I :PinInit <O ::Of <'a>,Error >>(&self, init:impl FnOnce () ->I ) ->Result { +// Allocate before calling the driver so allocation failure cannot follow open. +let data =KBox ::<O ::Of <'a>>::new_uninit (GFP_KERNEL )?; +let data = data.write_pin_init (init ())?; + +// SAFETY: Only the owning pointer moves; close drops the allocation in place. +let raw =KBox ::into_raw (unsafe {Pin ::into_inner_unchecked (data) }); +// SAFETY: First open is exclusive; the caller keeps borrowed resources alive +// through close. Lifetimes do not affect the allocation's layout. +unsafe { *self.open_data.get () = raw.cast ::<O ::Of <'static>>() }; +Ok (()) + } + +/// # Safety +/// +/// Call once after a successful open, with all callbacks drained and the next +/// open excluded. Registration data and borrowed resources must remain alive. +unsafe fn close (&self) { +// SAFETY: Last close exclusively takes the allocation published by open. +let raw =unsafe { core::mem::replace (&mut *self.open_data.get (), core::ptr::null_mut ()) }; +// SAFETY: The allocation came from KBox::into_raw and is destroyed without moving it. +unsafe {drop (KBox ::from_raw (raw)) }; + } +}/// Capability buffer supplied by VFIO for a region-info callback. -#[cfg( CONFIG_VFIO_PCI_CORE )] pub struct InfoCap <'a> { +#[cfg_attr(not( CONFIG_VFIO_PCI_CORE ), expect(dead_code))] raw: &'amut bindings::vfio_info_cap , }
rust/kernel/vfio/pci.rs +68 -139
357 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH 6/7 gpu: nova-core: publish typed SR-IOV PF APIs
In reply to Zhi WangRegister NovaCoreVfApi as typed SR-IOV PF data once the GPU is initialized.
Enable and disable VFs through sriov_configure(). The Rust PCI adapter removes VFs before dropping PF driver data. Declare the registration before the GPU so its borrowed services remain valid through VF teardown.
Expose a VF-side handle that borrows these services for each operation. Return an owned instance on open and close it on drop while the VF remains bound.
--- drivers/gpu/nova-core/driver.rs | 47 +++++++++++++++ drivers/gpu/nova-core/nova_core.rs | 2 + drivers/gpu/nova-core/vgpu/vgpu_api.rs | 82 +++++++++++++++++++++++++- 3 files changed, 130 insertions(+), 1 deletion(-)
drivers/gpu/nova-core/driver.rs +47 -0
@@ -35,6 +35,12 @@ }, // }; +#[cfg( CONFIG_PCI_IOV )] +use kernel::types::ForLt ; + +#[cfg( CONFIG_PCI_IOV )] +use crate ::vgpu::vgpu_api::NovaCoreVfApi ; +/// Counter for generating unique auxiliary device IDs. static AUXILIARY_ID_COUNTER :Atomic <u32 > =Atomic ::new (0 );
@@ -42,6 +48,10 @@pub (crate )struct NovaCore <'bound> {/// Firmware-control registration. _fwctl: fwctl::Registration <'bound,NovaCoreFwCtl >, +#[cfg( CONFIG_PCI_IOV )] +#[allow(clippy::type_complexity)] +#[pin] + _vf_registration: pci::VfRegistration <'bound,ForLt! (NovaCoreVfApi <'_>)>,#[pin] pub (crate ) gpu:Gpu <'bound>, bar: pci::Bar <'bound,BAR0_SIZE >,
@@ -114,6 +124,11 @@ fn probe<'bound>( pin_init::pin_init_scope (move || {dev_dbg! (pdev,"Probe Nova Core GPU driver.\n" ); +#[cfg( CONFIG_PCI_IOV )] +if pdev.is_virtfn () { +return Err (ENODEV ); + } + pdev.enable_device_mem ()?; pdev.set_master ();
@@ -144,6 +159,18 @@ fn probe<'bound>(// Run optional GPU selftests. #[cfg( CONFIG_NOVA_CORE_SELFTESTS )] _: { gpu.run_selftests(pdev) }, +#[cfg( CONFIG_PCI_IOV )] + _vf_registration <- { +// SAFETY: `gpu` is initialized at its pinned address and +// outlives the registration and its borrowed API data. +let gpu =unsafe { &(*this.as_ptr ()).gpu }; +let api =NovaCoreVfApi ::new (gpu, pdev); +// SAFETY: Probe has exclusive access to the registration +// slot and no VFs are enabled before successful probe. The +// registration keeps PF services available until VF removal +// completes and is dropped before its borrowed GPU resources. +unsafe { pci::VfRegistration ::new (pdev,Ok (api)) } + }, _reg: auxiliary::Registration::new( pdev.as_ref(), c"nova-drm",
@@ -176,4 +203,24 @@ fn probe<'bound>( })) }) } + +#[cfg( CONFIG_PCI_IOV )] +fn sriov_enable <'bound, 'callback>( + _dev: &'bound pci::Device <Core <'_>>, + _this:Pin <&Self ::Data <'bound>>, + token: pci::SriovEnable <'callback>, + ) ->Result <pci::SriovEnabled <'callback>> { +let num_vfs = token.num_vfs (); + token.enable (num_vfs) + } + +#[cfg( CONFIG_PCI_IOV )] +fn sriov_disable <'bound>( + _dev: &'bound pci::Device <Core <'_>>, + _this:Pin <&Self ::Data <'bound>>, + token: pci::SriovDisable <'_>, + ) ->Result { + token.disable (); +Ok (()) + } }
drivers/gpu/nova-core/nova_core.rs +2 -0
@@ -33,6 +33,8 @@#[cfg( CONFIG_PCI_IOV )] pub use vgpu::vgpu_api::{NovaCoreVfApi , +NovaCoreVfApiHandle , +VgpuInstance ,VgpuTypeInfo ,// };
drivers/gpu/nova-core/vgpu/vgpu_api.rs +81 -1
103 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH 7/7 vfio/nvidia-vgpu: add the Rust VFIO variant driver
In reply to Zhi WangFrom: Danilo Krummrich <[email protected]>
NVIDIA vGPU VFs need nova-core to manage their instances when assigned to userspace through VFIO.
Add a Rust VFIO PCI variant driver that uses the typed SR-IOV PF APIs to create an instance on first open and close it on last close. Expose the assigned device and subsystem IDs, enforce the assigned BAR1 aperture, and coordinate firmware reset with PCI reset.
Match NVIDIA devices through a VFIO override-only entry. Devices without an available Nova vGPU interface use ordinary PCI passthrough.
Use the VFIO PCI adapter to keep callback data available throughout registration and removal. Extend the GPU build rules to make nova-core crate metadata available to the VFIO driver and export the Rust symbols it references.
--- drivers/gpu/Makefile | 15 +- drivers/vfio/pci/Kconfig | 2 + drivers/vfio/pci/Makefile | 2 + drivers/vfio/pci/nvidia-vgpu/Kconfig | 20 ++ drivers/vfio/pci/nvidia-vgpu/Makefile | 3 + drivers/vfio/pci/nvidia-vgpu/nvidia_vgpu.rs | 366 ++++++++++++++++++++ 6 files changed, 405 insertions(+), 3 deletions(-) create mode 100644 drivers/vfio/pci/nvidia-vgpu/Kconfig create mode 100644 drivers/vfio/pci/nvidia-vgpu/Makefile create mode 100644 drivers/vfio/pci/nvidia-vgpu/nvidia_vgpu.rs
drivers/gpu/Makefile +12 -3
@@ -19,8 +19,9 @@ nova-core-y := nova-core/nova_core.o nova-core/nova_core_exports.o obj-$(CONFIG_DRM_NOVA) += nova-drm.o nova-drm-y := drm/nova/nova.o -# Export Rust symbols from nova-core only if nova-drm actually references them. -nova-core-export-deps := $(if $(CONFIG_DRM_NOVA),$(obj)/drm/nova/nova.o) +# Export Rust symbols from nova-core only if dependent modules reference them. +nova-core-export-deps := $(if $(CONFIG_DRM_NOVA),$(obj)/drm/nova/nova.o) \ + $(if $(CONFIG_NVIDIA_VGPU_VFIO_PCI),$(obj)/../vfio/pci/nvidia-vgpu/nvidia_vgpu.o) rust_needed_exports = \ { $(if $(strip $(2)),$(NM) -u $(2);,) echo "__DEFINED_RUST_SYMBOLS__"; \
@@ -57,10 +58,18 @@ $(obj)/nova-core/nova_core_exports.o: private cmd_gensymtypes_c = \ $(obj)/nova-core/nova_core.o endif -# Output nova-core's crate metadata for use by nova-drm at compile time. +# Output nova-core's crate metadata for its Rust consumers at compile time. RUSTFLAGS_nova-core/nova_core.o += \ --emit=metadata=$(objtree)/$(obj)/nova-core/libnova_core.rmeta # Allow nova-drm to import nova-core's types. $(obj)/drm/nova/nova.o: $(obj)/nova-core/nova_core.o RUSTFLAGS_drm/nova/nova.o := -L $(objtree)/$(obj)/nova-core --extern nova_core + +# Build the NVIDIA VFIO driver here so metadata and generated exports share +# the same dependency graph as nova-drm. +obj-$(CONFIG_NVIDIA_VGPU_VFIO_PCI) += nvidia-vgpu-vfio-pci.o +nvidia-vgpu-vfio-pci-y := ../vfio/pci/nvidia-vgpu/nvidia_vgpu.o + +$(obj)/../vfio/pci/nvidia-vgpu/nvidia_vgpu.o: $(obj)/nova-core/nova_core.o +RUSTFLAGS_../vfio/pci/nvidia-vgpu/nvidia_vgpu.o := -L $(objtree)/$(obj)/nova-core --extern nova_core
drivers/vfio/pci/Kconfig +2 -0
@@ -74,4 +74,6 @@ source "drivers/vfio/pci/qat/Kconfig" source "drivers/vfio/pci/xe/Kconfig" +source "drivers/vfio/pci/nvidia-vgpu/Kconfig" + endmenu
drivers/vfio/pci/Makefile +2 -0
@@ -24,3 +24,5 @@ obj-$(CONFIG_NVGRACE_GPU_VFIO_PCI) += nvgrace-gpu/ obj-$(CONFIG_QAT_VFIO_PCI) += qat/ obj-$(CONFIG_XE_VFIO_PCI) += xe/ + +# nvidia-vgpu is built from drivers/gpu/Makefile for nova-core crate linkage.
drivers/vfio/pci/nvidia-vgpu/Kconfig new file +20 -0
@@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NVIDIA_VGPU_VFIO_PCI + tristate "VFIO support for the NVIDIA vGPU" + depends on NOVA_CORE && PCI_IOV && RUST + select VFIO_PCI_CORE + help + This option enables VFIO (Virtual Function I/O) support for + NVIDIA virtual GPUs (vGPU). It allows the assignment of a virtual + GPU instance to userspace applications via VFIO, typically used + with hypervisors such as KVM and device emulators like QEMU. + + Devices without an available Nova vGPU interface use ordinary PCI + passthrough. Both paths use the VFIO PCI core defaults; vfio-pci + module options do not apply. + + The NVIDIA vGPU allows a physical GPU to be partitioned into + multiple virtual GPUs, each of which can be passed to a virtual + machine as a PCI device using the standard VFIO infrastructure. + + If you don't know what to do here, say N.
drivers/vfio/pci/nvidia-vgpu/Makefile new file +3 -0
@@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +# nvidia-vgpu is built from drivers/gpu/Makefile for nova-core crate linkage. +# nvidia_vgpu.o (rust-analyzer marker - DO NOT REMOVE).
drivers/vfio/pci/nvidia-vgpu/nvidia_vgpu.rs new file +366 -0
366 lines not on this page · raw message
Signature · 1 line
2.53.0