Register 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. Signed-off-by: Zhi Wang --- 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(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index dc6febeee656..99beab189879 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -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 = 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>, + _this: Pin<&Self::Data<'bound>>, + token: pci::SriovEnable<'callback>, + ) -> Result> { + let num_vfs = token.num_vfs(); + token.enable(num_vfs) + } + + #[cfg(CONFIG_PCI_IOV)] + fn sriov_disable<'bound>( + _dev: &'bound pci::Device>, + _this: Pin<&Self::Data<'bound>>, + token: pci::SriovDisable<'_>, + ) -> Result { + token.disable(); + Ok(()) + } } diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 619f32c79415..ee511bdf3544 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -33,6 +33,8 @@ #[cfg(CONFIG_PCI_IOV)] pub use vgpu::vgpu_api::{ NovaCoreVfApi, + NovaCoreVfApiHandle, + VgpuInstance, VgpuTypeInfo, // }; diff --git a/drivers/gpu/nova-core/vgpu/vgpu_api.rs b/drivers/gpu/nova-core/vgpu/vgpu_api.rs index f9f3e527bdc0..a440108d2c26 100644 --- a/drivers/gpu/nova-core/vgpu/vgpu_api.rs +++ b/drivers/gpu/nova-core/vgpu/vgpu_api.rs @@ -11,6 +11,9 @@ prelude::*, // }; +#[cfg(CONFIG_PCI_IOV)] +use kernel::types::ForLt; + use crate::{ driver::Bar0, gpu::Gpu, @@ -61,7 +64,6 @@ pub struct NovaCoreVfApi<'gpu> { } impl<'gpu> NovaCoreVfApi<'gpu> { - #[expect(dead_code)] pub(crate) fn new(gpu: &'gpu Gpu<'gpu>, pdev: &'gpu pci::Device) -> Self { Self { pdev, @@ -84,6 +86,20 @@ fn gfid(&self, gfid: u32) -> Result { } impl NovaCoreVfApi<'_> { + /// Obtains the enabled PF services for a bound VF. + #[cfg(CONFIG_PCI_IOV)] + pub fn handle(vf: &pci::Device) -> Result> { + let handle = NovaCoreVfApiHandle { vf }; + handle.with(|api| { + if api.is_available() { + Ok(()) + } else { + Err(ENODEV) + } + })?; + Ok(handle) + } + /// Creates and boots an instance for a one-based VF ID. /// /// `sbdf` encodes the VF address as `(segment << 16) | (bus << 8) | devfn`. @@ -156,3 +172,67 @@ pub fn reset_instance(&self, gfid: u32) -> Result { Ok(()) } } + +/// Access to PF services for the lifetime of a VF driver binding. +/// +/// The PF's registration keeps these services available until VF removal +/// completes and is dropped before the GPU resources they borrow. +#[cfg(CONFIG_PCI_IOV)] +pub struct NovaCoreVfApiHandle<'vf> { + vf: &'vf pci::Device, +} + +#[cfg(CONFIG_PCI_IOV)] +impl<'vf> NovaCoreVfApiHandle<'vf> { + /// Borrows the typed PF services for a single operation. + fn with( + &self, + f: impl for<'borrow, 'data> FnOnce(Pin<&'borrow NovaCoreVfApi<'data>>) -> Result, + ) -> Result { + self.vf + .vf_registration_data_with::), _>(f)? + } + + /// Creates and boots an instance that closes when dropped. + /// + /// The arguments have the same meaning as in [`NovaCoreVfApi::open_instance`]. + pub fn open(&self, gfid: u32, dbdf: u32, vm_pid: u32) -> Result> { + let type_info = self.with(|api| api.open_instance(gfid, dbdf, vm_pid))?; + Ok(VgpuInstance { + api: Self { vf: self.vf }, + gfid, + type_info, + }) + } + + /// Resets an active instance and scrubs its guest VRAM. + pub fn reset(&self, gfid: u32) -> Result { + self.with(|api| api.reset_instance(gfid)) + } +} + +/// An active instance whose teardown runs while its VF driver remains bound. +/// +/// Dropping this guard may sleep while firmware teardown completes. +#[cfg(CONFIG_PCI_IOV)] +pub struct VgpuInstance<'vf> { + api: NovaCoreVfApiHandle<'vf>, + gfid: u32, + type_info: VgpuTypeInfo, +} + +#[cfg(CONFIG_PCI_IOV)] +impl VgpuInstance<'_> { + /// Returns the assigned PCI IDs and BAR1 aperture size. + #[inline] + pub fn type_info(&self) -> &VgpuTypeInfo { + &self.type_info + } +} + +#[cfg(CONFIG_PCI_IOV)] +impl Drop for VgpuInstance<'_> { + fn drop(&mut self) { + let _ = self.api.with(|api| api.close_instance(self.gfid)); + } +} -- 2.53.0