All of lore.kernel.org
 help / color / mirror / Atom feed
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.

  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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.