[PATCH v5 7/9] rust: pci: add SR-IOV enable and disable tokens

From: Simon Song

Date: Fri Oct 30 2026 - 13:20:11 EST


From: Zhi Wang <zhiw@xxxxxxxxxx>

Enabling VFs can succeed before the driver's remaining setup fails.
Use a callback-scoped token to return an enabled guard that disables
SR-IOV when dropped, rolling back those VFs on a later error.

Add SriovEnable, SriovEnabled and SriovDisable for the split callbacks
introduced next. Tie each token to its PF and callback lifetime, and
limit the enabled count to the user's request. The PCI adapter disarms
the guard when accepting a successful enable callback.

Require a VfRegistration to own final VF teardown after that handoff.
Drivers that do not share data with VFs can register (). A disable
token permits explicit shutdown without forcing it on a rejected
request.

Suggested-by: Danilo Krummrich <dakr@xxxxxxxxxx>
Signed-off-by: Zhi Wang <zhiw@xxxxxxxxxx>
Signed-off-by: Simon Song <xinmengs@xxxxxxxxxx>
---
rust/kernel/pci.rs | 7 +++-
rust/kernel/pci/iov.rs | 94 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 97 insertions(+), 4 deletions(-)

diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 83d62f95b7e..3a768477014 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -61,7 +61,12 @@
Normal, //
};
#[cfg(CONFIG_PCI_IOV)]
-pub use self::iov::VfRegistration;
+pub use self::iov::{
+ SriovDisable,
+ SriovEnable,
+ SriovEnabled,
+ VfRegistration, //
+};
pub use self::irq::{
IrqType,
IrqTypes,
diff --git a/rust/kernel/pci/iov.rs b/rust/kernel/pci/iov.rs
index 83fa1ecfa50..197b0c2bb0f 100644
--- a/rust/kernel/pci/iov.rs
+++ b/rust/kernel/pci/iov.rs
@@ -16,7 +16,8 @@
};
use core::{
any::TypeId,
- marker::PhantomPinned, //
+ marker::PhantomPinned,
+ mem::ManuallyDrop, //
};

impl Device<device::Core<'_>> {
@@ -34,7 +35,6 @@ impl Device<device::CoreInternal<'_>> {
/// Enable the Single Root I/O Virtualization (SR-IOV) capability for this device,
/// where `nr_virtfn` is number of Virtual Functions (VF) to enable.
#[inline]
- #[expect(dead_code)]
pub(crate) fn enable_sriov(&self, nr_virtfn: u16) -> Result {
// SAFETY:
// - `self.as_raw` returns a valid pointer to a `struct pci_dev`.
@@ -51,7 +51,6 @@ pub(crate) fn enable_sriov(&self, nr_virtfn: u16) -> Result {

/// Disable the Single Root I/O Virtualization (SR-IOV) capability for this device.
#[inline]
- #[expect(dead_code)]
pub(crate) fn disable_sriov(&self) {
// SAFETY:
// - `self.as_raw` returns a valid pointer to a `struct pci_dev`.
@@ -66,6 +65,95 @@ pub(crate) fn disable_sriov(&self) {
}
}

+/// Permission to enable VFs during a driver's `sriov_enable()` callback.
+///
+/// The PCI adapter creates this token for the PF being configured. It cannot be cloned or sent
+/// to another thread, and its lifetime is restricted to the callback. Enabling VFs consumes it.
+///
+/// The callback lifetime cannot be extended:
+///
+/// ```ignore,compile_fail
+/// use kernel::pci::SriovEnable;
+///
+/// fn escape(token: SriovEnable<'_>) -> SriovEnable<'static> {
+/// token
+/// }
+/// ```
+pub struct SriovEnable<'a> {
+ pdev: &'a Device<device::CoreInternal<'a>>,
+ num_vfs: u16,
+}
+
+impl<'a> SriovEnable<'a> {
+ /// Returns the number of VFs requested for this callback.
+ #[inline]
+ pub fn num_vfs(&self) -> u16 {
+ self.num_vfs
+ }
+
+ /// Enables between one and the requested number of VFs.
+ ///
+ /// VF drivers can probe before this method returns, so the PF resources they access must
+ /// already be initialized. The returned guard disables the VFs if subsequent setup fails.
+ /// Return it from `sriov_enable()` to leave the VFs enabled on callback success.
+ pub fn enable(self, num_vfs: u16) -> Result<SriovEnabled<'a>> {
+ if num_vfs == 0 || num_vfs > self.num_vfs {
+ return Err(EINVAL);
+ }
+
+ // SAFETY: The PF's driver data and registration remain installed throughout this callback.
+ // The registration owns final VF teardown after the enable guard is disarmed.
+ if unsafe { (*self.pdev.as_raw()).vf_registration_data_rust }.is_null() {
+ return Err(ENODEV);
+ }
+
+ self.pdev.enable_sriov(num_vfs)?;
+ Ok(SriovEnabled {
+ pdev: self.pdev,
+ num_vfs,
+ })
+ }
+}
+
+/// Enabled VFs awaiting successful completion of `sriov_enable()`.
+pub struct SriovEnabled<'a> {
+ pdev: &'a Device<device::CoreInternal<'a>>,
+ num_vfs: u16,
+}
+
+impl SriovEnabled<'_> {
+ #[inline]
+ #[expect(dead_code)]
+ fn disarm(self) -> u16 {
+ ManuallyDrop::new(self).num_vfs
+ }
+}
+
+impl Drop for SriovEnabled<'_> {
+ fn drop(&mut self) {
+ self.pdev.disable_sriov();
+ }
+}
+
+/// Permission to disable VFs during a driver's `sriov_disable()` callback.
+///
+/// The PCI adapter creates this token for the PF being configured. It cannot be cloned or sent
+/// to another thread, and its lifetime is restricted to the callback. Dropping the token does
+/// not disable VFs, allowing the callback to reject a disable request.
+pub struct SriovDisable<'a> {
+ pdev: &'a Device<device::CoreInternal<'a>>,
+}
+
+impl SriovDisable<'_> {
+ /// Disables all VFs and waits for their drivers to unbind.
+ ///
+ /// The PF resources used by VF drivers must remain available until this method returns.
+ #[inline]
+ pub fn disable(self) {
+ self.pdev.disable_sriov();
+ }
+}
+
/// Wrapper for VF registration data stored inside a [`VfRegistration`].
///
/// Stores a [`TypeId`] header (derived from `F`) followed by the pinned data,
--
2.43.7