From: Zhi Wang <zhiw@nvidia.com>
To: <dakr@kernel.org>, <acourbot@nvidia.com>
Cc: <alex@shazbot.org>, <jgg@nvidia.com>, <yishaih@nvidia.com>,
<skolothumtho@nvidia.com>, <kevin.tian@intel.com>,
<airlied@gmail.com>, <simona@ffwll.ch>, <ojeda@kernel.org>,
<alex.gaynor@gmail.com>, <boqun.feng@gmail.com>,
<gary@garyguo.net>, <bjorn3_gh@protonmail.com>,
<lossin@kernel.org>, <a.hindborg@kernel.org>,
<aliceryhl@google.com>, <tmgross@umich.edu>,
<jhubbard@nvidia.com>, <ecourtney@nvidia.com>, <cjia@nvidia.com>,
<smitra@nvidia.com>, <kjaju@nvidia.com>, <alkumar@nvidia.com>,
<ankita@nvidia.com>, <aniketa@nvidia.com>, <kwankhede@nvidia.com>,
<targupta@nvidia.com>, <nova-gpu@lists.linux.dev>,
<linux-kernel@vger.kernel.org>, <rust-for-linux@vger.kernel.org>,
<zhiwang@kernel.org>, Zhi Wang <zhiw@nvidia.com>
Subject: [PATCH 11/14] rust: pci: add C FFI support to typed SR-IOV PF registration data
Date: Tue, 15 Sep 2026 23:56:55 +0300 [thread overview]
Message-ID: <20260915205659.76841-12-zhiw@nvidia.com> (raw)
In-Reply-To: <20260915205659.76841-1-zhiw@nvidia.com>
Rust VF drivers can borrow PF-owned registration data directly, but C VF
drivers need an ABI-checked operations table and a pinned context.
Place an optional struct rust_ffi descriptor at offset zero in the
registration header while retaining the Rust TypeId. Initialize
registrations created with new() with an empty descriptor, and add
new_ffi() to construct a descriptor for the same pinned payload.
Add pci_iov_borrow_rust_pf_data() to locate the PF descriptor and validate
the token, ABI version, and operations-table size. Require managed SR-IOV
so device-link ordering and registration teardown keep the descriptor and
its context alive until the VF driver is unbound.
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
drivers/pci/iov.c | 51 +++++++++++++++++
include/linux/pci.h | 34 +++++++++++-
rust/kernel/interop/ffi.rs | 15 ++++-
rust/kernel/pci/sriov.rs | 110 ++++++++++++++++++++++++++++---------
4 files changed, 179 insertions(+), 31 deletions(-)
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index ee5eff209e15..0f3ae75b8a7c 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -11,6 +11,7 @@
#include <linux/bits.h>
#include <linux/log2.h>
#include <linux/pci.h>
+#include <linux/rust_ffi.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/export.h>
@@ -80,6 +81,56 @@ void *pci_iov_get_pf_drvdata(struct pci_dev *dev, struct pci_driver *pf_driver)
}
EXPORT_SYMBOL_GPL(pci_iov_get_pf_drvdata);
+#ifdef CONFIG_RUST
+/**
+ * pci_iov_borrow_rust_pf_data - Validate and borrow Rust data from a VF's PF
+ * @dev: VF PCI device
+ * @token: Required FFI ABI token
+ * @abi_major: Required ABI major version
+ * @min_abi_minor: Minimum required ABI minor version
+ * @required_ops_size: Minimum required size of the operations table
+ *
+ * This may be called from a VF driver's probe() callback or from a context in
+ * which the VF driver is known to remain attached. If probe() succeeds, the
+ * returned pointer, its operations table, and its context are borrowed until
+ * the VF driver is fully unbound, including the return of its remove() callback
+ * when present. If probe() fails, the caller must discard the borrow before
+ * returning. The caller must drain all work that can use the FFI before the
+ * borrow ends.
+ *
+ * The PF must publish an immutable descriptor before enabling VFs, use
+ * managed_sriov, and retain the descriptor until its remove() callback.
+ * Managed SR-IOV installs a device link from every VF to its PF before the VF
+ * can probe. The driver core therefore waits for an in-progress VF probe and
+ * unbinds a bound VF before invoking the PF driver's remove() callback.
+ *
+ * Return: A borrowed FFI descriptor, or an ERR_PTR() value on failure.
+ */
+const struct rust_ffi *
+pci_iov_borrow_rust_pf_data(struct pci_dev *dev,
+ const struct rust_ffi_token *token,
+ u16 abi_major, u16 min_abi_minor,
+ size_t required_ops_size)
+{
+ const struct rust_ffi *ffi;
+ struct pci_dev *pf_dev;
+ struct pci_driver *pf_driver;
+
+ if (!dev->is_virtfn)
+ return ERR_PTR(-EINVAL);
+
+ pf_dev = pci_physfn(dev);
+ pf_driver = READ_ONCE(pf_dev->driver);
+ if (!pf_driver || !READ_ONCE(pf_driver->managed_sriov))
+ return ERR_PTR(-ENODEV);
+
+ ffi = READ_ONCE(pf_dev->vf_registration_data_rust);
+ return rust_ffi_borrow(ffi, token, abi_major, min_abi_minor,
+ required_ops_size);
+}
+EXPORT_SYMBOL_GPL(pci_iov_borrow_rust_pf_data);
+#endif
+
/*
* Per SR-IOV spec sec 3.3.10 and 3.3.11, First VF Offset and VF Stride may
* change when NumVFs changes.
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 1ccc7fee7495..4a7189ee247f 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -339,6 +339,8 @@ struct pcie_link_state;
struct pci_sriov;
struct pci_p2pdma;
struct rcec_ea;
+struct rust_ffi;
+struct rust_ffi_token;
/* struct pci_dev - describes a PCI device
*
@@ -352,9 +354,10 @@ struct rcec_ea;
* Such bridges are allocated additional MMIO and bus
* number resources to allow for hierarchy expansion.
* @is_pciehp: PCIe Hot-Plug Capable bridge.
- * @vf_registration_data_rust: Rust registration data published by the PF
- * before enabling VFs and retained until all VFs are
- * removed. The PF driver must use managed_sriov.
+ * @vf_registration_data_rust: Rust registration data beginning with a
+ * struct rust_ffi, published by the PF before
+ * enabling VFs and retained until all VFs are removed.
+ * The PF driver must use managed_sriov.
*/
struct pci_dev {
struct list_head bus_list; /* Node in per-bus list */
@@ -2614,6 +2617,22 @@ int pci_iov_virtfn_bus(struct pci_dev *dev, int id);
int pci_iov_virtfn_devfn(struct pci_dev *dev, int id);
int pci_iov_vf_id(struct pci_dev *dev);
void *pci_iov_get_pf_drvdata(struct pci_dev *dev, struct pci_driver *pf_driver);
+#ifdef CONFIG_RUST
+const struct rust_ffi *
+pci_iov_borrow_rust_pf_data(struct pci_dev *dev,
+ const struct rust_ffi_token *token,
+ u16 abi_major, u16 min_abi_minor,
+ size_t required_ops_size);
+#else
+static inline const struct rust_ffi *
+pci_iov_borrow_rust_pf_data(struct pci_dev *dev,
+ const struct rust_ffi_token *token,
+ u16 abi_major, u16 min_abi_minor,
+ size_t required_ops_size)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+#endif
int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn);
void pci_disable_sriov(struct pci_dev *dev);
@@ -2656,6 +2675,15 @@ static inline void *pci_iov_get_pf_drvdata(struct pci_dev *dev,
return ERR_PTR(-EINVAL);
}
+static inline const struct rust_ffi *
+pci_iov_borrow_rust_pf_data(struct pci_dev *dev,
+ const struct rust_ffi_token *token,
+ u16 abi_major, u16 min_abi_minor,
+ size_t required_ops_size)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
{ return -ENODEV; }
diff --git a/rust/kernel/interop/ffi.rs b/rust/kernel/interop/ffi.rs
index a8c16a29110a..ea23bfecad32 100644
--- a/rust/kernel/interop/ffi.rs
+++ b/rust/kernel/interop/ffi.rs
@@ -99,10 +99,23 @@ pub unsafe trait Abi: 'static {
/// [`struct rust_ffi`](srctree/include/linux/rust_ffi.h). It neither owns nor borrows the
/// operations table or Rust context at the type level. The transport that publishes it must ensure
/// that `ops` remains valid and that `context` remains alive at a stable address until all
-/// consumers have stopped using the descriptor.
+/// consumers have stopped using the descriptor. The default value exposes no operations.
#[repr(transparent)]
pub struct Descriptor(bindings::rust_ffi);
+impl Default for Descriptor {
+ fn default() -> Self {
+ Self(bindings::rust_ffi {
+ token: bindings::rust_ffi_token { high: 0, low: 0 },
+ abi_major: 0,
+ abi_minor: 0,
+ ops_size: 0,
+ ops: core::ptr::null(),
+ context: core::ptr::null(),
+ })
+ }
+}
+
impl Descriptor {
/// Creates a descriptor for a pinned Rust context.
///
diff --git a/rust/kernel/pci/sriov.rs b/rust/kernel/pci/sriov.rs
index efbe444e0733..a2590b66f78b 100644
--- a/rust/kernel/pci/sriov.rs
+++ b/rust/kernel/pci/sriov.rs
@@ -6,6 +6,10 @@
use crate::{
bindings,
device, //
+ interop::ffi::{
+ Abi,
+ Descriptor, //
+ },
prelude::*,
types::{
CovariantForLt,
@@ -91,26 +95,45 @@ pub fn num_vfs(&self) -> i32 {
// depends on its device context.
kernel::impl_device_context_deref!(unsafe { Device });
+#[repr(C)]
+struct VfRegistrationHeader {
+ ffi: Descriptor,
+ type_id: TypeId,
+}
+
+static_assert!(core::mem::offset_of!(VfRegistrationHeader, ffi) == 0);
+
#[repr(C)]
#[pin_data]
struct VfRegistrationData<'a, F: ForLt + 'static> {
- type_id: TypeId,
+ header: VfRegistrationHeader,
#[pin]
data: F::Of<'a>,
}
static_assert!(
- core::mem::offset_of!(VfRegistrationData<'static, CovariantForLt!(())>, type_id) == 0
+ core::mem::offset_of!(VfRegistrationData<'static, CovariantForLt!(())>, header) == 0
);
impl<'a, F: ForLt + 'static> VfRegistrationData<'a, F> {
- fn new<D>(data: D) -> impl PinInit<Self, Error> + use<'a, D, F>
+ fn new<D, M>(data: D, make_descriptor: M) -> impl PinInit<Self, Error> + use<'a, D, F, M>
where
D: PinInit<F::Of<'a>, Error> + 'a,
+ M: FnOnce(Pin<&F::Of<'a>>) -> Descriptor + 'a,
{
- try_pin_init!(Self {
- type_id: TypeId::of::<F>(),
+ try_pin_init!(&this in Self {
+ header: VfRegistrationHeader {
+ ffi: Descriptor::default(),
+ type_id: TypeId::of::<F>(),
+ },
data <- data,
+ _: {
+ // SAFETY: `data` has been initialized in place and will remain pinned at this
+ // address.
+ let data = unsafe { Pin::new_unchecked(&(*this.as_ptr()).data) };
+ // SAFETY: `header.ffi` is initialized and exclusively owned during construction.
+ unsafe { (*this.as_ptr()).header.ffi = make_descriptor(data) };
+ },
})
}
}
@@ -138,28 +161,14 @@ impl<'a, F: ForLt + 'static> VfRegistration<'a, F>
where
for<'b> F::Of<'b>: Send + Sync,
{
- /// Publishes typed PF data for bound VF drivers.
- ///
- /// This returns a pin-initializer so the registration and payload can be embedded directly in
- /// the PF driver's pinned data.
- ///
- /// Initialization returns [`ENODEV`] for a VF and [`EBUSY`] if the PF has enabled VFs or
- /// already has a registration.
- ///
- /// # Safety
- ///
- /// The caller must invoke this during the PCI driver's probe and embed the result in the driver
- /// data. On an SR-IOV PF, no VF may be enabled before probe successfully installs the complete
- /// driver data, and the driver must use managed SR-IOV. The registration must be dropped before
- /// anything its payload borrows and must not be forgotten. Probe must have exclusive access to
- /// the PF registration slot. On a conventional PCI function, the registration remains
- /// inactive.
- pub unsafe fn new<'core, D>(
+ fn new_with_descriptor<'core, D, M>(
pdev: &'a PciDevice<device::Core<'core>>,
data: D,
- ) -> impl PinInit<Self, Error> + use<'a, 'core, D, F>
+ make_descriptor: M,
+ ) -> impl PinInit<Self, Error> + use<'a, 'core, D, F, M>
where
D: PinInit<F::Of<'a>, Error> + 'a,
+ M: FnOnce(Pin<&F::Of<'a>>) -> Descriptor + 'a,
{
pin_init::pin_init_scope(move || {
if pdev.is_virtfn() {
@@ -179,7 +188,7 @@ pub unsafe fn new<'core, D>(
Ok(try_pin_init!(Self {
pdev,
- inner <- VfRegistrationData::new(data),
+ inner <- VfRegistrationData::new(data, make_descriptor),
published,
_pin: PhantomPinned,
_: {
@@ -192,6 +201,53 @@ pub unsafe fn new<'core, D>(
}))
})
}
+
+ /// Publishes typed PF data for bound VF drivers.
+ ///
+ /// This returns a pin-initializer so the registration and payload can be embedded directly in
+ /// the PF driver's pinned data.
+ ///
+ /// Initialization returns [`ENODEV`] for a VF and [`EBUSY`] if the PF has enabled VFs or
+ /// already has a registration.
+ ///
+ /// # Safety
+ ///
+ /// The caller must invoke this during the PCI driver's probe and embed the result in the driver
+ /// data. On an SR-IOV PF, no VF may be enabled before probe successfully installs the complete
+ /// driver data, and the driver must use managed SR-IOV. The registration must be dropped before
+ /// anything its payload borrows and must not be forgotten. Probe must have exclusive access to
+ /// the PF registration slot. On a conventional PCI function, the registration remains
+ /// inactive.
+ pub unsafe fn new<'core, D>(
+ pdev: &'a PciDevice<device::Core<'core>>,
+ data: D,
+ ) -> impl PinInit<Self, Error> + use<'a, 'core, D, F>
+ where
+ D: PinInit<F::Of<'a>, Error> + 'a,
+ {
+ Self::new_with_descriptor(pdev, data, |_| Descriptor::default())
+ }
+
+ /// Publishes typed PF data with an FFI operations table for C VF drivers.
+ ///
+ /// Rust VFs access the same payload through [`PciDevice::vf_registration_data()`] or
+ /// [`PciDevice::vf_registration_data_with()`].
+ ///
+ /// # Safety
+ ///
+ /// The caller must uphold the requirements of [`Self::new()`]. A C consumer must stop calling
+ /// and discard the borrow before returning from a failed VF probe, or before its VF remove
+ /// callback returns after a successful probe.
+ pub unsafe fn new_ffi<'core, A, D>(
+ pdev: &'a PciDevice<device::Core<'core>>,
+ data: D,
+ ) -> impl PinInit<Self, Error> + use<'a, 'core, A, D, F>
+ where
+ A: Abi<Context = F>,
+ D: PinInit<F::Of<'a>, Error> + 'a,
+ {
+ Self::new_with_descriptor(pdev, data, Descriptor::new::<A>)
+ }
}
#[pinned_drop]
@@ -251,9 +307,9 @@ unsafe fn vf_registration_data_pinned<F: ForLt + 'static>(&self) -> Result<Pin<&
return Err(ENOENT);
}
- // SAFETY: The published pointer addresses a `VfRegistrationData`, whose first field is a
- // `TypeId`.
- let type_id = unsafe { ptr.cast::<TypeId>().read() };
+ // SAFETY: The published pointer addresses a `VfRegistrationData`, whose first field is
+ // its header.
+ let type_id = unsafe { (&raw const (*ptr.cast::<VfRegistrationHeader>()).type_id).read() };
if type_id != TypeId::of::<F>() {
return Err(EINVAL);
}
next prev parent reply other threads:[~2026-09-15 20:58 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-15 20:56 [PATCH 00/14] Add Rust PCI SR-IOV support Zhi Wang
2026-09-15 20:56 ` [PATCH 01/14] PCI: add driver flag to opt into disabling SR-IOV on remove() Zhi Wang
2026-09-15 20:56 ` [PATCH 02/14] rust: pci: add {enable,disable}_sriov(), to control SR-IOV capability Zhi Wang
2026-09-15 20:56 ` [PATCH 03/14] rust: pci: add vtable attribute to pci::Driver trait Zhi Wang
2026-09-15 20:56 ` [PATCH 04/14] rust: pci: add bus callback sriov_configure(), to control SR-IOV from sysfs Zhi Wang
2026-09-15 20:56 ` [PATCH 05/14] rust: pci: add is_virtfn(), to check for VFs Zhi Wang
2026-09-15 20:56 ` [PATCH 06/14] rust: pci: add is_physfn(), to check for PFs Zhi Wang
2026-09-15 20:56 ` [PATCH 07/14] rust: pci: add num_vf(), to return number of VFs Zhi Wang
2026-09-15 20:56 ` [PATCH 08/14] rust: pci: add typed SR-IOV PF registration data Zhi Wang
2026-09-15 20:56 ` [PATCH 09/14] samples: rust: add Rust SR-IOV VF driver sample Zhi Wang
2026-09-15 20:56 ` [PATCH 10/14] rust: add C-to-Rust FFI descriptors and trampolines Zhi Wang
2026-09-15 20:56 ` Zhi Wang [this message]
2026-09-15 20:56 ` [PATCH 12/14] samples: rust: add C SR-IOV VF driver that calls into a Rust PF driver Zhi Wang
2026-09-15 20:56 ` [PATCH 13/14] gpu: nova-core: publish typed SR-IOV PF data for VF drivers Zhi Wang
2026-09-15 20:56 ` [PATCH 14/14] Documentation: rust: explain SR-IOV PF data sharing with VFs Zhi Wang
2026-09-16 11:10 ` [PATCH 00/14] Add Rust PCI SR-IOV support Danilo Krummrich
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=20260915205659.76841-12-zhiw@nvidia.com \
--to=zhiw@nvidia.com \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=airlied@gmail.com \
--cc=alex.gaynor@gmail.com \
--cc=alex@shazbot.org \
--cc=aliceryhl@google.com \
--cc=alkumar@nvidia.com \
--cc=aniketa@nvidia.com \
--cc=ankita@nvidia.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=cjia@nvidia.com \
--cc=dakr@kernel.org \
--cc=ecourtney@nvidia.com \
--cc=gary@garyguo.net \
--cc=jgg@nvidia.com \
--cc=jhubbard@nvidia.com \
--cc=kevin.tian@intel.com \
--cc=kjaju@nvidia.com \
--cc=kwankhede@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=nova-gpu@lists.linux.dev \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=skolothumtho@nvidia.com \
--cc=smitra@nvidia.com \
--cc=targupta@nvidia.com \
--cc=tmgross@umich.edu \
--cc=yishaih@nvidia.com \
--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®