rust: drm: gpuvm: require Send + Sync for the driver's associated data

DriverGpuVm permitted !Send/!Sync associated data on an abstraction whose
handles are shared and dropped across threads: obtain() runs from many
threads and the VA API performs deferred cross-thread drops. That is
unsound.

Require Send + Sync on the trait and its associated data so the GpuVm and
UniqueRefGpuVm handle impls need no per-impl bounds.

Fixes: 82b78182ea ("rust: drm: add base GPUVM immediate mode abstraction")
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Link: https://patch.msgid.link/20260611-gpuvm-sync-send-v4-1-6c7f4ab2778a@google.com
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
This commit is contained in:
Sami Tolvanen
2026-06-11 22:17:21 +00:00
committed by Alice Ryhl
parent 9a80ddbf66
commit b59ec72fec

View File

@@ -72,10 +72,12 @@ pub struct GpuVm<T: DriverGpuVm> {
data: UnsafeCell<T>,
}
// SAFETY: The GPUVM api does not assume that it is tied to a specific thread. The destructor will
// drop the `data` field, which is okay because it is guaranteed `Send` by the `DriverGpuVm` trait.
// SAFETY: It is safe to send a `GpuVm<T>` to another thread: all data reachable through it
// (`T`, `T::VmBoData`, and the GEM `T::Object`) is `Send` by the `DriverGpuVm` bounds.
unsafe impl<T: DriverGpuVm> Send for GpuVm<T> {}
// SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel.
// SAFETY: It is safe to share a `&GpuVm<T>` between threads: `&self` methods only alias data
// that is `Sync` by the `DriverGpuVm` bounds, and any thread may drop that data, or upgrade the
// reference and ultimately drop `T`, which the same bounds make `Send`.
unsafe impl<T: DriverGpuVm> Sync for GpuVm<T> {}
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
@@ -250,18 +252,22 @@ fn raw_resv(&self) -> *mut bindings::dma_resv {
}
/// The manager for a GPUVM.
pub trait DriverGpuVm: Sized + Send {
pub trait DriverGpuVm: Sized + Send + Sync {
/// Parent `Driver` for this object.
type Driver: drm::Driver;
/// The kind of GEM object stored in this GPUVM.
type Object: drm::driver::AllocImpl<Driver = Self::Driver>;
type Object: drm::driver::AllocImpl<Driver = Self::Driver> + Send + Sync;
/// Data stored with each [`struct drm_gpuva`](struct@GpuVa).
type VaData;
///
/// Only `Send` is required: the data has a single owner at all times, moving
/// between threads by value (handed back as a [`GpuVaRemoved`]) but never
/// accessed by two threads concurrently.
type VaData: Send;
/// Data stored with each [`struct drm_gpuvm_bo`](struct@GpuVmBo).
type VmBoData;
type VmBoData: Send + Sync;
/// The private data passed to callbacks.
type SmContext<'ctx>
@@ -298,12 +304,10 @@ fn sm_step_remap<'op, 'ctx>(
/// # Invariants
///
/// Each `GpuVm` instance has at most one `UniqueRefGpuVm` reference.
// `Send`/`Sync` derive from `ARef<GpuVm<T>>`; the trait bounds make them correct for the unique
// handle's `&mut T` access.
pub struct UniqueRefGpuVm<T: DriverGpuVm>(ARef<GpuVm<T>>);
// SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel, and
// concurrent access to `data` is safe due to the `T: Sync` requirement.
unsafe impl<T: DriverGpuVm + Sync> Sync for UniqueRefGpuVm<T> {}
impl<T: DriverGpuVm> UniqueRefGpuVm<T> {
/// Access the data owned by this `UniqueRefGpuVm` immutably.
#[inline]