Add Rust PCI SR-IOV support
Subsystems (9)
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
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
@@ -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
[email protected]
AI reviewRe: [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@@ -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
-0mod irq;pub use self::id::{rust/kernel/pci/iov.rs new file +0
-0@@ -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
Danilo Krummrich
maintainerRe: [PATCH v3 01/10] rust: pci: add internal SR-IOV enable and disable helpers
In reply to Zhi Wangrust/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) }) + }
PATCH v3 2/10 rust: pci: add vtable attribute to pci::Driver trait
In reply to Zhi WangFrom: 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
[email protected]
AI reviewRe: [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::Driverand its implementations to support the optionalsriov_enable()andsriov_disable()callbacks introduced later in this series.Suggested-by Danilo Krummrich <[email protected]>Signed-off-by Peter Colberg <[email protected]>Signed-off-by Zhi Wang <[email protected]>
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
PATCH v3 3/10 rust: pci: add is_virtfn(), to check for VFs
In reply to Zhi WangFrom: 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
@@ -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
[email protected]
AI reviewRe: [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@@ -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
PATCH v3 4/10 rust: pci: add is_physfn(), to check for PFs
In reply to Zhi WangFrom: 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
@@ -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
[email protected]
AI reviewRe: [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.
Reviewed-by Joel Fernandes <[email protected]>Signed-off-by Peter Colberg <[email protected]>Signed-off-by Zhi Wang <[email protected]>
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
PATCH v3 5/10 rust: pci: add num_vf(), to return number of VFs
In reply to Zhi WangFrom: 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
@@ -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
[email protected]
AI reviewRe: [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@@ -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
Danilo Krummrich
maintainerRe: [PATCH v3 05/10] rust: pci: add num_vf(), to return number of VFs
In reply to Zhi Wangrust/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 ()) } + } +}
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 aPhysical Function (PF).
+ pub fnnum_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 ()) } + } +}
PATCH v3 6/10 rust: pci: drop driver data before remove returns
In reply to Zhi WangDrop 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
@@ -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
@@ -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
[email protected]
AI reviewRe: [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_unbinddoes not release it again. All borrows must end before the data is removed.Suggested-by Danilo Krummrich <[email protected]>Signed-off-by Zhi Wang <[email protected]>
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
PATCH v3 7/10 rust: pci: add typed SR-IOV PF registration data
In reply to Zhi WangRust 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
@@ -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
@@ -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: &'boundDevice <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
[email protected]
AI reviewRe: [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
VfRegistrationto 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
Co-developed-by Danilo Krummrich <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>Signed-off-by Zhi Wang <[email protected]>
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
Danilo Krummrich
maintainerRe: [PATCH v3 07/10] rust: pci: add typed SR-IOV PF registration data
In reply to Zhi WangQuoted 14 lines · Zhi Wang
rust/kernel/pci.rs +2
-0@@ -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: &'aDevice <device::Core <'_>>, + data:impl PinInit <F ::Of <'a>,Error > + 'a, + ) ->impl PinInit <Self ,Error > + 'a { +let pdev: &'aDevice <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 <&'thisF ::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
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@@ -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,
VfRegistrationdisables
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, orFmust 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
physfnunion 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] ifFdoes 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 <&'thisF ::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:CovariantForLtpermits 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
PATCH v3 8/10 rust: pci: add SR-IOV enable and disable tokens
In reply to Zhi WangEnabling 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
@@ -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
[email protected]
AI reviewRe: [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@@ -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
Danilo Krummrich
maintainerRe: [PATCH v3 08/10] rust: pci: add SR-IOV enable and disable tokens
In reply to Zhi WangQuoted 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 <&'callbackDevice <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 (); + } + } +}
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<&'callbackDevice<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_vfsInstead 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 (); + } + } +}
PATCH v3 9/10 rust: pci: add SR-IOV enable and disable callbacks
In reply to Zhi WangFrom: 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
@@ -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: &'boundDevice <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: &'boundDevice <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: &'boundDevice <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
[email protected]
AI reviewRe: [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 intosriov_enable()andsriov_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.
Suggested-by Danilo Krummrich <[email protected]>Signed-off-by Peter Colberg <[email protected]>Signed-off-by Zhi Wang <[email protected]>
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
Danilo Krummrich
maintainerRe: [PATCH v3 09/10] rust: pci: add SR-IOV enable and disable callbacks
In reply to Zhi Wangrust/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: &'boundDevice <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 ()) + } + }) + } +}
PATCH v3 10/10 samples: rust: add Rust SR-IOV VF driver sample
In reply to Zhi WangFrom: 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
@@ -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
@@ -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
@@ -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
[email protected]
AI reviewRe: [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
[ ... ]
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
Danilo Krummrich
maintainerRe: [PATCH v3 10/10] samples: rust: add Rust SR-IOV VF driver sample
In reply to Zhi Wangsamples/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 <&'boundSelf ::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.
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 beforeSR-IOV is enabled;
+ // - the registration is pinned in the PF driver
data and dropped beforepdev;
+ // - no other registration is created for this PF;
and
+ // - VFs are enabled only after probe bysriov_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 PFdata.\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 callpdev.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()andset_master()have their own block as they will be replaced with apci::DeviceEnableGuardsoon.