From: "Danilo Krummrich" <dakr@kernel.org>
To: "Simon Song" <xinmengs@nvidia.com>
Cc: <acourbot@nvidia.com>, <kvm@vger.kernel.org>,
<rust-for-linux@vger.kernel.org>, <nova-gpu@lists.linux.dev>,
<linux-pci@vger.kernel.org>, <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>, <linux-kernel@vger.kernel.org>,
<zhiwang@kernel.org>, <zhiw@nvidia.com>, <boqun@kernel.org>,
<daniel.almeida@collabora.com>, <tamird@kernel.org>,
<work@onurozkan.dev>, <jgg@ziepe.ca>, <bhelgaas@google.com>,
<kwilczynski@kernel.org>, <dri-devel@lists.freedesktop.org>,
<peter@colberg.org>
Subject: Re: [PATCH v2 10/12] rust: vfio: separate common device handling from PCI
Date: Tue, 10 Nov 2026 20:44:09 +0100 [thread overview]
Message-ID: <DMRSE390GRTH.OZ9KS5QS8M4H@kernel.org> (raw)
In-Reply-To: <20261023154009.2246627-11-xinmengs@nvidia.com>
On Fri Oct 23, 2026 at 5:40 PM CEST, Simon Song wrote:
> +/// A VFIO device, independent of its bus-specific wrapper.
> +///
> +/// # Invariants
> +///
> +/// The underlying `vfio_device` is initialized and remains alive while borrowed.
> +/// Callback contexts are borrowed only for the corresponding callback and thread;
> +/// only [`Normal`] references can be reference-counted.
> +#[repr(transparent)]
> +pub struct Device<Ctx: DeviceContext = Normal> {
> + raw: Opaque<bindings::vfio_device>,
> + _context: PhantomData<(Ctx, NotThreadSafe)>,
> +}
I think this needs to be declared differently, see below.
> +impl<Ctx: DeviceContext> Device<Ctx> {
> + /// # Safety
> + ///
> + /// `raw` must point to an initialized `vfio_device` that remains alive for
> + /// `'a`. The caller must provide the guarantees of `Ctx` throughout the borrow.
> + #[cfg_attr(not(CONFIG_VFIO_PCI_CORE), expect(dead_code))]
> + unsafe fn from_raw<'a>(raw: *mut bindings::vfio_device) -> &'a Self {
> + // SAFETY: Self is a transparent wrapper; the caller guarantees validity and context.
> + unsafe { &*raw.cast() }
> + }
> +
> + fn as_raw(&self) -> *mut bindings::vfio_device {
> + self.raw.get()
> + }
> +}
> +
> +// SAFETY: VFIO uses the embedded class device's refcount to own the full allocation.
> +unsafe impl AlwaysRefCounted for Device {
> + fn inc_ref(&self) {
> + // SAFETY: A shared reference keeps the initialized VFIO device alive.
> + unsafe { bindings::get_device(&raw mut (*self.as_raw()).device) };
> + }
> +
> + unsafe fn dec_ref(obj: NonNull<Self>) {
> + // SAFETY: The caller owns the VFIO reference being released.
> + unsafe { bindings::put_device(&raw mut (*obj.as_ref().as_raw()).device) };
> + }
> +}
> +
> +// SAFETY: Ordinary VFIO references expose no unsynchronized callback operations.
> +unsafe impl Send for Device {}
> +// SAFETY: Shared access only changes the embedded class device's reference count.
> +unsafe impl Sync for Device {}
> +
> +/// Callback data stored after the bus-specific C device structure.
> +///
> +/// # Invariants
> +///
> +/// `reg_data` is dangling when unpublished; otherwise it borrows pinned data kept
> +/// alive until callback access has ended. `open_data` is null outside a successful
> +/// first open, otherwise owns a pinned allocation until the last close.
> +/// The bus-specific layer drains callbacks before releasing either allocation.
> +struct CallbackData<R: CovariantForLt, O: ForLt> {
> + reg_data: UnsafeCell<NonNull<R::Of<'static>>>,
> + open_data: UnsafeCell<*mut O::Of<'static>>,
> +}
We don't need to arbitrarily limit registration data to require covariance,
eventually we will need invariant data.
Also, I think we don't need ForLt/CovariantForLt at all. Both types are
associated types of an ops trait, so they can just be GATs, i.e.:
type RegistrationData<'a>: Send + Sync + 'a;
type OpenData<'a, 'data: 'a>: Send + Sync + 'a;
> +
> +#[cfg_attr(not(CONFIG_VFIO_PCI_CORE), expect(dead_code))]
> +impl<R: CovariantForLt, O: ForLt> CallbackData<R, O> {
> + fn new() -> Self {
> + Self {
> + reg_data: UnsafeCell::new(NonNull::dangling()),
> + open_data: UnsafeCell::new(core::ptr::null_mut()),
> + }
> + }
> +
> + /// Access registration data without allowing its erased lifetime to escape.
> + ///
> + /// # Safety
> + ///
> + /// Registration data must be published and remain valid through this call.
> + /// The caller must exclude concurrent changes to `reg_data`.
> + unsafe fn registration_data_with<V>(&self, f: impl for<'a> FnOnce(&'a R::Of<'a>) -> V) -> V {
> + // SAFETY: The caller keeps registration data alive and excludes mutation.
> + // Covariance permits shortening its erased lifetime to this borrow.
> + let reg_data = unsafe { (*self.reg_data.get()).cast::<R::Of<'_>>().as_ref() };
> + f(reg_data)
> + }
> +
> + /// # Safety
> + ///
> + /// The caller must be an I/O callback while VFIO keeps the device open.
> + /// Registration and open data must remain valid, with their pointer slots
> + /// unchanged throughout the call.
> + unsafe fn callback_data_with<V>(
> + &self,
> + f: impl for<'borrow, 'data> FnOnce(&'borrow R::Of<'data>, Pin<&'borrow O::Of<'data>>) -> V,
> + ) -> V {
> + // SAFETY: The caller excludes open/close and unregister while borrowing the data.
> + unsafe {
> + self.registration_data_with(|reg_data| {
> + let ptr = (*self.open_data.get()).cast::<O::Of<'_>>();
> + // The data lifetime stays independent of the callback borrow,
> + // so borrowed OpenData fields cannot be stored back into OpenData.
> + f(reg_data, Pin::new_unchecked(&*ptr))
> + })
> + }
> + }
I don't think we necessarily need the extra closure, we only ever pass this to
callbacks of the ops trait, but it would allow us to make the accessor safe:
If we move them onto Device directly, we can leverage the type states. E.g. we
can group them with traits and only implement e.g. callback_data_with() for type
states that allow for this.
> +
> + /// # Safety
> + ///
> + /// Call only during exclusive first open, with no existing open data.
> + /// All resources borrowed for `'a` must remain valid until `close()` returns.
> + unsafe fn open<'a, I: PinInit<O::Of<'a>, Error>>(&self, init: impl FnOnce() -> I) -> Result {
> + // Allocate before calling the driver so allocation failure cannot follow open.
> + let data = KBox::<O::Of<'a>>::new_uninit(GFP_KERNEL)?;
> + let data = data.write_pin_init(init())?;
> +
> + // SAFETY: Only the owning pointer moves; close drops the allocation in place.
> + let raw = KBox::into_raw(unsafe { Pin::into_inner_unchecked(data) });
> + // SAFETY: First open is exclusive; the caller keeps borrowed resources alive
> + // through close. Lifetimes do not affect the allocation's layout.
> + unsafe { *self.open_data.get() = raw.cast::<O::Of<'static>>() };
> + Ok(())
> + }
> +
> + /// # Safety
> + ///
> + /// Call once after a successful open, with all callbacks drained and the next
> + /// open excluded. Registration data and borrowed resources must remain alive.
> + unsafe fn close(&self) {
> + // SAFETY: Last close exclusively takes the allocation published by open.
> + let raw = unsafe { core::mem::replace(&mut *self.open_data.get(), core::ptr::null_mut()) };
> + // SAFETY: The allocation came from KBox::into_raw and is destroyed without moving it.
> + unsafe { drop(KBox::from_raw(raw)) };
> + }
This seems like a bit of a back and forth because logically we want those
methods on vfio::Device so you can take advantage of the type system
information.
But in this implementation CallbackData lives in the vfio::pci::Device, likely
for memory layout reasons.
But we can also do something like this:
#[repr(C)]
pub struct DeviceBase<Raw, R, O, T, Ctx = Normal> {
raw: Opaque<Raw>,
data: CallbackData<R, O>,
_context: PhantomData<(T, Ctx, NotThreadSafe)>,
}
// vfio.rs
pub type Device<T, Ctx = Normal> = DeviceBase<
bindings::vfio_device,
T,
<T as Operations>::RegistrationData<'static>,
<T as Operations>::OpenData<'static, 'static>,
Ctx,
>;
// vfio/pci.rs
pub type Device<T, Ctx = Normal> = super::DeviceBase<
bindings::vfio_pci_core_device,
T,
<T as Operations>::RegistrationData<'static>,
<T as Operations>::OpenData<'static, 'static>,
Ctx,
>;
Of course we can't have AsRef<vfio::Device> with this, but we don't need it.
For instance, there's only one AlwaysRefCounted impl needed:
unsafe impl<Raw: RawDevice, R, O, T> AlwaysRefCounted
for DeviceBase<Raw, R, O, Normal>
{
fn inc_ref(&self) {
let vdev = unsafe { Raw::raw_vfio_device(self.as_raw()) };
unsafe { bindings::get_device(&raw mut (*vdev).device) };
}
unsafe fn dec_ref(obj: NonNull<Self>) {
let raw = unsafe {
Opaque::cast_into(&raw const (*obj.as_ptr()).raw)
};
let vdev = unsafe { Raw::raw_vfio_device(raw) };
unsafe { bindings::put_device(&raw mut (*vdev).device) };
}
}
We can either rely on the fact that vfio_pci_core_device's first member is the
vfio_device, or make it explicit with something like:
/// Maps a C device structure to its embedded VFIO device.
///
/// # Safety
///
/// Implementations must return the address of the `vfio_device` at the
/// beginning of `Self`, preserving the input pointer's provenance.
unsafe trait RawDevice {
/// # Safety
///
/// `raw` must point to an initialized, live `Self`.
unsafe fn raw_vfio_device(
raw: *mut Self,
) -> *mut bindings::vfio_device;
}
Not that this isn't only to make use of some type system information (there's
other ways to achieve that too) but also to make vfio::Device useful for non-PCI
implementations, as they need CallbackData too.
next prev parent reply other threads:[~2026-11-10 19:44 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 15:39 [PATCH v2 00/12] Rust VFIO PCI support and NVIDIA vGPU driver Simon Song
2026-10-23 15:39 ` [PATCH v2 01/12] rust: pci: add VFIO override device IDs Simon Song
2026-10-23 20:07 ` Gary Guo
2026-10-30 20:34 ` Simon Song
2026-10-23 15:39 ` [PATCH v2 02/12] rust: pci: expose the VF index within its PF Simon Song
2026-11-09 22:17 ` Danilo Krummrich
2026-10-23 15:39 ` [PATCH v2 03/12] rust: pci: allow drivers to manage DMA ownership Simon Song
2026-11-09 22:18 ` Danilo Krummrich
2026-10-23 15:39 ` [PATCH v2 04/12] rust: vfio: add PCI variant driver abstractions Simon Song
2026-10-23 15:40 ` [PATCH v2 05/12] rust: pci: add reset callbacks Simon Song
2026-11-09 23:19 ` Danilo Krummrich
2026-10-23 15:40 ` [PATCH v2 06/12] rust: pci: add a bind callback after installing driver data Simon Song
2026-11-10 0:10 ` Danilo Krummrich
2026-10-23 15:40 ` [PATCH v2 07/12] rust: pci: add an error_detected callback Simon Song
2026-11-10 0:47 ` Danilo Krummrich
2026-10-23 15:40 ` [PATCH v2 08/12] rust: pci: add runtime PM callbacks Simon Song
2026-11-10 0:23 ` Danilo Krummrich
2026-10-23 15:40 ` [PATCH v2 09/12] rust: vfio: use the PCI adapter for variant drivers Simon Song
2026-10-23 15:40 ` [PATCH v2 10/12] rust: vfio: separate common device handling from PCI Simon Song
2026-11-10 19:44 ` Danilo Krummrich [this message]
2026-10-23 15:40 ` [PATCH v2 11/12] gpu: nova-core: publish typed SR-IOV PF APIs Simon Song
2026-11-10 15:50 ` Danilo Krummrich
2026-10-23 15:40 ` [PATCH v2 12/12] vfio/nvidia-vgpu: add the Rust VFIO variant driver Simon Song
2026-10-30 20:40 ` [PATCH v2 00/12] Rust VFIO PCI support and NVIDIA vGPU driver Simon Song
2026-10-30 20:49 ` Danilo Krummrich
2026-11-10 1:04 ` 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=DMRSE390GRTH.OZ9KS5QS8M4H@kernel.org \
--to=dakr@kernel.org \
--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=bhelgaas@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=boqun@kernel.org \
--cc=cjia@nvidia.com \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=ecourtney@nvidia.com \
--cc=gary@garyguo.net \
--cc=jgg@nvidia.com \
--cc=jgg@ziepe.ca \
--cc=jhubbard@nvidia.com \
--cc=kevin.tian@intel.com \
--cc=kjaju@nvidia.com \
--cc=kvm@vger.kernel.org \
--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=nova-gpu@lists.linux.dev \
--cc=ojeda@kernel.org \
--cc=peter@colberg.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=skolothumtho@nvidia.com \
--cc=smitra@nvidia.com \
--cc=tamird@kernel.org \
--cc=targupta@nvidia.com \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
--cc=xinmengs@nvidia.com \
--cc=yishaih@nvidia.com \
--cc=zhiw@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®