[PATCH v4 9/9] samples: rust: add Rust SR-IOV PF and VF driver samples

From: Zhi Wang

Date: Sun Oct 04 2026 - 08:15:01 EST


From: Peter Colberg <peter@xxxxxxxxxxx>

Add separate PF and VF driver modules that demonstrate SR-IOV using
QEMU's 82576 (igb) emulation.

The PF publishes a pinned API object in its private data and exposes a
submit_request() helper. The VF calls this helper with its Bound device
borrow. The helper performs the typed registration lookup in the PF
crate, keeping the API object and its lifetime encoding private. The PF
also reads the same object through its pinned registration borrow.

Adapt the nova-core/nova-drm rules in drivers/gpu/Makefile, preserving
their metadata, export-selection and MODVERSIONS recipes with the sample
object names and paths. Set the PF crate name explicitly because its Rust
root file has a _main suffix.

Keep the helper out of line so the VF module has a real symbol dependency
on the PF module. The module loader therefore holds the PF module while
the VF module is loaded, and modprobe loads the dependency automatically.

Each driver has its own module registration. The VF module can be unloaded
and reloaded while the PF remains bound. Unbinding the PF disables SR-IOV
and removes its VFs before releasing the registered data. The shared
Kconfig option builds both drivers as modules or both into the kernel.

Link: https://www.qemu.org/docs/master/system/devices/igb.html
Signed-off-by: Peter Colberg <peter@xxxxxxxxxxx>
Co-developed-by: Zhi Wang <zhiw@xxxxxxxxxx>
Signed-off-by: Zhi Wang <zhiw@xxxxxxxxxx>
---
MAINTAINERS | 1 +
samples/rust/Kconfig | 12 ++
samples/rust/Makefile | 50 ++++++
samples/rust/rust_driver_sriov_pf_exports.c | 9 +
samples/rust/rust_driver_sriov_pf_main.rs | 173 ++++++++++++++++++++
samples/rust/rust_driver_sriov_vf.rs | 70 ++++++++
6 files changed, 315 insertions(+)
create mode 100644 samples/rust/rust_driver_sriov_pf_exports.c
create mode 100644 samples/rust/rust_driver_sriov_pf_main.rs
create mode 100644 samples/rust/rust_driver_sriov_vf.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 5534401fdcce..15298da872a7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21136,6 +21136,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_*

PCIE BANDWIDTH CONTROLLER
M: Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx>
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 31d62533ef25..00e20e9f1222 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -128,6 +128,18 @@ config SAMPLE_RUST_DRIVER_PLATFORM

If unsure, say N.

+config SAMPLE_RUST_DRIVER_SRIOV
+ tristate "SR-IOV PF and VF Drivers"
+ depends on PCI_IOV
+ help
+ This option builds the Rust SR-IOV PF and VF driver samples.
+
+ To compile these as modules, choose M here:
+ the modules will be called rust_driver_sriov_pf and rust_driver_sriov_vf.
+ The VF module depends on the PF module.
+
+ If unsure, say N.
+
config SAMPLE_RUST_DRIVER_USB
tristate "USB Driver"
depends on USB = y
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index b986b681cde5..f7142bc4ddc3 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -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_pf.o rust_driver_sriov_vf.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
@@ -20,4 +21,53 @@ obj-$(CONFIG_SAMPLE_RUST_SOC) += rust_soc.o

rust_print-y := rust_print_main.o rust_print_events.o

+rust_driver_sriov_pf-y := rust_driver_sriov_pf_main.o rust_driver_sriov_pf_exports.o
+
+# Follow the nova-core/nova-drm cross-crate rules in drivers/gpu/Makefile.
+# Export Rust symbols from the PF only if the VF actually references them.
+sriov-pf-export-deps := $(if $(CONFIG_SAMPLE_RUST_DRIVER_SRIOV),$(obj)/rust_driver_sriov_vf.o)
+
+rust_needed_exports = \
+ { $(if $(strip $(2)),$(NM) -u $(2);,) echo "__DEFINED_RUST_SYMBOLS__"; \
+ $(NM) -p --defined-only $(1); } | \
+ awk -v fmt='$(3)' ' \
+ /^__DEFINED_RUST_SYMBOLS__$$/ { defs = 1; next } \
+ !defs { if ($$NF ~ /^_R/) needed[$$NF] = 1; next } \
+ defs && $$2 ~ /(T|R|D|B)/ && $$3 ~ /^_R/ && \
+ $$3 !~ /_(init|cleanup)_module$$/ && \
+ $$3 !~ /__(pfx|cfi|odr_asan)/ && \
+ $$3 in needed { printf fmt, $$3 } \
+ '
+
+quiet_cmd_exports = EXPORTS $@
+ cmd_exports = \
+ $(call rust_needed_exports,$<,$(sriov-pf-export-deps),EXPORT_SYMBOL_RUST_GPL(%s);\n) > $@
+
+$(obj)/exports_rust_driver_sriov_pf_generated.h: $(obj)/rust_driver_sriov_pf_main.o $(sriov-pf-export-deps) FORCE
+ $(call if_changed,exports)
+
+targets += exports_rust_driver_sriov_pf_generated.h
+
+$(obj)/rust_driver_sriov_pf_exports.o: $(obj)/exports_rust_driver_sriov_pf_generated.h
+CFLAGS_rust_driver_sriov_pf_exports.o := -I $(objtree)/$(obj)
+
+ifdef CONFIG_MODVERSIONS
+# The C export shim declares Rust symbols as `extern int`, so reuse its export
+# list but generate symbol CRCs from the Rust object instead of the shim's DWARF.
+$(obj)/rust_driver_sriov_pf_exports.o: private cmd_gensymtypes_c = \
+ $(call getexportsymbols,\1) | \
+ $(objtree)/scripts/gendwarfksyms/gendwarfksyms \
+ $(if $(KBUILD_GENDWARFKSYMS_STABLE), --stable) \
+ $(if $(KBUILD_SYMTYPES), --symtypes $(@:.o=.symtypes),) \
+ $(obj)/rust_driver_sriov_pf_main.o
+endif
+
+# Output the PF's crate metadata for use by the VF at compile time.
+RUSTFLAGS_rust_driver_sriov_pf_main.o += --crate-name rust_driver_sriov_pf \
+ --emit=metadata=$(objtree)/$(obj)/librust_driver_sriov_pf.rmeta
+
+# Allow the VF to import the PF's types.
+$(obj)/rust_driver_sriov_vf.o: $(obj)/rust_driver_sriov_pf_main.o
+RUSTFLAGS_rust_driver_sriov_vf.o := -L $(objtree)/$(obj) --extern rust_driver_sriov_pf
+
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
diff --git a/samples/rust/rust_driver_sriov_pf_exports.c b/samples/rust/rust_driver_sriov_pf_exports.c
new file mode 100644
index 000000000000..49e435f8f6ec
--- /dev/null
+++ b/samples/rust/rust_driver_sriov_pf_exports.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Export the PF crate's symbols used by the VF driver. */
+
+#include <linux/export.h>
+
+#define EXPORT_SYMBOL_RUST_GPL(sym) extern int sym; EXPORT_SYMBOL_GPL(sym)
+
+#include "exports_rust_driver_sriov_pf_generated.h"
diff --git a/samples/rust/rust_driver_sriov_pf_main.rs b/samples/rust/rust_driver_sriov_pf_main.rs
new file mode 100644
index 000000000000..2fb21ff27a49
--- /dev/null
+++ b/samples/rust/rust_driver_sriov_pf_main.rs
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust SR-IOV driver sample based on QEMU's 82576 ([igb]) emulation.
+//!
+//! To make this driver probe, QEMU must be run with `-device igb`.
+//!
+//! Further, enable [vIOMMU] with interrupt remapping using, e.g.,
+//!
+//! `-M q35,accel=kvm,kernel-irqchip=split -device intel-iommu,intremap=on,caching-mode=on`
+//!
+//! and append `intel_iommu=on` to the guest kernel arguments.
+//!
+//! Load `rust_driver_sriov_vf` with `modprobe` to load both the PF and VF drivers.
+//! The VF module depends on the PF module through [`submit_request()`].
+//!
+//! [igb]: https://www.qemu.org/docs/master/system/devices/igb.html
+//! [vIOMMU]: https://wiki.qemu.org/Features/VT-d
+
+use kernel::{
+ device::{
+ Bound,
+ Core, //
+ },
+ new_mutex,
+ pci,
+ prelude::*,
+ sync::Mutex,
+ types::CovariantForLt, //
+};
+
+struct SamplePfDriver;
+
+#[pin_data]
+struct PfApi<'bound> {
+ pdev: &'bound pci::Device<Bound>,
+ #[pin]
+ requests: Mutex<u64>,
+}
+
+type PfApiForLt = CovariantForLt!(PfApi<'_>);
+
+impl PfApi<'_> {
+ fn submit(self: Pin<&Self>, vf: &pci::Device<Bound>) -> Result<u64> {
+ let mut requests = self.requests.lock();
+ let request = requests.checked_add(1).ok_or(EOVERFLOW)?;
+ *requests = request;
+ drop(requests);
+
+ dev_info!(
+ self.pdev,
+ "Handle PF request {} from VF devfn {:#x}.\n",
+ request,
+ vf.dev_id()
+ );
+
+ Ok(request)
+ }
+}
+
+/// Submits a request to the PF associated with a bound VF.
+// Keep this call out of line so the VF module has a symbol dependency on the PF module.
+#[inline(never)]
+pub fn submit_request(vf: &pci::Device<Bound>) -> Result<u64> {
+ vf.vf_registration_data::<PfApiForLt>()?.submit(vf)
+}
+
+#[pin_data]
+struct PfDriverData<'bound> {
+ #[pin]
+ registration: pci::VfRegistration<'bound, PfApiForLt>,
+ pdev: &'bound pci::Device<Bound>,
+}
+
+impl<'bound> PfDriverData<'bound> {
+ fn api(self: Pin<&Self>) -> Pin<&PfApi<'bound>> {
+ // SAFETY: `registration` is structurally pinned.
+ unsafe { self.map_unchecked(|data| &data.registration) }.data()
+ }
+}
+
+kernel::pci_device_table!(
+ PF_TABLE,
+ <SamplePfDriver as pci::Driver>::IdInfo,
+ [(
+ // E1000_DEV_ID_82576
+ pci::DeviceId::from_id(pci::Vendor::INTEL, 0x10c9),
+ ()
+ )]
+);
+
+#[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 {
+ try_pin_init!(PfDriverData {
+ _: {
+ dev_info!(
+ pdev,
+ "Probe Rust SR-IOV PF sample (PCI ID: {}, 0x{:x}).\n",
+ pdev.vendor_id(),
+ pdev.device_id()
+ );
+ },
+ // SAFETY: The registration is embedded in the PF driver's private data.
+ registration <- unsafe {
+ pci::VfRegistration::new(
+ pdev,
+ try_pin_init!(PfApi {
+ pdev,
+ requests <- new_mutex!(0),
+ }),
+ )
+ },
+ _: {
+ pdev.enable_device_mem()?;
+ pdev.set_master();
+ },
+ pdev,
+ })
+ }
+
+ fn sriov_enable<'a>(
+ dev: &'a pci::Device<Core<'_>>,
+ data: Pin<&Self::Data<'a>>,
+ token: pci::SriovEnable<'a>,
+ ) -> Result<pci::SriovEnabled<'a>> {
+ dev_info!(
+ data.pdev,
+ "Enable SR-IOV (PCI ID: {}, 0x{:x}).\n",
+ data.pdev.vendor_id(),
+ data.pdev.device_id()
+ );
+
+ let num_vfs = token.num_vfs();
+ let enabled = token.enable(num_vfs)?;
+ assert_eq!(dev.num_vf(), num_vfs);
+ let api = data.api();
+ dev_info!(dev, "PF handled {} VF requests.\n", *api.requests.lock());
+ Ok(enabled)
+ }
+
+ fn sriov_disable<'a>(
+ dev: &'a pci::Device<Core<'_>>,
+ data: Pin<&Self::Data<'a>>,
+ token: pci::SriovDisable<'a>,
+ ) -> Result {
+ dev_info!(
+ data.pdev,
+ "Disable SR-IOV (PCI ID: {}, 0x{:x}).\n",
+ data.pdev.vendor_id(),
+ data.pdev.device_id()
+ );
+
+ token.disable();
+ assert_eq!(dev.num_vf(), 0);
+ Ok(())
+ }
+}
+
+kernel::module_pci_driver! {
+ type: SamplePfDriver,
+ name: "rust_driver_sriov_pf",
+ authors: ["Peter Colberg"],
+ description: "Rust SR-IOV PF driver",
+ license: "GPL v2",
+}
diff --git a/samples/rust/rust_driver_sriov_vf.rs b/samples/rust/rust_driver_sriov_vf.rs
new file mode 100644
index 000000000000..bc8e5fdc8ce7
--- /dev/null
+++ b/samples/rust/rust_driver_sriov_vf.rs
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Virtual Function driver for the Rust SR-IOV sample.
+
+use kernel::{
+ device::{
+ Bound,
+ Core, //
+ },
+ pci,
+ prelude::*, //
+};
+
+struct SampleVfDriver;
+
+#[pin_data]
+struct VfDriverData<'bound> {
+ pdev: &'bound pci::Device<Bound>,
+}
+
+kernel::pci_device_table!(
+ VF_TABLE,
+ <SampleVfDriver as pci::Driver>::IdInfo,
+ [(
+ // E1000_DEV_ID_82576_VF
+ pci::DeviceId::from_id(pci::Vendor::INTEL, 0x10ca),
+ ()
+ )]
+);
+
+#[vtable]
+impl pci::Driver for SampleVfDriver {
+ type IdInfo = ();
+ type Data<'bound> = VfDriverData<'bound>;
+
+ 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 {
+ try_pin_init!(VfDriverData {
+ _: {
+ dev_info!(
+ pdev,
+ "Probe Rust SR-IOV VF sample (PCI ID: {}, 0x{:x}).\n",
+ pdev.vendor_id(),
+ pdev.device_id()
+ );
+ },
+ _: {
+ let request = rust_driver_sriov_pf::submit_request(pdev)?;
+ dev_info!(pdev, "Submitted request {} through PF data.\n", request);
+ },
+ _: {
+ pdev.enable_device_mem()?;
+ pdev.set_master();
+ },
+ pdev,
+ })
+ }
+}
+
+kernel::module_pci_driver! {
+ type: SampleVfDriver,
+ name: "rust_driver_sriov_vf",
+ authors: ["Peter Colberg"],
+ description: "Rust SR-IOV VF driver",
+ license: "GPL v2",
+}
--
2.53.0