mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Zhi Wang <zhiw@nvidia.com>
To: <rust-for-linux@vger.kernel.org>, <linux-pci@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>
Cc: <dakr@kernel.org>, <aliceryhl@google.com>, <bhelgaas@google.com>,
	<kwilczynski@kernel.org>, <ojeda@kernel.org>, <boqun@kernel.org>,
	<gary@garyguo.net>, <bjorn3_gh@protonmail.com>,
	<lossin@kernel.org>, <a.hindborg@kernel.org>, <tmgross@umich.edu>,
	<markus.probst@posteo.de>, <cjia@nvidia.com>, <smitra@nvidia.com>,
	<ankita@nvidia.com>, <aniketa@nvidia.com>, <kwankhede@nvidia.com>,
	<targupta@nvidia.com>, <kjaju@nvidia.com>, <alkumar@nvidia.com>,
	<acourbot@nvidia.com>, <jhubbard@nvidia.com>,
	<zhiwang@kernel.org>, <jgg@nvidia.com>, <alex@shazbot.org>,
	Peter Colberg <peter@colberg.org>, Zhi Wang <zhiw@nvidia.com>
Subject: [PATCH v4 9/9] samples: rust: add Rust SR-IOV PF and VF driver samples
Date: Sun, 4 Oct 2026 15:07:30 +0300	[thread overview]
Message-ID: <20261004120732.1045629-10-zhiw@nvidia.com> (raw)
In-Reply-To: <20261004120732.1045629-1-zhiw@nvidia.com>

From: Peter Colberg <peter@colberg.org>

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@colberg.org>
Co-developed-by: Zhi Wang <zhiw@nvidia.com>
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
 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@linux.intel.com>
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


      parent reply	other threads:[~2026-10-04 12:09 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-04 12:07 [PATCH v4 0/9] Add Rust PCI SR-IOV support Zhi Wang
2026-10-04 12:07 ` [PATCH v4 1/9] rust: pci: add internal SR-IOV enable and disable helpers Zhi Wang
2026-10-04 12:07 ` [PATCH v4 2/9] rust: pci: add vtable attribute to pci::Driver trait Zhi Wang
2026-10-04 12:07 ` [PATCH v4 3/9] rust: pci: add is_virtfn(), to check for VFs Zhi Wang
2026-10-04 12:07 ` [PATCH v4 4/9] rust: pci: add num_vf(), to return number of VFs Zhi Wang
2026-10-04 12:07 ` [PATCH v4 5/9] rust: pci: drop driver data before remove returns Zhi Wang
2026-10-04 12:07 ` [PATCH v4 6/9] rust: pci: add typed SR-IOV PF registration data Zhi Wang
2026-10-04 12:07 ` [PATCH v4 7/9] rust: pci: add SR-IOV enable and disable tokens Zhi Wang
2026-10-04 12:07 ` [PATCH v4 8/9] rust: pci: add SR-IOV enable and disable callbacks Zhi Wang
2026-10-04 12:07 ` Zhi Wang [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261004120732.1045629-10-zhiw@nvidia.com \
    --to=zhiw@nvidia.com \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=alex@shazbot.org \
    --cc=aliceryhl@google.com \
    --cc=alkumar@nvidia.com \
    --cc=aniketa@nvidia.com \
    --cc=ankita@nvidia.com \
    --cc=bhelgaas@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=cjia@nvidia.com \
    --cc=dakr@kernel.org \
    --cc=gary@garyguo.net \
    --cc=jgg@nvidia.com \
    --cc=jhubbard@nvidia.com \
    --cc=kjaju@nvidia.com \
    --cc=kwankhede@nvidia.com \
    --cc=kwilczynski@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=markus.probst@posteo.de \
    --cc=ojeda@kernel.org \
    --cc=peter@colberg.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=smitra@nvidia.com \
    --cc=targupta@nvidia.com \
    --cc=tmgross@umich.edu \
    --cc=zhiwang@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®