mirror of https://lore.kernel.org/lkml/
 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 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®