Skip to contentSkip to conversation

Add Rust PCI SR-IOV support

PATCH Superseded Zhi Wang 3 participants 31 messages Sep 30 – Oct 4

Subsystems (9)
DRM drivers PCI Driver core Rust +5 more subsystems

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Zhi Wang

author
#0 lore raw

v3 cover Add Rust PCI SR-IOV support

Rust PCI drivers need to configure SR-IOV and let VF drivers borrow selected PF-owned data. This series provides those operations and a registration that keeps the shared data available until VF removal completes.

This builds on Peter Colberg's Rust PCI SR-IOV series [1] and Danilo Krummrich's typed PF registration design [2]. The PF/VF sample demonstrates the interface using QEMU's 82576 emulation.

Following Danilo's review, drivers now implement separate sriov_enable() and sriov_disable() callbacks. Each receives pinned driver data and a token for the requested operation. Enabling VFs returns a guard that disables them if later setup fails. Returning the guard successfully leaves the VFs enabled, with VfRegistration owning their final teardown. Drivers that do not share data with VFs can register ().

The series is based on driver-core-testing.

Changes since v2 [3]:
- Keep enable_sriov() and disable_sriov() internal to Device<CoreInternal>. Add the callback tokens and rollback guard in a separate patch.
- Replace sriov_configure() in the Rust Driver trait with paired enable and disable callbacks.
- Remove explicit SR-IOV cleanup from the PCI remove adapter and leave it to VfRegistration.
- Move the SR-IOV implementation into CONFIG_PCI_IOV-gated pci/iov.rs.
- Remove the registration's published flag and raw-pointer accessors;
- Restrict num_vf() to the PCI core callback context to serialize it with VF configuration.
- Remove the separate PF device accessor and match the auxiliary bus's model.
- Update the sample for the split callbacks and Peter's email address throughout the series.

[1] https://lore.kernel.org/rust-for-linux/[email protected]/
[2] https://lore.kernel.org/nova-gpu/[email protected]/
[3] https://lore.kernel.org/rust-for-linux/[email protected]/

John Hubbard (1):
rust: pci: add is_virtfn(), to check for VFs

Peter Colberg (6):
rust: pci: add internal SR-IOV enable and disable helpers
rust: pci: add vtable attribute to pci::Driver trait
rust: pci: add is_physfn(), to check for PFs
rust: pci: add num_vf(), to return number of VFs
rust: pci: add SR-IOV enable and disable callbacks
samples: rust: add Rust SR-IOV VF driver sample

Zhi Wang (3):
rust: pci: drop driver data before remove returns
rust: pci: add typed SR-IOV PF registration data
rust: pci: add SR-IOV enable and disable tokens

 MAINTAINERS                           |   1 +
 drivers/gpu/nova-core/driver.rs       |   1 +
 include/linux/pci.h                   |   7 +
 rust/kernel/device.rs                 |   4 +-
 rust/kernel/pci.rs                    | 103 +++++++
 rust/kernel/pci/iov.rs                | 413 ++++++++++++++++++++++++++
 samples/rust/Kconfig                  |  11 +
 samples/rust/Makefile                 |   1 +
 samples/rust/rust_dma.rs              |   1 +
 samples/rust/rust_driver_auxiliary.rs |   1 +
 samples/rust/rust_driver_pci.rs       |   1 +
 samples/rust/rust_driver_sriov.rs     | 249 ++++++++++++++++
 12 files changed, 791 insertions(+), 2 deletions(-)
 create mode 100644 rust/kernel/pci/iov.rs
 create mode 100644 samples/rust/rust_driver_sriov.rs

base-commit: c15c5befd6d6150e92ab7c6c7fde1088e0f543e6

Signature · 1 line
2.53.0
author
#1 lore raw

PATCH v3 1/10 rust: pci: add internal SR-IOV enable and disable helpers

From: Peter Colberg <[email protected]>

Wrap pci_enable_sriov() and pci_disable_sriov() on Device<CoreInternal> for use by the SR-IOV callback tokens and their rollback guard. Keep these operations internal so drivers can only enable or disable VFs through the callback-scoped API introduced later in this series.

---
 rust/kernel/pci.rs     |  2 ++
 rust/kernel/pci/iov.rs | 46 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)
 create mode 100644 rust/kernel/pci/iov.rs
rust/kernel/pci.rs +2 -0
index 3ec897709e89..b11a1d32c5db 100644
@@ -34,6 +34,8 @@
 
 mod id;
 mod io;
+#[cfg(CONFIG_PCI_IOV)]
+mod iov;
 mod irq;
 
 pub use self::id::{
rust/kernel/pci/iov.rs new file +46 -0

46 lines not on this page · raw message

Signature · 1 line
2.53.0
#2 lore raw

Re: [PATCH v3 01/10] rust: pci: add internal SR-IOV enable and disable helpers

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] rust: conditional compilation gap for SR-IOV API forcing downstream #cfg usage - [Low] rust…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] rust: conditional compilation gap for SR-IOV API forcing downstream #cfg usage
- [Low] rust: missing #[inline] on abstraction functions forwarding to C bindings --

commit 3f2a7b7fc60a2b23673b53641eba3ae3ba445ce8
Author: Peter Colberg <[email protected]>

rust: pci: add internal SR-IOV enable and disable helpers

Wrap pci_enable_sriov() and pci_disable_sriov() on Device<CoreInternal> for use by the SR-IOV callback tokens and their rollback guard. Keep these operations internal so drivers can only enable or disable VFs through the callback-scoped API introduced later in this series.

rust/kernel/pci.rs line 38

Quoted 10 lines
rust/kernel/pci.rs +2 -0
index 3ec897709e890..b11a1d32c5db5 100644
@@ -34,6 +34,8 @@
 
 mod id;
 mod io;
+#[cfg(CONFIG_PCI_IOV)]
+mod iov;

[Severity: Medium]
Does hiding the iov module unconditionally when CONFIG_PCI_IOV is disabled break downstream driver compilation?

By gating the iov module behind #[cfg(CONFIG_PCI_IOV)], this cascades into missing types (SriovEnable) and trait methods (Driver::sriov_enable). This forces Rust driver developers to either pollute their code with #[cfg(CONFIG_PCI_IOV)] on trait methods or add depends on PCI_IOV to their Kconfig to avoid compilation failures.

In the C PCI subsystem, .sriov_configure and pci_enable_sriov() are unconditionally available and fall back to static inlines returning -ENODEV when disabled, allowing drivers to compile seamlessly without #ifdefs. Could we provide !CONFIG_PCI_IOV helpers in rust/helpers/pci.c to maintain C parity and avoid breaking valid Kconfig combinations?

Quoted 9 lines
rust/kernel/pci.rs +0 -0
 mod irq;
 
 pub use self::id::{
rust/kernel/pci/iov.rs new file +0 -0
new file mode 100644
index 0000000000000..c37ce51346731
@@ -0,0 +1,46 @@

[ ... ]

rust/kernel/pci/iov.rs line 18

Quoted 5 lines
rust/kernel/pci/iov.rs new file +5 -0
+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.
+    #[expect(dead_code)]
+    pub(crate) fn enable_sriov(&self, nr_virtfn: c_int) -> Result {

[Severity: Low]
This isn't a bug, but should this function have an #[inline] annotation?

The rust.md guidelines recommend that small functions or functions forwarding to a C binding call in abstractions (like the kernel crate) be annotated with #[inline]. enable_sriov() directly forwards to bindings::pci_enable_sriov.

Quoted 1 line

+ // SAFETY:

[ ... ]

rust/kernel/pci/iov.rs line 34

Quoted 6 lines
rust/kernel/pci/iov.rs new file +6 -0
+        to_result(unsafe { bindings::pci_enable_sriov(self.as_raw(), nr_virtfn) })
+    }
+
+    /// Disable the Single Root I/O Virtualization (SR-IOV) capability for this device.
+    #[expect(dead_code)]
+    pub(crate) fn disable_sriov(&self) {

[Severity: Low]
This isn't a bug, but should this function also have an #[inline] annotation?

Similar to enable_sriov(), disable_sriov() directly forwards to bindings::pci_disable_sriov and lacks the #[inline] annotation recommended for abstraction methods.

Quoted 1 line

+ // SAFETY:

[ ... ]

Quoted 3 lines
rust/kernel/pci/iov.rs new file +3 -0
+        unsafe { bindings::pci_disable_sriov(self.as_raw()) };
+    }
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
maintainer
#3 lore raw

Re: [PATCH v3 01/10] rust: pci: add internal SR-IOV enable and disable helpers

In reply to Zhi Wang

rust/kernel/pci/iov.rs line 18

Quoted 5 lines · Zhi Wang
rust/kernel/pci/iov.rs new file +5 -0
+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.
+    #[expect(dead_code)]
+    pub(crate) fn enable_sriov(&self, nr_virtfn: c_int) -> Result {

This can just be a u16; there are no implicit type conversions in Rust and the PCIe SR-IOV spec defines this as 16bit register.

The conversion should be infallible, so you can just pass nr_virtfn.into().

rust/kernel/pci/iov.rs line 28

Quoted 10 lines
rust/kernel/pci/iov.rs new file +10 -0
+        // SAFETY:
+        // `self.as_raw` returns a valid pointer to a `struct pci_dev`.
+        //
+        // `pci_enable_sriov()` checks that the enable operation is valid:
+        // - the device is a Physical Function (PF),
+        // - SR-IOV is currently disabled, and
+        // - `nr_virtfn` does not exceed the total number of supported VFs.
+        //
+        // The CoreInternal device context inherits from the Bound device context,
+        // which guarantees that the PF device is bound to a driver.
	// - `self.as_raw` returns a valid pointer to a `struct pci_dev`.
	//
	// - `pci_enable_sriov()` checks that the enable operation is valid:
	//   - the device is a Physical Function (PF),
	//   - SR-IOV is currently disabled, and
	//   - `nr_virtfn` does not exceed the total number of supported VFs.
	//
	// - The [`CoreInternal`] device context inherits from the [`Bound`] device context,
	//   which guarantees that the PF device is bound to a driver.
Quoted 2 lines
(quoted diff) +2 -0
+        to_result(unsafe { bindings::pci_enable_sriov(self.as_raw(), nr_virtfn) })
+    }
author
#4 lore raw

PATCH v3 2/10 rust: pci: add vtable attribute to pci::Driver trait

In reply to Zhi Wang

From: Peter Colberg <[email protected]>

Add the #[vtable] attribute to pci::Driver and its implementations to support the optional sriov_enable() and sriov_disable() callbacks introduced later in this series.

---
 drivers/gpu/nova-core/driver.rs       | 1 +
 rust/kernel/pci.rs                    | 2 ++
 samples/rust/rust_dma.rs              | 1 +
 samples/rust/rust_driver_auxiliary.rs | 1 +
 samples/rust/rust_driver_pci.rs       | 1 +
 5 files changed, 6 insertions(+)
drivers/gpu/nova-core/driver.rs +1 -0
index cf3534dd47d4..47a599c9ea4f 100644
@@ -63,6 +63,7 @@ pub(crate) struct NovaCore<'bound> {
     ]
 );
 
+#[vtable]
 impl pci::Driver for NovaCoreDriver {
     type IdInfo = ();
     type Data<'bound> = NovaCore<'bound>;
rust/kernel/pci.rs +2 -0
index b11a1d32c5db..dcb0cdd0d426 100644
@@ -274,6 +274,7 @@ macro_rules! pci_device_table {
 ///     ]
 /// );
 ///
+/// #[vtable]
 /// impl pci::Driver for MyDriver {
 ///     type IdInfo = ();
 ///     type Data<'bound> = Self;
@@ -289,6 +290,7 @@ macro_rules! pci_device_table {
 ///```
 /// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the
 /// `Adapter` documentation for an example.
+#[vtable]
 pub trait Driver {
     /// The type holding information about each device id supported by the driver.
     // TODO: Use `associated_type_defaults` once stabilized:
samples/rust/rust_dma.rs +1 -0
index ffb693544673..804d55a63005 100644
@@ -69,6 +69,7 @@ unsafe impl kernel::transmute::FromBytes for MyStruct {}
     [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
 );
 
+#[vtable]
 impl pci::Driver for DmaSampleDriver {
     type IdInfo = ();
     type Data<'bound> = DmaSampleData<'bound>;
samples/rust/rust_driver_auxiliary.rs +1 -0
index 0bee16faecc6..6cff9bf8c80c 100644
@@ -90,6 +90,7 @@ struct ParentData<'bound> {
     [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
 );
 
+#[vtable]
 impl pci::Driver for ParentDriver {
     type IdInfo = ();
     type Data<'bound> = ParentData<'bound>;
samples/rust/rust_driver_pci.rs +1 -0
index 13b035a95756..50c8709739c6 100644
@@ -139,6 +139,7 @@ fn config_space(pdev: &pci::Device<Bound>) {
     }
 }
 
+#[vtable]
 impl pci::Driver for SampleDriver {
     type IdInfo = TestIndex;
     type Data<'bound> = SampleDriverData<'bound>;
Signature · 1 line
2.53.0
#5 lore raw

Re: [PATCH v3 02/10] rust: pci: add vtable attribute to pci::Driver trait

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 9 lines

From: Peter Colberg <[email protected]>

Add the #[vtable] attribute to pci::Driver and its implementations to support the optional sriov_enable() and sriov_disable() callbacks introduced later in this series.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
author
#6 lore raw

PATCH v3 3/10 rust: pci: add is_virtfn(), to check for VFs

In reply to Zhi Wang

From: John Hubbard <[email protected]>

Add a method to check if a PCI device is a Virtual Function (VF) created through Single Root I/O Virtualization (SR-IOV).

---
 rust/kernel/pci/iov.rs | 9 +++++++++
 1 file changed, 9 insertions(+)
rust/kernel/pci/iov.rs +9 -0
index c37ce5134673..272ed677f145 100644
@@ -11,6 +11,15 @@
     prelude::*, //
 };
 
+impl Device {
+    /// Returns `true` if this device is a Virtual Function (VF).
+    #[inline]
+    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 }
+    }
+}
+
 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.
Signature · 1 line
2.53.0
#7 lore raw

Re: [PATCH v3 03/10] rust: pci: add is_virtfn(), to check for VFs

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] pci: conditional compilation bug for pci::Device::is_virtfn() --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] pci: conditional compilation bug for pci::Device::is_virtfn()
--

commit 1fc1516a8aa1429dbd6a2108303ca0548d1cd4cf
Author: John Hubbard <[email protected]>

rust: pci: add is_virtfn(), to check for VFs

This commit adds a method to the PCI device abstraction to check if a device is a Virtual Function (VF) created through SR-IOV.

rust/kernel/pci/iov.rs line 17

Quoted 12 lines
rust/kernel/pci/iov.rs +4 -0
index c37ce51346731..272ed677f1451 100644
@@ -11,6 +11,15 @@
     prelude::*, //
 };
 
+impl Device {
+    /// Returns `true` if this device is a Virtual Function (VF).
+    #[inline]
+    pub fn is_virtfn(&self) -> bool {

[Severity: Medium]
Does this introduce a conditional compilation issue when CONFIG_PCI_IOV is disabled?

Since the is_virtfn method is defined in rust/kernel/pci/iov.rs, which is entirely compiled out when CONFIG_PCI_IOV is not set, it becomes unavailable to drivers. In the C API, the is_virtfn bitfield in struct pci_dev is unconditionally available regardless of the CONFIG_PCI_IOV configuration.

If a Rust driver attempts to call pdev.is_virtfn() on a kernel configured with CONFIG_PCI_IOV=n, the build will fail. Should this method be moved to a module that is always compiled, matching the C API's availability?

Quoted 4 lines
rust/kernel/pci/iov.rs +4 -0
+        // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
+        unsafe { (*self.as_raw()).is_virtfn() != 0 }
+    }
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
author
#8 lore raw

PATCH v3 4/10 rust: pci: add is_physfn(), to check for PFs

In reply to Zhi Wang

From: Peter Colberg <[email protected]>

Add an internal method to check if a PCI device is a Physical Function (PF). The SR-IOV implementation uses this role check to validate configuration requests.

---
 rust/kernel/pci/iov.rs | 8 ++++++++
 1 file changed, 8 insertions(+)
rust/kernel/pci/iov.rs +8 -0
index 272ed677f145..a4a23a9ea77a 100644
@@ -12,6 +12,14 @@
 };
 
 impl Device {
+    /// Returns `true` if this device is a Physical Function (PF).
+    #[inline]
+    #[expect(dead_code)]
+    pub(crate) fn is_physfn(&self) -> bool {
+        // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
+        unsafe { (*self.as_raw()).is_physfn() != 0 }
+    }
+
     /// Returns `true` if this device is a Virtual Function (VF).
     #[inline]
     pub fn is_virtfn(&self) -> bool {
Signature · 1 line
2.53.0
#9 lore raw

Re: [PATCH v3 04/10] rust: pci: add is_physfn(), to check for PFs

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 9 lines

From: Peter Colberg <[email protected]>

Add an internal method to check if a PCI device is a Physical Function (PF). The SR-IOV implementation uses this role check to validate configuration requests.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
author
#10 lore raw

PATCH v3 5/10 rust: pci: add num_vf(), to return number of VFs

In reply to Zhi Wang

From: Peter Colberg <[email protected]>

Add a method to return the number of Virtual Functions (VF) enabled for a Physical Function (PF).

Expose the query in the PCI core callback context.

---
 rust/kernel/pci/iov.rs | 8 ++++++++
 1 file changed, 8 insertions(+)
rust/kernel/pci/iov.rs +8 -0
index a4a23a9ea77a..4f611d5d0b89 100644
@@ -28,6 +28,14 @@ pub fn is_virtfn(&self) -> bool {
     }
 }
 
+impl Device<device::Core<'_>> {
+    /// Returns the number of Virtual Functions (VF) enabled for a Physical Function (PF).
+    pub fn num_vf(&self) -> i32 {
+        // SAFETY: `self.as_raw()` is valid and this call runs in the PCI core callback context.
+        unsafe { bindings::pci_num_vf(self.as_raw()) }
+    }
+}
+
 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.
Signature · 1 line
2.53.0
#11 lore raw

Re: [PATCH v3 05/10] rust: pci: add num_vf(), to return number of VFs

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] rust: pci: missing #[inline] on num_vf() abstraction --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] rust: pci: missing #[inline] on num_vf() abstraction
--

commit c909cec9ddd3766a8c4ba947c0d935d2555ad9f2
Author: Peter Colberg <[email protected]>

rust: pci: add num_vf(), to return number of VFs

This commit adds a num_vf method to the Device abstraction to query the number of Virtual Functions enabled for a Physical Function.

rust/kernel/pci/iov.rs pub fn is_virtfn(&self) -> bool { · line 33

Quoted 11 lines
rust/kernel/pci/iov.rs +3 -0
index a4a23a9ea77ae..4f611d5d0b89f 100644
@@ -28,6 +28,14 @@ pub fn is_virtfn(&self) -> bool {
     }
 }
 
+impl Device<device::Core<'_>> {
+    /// Returns the number of Virtual Functions (VF) enabled for a Physical Function (PF).
+    pub fn num_vf(&self) -> i32 {

[Severity: Low]
Does num_vf() need an #[inline] attribute? Small functions in abstraction crates that merely forward to a C binding usually require the #[inline] attribute to ensure optimal code generation, according to the Rust subsystem coding guidelines.

Quoted 4 lines
rust/kernel/pci/iov.rs +4 -0
+        // SAFETY: `self.as_raw()` is valid and this call runs in the PCI core callback context.
+        unsafe { bindings::pci_num_vf(self.as_raw()) }
+    }
+}

[ ... ]

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
maintainer
#12 lore raw

Re: [PATCH v3 05/10] rust: pci: add num_vf(), to return number of VFs

In reply to Zhi Wang

rust/kernel/pci/iov.rs pub fn is_virtfn(&self) -> bool { · line 33

Quoted 3 lines · Zhi Wang
rust/kernel/pci/iov.rs +3 -0
+impl Device<device::Core<'_>> {
+    /// Returns the number of Virtual Functions (VF) enabled for a Physical Function (PF).
+    pub fn num_vf(&self) -> i32 {

For the reasons mentioned in patch 1, I'd make this return u16.

I suggest to do the converstion with .try_into().expect("pdev->sriov->num_VFs is u16").

Quoted 4 lines
rust/kernel/pci/iov.rs +4 -0
+        // SAFETY: `self.as_raw()` is valid and this call runs in the PCI core callback context.
+        unsafe { bindings::pci_num_vf(self.as_raw()) }
+    }
+}
author
#13 lore raw

Re: [PATCH v3 05/10] rust: pci: add num_vf(), to return number of VFs

Quoted 11 lines · On Wed, 30 Sep 2026 13:13:25 +0200 "Danilo Krummrich" <[email protected]> wrote:
Quoted 4 lines · Zhi Wang
(quoted diff) +2 -0
+impl Device<device::Core<'_>> {
+    /// Returns the number of Virtual Functions (VF) enabled for a

Physical Function (PF).
+ pub fn num_vf(&self) -> i32 {

For the reasons mentioned in patch 1, I'd make this return u16.

I suggest to do the converstion with .try_into().expect("pdev->sriov->num_VFs is u16").

Got it. Will fix that in the next respin.

Quoted 5 lines
Quoted 5 lines

+ // SAFETY: self.as_raw() is valid and this call runs in
the PCI core callback context.

rust/kernel/pci/iov.rs +3 -0
+        unsafe { bindings::pci_num_vf(self.as_raw()) }
+    }
+}
author
#14 lore raw

PATCH v3 6/10 rust: pci: drop driver data before remove returns

In reply to Zhi Wang

Drop PCI private data before the remove callback returns, so its destructors run within PCI core's removal context.

Call unbind while private data remains installed, then take and release the data. Taking it clears drvdata, so post_unbind does not release it again. All borrows must end before the data is removed.

---
 rust/kernel/device.rs | 4 ++--
 rust/kernel/pci.rs    | 6 ++++++
 2 files changed, 8 insertions(+), 2 deletions(-)
rust/kernel/device.rs +2 -2
index 2291d85b6849..1f6bb58396e2 100644
@@ -243,8 +243,8 @@ impl<Ctx: InternalBoundContext> Device<Ctx> {
     ///
     /// # Safety
     ///
-    /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before the
-    ///   device is fully unbound.
+    /// - The data stored by [`Device::set_drvdata`] must still be installed and remain valid
+    ///   for the returned borrow.
     /// - The type `T` must match the type of the `ForeignOwnable` previously stored by
     ///   [`Device::set_drvdata`].
     pub unsafe fn drvdata_borrow<T>(&self) -> Pin<&T> {
rust/kernel/pci.rs +6 -0
index dcb0cdd0d426..1599b3a613d7 100644
@@ -141,6 +141,12 @@ extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
         let data = unsafe { pdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
 
         T::unbind(pdev, data);
+
+        // SAFETY: The driver's unbind callback has returned, and no callbacks retain a borrow.
+        let data = unsafe { pdev.as_ref().drvdata_obtain::<T::Data<'_>>() };
+
+        // Drop private data before returning to PCI core, while its removal context is valid.
+        drop(data);
     }
 }
 
Signature · 1 line
2.53.0
#15 lore raw

Re: [PATCH v3 06/10] rust: pci: drop driver data before remove returns

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 9 lines

Drop PCI private data before the remove callback returns, so its destructors run within PCI core's removal context.

Call unbind while private data remains installed, then take and release the data. Taking it clears drvdata, so post_unbind does not release it again. All borrows must end before the data is removed.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
author
#16 lore raw

PATCH v3 7/10 rust: pci: add typed SR-IOV PF registration data

In reply to Zhi Wang

Rust PF and VF drivers bind to separate PCI devices and may reside in different modules. A VF driver that calls PF operations needs typed access to the data exposed by the PF driver. This data must remain valid until VF driver removal completes.

Add VfRegistration to register a pinned Rust object in the PF's driver data. Add accessors for VF drivers to borrow this object after checking the requested Rust type. During registration teardown, disable SR-IOV and wait for VF remove callbacks to finish before dropping the object.

---
 include/linux/pci.h    |   7 ++
 rust/kernel/pci.rs     |   5 +
 rust/kernel/pci/iov.rs | 202 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 213 insertions(+), 1 deletion(-)
include/linux/pci.h +7 -0
index d31a8d107b1e..9b6ae544469e 100644
@@ -551,6 +551,13 @@ struct pci_dev {
 	u16		ats_cap;	/* ATS Capability offset */
 	u8		ats_stu;	/* ATS Smallest Translation Unit */
 #endif
+#if defined(CONFIG_PCI_IOV) && defined(CONFIG_RUST)
+	/*
+	 * Private data owned by the PF's Rust driver, readable by VF drivers
+	 * through the PCI VF registration data Rust abstraction.
+	 */
+	void		*vf_registration_data_rust;
+#endif
 #ifdef CONFIG_PCI_PRI
 	u16		pri_cap;	/* PRI Capability offset */
 	u32		pri_reqs_alloc; /* Number of PRI requests allocated */
rust/kernel/pci.rs +5 -0
index 1599b3a613d7..8782e9b06e64 100644
@@ -51,6 +51,8 @@
     Extended,
     Normal, //
 };
+#[cfg(CONFIG_PCI_IOV)]
+pub use self::iov::VfRegistration;
 pub use self::irq::{
     IrqType,
     IrqTypes,
@@ -331,6 +333,9 @@ fn probe<'bound>(
     /// operations to gracefully tear down the device.
     ///
     /// Otherwise, release operations for driver resources should be performed in `Drop`.
+    ///
+    /// For a PF with enabled VFs, `VfRegistration` disables SR-IOV when it is dropped. This
+    /// callback must leave resources accessed by VF drivers available until then.
     fn unbind<'bound>(dev: &'bound Device<device::Core<'_>>, this: Pin<&Self::Data<'bound>>) {
         let _ = (dev, this);
     }
rust/kernel/pci/iov.rs +201 -1

211 lines not on this page · raw message

Signature · 1 line
2.53.0
#17 lore raw

Re: [PATCH v3 07/10] rust: pci: add typed SR-IOV PF registration data

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 11 lines

Rust PF and VF drivers bind to separate PCI devices and may reside in different modules. A VF driver that calls PF operations needs typed access to the data exposed by the PF driver. This data must remain valid until VF driver removal completes.

Add VfRegistration to register a pinned Rust object in the PF's driver data. Add accessors for VF drivers to borrow this object after checking the requested Rust type. During registration teardown, disable SR-IOV and wait for VF remove callbacks to finish before dropping the object.

Quoted 3 lines

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
maintainer
#18 lore raw

Re: [PATCH v3 07/10] rust: pci: add typed SR-IOV PF registration data

In reply to Zhi Wang
Quoted 14 lines · Zhi Wang
rust/kernel/pci.rs +2 -0
index 1599b3a613d7..8782e9b06e64 100644
@@ -51,6 +51,8 @@
     Extended,
     Normal, //
 };
+#[cfg(CONFIG_PCI_IOV)]
+pub use self::iov::VfRegistration;
 pub use self::irq::{
     IrqType,
     IrqTypes,

rust/kernel/pci.rs fn probe<'bound>( · line 338

Quoted 6 lines
rust/kernel/pci.rs +3 -0
@@ -331,6 +333,9 @@ fn probe<'bound>(
     /// operations to gracefully tear down the device.
     ///
     /// Otherwise, release operations for driver resources should be performed in `Drop`.
+    ///
+    /// For a PF with enabled VFs, `VfRegistration` disables SR-IOV when it is dropped. This
+    /// callback must leave resources accessed by VF drivers available until then.

I don't think we need this comment, neither this callback (which I plan to remove anyway) nor T::Data::drop() can remove resources that can still be accessed by VF drivers in the first place.

You could have something like Mutex<Option<_>> of course, but that would be intentional then.

rust/kernel/pci.rs fn probe<'bound>( · line 341

Quoted 3 lines
rust/kernel/pci.rs +3 -0
+impl<'a, F: ForLt + 'static> VfRegistrationData<'a, F> {
+    /// Pin-initializer for the registration data.
+    fn new(data: impl PinInit<F::Of<'a>, Error>) -> impl PinInit<Self, Error> {

I think we can accept any error type E?

Quoted 6 lines
rust/kernel/pci.rs +6 -0
+        try_pin_init!(Self {
+            type_id: TypeId::of::<F>(),
+            data <- data,
+        })
+    }
+}

<snip>

rust/kernel/pci.rs

Quoted 12 lines
rust/kernel/pci.rs +12 -0
+impl<'a, F: ForLt + 'static> VfRegistration<'a, F>
+where
+    for<'b> F::Of<'b>: Send + Sync,
+{
+    /// Create a new VF registration.
+    ///
+    /// Returns a pin-initializer so the registration can be embedded directly
+    /// in the PF driver's bus device private data.
+    ///
+    /// # Safety
+    ///
+    /// The returned registration must be embedded in the driver's bus device private data.

I think this is the only requirement we need.

rust/kernel/pci.rs

Quoted 3 lines
rust/kernel/pci.rs +3 -0
+    /// The initializer must run as part of the PF driver's probe.
+    /// The driver's `unbind` callback and the enclosing data's destructor must keep resources
+    /// accessed by VF drivers available until this registration has disabled SR-IOV.

This is not a safety requirement; it would require unsafe code to do this.

Quoted 9 lines
rust/kernel/pci.rs +9 -0
+    pub unsafe fn new(
+        pdev: &'a Device<device::Core<'_>>,
+        data: impl PinInit<F::Of<'a>, Error> + 'a,
+    ) -> impl PinInit<Self, Error> + 'a {
+        let pdev: &'a Device<device::Bound> = pdev;
+        try_pin_init!(Self {
+            _: {
+                if pdev.is_virtfn() {
+                    return Err(ENODEV);

rust/kernel/pci.rs

Quoted 10 lines
rust/kernel/pci.rs +10 -0
+                }
+            },
+            pdev,
+            inner <- VfRegistrationData::new(data),
+            _pin: PhantomPinned,
+            _: {
+                // Check after initialization in case it registered another object for this PF.
+                // SAFETY: The caller runs this initializer during PF probing, before VF
+                // configuration callbacks can run.
+                if unsafe { bindings::pci_num_vf(pdev.as_raw()) } != 0 {

Don't we have a safe pci::Device::num_vf() method already?

rust/kernel/pci.rs

Quoted 7 lines
rust/kernel/pci.rs +7 -0
+                    return Err(EBUSY);
+                }
+
+                // SAFETY: This initializer runs during PF probing, and no VFs are enabled.
+                if !unsafe { (*pdev.as_raw()).vf_registration_data_rust }.is_null() {
+                    return Err(EBUSY);
+                }

Those two checks can go into the first _: block, no?

Quoted 35 lines
rust/kernel/pci.rs +35 -0
+
+                // SAFETY: The data is initialized and pinned, the slot is unoccupied, and
+                // no VF can access it yet. No fallible work follows publication.
+                unsafe {
+                    (*pdev.as_raw()).vf_registration_data_rust =
+                        core::ptr::from_ref(inner.as_ref().get_ref()).cast_mut().cast();
+                }
+            },
+        })
+    }
+}
+
+#[pinned_drop]
+impl<F: ForLt + 'static> PinnedDrop for VfRegistration<'_, F> {
+    fn drop(self: Pin<&mut Self>) {
+        // SAFETY: `pci_disable_sriov()` is safe to call on any `pci_dev`; it
+        // is a no-op if the device has no VFs enabled. When VFs are enabled,
+        // this blocks until all VF `remove()` callbacks complete.
+        unsafe { bindings::pci_disable_sriov(self.pdev.as_raw()) };
+
+        // SAFETY: The device is valid and all VF remove callbacks have completed, so no VF
+        // can access the registration pointer. The data remains alive until this returns.
+        unsafe { (*self.pdev.as_raw()).vf_registration_data_rust = core::ptr::null_mut() };
+    }
+}
+
+// SAFETY: The inner data is `Send` (enforced by the bound), and `&Device` is `Send + Sync`.
+unsafe impl<F: ForLt> Send for VfRegistration<'_, F> where for<'a> F::Of<'a>: Send {}
+
+// SAFETY: The inner data is `Send + Sync`. `VfRegistration` doesn't expose mutable access;
+// VF drivers only read the data through an immutable pinned reference.
+unsafe impl<F: ForLt> Sync for VfRegistration<'_, F> where for<'a> F::Of<'a>: Send + Sync {}
+
+impl Device<device::Bound> {
+    /// Internal helper: reads the `vf_registration_data_rust` pointer from the

rust/kernel/pci.rs

Quoted 10 lines
rust/kernel/pci.rs +10 -0
+    /// PF, checks the `TypeId`, and returns a pinned reference.
+    ///
+    /// # Safety
+    ///
+    /// The data lifetime must be hidden behind a higher-ranked closure independently of the
+    /// reference lifetime, or `F` must be covariant in its encoded lifetime.
+    unsafe fn vf_registration_data_pinned<F: ForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
+        if !self.is_virtfn() {
+            return Err(ENODEV);
+        }

We don't want to exclude PF drivers to access this. Have a look at drivers/gpu/nova-core/api.rs, it makes sense for the PF driver to provide a helper API around this.

rust/kernel/pci.rs

Quoted 9 lines
rust/kernel/pci.rs +9 -0
+        // SAFETY: This bound VF uses the `physfn` union field. PCI retains its PF until
+        // VF removal completes, and the PF keeps the registration installed until then.
+        let ptr = unsafe {
+            let pf = (*self.as_raw()).__bindgen_anon_1.physfn;
+            (*pf).vf_registration_data_rust
+        };
+
+        if ptr.is_null() {
+            return Err(ENOENT);

Maybe ENODEV?

Quoted 55 lines
rust/kernel/pci.rs +53 -0
+        }
+
+        // SAFETY: The registration keeps its data installed until VF removal completes,
+        // including when probe initialization rolls back.
+        // `ptr` points to a `VfRegistrationData` whose first field is a `TypeId`.
+        let type_id = unsafe { ptr.cast::<TypeId>().read() };
+        if type_id != TypeId::of::<F>() {
+            return Err(EINVAL);
+        }
+
+        // SAFETY: TypeId check confirms the stored type matches `F`. The data
+        // is pinned inside the PF's driver data struct. Lifetime shortening
+        // from the PF's binding scope to `'_` is layout-compatible.
+        let data_ptr = unsafe {
+            let vfrd = ptr.cast::<VfRegistrationData<'_, F>>();
+            &raw const (*vfrd).data
+        };
+
+        // SAFETY: `data` is structurally pinned inside `VfRegistrationData`.
+        Ok(unsafe { Pin::new_unchecked(&*data_ptr) })
+    }
+
+    /// Access the VF registration data through a closure with an HRTB lifetime.
+    ///
+    /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. Returns
+    /// [`ENODEV`] if this is not a VF, [`ENOENT`] if no data was registered,
+    /// or [`EINVAL`] if `F` does not match the type registered by the PF.
+    ///
+    /// The reference is borrowed from this VF, while the registration data's lifetime remains
+    /// abstract so the closure cannot store shorter-lived references in invariant data.
+    pub fn vf_registration_data_with<'this, F: ForLt + 'static, R>(
+        &'this self,
+        f: impl for<'a> FnOnce(Pin<&'this F::Of<'a>>) -> R,
+    ) -> Result<R> {
+        // SAFETY: The closure is higher-ranked over the data lifetime, independently of `'this`.
+        // It cannot insert shorter-lived references, and the outer borrow cannot outlive this VF.
+        let pinned = unsafe { self.vf_registration_data_pinned::<F>()? };
+        Ok(f(pinned))
+    }
+
+    /// Returns a pinned reference to the VF registration data.
+    ///
+    /// Available only when `F` implements [`CovariantForLt`](trait@crate::types::CovariantForLt),
+    /// guaranteeing that shortening the PF data lifetime is sound.
+    ///
+    /// For non-covariant types, use [`Self::vf_registration_data_with()`].
+    ///
+    /// It returns the same errors as [`Self::vf_registration_data_with()`].
+    pub fn vf_registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
+        // SAFETY: `CovariantForLt` permits shortening the encoded lifetime to this borrow.
+        unsafe { self.vf_registration_data_pinned::<F>() }
+    }
+}
Signature · 1 line
2.53.0
author
#19 lore raw

Re: [PATCH v3 07/10] rust: pci: add typed SR-IOV PF registration data

Quoted 20 lines · On Wed, 30 Sep 2026 15:59:27 +0200 "Danilo Krummrich" <[email protected]> wrote:
Quoted 19 lines · Zhi Wang
rust/kernel/pci.rs +2 -0
index 1599b3a613d7..8782e9b06e64 100644
@@ -51,6 +51,8 @@
     Extended,
     Normal, //
 };
+#[cfg(CONFIG_PCI_IOV)]
+pub use self::iov::VfRegistration;
 pub use self::irq::{
     IrqType,
     IrqTypes,
@@ -331,6 +333,9 @@ fn probe<'bound>(
     /// operations to gracefully tear down the device.
     ///
     /// Otherwise, release operations for driver resources should

be performed in Drop.
+ ///

Quoted 9 lines
Quoted 4 lines

+ /// For a PF with enabled VFs, VfRegistration disables
SR-IOV when it is dropped. This
+ /// callback must leave resources accessed by VF drivers
available until then.

I don't think we need this comment, neither this callback (which I plan to remove anyway) nor T::Data::drop() can remove resources that can still be accessed by VF drivers in the first place.

I see.

Quoted 3 lines

You could have something like Mutex<Option<_>> of course, but that would be intentional then.

Got it. Will proceed in the next re-spin.

Quoted 10 lines

rust/kernel/pci.rs

Quoted 7 lines

!= 0 {
+ // SAFETY: This initializer runs during PF
probing, and no VFs are enabled.
+ if !unsafe {
(*pdev.as_raw()).vf_registration_data_rust }.is_null() {

rust/kernel/pci.rs +2 -0
+                    return Err(EBUSY);
+                }

Those two checks can go into the first _: block, no?

Nice idea. Thanks for the suggestion.

Quoted 7 lines
Quoted 7 lines
rust/kernel/pci.rs +2 -0
+
+                // SAFETY: The data is initialized and pinned, the

slot is unoccupied, and
+ // no VF can access it yet. No fallible work
follows publication.

rust/kernel/pci.rs +2 -0
+                unsafe {
+                    (*pdev.as_raw()).vf_registration_data_rust =

<snip>

Quoted 11 lines

rust/kernel/pci.rs

Quoted 10 lines
rust/kernel/pci.rs +2 -0
+    ///
+    /// The data lifetime must be hidden behind a higher-ranked

closure independently of the
+ /// reference lifetime, or F must be covariant in its
encoded lifetime.
+ unsafe fn vf_registration_data_pinned<F: ForLt +
'static>(&self) -> Result<Pin<&F::Of<'_>>> {

rust/kernel/pci.rs +3 -0
+        if !self.is_virtfn() {
+            return Err(ENODEV);
+        }
Quoted 3 lines

We don't want to exclude PF drivers to access this. Have a look at drivers/gpu/nova-core/api.rs, it makes sense for the PF driver to provide a helper API around this.

Got it. I will add a helper for the PF driver to access this.

Quoted 91 lines

rust/kernel/pci.rs

Quoted 12 lines

+ // SAFETY: This bound VF uses the physfn union field.
PCI retains its PF until
+ // VF removal completes, and the PF keeps the registration
installed until then.

rust/kernel/pci.rs +7 -0
+        let ptr = unsafe {
+            let pf = (*self.as_raw()).__bindgen_anon_1.physfn;
+            (*pf).vf_registration_data_rust
+        };
+
+        if ptr.is_null() {
+            return Err(ENOENT);

Maybe ENODEV?

Quoted 75 lines
rust/kernel/pci.rs +3 -0
+        }
+
+        // SAFETY: The registration keeps its data installed until

VF removal completes,

rust/kernel/pci.rs +2 -0
+        // including when probe initialization rolls back.
+        // `ptr` points to a `VfRegistrationData` whose first

field is a TypeId.

rust/kernel/pci.rs +6 -0
+        let type_id = unsafe { ptr.cast::<TypeId>().read() };
+        if type_id != TypeId::of::<F>() {
+            return Err(EINVAL);
+        }
+
+        // SAFETY: TypeId check confirms the stored type matches

F. The data
+ // is pinned inside the PF's driver data struct. Lifetime
shortening
+ // from the PF's binding scope to '_ is
layout-compatible.

rust/kernel/pci.rs +6 -0
+        let data_ptr = unsafe {
+            let vfrd = ptr.cast::<VfRegistrationData<'_, F>>();
+            &raw const (*vfrd).data
+        };
+
+        // SAFETY: `data` is structurally pinned inside

VfRegistrationData.

rust/kernel/pci.rs +4 -0
+        Ok(unsafe { Pin::new_unchecked(&*data_ptr) })
+    }
+
+    /// Access the VF registration data through a closure with an

HRTB lifetime.

rust/kernel/pci.rs +2 -0
+    ///
+    /// `F` is the [`ForLt`](trait@ForLt) encoding of the data

type. Returns
+ /// [ENODEV] if this is not a VF, [ENOENT] if no data was
registered,
+ /// or [EINVAL] if F does not match the type registered by
the PF.

rust/kernel/pci.rs +2 -0
+    ///
+    /// The reference is borrowed from this VF, while the

registration data's lifetime remains
+ /// abstract so the closure cannot store shorter-lived
references in invariant data.

rust/kernel/pci.rs +5 -0
+    pub fn vf_registration_data_with<'this, F: ForLt + 'static, R>(
+        &'this self,
+        f: impl for<'a> FnOnce(Pin<&'this F::Of<'a>>) -> R,
+    ) -> Result<R> {
+        // SAFETY: The closure is higher-ranked over the data

lifetime, independently of 'this.
+ // It cannot insert shorter-lived references, and the
outer borrow cannot outlive this VF.
+ let pinned = unsafe {
self.vf_registration_data_pinned::<F>()? };

rust/kernel/pci.rs +6 -0
+        Ok(f(pinned))
+    }
+
+    /// Returns a pinned reference to the VF registration data.
+    ///
+    /// Available only when `F` implements

[CovariantForLt](trait@crate::types::CovariantForLt),

rust/kernel/pci.rs +3 -0
+    /// guaranteeing that shortening the PF data lifetime is sound.
+    ///
+    /// For non-covariant types, use

[Self::vf_registration_data_with()].

rust/kernel/pci.rs +2 -0
+    ///
+    /// It returns the same errors as

[Self::vf_registration_data_with()].
+ pub fn vf_registration_data<F: CovariantForLt +
'static>(&self) -> Result<Pin<&F::Of<'_>>> {
+ // SAFETY: CovariantForLt permits shortening the encoded
lifetime to this borrow.

rust/kernel/pci.rs +3 -0
+        unsafe { self.vf_registration_data_pinned::<F>() }
+    }
+}
Signature · 1 line
2.53.0
author
#20 lore raw

PATCH v3 8/10 rust: pci: add SR-IOV enable and disable tokens

In reply to Zhi Wang

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.

---
 rust/kernel/pci.rs     |  7 +++-
 rust/kernel/pci/iov.rs | 92 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 96 insertions(+), 3 deletions(-)
rust/kernel/pci.rs +6 -1
index 8782e9b06e64..8a87e2202a2c 100644
@@ -52,7 +52,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,
rust/kernel/pci/iov.rs +90 -2

110 lines not on this page · raw message

Signature · 1 line
2.53.0
#21 lore raw

Re: [PATCH v3 08/10] rust: pci: add SR-IOV enable and disable tokens

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] rust: missing #[inline] for small/binding wrapper abstraction functions --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] rust: missing #[inline] for small/binding wrapper abstraction functions --

commit 4e2162272f9a80159a9c71ad8cb46433c7d6fdae
Author: Zhi Wang <[email protected]>

rust: pci: add SR-IOV enable and disable tokens

The commit introduces callback-scoped tokens to manage SR-IOV enable/disable lifecycles and rollback.

rust/kernel/pci/iov.rs pub fn num_vf(&self) -> i32 { · line 50

Quoted 10 lines
rust/kernel/pci/iov.rs +0 -1
index 3411a9101564c..e608b2f1e70c7 100644
@@ -47,7 +47,6 @@ pub fn num_vf(&self) -> i32 {
 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.
-    #[expect(dead_code)]
     pub(crate) fn enable_sriov(&self, nr_virtfn: c_int) -> Result {

[Severity: Low]
This isn't a bug, but should this function have an #[inline] annotation? The Rust subsystem guidelines state that C binding wrappers should be inlined.

[ ... ]

rust/kernel/pci/iov.rs pub(crate) fn enable_sriov(&self, nr_virtfn: c_int) -> Result { · line 65

Quoted 3 lines
rust/kernel/pci/iov.rs +0 -1
     /// Disable the Single Root I/O Virtualization (SR-IOV) capability for this device.
-    #[expect(dead_code)]
     pub(crate) fn disable_sriov(&self) {

[Severity: Low]
This isn't a bug, but does this wrapper function also need an #[inline] annotation since it forwards directly to the C bindings?

[ ... ]

rust/kernel/pci/iov.rs pub(crate) fn disable_sriov(&self) { · line 102

Quoted 5 lines
rust/kernel/pci/iov.rs +5 -0
+impl<'callback> SriovEnable<'callback> {
+    /// Returns the number of VFs requested for this callback.
+    pub fn num_vfs(&self) -> u32 {
+        self.num_vfs
+    }

[Severity: Low]
This isn't a bug, but could we add an #[inline] annotation to this trivial getter function?

[ ... ]

rust/kernel/pci/iov.rs pub(crate) fn disable_sriov(&self) { · line 141

Quoted 7 lines
rust/kernel/pci/iov.rs +7 -0
+impl SriovEnabled<'_> {
+    #[expect(dead_code)]
+    fn disarm(mut self) -> c_int {
+        self.pdev = None;
+        self.num_vfs
+    }
+}

[Severity: Low]
This isn't a bug, but would it be appropriate to mark this small state-mutating function with #[inline]?

[ ... ]

rust/kernel/pci/iov.rs pub(crate) fn disable_sriov(&self) { · line 167

Quoted 8 lines
rust/kernel/pci/iov.rs +8 -0
+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.
+    pub fn disable(self) {
+        self.pdev.disable_sriov();
+    }
+}

[Severity: Low]
This isn't a bug, but since this function directly forwards to disable_sriov(), should it be annotated with #[inline]?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
maintainer
#22 lore raw

Re: [PATCH v3 08/10] rust: pci: add SR-IOV enable and disable tokens

In reply to Zhi Wang
Quoted 1 line · Zhi Wang

+pub struct SriovEnable<'callback> {

I understand why you chose 'callback, but I think just 'a is good enough.

rust/kernel/pci/iov.rs pub(crate) fn disable_sriov(&self) { · line 95

Quoted 2 lines
rust/kernel/pci/iov.rs +2 -0
+    pdev: &'callback Device<device::CoreInternal<'callback>>,
+    num_vfs: u32,

Please see patch 1.

Quoted 26 lines
rust/kernel/pci/iov.rs +26 -0
+}
+
+impl<'callback> SriovEnable<'callback> {
+    /// Returns the number of VFs requested for this callback.
+    pub fn num_vfs(&self) -> u32 {
+        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: u32) -> Result<SriovEnabled<'callback>> {
+        if num_vfs == 0 || num_vfs > self.num_vfs {
+            return Err(EINVAL);
+        }
+        let num_vfs = c_int::try_from(num_vfs).map_err(|_| EOVERFLOW)?;
+
+        // 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)?;

rust/kernel/pci/iov.rs pub(crate) fn disable_sriov(&self) { · line 131

Quoted 10 lines
rust/kernel/pci/iov.rs +10 -0
+        Ok(SriovEnabled {
+            pdev: Some(self.pdev),
+            num_vfs,
+        })
+    }
+}
+
+/// Enabled VFs awaiting successful completion of `sriov_enable()`.
+pub struct SriovEnabled<'callback> {
+    pdev: Option<&'callback Device<device::CoreInternal<'callback>>>,

This doesn't need to be an Option.

rust/kernel/pci/iov.rs pub(crate) fn disable_sriov(&self) { · line 139

Quoted 8 lines
rust/kernel/pci/iov.rs +8 -0
+    num_vfs: c_int,
+}
+
+impl SriovEnabled<'_> {
+    #[expect(dead_code)]
+    fn disarm(mut self) -> c_int {
+        self.pdev = None;
+        self.num_vfs

Instead of the Option dance, you can use ManuallyDrop to prevent the destructor from running.

Quoted 10 lines
rust/kernel/pci/iov.rs +10 -0
+    }
+}
+
+impl Drop for SriovEnabled<'_> {
+    fn drop(&mut self) {
+        if let Some(pdev) = self.pdev.take() {
+            pdev.disable_sriov();
+        }
+    }
+}
author
#23 lore raw

Re: [PATCH v3 08/10] rust: pci: add SR-IOV enable and disable tokens

Quoted 3 lines · On Wed, 30 Sep 2026 16:25:36 +0200 "Danilo Krummrich" <[email protected]> wrote:
Quoted 1 line · Zhi Wang

+pub struct SriovEnable<'callback> {

snip

Quoted 7 lines
Quoted 4 lines
rust/kernel/pci/iov.rs +3 -0
+/// Enabled VFs awaiting successful completion of `sriov_enable()`.
+pub struct SriovEnabled<'callback> {
+    pdev: Option<&'callback

Device<device::CoreInternal<'callback>>>,

This doesn't need to be an Option.

I realize that with ManullayDrop, I don't need Option to be used as the Guard now. Thanks!

Quoted 22 lines

rust/kernel/pci/iov.rs pub(crate) fn disable_sriov(&self) { · line 139

Quoted 8 lines
rust/kernel/pci/iov.rs +8 -0
+    num_vfs: c_int,
+}
+
+impl SriovEnabled<'_> {
+    #[expect(dead_code)]
+    fn disarm(mut self) -> c_int {
+        self.pdev = None;
+        self.num_vfs

Instead of the Option dance, you can use ManuallyDrop to prevent the destructor from running.

Quoted 10 lines
rust/kernel/pci/iov.rs +10 -0
+    }
+}
+
+impl Drop for SriovEnabled<'_> {
+    fn drop(&mut self) {
+        if let Some(pdev) = self.pdev.take() {
+            pdev.disable_sriov();
+        }
+    }
+}
author
#24 lore raw

PATCH v3 9/10 rust: pci: add SR-IOV enable and disable callbacks

In reply to Zhi Wang

From: Peter Colberg <[email protected]>

Allow Rust PCI drivers to configure SR-IOV through sriov_numvfs. Split the C configure callback into sriov_enable() and sriov_disable(), passing pinned driver data and a token for the requested operation. Require drivers to implement both callbacks together.

Return the enabled guard's VF count on success and disarm its rollback. On a disable request, verify that the driver has removed all VFs before reporting success. VfRegistration handles VF removal during PF teardown; these callbacks only handle explicit configuration requests.

---
 rust/kernel/pci.rs     | 83 ++++++++++++++++++++++++++++++++++++++++++
 rust/kernel/pci/iov.rs | 62 +++++++++++++++++++++++++++----
 2 files changed, 137 insertions(+), 8 deletions(-)
rust/kernel/pci.rs +83 -0
index 8a87e2202a2c..87d2e637d902 100644
@@ -87,12 +87,22 @@ unsafe fn register(
         name: &'static CStr,
         module: &'static ThisModule,
     ) -> Result {
+        #[cfg(CONFIG_PCI_IOV)]
+        build_assert!(
+            T::HAS_SRIOV_ENABLE == T::HAS_SRIOV_DISABLE,
+            "PCI drivers must implement both sriov_enable and sriov_disable"
+        );
+
         // SAFETY: It's safe to set the fields of `struct pci_driver` on initialization.
         unsafe {
             (*pdrv.get()).name = name.as_char_ptr();
             (*pdrv.get()).probe = Some(Self::probe_callback);
             (*pdrv.get()).remove = Some(Self::remove_callback);
             (*pdrv.get()).id_table = T::ID_TABLE.as_ptr();
+            #[cfg(CONFIG_PCI_IOV)]
+            if T::HAS_SRIOV_ENABLE {
+                (*pdrv.get()).sriov_configure = Some(Self::sriov_configure_callback);
+            }
         }
 
         // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
@@ -344,6 +354,79 @@ fn probe<'bound>(
     fn unbind<'bound>(dev: &'bound Device<device::Core<'_>>, this: Pin<&Self::Data<'bound>>) {
         let _ = (dev, this);
     }
+
+    /// Enables Single Root I/O Virtualization (SR-IOV) for a PF.
+    ///
+    /// Called when userspace writes a nonzero VF count to `sriov_numvfs`. The token carries the
+    /// requested count and permission to enable VFs on this PF. Return the guard obtained from
+    /// [`SriovEnable::enable()`] after completing setup; dropping it rolls back the enable.
+    ///
+    /// The PF must own a [`VfRegistration`] before enabling VFs. That registration disables
+    /// SR-IOV when the driver data is destroyed, while resources needed by VF drivers remain live.
+    /// Implement this callback and [`Self::sriov_disable()`] together, or implement neither.
+    ///
+    /// See [PCI Express I/O Virtualization].
+    ///
+    /// [PCI Express I/O Virtualization]: https://docs.kernel.org/PCI/pci-iov-howto.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use kernel::{device::Core, pci, prelude::*};
+    /// # struct Data;
+    /// fn sriov_enable<'callback>(
+    ///     _dev: &pci::Device<Core<'_>>,
+    ///     _this: Pin<&Data>,
+    ///     token: pci::SriovEnable<'callback>,
+    /// ) -> Result<pci::SriovEnabled<'callback>> {
+    ///     let num_vfs = token.num_vfs();
+    ///     let enabled = token.enable(num_vfs)?;
+    ///     // Complete any additional setup before returning the guard.
+    ///     Ok(enabled)
+    /// }
+    /// ```
+    #[cfg(CONFIG_PCI_IOV)]
+    fn sriov_enable<'bound, 'callback>(
+        dev: &'bound Device<device::Core<'_>>,
+        this: Pin<&Self::Data<'bound>>,
+        token: SriovEnable<'callback>,
+    ) -> Result<SriovEnabled<'callback>> {
+        let _ = (dev, this, token);
+        build_error!(crate::error::VTABLE_DEFAULT_ERROR)
+    }
+
+    /// Disables all VFs of a PF in response to a userspace request.
+    ///
+    /// Called when userspace writes zero to `sriov_numvfs`. Call [`SriovDisable::disable()`]
+    /// while resources used by VF drivers are still available. Returning an error before calling
+    /// it leaves the VFs enabled. Returning success requires that all VFs have been disabled.
+    ///
+    /// This callback is not invoked during PF unbind; [`VfRegistration`] owns that teardown.
+    /// Implement this callback and [`Self::sriov_enable()`] together, or implement neither.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use kernel::{device::Core, pci, prelude::*};
+    /// # struct Data;
+    /// fn sriov_disable(
+    ///     _dev: &pci::Device<Core<'_>>,
+    ///     _this: Pin<&Data>,
+    ///     token: pci::SriovDisable<'_>,
+    /// ) -> Result {
+    ///     token.disable();
+    ///     Ok(())
+    /// }
+    /// ```
+    #[cfg(CONFIG_PCI_IOV)]
+    fn sriov_disable<'bound>(
+        dev: &'bound Device<device::Core<'_>>,
+        this: Pin<&Self::Data<'bound>>,
+        token: SriovDisable<'_>,
+    ) -> Result {
+        let _ = (dev, this, token);
+        build_error!(crate::error::VTABLE_DEFAULT_ERROR)
+    }
 }
 
 /// The PCI device representation.
rust/kernel/pci/iov.rs +54 -8

114 lines not on this page · raw message

Signature · 1 line
2.53.0
#25 lore raw

Re: [PATCH v3 09/10] rust: pci: add SR-IOV enable and disable callbacks

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 7 lines

From: Peter Colberg <[email protected]>

Allow Rust PCI drivers to configure SR-IOV through sriov_numvfs. Split the C configure callback into sriov_enable() and sriov_disable(), passing pinned driver data and a token for the requested operation. Require drivers to implement both callbacks together.

Quoted 8 lines

Return the enabled guard's VF count on success and disarm its rollback. On a disable request, verify that the driver has removed all VFs before reporting success. VfRegistration handles VF removal during PF teardown; these callbacks only handle explicit configuration requests.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
maintainer
#26 lore raw

Re: [PATCH v3 09/10] rust: pci: add SR-IOV enable and disable callbacks

In reply to Zhi Wang

rust/kernel/pci.rs fn probe<'bound>( · line 389

Quoted 2 lines · Zhi Wang
rust/kernel/pci.rs +2 -0
+    #[cfg(CONFIG_PCI_IOV)]
+    fn sriov_enable<'bound, 'callback>(

I'd just call it 'a.

Quoted 1 line

+ dev: &'bound Device<device::Core<'_>>,

This is the same lifetime, as the token stores Device<Core<'_>>, so I'd also make it 'a.

Quoted 1 line

+ this: Pin<&Self::Data<'bound>>,

Let's call this 'data', unbind() has 'this' for historical reasons.

Quoted 5 lines
rust/kernel/pci.rs +5 -0
+        token: SriovEnable<'callback>,
+    ) -> Result<SriovEnabled<'callback>> {
+        let _ = (dev, this, token);
+        build_error!(crate::error::VTABLE_DEFAULT_ERROR)
+    }

<snip>

Quoted 10 lines
rust/kernel/pci.rs +9 -0
+    #[cfg(CONFIG_PCI_IOV)]
+    fn sriov_disable<'bound>(
+        dev: &'bound Device<device::Core<'_>>,
+        this: Pin<&Self::Data<'bound>>,
+        token: SriovDisable<'_>,
+    ) -> Result {
+        let _ = (dev, this, token);
+        build_error!(crate::error::VTABLE_DEFAULT_ERROR)
+    }
 }

<snip>

Quoted 9 lines
rust/kernel/pci/iov.rs +9 -0
+impl<T: Driver> Adapter<T> {
+    pub(super) extern "C" fn sriov_configure_callback(
+        pdev: *mut bindings::pci_dev,
+        nr_virtfn: c_int,
+    ) -> c_int {
+        // SAFETY: The PCI bus only ever calls the sriov_configure callback with a valid pointer to
+        // a `struct pci_dev`.
+        //
+        // INVARIANT: `pdev` is valid for the duration of `sriov_configure_callback()`.

rust/kernel/pci/iov.rs pub fn disable(self) { · line 192

Quoted 10 lines
rust/kernel/pci/iov.rs +10 -0
+        let pdev = unsafe { &*pdev.cast::<Device<device::CoreInternal<'_>>>() };
+
+        // SAFETY: `sriov_configure` is called only after a successful probe and before unbind, so
+        // the stored pointer has type `T::Data<'_>` and remains valid throughout this callback.
+        let data = unsafe { pdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
+
+        from_result(|| {
+            if !pdev.is_physfn() {
+                return Err(ENODEV);
+            }

I don't think this can ever happen.

Quoted 21 lines
rust/kernel/pci/iov.rs +21 -0
+            if nr_virtfn == 0 {
+                T::sriov_disable(pdev, data, SriovDisable { pdev })?;
+                if pdev.num_vf() != 0 {
+                    return Err(EBUSY);
+                }
+                Ok(0)
+            } else {
+                let num_vfs = u16::try_from(nr_virtfn).map_err(|_| EINVAL)?;
+                let enabled = T::sriov_enable(
+                    pdev,
+                    data,
+                    SriovEnable {
+                        pdev,
+                        num_vfs: u32::from(num_vfs),
+                    },
+                )?;
+                Ok(enabled.disarm())
+            }
+        })
+    }
+}
author
#27 lore raw

PATCH v3 10/10 samples: rust: add Rust SR-IOV VF driver sample

In reply to Zhi Wang

From: Peter Colberg <[email protected]>

Add a new SR-IOV driver sample that demonstrates how to enable and disable the Single Root I/O Virtualization capability for a PCI device.

The sample may be exercised using QEMU's 82576 (igb) emulation.

Implement the PF and VF as ordinary PCI drivers. Publish pinned PF data with a mutex-protected counter inline in the PF driver data, then borrow and invoke it explicitly from VF probe. Register the VF driver first so it is ready before the PF can enable VFs.

---
 MAINTAINERS                       |   1 +
 samples/rust/Kconfig              |  11 ++
 samples/rust/Makefile             |   1 +
 samples/rust/rust_driver_sriov.rs | 249 ++++++++++++++++++++++++++++++
 4 files changed, 262 insertions(+)
 create mode 100644 samples/rust/rust_driver_sriov.rs
MAINTAINERS +1 -0
index f7a513c4ba24..da6c34bc3dcd 100644
@@ -21129,6 +21129,7 @@ F:	rust/helpers/pci.c
 F:	rust/kernel/pci.rs
 F:	rust/kernel/pci/
 F:	samples/rust/rust_driver_pci.rs
+F:	samples/rust/rust_driver_sriov.rs
 
 PCIE BANDWIDTH CONTROLLER
 M:	Ilpo Järvinen <[email protected]>
samples/rust/Kconfig +11 -0
index 31d62533ef25..737670fd68f8 100644
@@ -128,6 +128,17 @@ config SAMPLE_RUST_DRIVER_PLATFORM
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_DRIVER_SRIOV
+	tristate "SR-IOV Driver"
+	depends on PCI_IOV
+	help
+	  This option builds the Rust SR-IOV driver sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_driver_sriov.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_DRIVER_USB
 	tristate "USB Driver"
 	depends on USB = y
samples/rust/Makefile +1 -0
index b986b681cde5..238a11d5ec39 100644
@@ -11,6 +11,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C)		+= rust_driver_i2c.o
 obj-$(CONFIG_SAMPLE_RUST_I2C_CLIENT)		+= rust_i2c_client.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI)		+= rust_driver_pci.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_SRIOV)		+= rust_driver_sriov.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB)		+= rust_driver_usb.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX)		+= rust_driver_faux.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY)	+= rust_driver_auxiliary.o
samples/rust/rust_driver_sriov.rs new file +249 -0

249 lines not on this page · raw message

Signature · 1 line
2.53.0
#28 lore raw

Re: [PATCH v3 10/10] samples: rust: add Rust SR-IOV VF driver sample

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] pci: missing pci_disable_device() leaks enable_cnt and bus master state --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] pci: missing pci_disable_device() leaks enable_cnt and bus master state --

commit 775047d7dfd13478598c3e7650d421010c6a0cef
Author: Peter Colberg <[email protected]>

samples: rust: add Rust SR-IOV VF driver sample

Add a new SR-IOV driver sample that demonstrates how to enable and disable the Single Root I/O Virtualization capability for a PCI device.

Quoted 4 lines
samples/rust/rust_driver_sriov.rs new file +0 -0
new file mode 100644

[ ... ]

Quoted 6 lines
samples/rust/rust_driver_sriov.rs new file +6 -0
+    fn probe<'bound>(
+        pdev: &'bound pci::Device<Core<'_>>,
+        _info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
+        pin_init::pin_init_scope(move || {
+            dev_info!(

samples/rust/rust_driver_sriov.rs line 121

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+                pdev,
+                "Probe Rust SR-IOV PF sample (PCI ID: {}, 0x{:x}).\n",
+                pdev.vendor_id(),
+                pdev.device_id()
+            );
+
+            pdev.enable_device_mem()?;
+            pdev.set_master();
+
+            Ok(try_pin_init!(PfDriverData {

[Severity: Medium]
Does this code leak the enable_cnt and leave bus mastering active if the subsequent try_pin_init!() block fails?

pdev.enable_device_mem() calls pci_enable_device_mem(), incrementing the enable_cnt of the PCI device, and pdev.set_master() enables bus mastering. Returning an error without calling pci_disable_device() or pci_clear_master() would leave the device in an unbalanced state.

Quoted 7 lines
samples/rust/rust_driver_sriov.rs new file +7 -0
+                // SAFETY:
+                // - probe has exclusive access to this PF before SR-IOV is enabled;
+                // - the registration is pinned in the PF driver data and dropped before `pdev`;
+                // - no other registration is created for this PF; and
+                // - VFs are enabled only after probe by `sriov_enable`.
+                _registration <- unsafe {
+                    pci::VfRegistration::new(

[ ... ]

Quoted 9 lines
samples/rust/rust_driver_sriov.rs new file +9 -0
+    fn probe<'bound>(
+        pdev: &'bound pci::Device<Core<'_>>,
+        _info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
+        pin_init::pin_init_scope(move || {
+            dev_info!(
+                pdev,
+                "Probe Rust SR-IOV VF sample (PCI ID: {}, 0x{:x}).\n",
+                pdev.vendor_id(),

samples/rust/rust_driver_sriov.rs line 202

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+                pdev.device_id()
+            );
+
+            let pdev_bound: &'bound pci::Device<Bound> = pdev;
+            let pf_api = pdev_bound.vf_registration_data::<PfApiForLt>()?;
+
+            pdev.enable_device_mem()?;
+            pdev.set_master();
+
+            let request = pf_api.submit(pdev)?;

[Severity: Medium]
Similarly, does this code leak the enable_cnt and leave bus mastering active if pf_api.submit(pdev)? fails and propagates an error?

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+            dev_info!(pdev, "Submitted request {} through PF data.\n", request);
+
+            Ok(try_pin_init!(VfDriverData { pdev: pdev.into() }))
+        })
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for PfDriverData<'_> {
+    fn drop(self: Pin<&mut Self>) {

samples/rust/rust_driver_sriov.rs line 222

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+        dev_info!(self.pdev, "Remove Rust SR-IOV PF sample.\n");
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for VfDriverData {
+    fn drop(self: Pin<&mut Self>) {
+        dev_info!(self.pdev, "Remove Rust SR-IOV VF sample.\n");
+    }
+}

[Severity: Medium]
During driver teardown, does the PinnedDrop implementation successfully execute but fail to disable the device memory and clear the master bit?

Leaving these resources enabled leads to an unbalanced enable_cnt which prevents correct hot-unplug behavior and resource releasing.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=10
maintainer
#29 lore raw

Re: [PATCH v3 10/10] samples: rust: add Rust SR-IOV VF driver sample

In reply to Zhi Wang

samples/rust/rust_driver_sriov.rs line 68

Quoted 3 lines · Zhi Wang
samples/rust/rust_driver_sriov.rs new file +3 -0
+#[pin_data(PinnedDrop)]
+struct PfDriverData<'bound> {
+    // Keep the device alive until the registration stops exposing `PfApi::pdev`.

What does this mean?

samples/rust/rust_driver_sriov.rs line 71

Quoted 3 lines
samples/rust/rust_driver_sriov.rs new file +3 -0
+    #[pin]
+    _registration: pci::VfRegistration<'bound, PfApiForLt>,
+    pdev: ARef<pci::Device>,

pdev: &'bound pci::Device<Bound>,

samples/rust/rust_driver_sriov.rs line 76

Quoted 5 lines
samples/rust/rust_driver_sriov.rs new file +5 -0
+}
+
+#[pin_data(PinnedDrop)]
+struct VfDriverData {
+    pdev: ARef<pci::Device>,

pdev: &'bound pci::Device<Bound>,

Quoted 11 lines
samples/rust/rust_driver_sriov.rs new file +11 -0
+}
+
+kernel::pci_device_table!(
+    PF_TABLE,
+    <SamplePfDriver as pci::Driver>::IdInfo,
+    [(
+        // E1000_DEV_ID_82576
+        pci::DeviceId::from_id(pci::Vendor::INTEL, 0x10c9),
+        ()
+    )]
+);

samples/rust/rust_driver_sriov.rs line 97

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+
+kernel::pci_device_table!(
+    VF_TABLE,
+    <SampleVfDriver as pci::Driver>::IdInfo,
+    [(
+        // E1000_DEV_ID_82576_VF
+        pci::DeviceId::from_id(pci::Vendor::INTEL, 0x10ca),
+        ()
+    )]
+);

Since this is two drivers in the same module, can we please put everythng that belongs to the PF driver first and then everything that belongs into the VF driver second please? We could also make it two separate files.

Quoted 30 lines
samples/rust/rust_driver_sriov.rs new file +30 -0
+#[vtable]
+impl pci::Driver for SamplePfDriver {
+    type IdInfo = ();
+    type Data<'bound> = PfDriverData<'bound>;
+
+    const ID_TABLE: pci::IdTable<Self::IdInfo> = &PF_TABLE;
+
+    fn probe<'bound>(
+        pdev: &'bound pci::Device<Core<'_>>,
+        _info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
+        pin_init::pin_init_scope(move || {
+            dev_info!(
+                pdev,
+                "Probe Rust SR-IOV PF sample (PCI ID: {}, 0x{:x}).\n",
+                pdev.vendor_id(),
+                pdev.device_id()
+            );
+
+            pdev.enable_device_mem()?;
+            pdev.set_master();
+
+            Ok(try_pin_init!(PfDriverData {
+                // SAFETY:
+                // - probe has exclusive access to this PF before SR-IOV is enabled;
+                // - the registration is pinned in the PF driver data and dropped before `pdev`;
+                // - no other registration is created for this PF; and
+                // - VFs are enabled only after probe by `sriov_enable`.
+                _registration <- unsafe {
+                    pci::VfRegistration::new(

samples/rust/rust_driver_sriov.rs line 138

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+                        pdev,
+                        try_pin_init!(PfApi {
+                            pdev,
+                            requests <- new_mutex!(0),
+                        }),
+                    )
+                },
+                pdev: pdev.into(),
+            }))
+        })

Let's put everything within a single try_pin_init!() block please.

Quoted 20 lines
samples/rust/rust_driver_sriov.rs new file +20 -0
+#[vtable]
+impl pci::Driver for SampleVfDriver {
+    type IdInfo = ();
+    type Data<'bound> = VfDriverData;
+
+    const ID_TABLE: pci::IdTable<Self::IdInfo> = &VF_TABLE;
+
+    fn probe<'bound>(
+        pdev: &'bound pci::Device<Core<'_>>,
+        _info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
+        pin_init::pin_init_scope(move || {
+            dev_info!(
+                pdev,
+                "Probe Rust SR-IOV VF sample (PCI ID: {}, 0x{:x}).\n",
+                pdev.vendor_id(),
+                pdev.device_id()
+            );
+
+            let pdev_bound: &'bound pci::Device<Bound> = pdev;

samples/rust/rust_driver_sriov.rs line 206

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+            let pf_api = pdev_bound.vf_registration_data::<PfApiForLt>()?;
+
+            pdev.enable_device_mem()?;
+            pdev.set_master();
+
+            let request = pf_api.submit(pdev)?;
+            dev_info!(pdev, "Submitted request {} through PF data.\n", request);
+
+            Ok(try_pin_init!(VfDriverData { pdev: pdev.into() }))
+        })

Same here. Also no need for pdev_bound, it derefs automatically, you can just call pdev.vf_registration_data().

Quoted 6 lines
samples/rust/rust_driver_sriov.rs new file +6 -0
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for PfDriverData<'_> {
+    fn drop(self: Pin<&mut Self>) {

samples/rust/rust_driver_sriov.rs line 222

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+        dev_info!(self.pdev, "Remove Rust SR-IOV PF sample.\n");
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for VfDriverData {
+    fn drop(self: Pin<&mut Self>) {
+        dev_info!(self.pdev, "Remove Rust SR-IOV VF sample.\n");
+    }
+}

Let's drop those, we don't really need them.

Here's the two probe() functions I came up with:

    fn probe<'bound>(
        pdev: &'bound pci::Device<Core<'_>>,
        _info: Option<&'bound Self::IdInfo>,
    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
        try_pin_init!(PfDriverData {
            _: {
                dev_info!(
                    pdev,
                    "Probe Rust SR-IOV PF sample (PCI ID: {}, 0x{:x}).\n",
                    pdev.vendor_id(),
                    pdev.device_id()
                );
            },

            _: {
                pdev.enable_device_mem()?;
                pdev.set_master();
            },

            // SAFETY:
            // - probe has exclusive access to this PF before SR-IOV is enabled;
            // - the registration is pinned in the PF driver data and dropped before `pdev`;
            // - no other registration is created for this PF; and
            // - VFs are enabled only after probe by `sriov_enable`.
            _registration <- unsafe {
                pci::VfRegistration::new(
                    pdev,
                    try_pin_init!(PfApi {
                        pdev,
                        requests <- new_mutex!(0),
                    }),
                )
            },

            pdev,
        })
    }

    fn probe<'bound>(
        pdev: &'bound pci::Device<Core<'_>>,
        _info: Option<&'bound Self::IdInfo>,
    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
        try_pin_init!(VfDriverData {
            _: {
                dev_info!(
                    pdev,
                    "Probe Rust SR-IOV VF sample (PCI ID: {}, 0x{:x}).\n",
                    pdev.vendor_id(),
                    pdev.device_id()
                );
            },

            _: {
                pdev.enable_device_mem()?;
                pdev.set_master();
            },

            pdev,

            _: {
                let pf_api = pdev.vf_registration_data::<PfApiForLt>()?;

                let request = pf_api.submit(pdev)?;
                dev_info!(pdev, "Submitted request {} through PF data.\n", request);
            },
        })
    }

Note that enable_device() and set_master() have their own block as they will be replaced with a pci::DeviceEnableGuard soon.

author
#30 lore raw

Re: [PATCH v3 10/10] samples: rust: add Rust SR-IOV VF driver sample

Quoted 1 line · On Wed, 30 Sep 2026 17:15:00 +0200 "Danilo Krummrich" <[email protected]> wrote:

On Wed Sep 30, 2026 at 12:18 PM CEST, Zhi Wang wrote:

<snip>

Quoted 6 lines
Quoted 6 lines
samples/rust/rust_driver_sriov.rs new file +6 -0
+kernel::pci_device_table!(
+    VF_TABLE,
+    <SampleVfDriver as pci::Driver>::IdInfo,
+    [(
+        // E1000_DEV_ID_82576_VF
+        pci::DeviceId::from_id(pci::Vendor::INTEL, 0x10ca),
Quoted 9 lines

samples/rust/rust_driver_sriov.rs line 87

Quoted 3 lines
samples/rust/rust_driver_sriov.rs new file +3 -0
+        ()
+    )]
+);

Since this is two drivers in the same module, can we please put everythng that belongs to the PF driver first and then everything that belongs into the VF driver second please? We could also make it two separate files.

I think it is a great idea. I will split them in the next spin.

Quoted 175 lines
Quoted 35 lines
samples/rust/rust_driver_sriov.rs new file +15 -0
+#[vtable]
+impl pci::Driver for SamplePfDriver {
+    type IdInfo = ();
+    type Data<'bound> = PfDriverData<'bound>;
+
+    const ID_TABLE: pci::IdTable<Self::IdInfo> = &PF_TABLE;
+
+    fn probe<'bound>(
+        pdev: &'bound pci::Device<Core<'_>>,
+        _info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
+        pin_init::pin_init_scope(move || {
+            dev_info!(
+                pdev,
+                "Probe Rust SR-IOV PF sample (PCI ID: {},

0x{:x}).\n",

samples/rust/rust_driver_sriov.rs new file +10 -0
+                pdev.vendor_id(),
+                pdev.device_id()
+            );
+
+            pdev.enable_device_mem()?;
+            pdev.set_master();
+
+            Ok(try_pin_init!(PfDriverData {
+                // SAFETY:
+                // - probe has exclusive access to this PF before

SR-IOV is enabled;
+ // - the registration is pinned in the PF driver
data and dropped before pdev;
+ // - no other registration is created for this PF;
and
+ // - VFs are enabled only after probe by
sriov_enable.

samples/rust/rust_driver_sriov.rs new file +2 -0
+                _registration <- unsafe {
+                    pci::VfRegistration::new(

samples/rust/rust_driver_sriov.rs line 138

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+                        pdev,
+                        try_pin_init!(PfApi {
+                            pdev,
+                            requests <- new_mutex!(0),
+                        }),
+                    )
+                },
+                pdev: pdev.into(),
+            }))
+        })

Let's put everything within a single try_pin_init!() block please.

Quoted 29 lines
samples/rust/rust_driver_sriov.rs new file +15 -0
+#[vtable]
+impl pci::Driver for SampleVfDriver {
+    type IdInfo = ();
+    type Data<'bound> = VfDriverData;
+
+    const ID_TABLE: pci::IdTable<Self::IdInfo> = &VF_TABLE;
+
+    fn probe<'bound>(
+        pdev: &'bound pci::Device<Core<'_>>,
+        _info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
+        pin_init::pin_init_scope(move || {
+            dev_info!(
+                pdev,
+                "Probe Rust SR-IOV VF sample (PCI ID: {},

0x{:x}).\n",

samples/rust/rust_driver_sriov.rs new file +6 -0
+                pdev.vendor_id(),
+                pdev.device_id()
+            );
+
+            let pdev_bound: &'bound pci::Device<Bound> = pdev;
+            let pf_api =

pdev_bound.vf_registration_data::<PfApiForLt>()?; +

samples/rust/rust_driver_sriov.rs new file +5 -0
+            pdev.enable_device_mem()?;
+            pdev.set_master();
+
+            let request = pf_api.submit(pdev)?;
+            dev_info!(pdev, "Submitted request {} through PF

data.\n", request); +

Review

Quoted 2 lines
(quoted diff) +2 -0
+            Ok(try_pin_init!(VfDriverData { pdev: pdev.into() }))
+        })

Same here. Also no need for pdev_bound, it derefs automatically, you can just call pdev.vf_registration_data().

Quoted 6 lines
samples/rust/rust_driver_sriov.rs new file +6 -0
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for PfDriverData<'_> {
+    fn drop(self: Pin<&mut Self>) {

samples/rust/rust_driver_sriov.rs line 222

Quoted 10 lines
samples/rust/rust_driver_sriov.rs new file +10 -0
+        dev_info!(self.pdev, "Remove Rust SR-IOV PF sample.\n");
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for VfDriverData {
+    fn drop(self: Pin<&mut Self>) {
+        dev_info!(self.pdev, "Remove Rust SR-IOV VF sample.\n");
+    }
+}

Let's drop those, we don't really need them.

Here's the two probe() functions I came up with:

    fn probe<'bound>(
        pdev: &'bound pci::Device<Core<'_>>,
        _info: Option<&'bound Self::IdInfo>,
    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
        try_pin_init!(PfDriverData {
            _: {
                dev_info!(
                    pdev,
                    "Probe Rust SR-IOV PF sample (PCI ID: {},
0x{:x}).\n", pdev.vendor_id(),
                    pdev.device_id()
                );
            },

            _: {
                pdev.enable_device_mem()?;
                pdev.set_master();
            },

            // SAFETY:
            // - probe has exclusive access to this PF before SR-IOV
is enabled; // - the registration is pinned in the PF driver data and
dropped before `pdev`; // - no other registration is created for this
PF; and // - VFs are enabled only after probe by `sriov_enable`.
            _registration <- unsafe {
                pci::VfRegistration::new(
                    pdev,
                    try_pin_init!(PfApi {
                        pdev,
                        requests <- new_mutex!(0),
                    }),
                )
            },

            pdev,
        })
    }

    fn probe<'bound>(
        pdev: &'bound pci::Device<Core<'_>>,
        _info: Option<&'bound Self::IdInfo>,
    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
        try_pin_init!(VfDriverData {
            _: {
                dev_info!(
                    pdev,
                    "Probe Rust SR-IOV VF sample (PCI ID: {},
0x{:x}).\n", pdev.vendor_id(),
                    pdev.device_id()
                );
            },

            _: {
                pdev.enable_device_mem()?;
                pdev.set_master();
            },

            pdev,

            _: {
                let pf_api =

pdev.vf_registration_data::<PfApiForLt>()?;

let request = pf_api.submit(pdev)?; dev_info!(pdev, "Submitted request {} through PF
data.\n", request); },

        })
    }

Note that enable_device() and set_master() have their own block as they will be replaced with a pci::DeviceEnableGuard soon.