mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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 14/14] Documentation: rust: explain SR-IOV PF data sharing with VFs
Date: Tue, 15 Sep 2026 23:56:58 +0300	[thread overview]
Message-ID: <20260915205659.76841-15-zhiw@nvidia.com> (raw)
In-Reply-To: <20260915205659.76841-1-zhiw@nvidia.com>

Rust and C VF drivers may need a narrow PF-owned interface without
gaining access to all private data belonging to the PF driver. The
typed PF registration and C FFI abstractions span PCI topology, type
checking, driver lifetime, and synchronization, which need a single
explanation.

The design embeds one pinned PF/VF contract inline in ordinary PCI
driver data. Rust consumers borrow it as `Pin<&T>`; C consumers borrow a
checked operations table and context. Both paths invoke the same Rust
implementation, while managed SR-IOV orders VF teardown before PF data
destruction.

Document the ownership and borrowing model, in-place initialization,
publication and teardown rules, FFI versioning and trampolines, locking
and asynchronous drain requirements, and limitations. Illustrate the
storage, lifetime, and Rust and C call paths with ASCII diagrams.

Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
 Documentation/rust/index.rst             |   1 +
 Documentation/rust/pci-sriov-pf-data.rst | 330 +++++++++++++++++++++++
 MAINTAINERS                              |   1 +
 3 files changed, 332 insertions(+)
 create mode 100644 Documentation/rust/pci-sriov-pf-data.rst

diff --git a/Documentation/rust/index.rst b/Documentation/rust/index.rst
index b78ed0efa784..a3e8924d678a 100644
--- a/Documentation/rust/index.rst
+++ b/Documentation/rust/index.rst
@@ -37,6 +37,7 @@ more details.
     coding-guidelines
     arch-support
     testing
+    pci-sriov-pf-data
 
 You can also find learning materials for Rust in its section in
 :doc:`../process/kernel-docs`.
diff --git a/Documentation/rust/pci-sriov-pf-data.rst b/Documentation/rust/pci-sriov-pf-data.rst
new file mode 100644
index 000000000000..acc6f3582386
--- /dev/null
+++ b/Documentation/rust/pci-sriov-pf-data.rst
@@ -0,0 +1,330 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _pci_rust_sriov_pf_data:
+
+===========================================
+Sharing Rust PF data with SR-IOV VF drivers
+===========================================
+
+An SR-IOV Physical Function (PF) and its Virtual Functions (VFs) are
+independent PCI devices.  Their drivers may live in different modules, and a
+VF driver may be written in either Rust or C.  Nevertheless, a VF often needs
+to invoke a small PF-owned interface for coordination with the physical
+device.
+
+This document describes how a Rust PF driver can publish pinned data for its
+VFs without exposing its complete private ``drvdata``.  It supplements the
+general SR-IOV description in :doc:`../PCI/pci-iov-howto`.
+
+The idea
+========
+
+The PF publishes one deliberately chosen data object through
+``VfRegistration`` before enabling VFs.  The registration and object are
+initialized inline in the PF's pinned driver data.  A Rust VF receives a typed
+``Pin<&T>``.  A C VF receives a checked ``struct rust_ffi`` descriptor whose
+operations call the same object through generated C ABI trampolines.
+
+The published object is an explicit PF/VF contract, not a replacement for PF
+``drvdata``.  PCI supplies the route to the correct PF and orders driver
+teardown; the chosen object supplies only the data and operations that the PF
+intends to share.  VFs borrow that object and never own it.
+
+There is no global interface registry.  PCI topology selects the provider:
+the consumer is a VF and its ``physfn`` identifies the PF.  The consumer path
+then checks that the selected PF published the expected Rust type or C ABI.
+
+::
+
+        PF driver data (pinned)
+        +--------------------------------+
+        | VfRegistration                 |
+        |  +-----------------------------+
+        |  | rust_ffi (at offset 0)      |<----+
+        |  | TypeId | pinned T           |<--+ |
+        |  +-----------------------------+   | |
+        | remaining PF data              |   | |
+        +--------------------------------+   | |
+                                             | |
+        PF struct pci_dev                    | |
+        +----------------------------+       | |
+        | vf_registration_data_rust -+-------+ |
+        +----------------------------+         |
+                                               |
+        Rust VF: TypeId check -> Pin<&T> -------+
+        C VF: token/ABI/size -> ops/context ----+
+
+The design separates four concerns:
+
+* PCI topology selects the actual PF for a VF.
+* Rust ``TypeId`` or the C FFI token and version check the requested
+  interface.
+* A managed device link and the inline registration determine the lifetime
+  of the borrow.
+* The published data type supplies any synchronization needed by concurrent
+  callers.
+
+The FFI descriptor does not hold a second copy of the PF data.  Its
+``context`` points at the same pinned object that a Rust VF borrows directly.
+For example, both Rust and C VFs in the SR-IOV sample eventually call
+``PfApi::submit()``.  The C path adds only an ABI trampoline and return-value
+conversion.
+
+Lifetime foundation
+===================
+
+The published pointer is borrowed; it is not a reference-counted handle.
+Its lifetime is based on managed SR-IOV and a persistent managed device link
+from each VF consumer to its PF supplier.
+
+The PCI core creates that link before the VF is allowed to probe.  The link
+remains after a failed probe or a normal driver unbind so it also protects a
+later bind.  The driver core consequently waits for an in-progress VF probe
+and unbinds every bound VF consumer before unbinding the PF supplier.  On PF
+removal, managed SR-IOV also invokes ``sriov_configure(0)`` before the PF
+driver's remove callback if VFs are still enabled.  VF unbind, including
+destruction of its driver data, completes before PF removal proceeds.
+
+::
+
+        PF probe
+           |
+           +-- initialize and pin VfRegistration and its data
+           |
+           +-- publish as the registration's final initialization step
+           |
+        PF probe returns and installs all PF driver data
+           |
+        sriov_configure(n) enables VFs
+           |
+           +-- PCI creates each VF
+           |
+           +-- PCI adds a managed link: VF consumer -> PF supplier
+           |
+           +-- VF probe borrows and uses the PF data
+           |
+        PF unbind is requested
+           |
+           +-- driver core unbinds every VF consumer
+           |      |
+           |      +-- VF remove stops and drains all PF calls
+           |      |
+           |      +-- VF driver data is destroyed
+           |
+           +-- if VFs remain, PCI invokes sriov_configure(0)
+           |
+           +-- disabling SR-IOV destroys VF devices and links
+           |
+           +-- PF remove runs
+           |
+           +-- PF driver data is destroyed
+                  |
+                  +-- registration disables any remaining VFs
+                  |
+                  +-- registration withdraws and drops the data
+
+Disabling VFs through ``sriov_numvfs`` follows the shorter part of the same
+ordering: VF drivers are removed before their VF devices disappear, while the
+PF driver remains bound and its registration remains published.
+
+If ``sriov_configure(0)`` does not disable all VFs during PF unbind, the PCI
+core warns and forcibly disables SR-IOV.  The lifetime guarantee therefore
+does not depend on a successful driver callback.
+
+A successful VF probe may retain the borrow in its driver data for the
+duration of that binding.  A failed probe must discard the borrow before
+returning.  A VF remove callback must stop and drain all work that could use
+the PF data before the callback returns.  These rules also apply to raw
+descriptor and context pointers retained by a C VF.
+
+Publishing PF data
+==================
+
+PF and VF drivers use the ordinary ``pci::Driver`` abstraction.
+``VfRegistration::new()`` publishes ``ForLt``-encoded data for Rust
+consumers.  ``VfRegistration::new_ffi()`` publishes the same data and adds a
+C-callable FFI descriptor.  Both return a pin-initializer rather than an
+allocated registration handle.  The PF embeds it with ``<-`` in a
+``#[pin]`` field of its driver data::
+
+        #[pin_data]
+        struct PfData<'a> {
+            #[pin]
+            vf_registration: pci::VfRegistration<'a, MyApiForLt>,
+            // Fields borrowed by MyApi follow the registration.
+        }
+
+The constructor rejects a VF.  On a conventional PCI function without an
+SR-IOV capability it creates an inactive registration, allowing one PF-side
+driver to continue supporting devices with and without SR-IOV.
+
+The constructors are unsafe because the PF driver establishes conditions
+that cannot be expressed entirely in the type system.  A provider must:
+
+* Call the constructor during PCI probe, before any VF can be enabled.  It
+  publishes only when that function is an SR-IOV PF.
+* Publish at most one registration for a PF.
+* Initialize it in the pinned PF driver data and do not forget that data.
+* Enable VFs only after PF probe has returned and installed that driver data.
+* Use managed SR-IOV so VF consumers are unbound before the registration is
+  dropped.
+* Declare it before any PF driver fields borrowed by the published object, so
+  the registration is dropped first.
+
+The published type must be ``Send + Sync`` for every lifetime because VFs may
+call it from different threads.
+
+The Rust PCI adapter opts drivers into managed SR-IOV.  Its
+``sriov_configure`` callback receives a checked ``pci::sriov::Device`` and a
+pinned reference to the PF driver data.  It enables or disables VFs with
+``enable_sriov()`` and ``disable_sriov()``.
+
+Rust VF consumers
+=================
+
+A Rust VF implements ``pci::Driver`` and explicitly requests PF data during
+probe.  The accessor verifies that the PCI device is a VF, follows its PF
+relationship, checks that data was published, compares its ``TypeId``, and
+returns a pinned shared reference.  The VF does not receive the PF's
+``pci::Device``, the PF driver object, or an untyped pointer.
+
+PF and VF drivers may be registered by separate modules.  They must share the
+exact ``ForLt`` type that identifies the PF data.  Defining look-alike types
+independently does not work because they have different ``TypeId`` values.
+When separate Rust crates are used, put the shared definition in a crate that
+both can import.
+
+``vf_registration_data()`` is the direct accessor for data encoded by
+``CovariantForLt``.  ``vf_registration_data_with()`` supports invariant
+data; its higher-ranked closure prevents that data from escaping with a
+shortened lifetime.  A domain-specific VF handle may store only the VF device
+and use the closure accessor for each operation, rather than retaining a
+separate raw PF pointer.
+
+If one module registers both drivers, register the VF driver first.  This
+ensures that it is ready before the PF can enable VFs.  PF-only and VF-only
+modules register their ordinary PCI drivers independently.
+
+C VF consumers
+==============
+
+The common C descriptor is declared in ``include/linux/rust_ffi.h``::
+
+        struct rust_ffi
+        +---------------------------------------------------+
+        | token | ABI version | ops size | ops | context    |
+        +---------------------------------------------------+
+
+The token identifies the type and semantics of an operations table.  It is
+not a PCI device identifier, a secret, an authorization check, a registry
+key, or a lifetime handle.  PCI locates the PF before comparing the token.
+
+A driver-specific header defines the stable token, ABI version, and C
+operations structure shared by the Rust provider and C consumers.  ABI
+compatibility follows these rules:
+
+* The major version must match exactly.
+* A provider's minor version must be at least the consumer's requested minor
+  version.
+* A minor-version update may only append operations to the table.
+* ``ops_size`` must cover the table prefix used by the consumer.
+
+The Rust provider implements ``interop::ffi::Abi`` and applies
+``#[ffi_vtable]`` to methods on its PF data.  The macro verifies the complete
+bindgen operations-table layout and generates a private static operations
+table and private C ABI trampolines.  It does not generate the C header.  A
+trampoline recovers ``Pin<&T>`` from ``context`` and invokes the same Rust
+method used by Rust VFs.  It converts ``Result<()>`` into zero or a negative
+errno, and ``Result<c_int>`` into its successful value or a negative errno.
+
+::
+
+        Rust VF                                  C VF
+        vf_registration_data()                   borrow + ABI checks
+             + TypeId check                               |
+                   |                                      v
+                   v                         ops->submit(context, id)
+              Pin<&PfApi>                                |
+                   |                            generated trampoline
+                   +------------------+-------------------+
+                                      |
+                                      v
+                             PfApi::submit() -> Result
+                                |                 |
+                           Rust error         C 0 or -errno
+
+A C VF includes ``linux/rust_ffi.h`` directly or through its driver-specific
+header and borrows the interface during probe.  The essential call sequence
+is::
+
+        const struct my_pf_ops *ops;
+        const struct rust_ffi *ffi;
+        int ret;
+
+        ffi = pci_iov_borrow_rust_pf_data(vf, &my_token,
+                                          MY_ABI_MAJOR,
+                                          MY_ABI_MINOR,
+                                          sizeof(*ops));
+        if (IS_ERR(ffi))
+                return PTR_ERR(ffi);
+
+        ops = ffi->ops;
+        if (!ops->submit)
+                return -EOPNOTSUPP;
+
+        ret = ops->submit(ffi->context, pci_dev_id(vf));
+        if (ret)
+                return ret;
+
+The PCI helper verifies that the device is a VF, that its PF is bound to a
+managed SR-IOV driver, and that the descriptor satisfies the requested token,
+version, and size.  It returns a borrow, so there is no matching ``put``
+operation.  The size check does not prove that an individual operation is
+implemented, so the consumer must still check each callback it needs.  The
+pointers must not be used after VF probe fails or after VF remove returns.
+
+Synchronization and teardown
+============================
+
+All VFs of a PF borrow the same object and may call it concurrently.  Pinning
+keeps the object's address stable; it does not serialize access.  The PF data
+must use interior synchronization appropriate for each operation, such as a
+mutex for sleepable methods or an atomic for a simple counter.
+
+The sample uses ``Mutex<u64>`` for its request count to demonstrate shared,
+synchronized PF state.  The count is an internal implementation detail and
+is not returned through the C ABI.  Both consumers see only whether
+``submit()`` succeeded.
+
+Document for every C operation whether it may sleep and which calling
+contexts are permitted.  Before VF removal returns, cancel or flush any work
+that could still call an operation.  Do not wait for such work while holding
+a lock that the operation itself needs.
+
+Disabling SR-IOV through sysfs removes VFs synchronously while holding the PF
+device lock.  A VF remove path must not wait for an FFI operation that must
+acquire that same lock, or the two paths can deadlock.
+
+The managed device link supplies driver-presence and teardown ordering, but
+not runtime-PM integration.  Operations that access powered PF hardware must
+arrange runtime PM separately.
+
+Examples
+========
+
+The complete examples are:
+
+* ``samples/rust/rust_driver_sriov.rs``: a Rust PF and Rust VF sharing a
+  pinned PF object with a mutex-protected counter;
+* ``samples/rust/rust_driver_sriov.h``: the C ABI token, version, and
+  operations table;
+* ``samples/rust/rust_driver_sriov_c_vf.c``: a C VF borrowing and calling the
+  Rust PF object; and
+* ``drivers/gpu/nova-core/driver.rs``: a regular Rust PCI driver publishing
+  unit data as a typed PF-readiness marker without a C ABI.
+
+The Rust and C sample VF drivers match the same device ID, so only one can
+bind to a given VF.  Use the module ordering described by their Kconfig help
+or ``driver_override`` to select the C path deterministically.
+
+See also :doc:`../driver-api/device_link` for the general device-link model.
diff --git a/MAINTAINERS b/MAINTAINERS
index e03ebe44c341..922cfddcb2dc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21137,6 +21137,7 @@ L:	linux-pci@vger.kernel.org
 S:	Maintained
 C:	irc://irc.oftc.net/linux-pci
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
+F:	Documentation/rust/pci-sriov-pf-data.rst
 F:	rust/helpers/pci.c
 F:	rust/kernel/pci.rs
 F:	rust/kernel/pci/

  parent reply	other threads:[~2026-09-15 20:59 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 ` [PATCH 11/14] rust: pci: add C FFI support to typed SR-IOV PF registration data Zhi Wang
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 ` Zhi Wang [this message]
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-15-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®