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. Link: https://www.qemu.org/docs/master/system/devices/igb.html Signed-off-by: Peter Colberg <[email protected]> --- Changes in v2: - Use "kernel vertical" style on imports. - Demonstrate how to reach driver data of PF device from VF device. --- MAINTAINERS | 1 + samples/rust/Kconfig | 11 ++++ samples/rust/Makefile | 1 + samples/rust/rust_driver_sriov.rs | 127 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index b277baee5eb6725b3a5126cefb6eef6190b02413..96dffd777c24473063baaeda017f64b15e8c0ab9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20242,6 +20242,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]> diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index c49ab910634596aea4a1a73dac87585e084f420a..f244df89c4fc9d741915f581de76107e8eb0121b 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -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 diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 6c0aaa58ccccfd12ef019f68ca784f6d977bc668..19d700f8210151e298cc049dacc249a121d0f2c4 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.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 diff --git a/samples/rust/rust_driver_sriov.rs b/samples/rust/rust_driver_sriov.rs new file mode 100644 index 0000000000000000000000000000000000000000..84d057629c7b03d743179a4e05ccc092f814bf6b --- /dev/null +++ b/samples/rust/rust_driver_sriov.rs @@ -0,0 +1,127 @@ +// 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. +//! +//! [igb]: https://www.qemu.org/docs/master/system/devices/igb.html +//! [vIOMMU]: https://wiki.qemu.org/Features/VT-d + +use kernel::{ + device::Core, + pci, + prelude::*, + sync::aref::ARef, // +}; + +use core::any::TypeId; + +#[pin_data(PinnedDrop)] +struct SampleDriver { + pdev: ARef<pci::Device>, + private: TypeId, +} + +kernel::pci_device_table!( + PCI_TABLE, + MODULE_PCI_TABLE, + <SampleDriver as pci::Driver>::IdInfo, + [ + // E1000_DEV_ID_82576 + (pci::DeviceId::from_id(pci::Vendor::INTEL, 0x10c9), ()), + // E1000_DEV_ID_82576_VF + (pci::DeviceId::from_id(pci::Vendor::INTEL, 0x10ca), ()) + ] +); + +#[vtable] +impl pci::Driver for SampleDriver { + type IdInfo = (); + + const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; + + fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> { + pin_init::pin_init_scope(move || { + dev_info!( + pdev.as_ref(), + "Probe Rust SR-IOV driver sample (PCI ID: {}, 0x{:x}).\n", + pdev.vendor_id(), + pdev.device_id() + ); + + if pdev.is_virtfn() { + let physfn = pdev.physfn()?; + let drvdata = physfn.as_ref().drvdata::<Self>()?; + + assert!(physfn.is_physfn()); + + dev_info!( + pdev.as_ref(), + "Parent device is PF (PCI ID: {}, 0x{:x}).\n", + physfn.vendor_id(), + physfn.device_id() + ); + + dev_info!( + pdev.as_ref(), + "We have access to the private data of {:?}.\n", + drvdata.private + ); + } + + pdev.enable_device_mem()?; + pdev.set_master(); + + Ok(try_pin_init!(Self { + pdev: pdev.into(), + private: TypeId::of::<Self>() + })) + }) + } + + fn sriov_configure(pdev: &pci::Device<Core>, nr_virtfn: i32) -> Result<i32> { + assert!(pdev.is_physfn()); + + if nr_virtfn == 0 { + dev_info!( + pdev.as_ref(), + "Disable SR-IOV (PCI ID: {}, 0x{:x}).\n", + pdev.vendor_id(), + pdev.device_id() + ); + pdev.disable_sriov(); + } else { + dev_info!( + pdev.as_ref(), + "Enable SR-IOV (PCI ID: {}, 0x{:x}).\n", + pdev.vendor_id(), + pdev.device_id() + ); + pdev.enable_sriov(nr_virtfn)?; + } + + assert_eq!(pdev.num_vf(), nr_virtfn); + Ok(nr_virtfn) + } +} + +#[pinned_drop] +impl PinnedDrop for SampleDriver { + fn drop(self: Pin<&mut Self>) { + dev_info!(self.pdev.as_ref(), "Remove Rust SR-IOV driver sample.\n"); + } +} + +kernel::module_pci_driver! { + type: SampleDriver, + name: "rust_driver_sriov", + authors: ["Peter Colberg"], + description: "Rust SR-IOV driver", + license: "GPL v2", +} -- 2.52.0
