From nobody Thu Apr 2 17:44:30 2026 Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C56E03B3898 for ; Fri, 20 Mar 2026 16:08:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022921; cv=none; b=LgeOxGdxrZO/HhEwY4uwQzTfsLUjSxq3i75rQE7LUjkbidzWOEUm4Mw7XQXiDh2K+AekMkj7t3erIf9Pbp1tGksrPJ1E3pD3cAXOMXmzMfLwMP6kQ1WEWMSLLi5/aR2R3DEnxfhy9MT8Udbc30qF5L/8/Lj6LuGzf1Se4ik2+A4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022921; c=relaxed/simple; bh=AJRJTv/XOisCZhGoR/KWyXEIpjM4Z+W4S7cWxvUzqFI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=H7cIAWrRcAJ0vhBu9FYbL5Z8ZxYO+EC2VeWVcTL0xepdmGmTYI8gN+82vrBdMh6XrKX16NQY4UAfkft+ArN/rZvnlr3mLVXq43A0A0W/gcSqSEC6BqehE/+WUcN1XJD6eUinnDw88Yl3aKt/Qlet5bFgglsjhqnwwAiwyjaOKvE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=e5xcxSrf; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="e5xcxSrf" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-486fa35b005so20062095e9.2 for ; Fri, 20 Mar 2026 09:08:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1774022918; x=1774627718; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=D80I3HU5KXLVyy3+ihLGNZqqUhmHdLXC6rbmWz73MZU=; b=e5xcxSrf2eEbNum3hXRXCyBezFhA+QDLgbH/dOiZH8azvfIul74O6n+8i78ODyv6Mk BKhvdk9JJesmYpIcaXToC5uw2c+sEmbqz32HpbOW9yRfPYwAyx9rAxLNWqk7VNFMDHs4 kyLUhc6N4dDmrvvUA6ft0MkC+J5KMGhX4R49PgcPpZyNHl/d/UpLymaWe7dzI/v5fBVr 7gEwBdw8pO7m9S9ozOfy/PZNYfwnCiIt5bVjlTQYfaDG8KL7kgOest3LLEDoxKxbfmUH QKmB5ehVY2RAzcrKjLq+ieW7XyqgwgMIpdGtqySMYd5EFFoE8GCPLpySKKIk/4L3AdGB YQqw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774022918; x=1774627718; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=D80I3HU5KXLVyy3+ihLGNZqqUhmHdLXC6rbmWz73MZU=; b=jLDe0uTDjCYrkS7znhOr9uFV3tIF84fJvaQbyFHxXat3K9t9e8gw/HjxMynR7qCZ/n bkjV+FKR/4W8WPxxrbXK43lP3A7qdzLBgavSrAh2FmZwjvGZMk5VLOndoXsaKCxyTLIm oAc3i5Wq3aYDa4qvr+dfCChHj30b4fxWZvetehWW9/7oXSdLruL7zLMZmLP8IskQRrsI G4gn3+Wv8AHwW1vAwvfvyjpMvsbcK08pSJoLgL4956x0Mpa4yl+6Gvd/uGLjsIdkAKvs eHdQEZ+X+BDCzPkFIIvWiN9mS3gdiInzWoWxfbNNCa+9qLpfEGc2Uph51gZOtZGNP0iU ipSg== X-Forwarded-Encrypted: i=1; AJvYcCWIcJuNkDreYHQXGXq8mCfvlxQ99Apo7/kNUKMrGKuT/6MhOG7I8q+bh58nOqo7RpfmaRy3/JwDhikVZU0=@vger.kernel.org X-Gm-Message-State: AOJu0Yz3omns4TKCRIwqr+WeD32z26CL4OtzVlZr/SM6XdDSbP87w2Rv 1zfFGLuM4kmbIaiJxq/Y+j/jqMwpVcuMdFd+FRm6G46yhKvf5gBEf1najvcoB6FnLgm4EQWN76O 9Z5SfQmC4n2LuqOf0MA== X-Received: from wmhf4.prod.google.com ([2002:a7b:cc04:0:b0:485:4553:1a97]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:820e:b0:483:7903:c3b1 with SMTP id 5b1f17b1804b1-486fee12c91mr61265695e9.20.1774022918097; Fri, 20 Mar 2026 09:08:38 -0700 (PDT) Date: Fri, 20 Mar 2026 16:08:25 +0000 In-Reply-To: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=12069; i=aliceryhl@google.com; h=from:subject:message-id; bh=v76ZDsVu/1UhrfrTDjVTthU39HTgj1OywVri4dMpLAI=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpvXECKNj6DDNTpdZlxRIIfK0jSM9yRVe4EMwB/ JorY6eecOOJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCab1xAgAKCRAEWL7uWMY5 RkCTD/9FXSVp1evaQIeZX0cKFxjUKpczWbdFVWT4FDltEfkX+a/LDt7nLB3YGtjZ2EN+ZNJmoAe D74hTAedjF0viHW+qAZ6tLwojd0oKCJCc6A84ypDPQlEtt+OUZTQHgGxA3vwultRxSWfViGTl24 oyD7EjobvJ3nq6wsUVDHLAjZaczji2lEfZqK7zX0DwwsG3pbVlPtn+LSBdnM4CG4+S2eMWG/5Eo rT13Np5cAMvMQrfPpIb2vqJtfR3rAB1NY9ULDbnapPqUxv5jJcGMgh4dsDhyqv9uwelsMNEF4wc 3+Vl2PeG7WnRk/h5pvneeFwX8tGuyTSKwcaeTI8rlz6OZrb6l10kD6Wo1j/4h0ISQ0ynh7SNvOm QMVAoUsMUHD23BBvgjJjh7DwCXZaCPa6B6mhaKt7CxtwAmcrdN+lSWBO3PKnfZHFqjgjB/v8BNF +ykw4yY3mDGJngTe9kA4stQKjMmoE72hYu2KjQ0ZfSFsRAdSQ07T6TI6kAZQGiP3JGz4Y/Q1cZ7 n/oG4QTNUViUZ4EHa71AzOS4Xmb3PlS+hHjeMkkhJJuvF8hg8EJzIL2qwMpXjBCvAhvnV33bfn1 y+8xHDu9jOjEWbZfOml+HbCVJR943SXY6U4dC5uonoHr4HzlvwP3L0//hL33CbvytrH9UlYYo/r PkazWuR3u2RWS+w== X-Mailer: b4 0.14.3 Message-ID: <20260320-gpuvm-rust-v5-1-76fd44f17a87@google.com> Subject: [PATCH v5 1/6] rust: drm: add base GPUVM immediate mode abstraction From: Alice Ryhl To: Danilo Krummrich , Daniel Almeida Cc: Boris Brezillon , Janne Grunau , Matthew Brost , "=?utf-8?q?Thomas_Hellstr=C3=B6m?=" , Lyude Paul , Asahi Lina , Sumit Semwal , "=?utf-8?q?Christian_K=C3=B6nig?=" , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Asahi Lina Add a GPUVM abstraction to be used by Rust GPU drivers. GPUVM keeps track of a GPU's virtual address (VA) space and manages the corresponding virtual mappings represented by "GPU VA" objects. It also keeps track of the gem::Object used to back the mappings through GpuVmBo. This abstraction is only usable by drivers that wish to use GPUVM in immediate mode. This allows us to build the locking scheme into the API design. It means that the GEM mutex is used for the GEM gpuva list, and that the resv lock is used for the extobj list. The evicted list is not yet used in this version. This abstraction provides a special handle called the UniqueRefGpuVm, which is a wrapper around ARef that provides access to the interval tree. Generally, all changes to the address space requires mutable access to this unique handle. Signed-off-by: Asahi Lina Co-developed-by: Daniel Almeida Signed-off-by: Daniel Almeida Reviewed-by: Daniel Almeida Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl --- MAINTAINERS | 2 + rust/bindings/bindings_helper.h | 1 + rust/helpers/drm_gpuvm.c | 20 ++++ rust/helpers/helpers.c | 1 + rust/kernel/drm/gpuvm/mod.rs | 238 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/drm/mod.rs | 1 + 6 files changed, 263 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 96ea84948d76..b40923a18066 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8840,6 +8840,8 @@ S: Supported T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/drm_gpuvm.c F: include/drm/drm_gpuvm.h +F: rust/helpers/drm_gpuvm.c +F: rust/kernel/drm/gpuvm/ =20 DRM LOG M: Jocelyn Falempe diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 083cc44aa952..ef0f010c268b 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/drm_gpuvm.c b/rust/helpers/drm_gpuvm.c new file mode 100644 index 000000000000..18cf104a8bc7 --- /dev/null +++ b/rust/helpers/drm_gpuvm.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +#ifdef CONFIG_DRM_GPUVM + +#include + +__rust_helper +struct drm_gpuvm *rust_helper_drm_gpuvm_get(struct drm_gpuvm *obj) +{ + return drm_gpuvm_get(obj); +} + +__rust_helper +bool rust_helper_drm_gpuvm_is_extobj(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj) +{ + return drm_gpuvm_is_extobj(gpuvm, obj); +} + +#endif // CONFIG_DRM_GPUVM diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index a3c42e51f00a..a83060f8500c 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -29,6 +29,7 @@ #include "device.c" #include "dma.c" #include "drm.c" +#include "drm_gpuvm.c" #include "err.c" #include "irq.c" #include "fs.c" diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs new file mode 100644 index 000000000000..5086625bbbe7 --- /dev/null +++ b/rust/kernel/drm/gpuvm/mod.rs @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +#![cfg(CONFIG_DRM_GPUVM =3D "y")] + +//! DRM GPUVM in immediate mode +//! +//! Rust abstractions for using GPUVM in immediate mode. This is when the = GPUVM state is updated +//! during `run_job()`, i.e., in the DMA fence signalling critical path, t= o ensure that the GPUVM +//! and the GPU's virtual address space has the same state at all times. +//! +//! C header: [`include/drm/drm_gpuvm.h`](srctree/include/drm/drm_gpuvm.h) + +use kernel::{ + alloc::AllocError, + bindings, + drm, + drm::gem::IntoGEMObject, + prelude::*, + sync::aref::{ + ARef, + AlwaysRefCounted, // + }, + types::Opaque, // +}; + +use core::{ + cell::UnsafeCell, + ops::{ + Deref, + Range, // + }, + ptr::NonNull, // +}; + +/// A DRM GPU VA manager. +/// +/// This object is refcounted, but the locations of mapped ranges may only= be accessed or changed +/// via the special unique handle [`UniqueRefGpuVm`]. +/// +/// # Invariants +/// +/// * Stored in an allocation managed by the refcount in `self.vm`. +/// * Access to `data` and the gpuvm interval tree is controlled via the [= `UniqueRefGpuVm`] type. +#[pin_data] +pub struct GpuVm { + #[pin] + vm: Opaque, + /// Accessed only through the [`UniqueRefGpuVm`] reference. + data: UnsafeCell, +} + +// SAFETY: The GPUVM api does not assume that it is tied to a specific thr= ead. +unsafe impl Send for GpuVm {} +// SAFETY: The GPUVM api is designed to allow &self methods to be called i= n parallel. +unsafe impl Sync for GpuVm {} + +// SAFETY: By type invariants, the allocation is managed by the refcount i= n `self.vm`. +unsafe impl AlwaysRefCounted for GpuVm { + fn inc_ref(&self) { + // SAFETY: By type invariants, the allocation is managed by the re= fcount in `self.vm`. + unsafe { bindings::drm_gpuvm_get(self.vm.get()) }; + } + + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: By type invariants, the allocation is managed by the re= fcount in `self.vm`. + unsafe { bindings::drm_gpuvm_put((*obj.as_ptr()).vm.get()) }; + } +} + +impl GpuVm { + const fn vtable() -> &'static bindings::drm_gpuvm_ops { + &bindings::drm_gpuvm_ops { + vm_free: Some(Self::vm_free), + op_alloc: None, + op_free: None, + vm_bo_alloc: None, + vm_bo_free: None, + vm_bo_validate: None, + sm_step_map: None, + sm_step_unmap: None, + sm_step_remap: None, + } + } + + /// Creates a GPUVM instance. + #[expect(clippy::new_ret_no_self)] + pub fn new( + name: &'static CStr, + dev: &drm::Device, + r_obj: &T::Object, + range: Range, + reserve_range: Range, + data: T, + ) -> Result, E> + where + E: From, + E: From, + { + let obj =3D KBox::try_pin_init::( + try_pin_init!(Self { + data: UnsafeCell::new(data), + vm <- Opaque::ffi_init(|vm| { + // SAFETY: These arguments are valid. `vm` is valid un= til refcount drops to + // zero. + unsafe { + bindings::drm_gpuvm_init( + vm, + name.as_char_ptr(), + bindings::drm_gpuvm_flags_DRM_GPUVM_IMMEDIATE_= MODE + | bindings::drm_gpuvm_flags_DRM_GPUVM_RESV= _PROTECTED, + dev.as_raw(), + r_obj.as_raw(), + range.start, + range.end - range.start, + reserve_range.start, + reserve_range.end - reserve_range.start, + const { Self::vtable() }, + ) + } + }), + }? E), + GFP_KERNEL, + )?; + // SAFETY: This transfers the initial refcount to the ARef. + let aref =3D unsafe { + ARef::from_raw(NonNull::new_unchecked(KBox::into_raw( + Pin::into_inner_unchecked(obj), + ))) + }; + // INVARIANT: This reference is unique. + Ok(UniqueRefGpuVm(aref)) + } + + /// Access this [`GpuVm`] from a raw pointer. + /// + /// # Safety + /// + /// The pointer must reference the `struct drm_gpuvm` in a valid [`Gpu= Vm`] that remains + /// valid for at least `'a`. + #[inline] + pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm) -> &'a Self { + // SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<= T>`. Caller ensures the + // pointer is valid for 'a. + unsafe { &*kernel::container_of!(Opaque::cast_from(ptr), Self, vm)= } + } + + /// Returns a raw pointer to the embedded `struct drm_gpuvm`. + #[inline] + pub fn as_raw(&self) -> *mut bindings::drm_gpuvm { + self.vm.get() + } + + /// The start of the VA space. + #[inline] + pub fn va_start(&self) -> u64 { + // SAFETY: The `mm_start` field is immutable. + unsafe { (*self.as_raw()).mm_start } + } + + /// The length of the GPU's virtual address space. + #[inline] + pub fn va_length(&self) -> u64 { + // SAFETY: The `mm_range` field is immutable. + unsafe { (*self.as_raw()).mm_range } + } + + /// Returns the range of the GPU virtual address space. + #[inline] + pub fn va_range(&self) -> Range { + let start =3D self.va_start(); + // OVERFLOW: This reconstructs the Range passed to the constr= uctor, so it won't fail. + let end =3D start + self.va_length(); + Range { start, end } + } + + /// Clean up buffer objects that are no longer used. + #[inline] + pub fn deferred_cleanup(&self) { + // SAFETY: This GPUVM uses immediate mode. + unsafe { bindings::drm_gpuvm_bo_deferred_cleanup(self.as_raw()) } + } + + /// Check if this GEM object is an external object for this GPUVM. + #[inline] + pub fn is_extobj(&self, obj: &T::Object) -> bool { + // SAFETY: We may call this with any GPUVM and GEM object. + unsafe { bindings::drm_gpuvm_is_extobj(self.as_raw(), obj.as_raw()= ) } + } + + /// Free this GPUVM. + /// + /// # Safety + /// + /// Called when refcount hits zero. + unsafe extern "C" fn vm_free(me: *mut bindings::drm_gpuvm) { + // SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<= T>`. + let me =3D unsafe { kernel::container_of!(Opaque::cast_from(me), S= elf, vm).cast_mut() }; + // SAFETY: By type invariants we can free it when refcount hits ze= ro. + drop(unsafe { KBox::from_raw(me) }) + } +} + +/// The manager for a GPUVM. +pub trait DriverGpuVm: Sized { + /// Parent `Driver` for this object. + type Driver: drm::Driver; + + /// The kind of GEM object stored in this GPUVM. + type Object: IntoGEMObject; +} + +/// The core of the DRM GPU VA manager. +/// +/// This object is a unique reference to the VM that can access the interv= al tree and the Rust +/// `data` field. +/// +/// # Invariants +/// +/// Each `GpuVm` instance has at most one `UniqueRefGpuVm` reference. +pub struct UniqueRefGpuVm(ARef>); + +impl UniqueRefGpuVm { + /// Access the core data of this GPUVM. + #[inline] + pub fn data(&mut self) -> &mut T { + // SAFETY: By the type invariants we may access `core`. + unsafe { &mut *self.0.data.get() } + } +} + +impl Deref for UniqueRefGpuVm { + type Target =3D GpuVm; + + #[inline] + fn deref(&self) -> &GpuVm { + &self.0 + } +} diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs index 1b82b6945edf..a4b6c5430198 100644 --- a/rust/kernel/drm/mod.rs +++ b/rust/kernel/drm/mod.rs @@ -6,6 +6,7 @@ pub mod driver; pub mod file; pub mod gem; +pub mod gpuvm; pub mod ioctl; =20 pub use self::device::Device; --=20 2.53.0.959.g497ff81fa9-goog From nobody Thu Apr 2 17:44:30 2026 Received: from mail-wm1-f74.google.com (mail-wm1-f74.google.com [209.85.128.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 592803CE4B0 for ; Fri, 20 Mar 2026 16:08:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022923; cv=none; b=XMCh/W6+UJ5sOmFoVx1AGorHKpnjOWJuXa+BnvmSwnso+APdgiWwv9zttDzbtIh/5pCD+Z0NBnWpzJfvsgSAS9/ca+DF0wTmiBsMX8Ti4+s5JKhW8LTgdfkqTNssvu47aF9cIStr6J6aFVBWZmF9/9RExcLiymR+96GIslXcKfE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022923; c=relaxed/simple; bh=XUmjxiTs2bIlSFXMWUx0wXAStM3lUYmp8iN8+KzcPzM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=lU876dKJBwvknut2bdNf9zj7kIgrFMw7FmcEnyooFYOJt7CIxsz6IsqsHTP5I2PSSJokvR4IbIuNCkt2iJTGjPIu8oTnqSeBJme19Kiu3qvmNPN2b8DcI6lHQeCgjQAPNFO/N2Qd2/VkvGJJNm1B7wdTDK8wQ+1dn9gErDDXcGo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Wc10DcwA; arc=none smtp.client-ip=209.85.128.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Wc10DcwA" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-48531e8ae62so5107985e9.3 for ; Fri, 20 Mar 2026 09:08:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1774022919; x=1774627719; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=/BAV0UN0oxOpyXD+zO9adlHObupqiFm/cwhTS5hHbNM=; b=Wc10DcwA/uN5kvqM/E7H+2h5OQAJf6uMiH0OBLGp2t+ndpIfa9+f3aqpfaKHWvdjY8 VurVnCVuANwVptrvkPflCWRc4o12i2M9/XRlq63NnodHla/5Hu/1y9mnn4L/aNh15Dzc LsC4xUhCqVg8KDOjsuYrYW6FJOGOTBJCvgUShQdvA+eSIUJkrnMo8jLw1zkhR/AhKZPF LkK5V9znpoizgXRh6VEoDQ9ywNPMVLas0Qfa3eVlydumAFxmvyxue/070e0XsNhS+xRA K8mdkQ/4XfSEs1Y4xrA6P2EzIyplB+0dX3YmVc/Yj/4IQo3fV/d7hGSj9P0Q+1aFogHK s0qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774022919; x=1774627719; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=/BAV0UN0oxOpyXD+zO9adlHObupqiFm/cwhTS5hHbNM=; b=GBNTpc5E2gy0KpPftY9HnYROX1AYi0r4X317pOSeNkwnazCpYO3RRDOOHKbn8IHDcK JZPZTDaCSZmYtAfsvdIM3a5UqhaAro7YVIc8ogwtPY//Wc/g6txWEAXzmrQjWGiCtmMJ V6gKcpHxSssQD6CY2gnmIK9QRm18bSK2HTS2nU3L5DTBAqzBq6+0NBwrUmSDZj2sSllj XTUm0BGwSrn1nRUFpVWNGeBtgAqGyPsrki4Wu92sIkY6O/1Gvq9jqWMQ2q/MUmQznXsD znUOpxNzXUCyY0DfkIrf6LTlnZ5AsOXikO7l0+8ebb0Jvp1MhvOzW4yLOrOs22pTFLmv YB8Q== X-Forwarded-Encrypted: i=1; AJvYcCX89ajQ++NQid0RIqLlw123uxsKmMnxQ4tHfyfJS4jNMmT6W1Q+AoHaIChF/oV38RfVvZwyRbMnQXfFmNc=@vger.kernel.org X-Gm-Message-State: AOJu0YyIceL/prb2FxNJSdrDD39bysoRmhCukIDcsuc+tRslVg5QlcHc OBsOC2tkuFL9ZzpvEddHv15d/DmN0ni3qWuAmVm3UN0mGCbZq8HPwj0J1BIs964PHjLhqX/j5oD 5afKX16YiNckYmlUA7A== X-Received: from wmej19.prod.google.com ([2002:a05:600c:42d3:b0:485:4f4a:bd87]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:5291:b0:485:4278:24fb with SMTP id 5b1f17b1804b1-486ff04d6afmr53214535e9.32.1774022919337; Fri, 20 Mar 2026 09:08:39 -0700 (PDT) Date: Fri, 20 Mar 2026 16:08:26 +0000 In-Reply-To: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=2619; i=aliceryhl@google.com; h=from:subject:message-id; bh=9MaSYeQMVsO/xbbV7cBT0cy87FW81cANB6QK+J3LWZk=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpvXEClNe6r8Fk87JnvEfKv8OUplzwJoPGzIoGr 3cMu7WCI+6JAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCab1xAgAKCRAEWL7uWMY5 RhH/D/9TrPthCru1C4LRuKvU6ahTZweO8txlFbO6CbGuDachWky66rslljqV7IqQsMd0y+JrJ1G SgzuIT2lcF/BLpHi5xsHV55YemywXjL5hcw8qd/XirPkt/fK+Jk6AqjGBUGOmp8RJOvCbhvLyEn 8H33yDXFid4QqQC59tU2miqHhb3ZLyWEu7LHQCeMGXWAhIJ18viTkhaKczyBRLAw6FTFkVOTVcs nWqP2jVMg6Ma4BMbd1kHM9iTMG4HdLW1ruYoYQDGESlFUsnCBJod73ZZOwUIi03SMZL2oi8PUQs cE6lYtphUfeRwjGkW+ghdy0XtvBQRD31bdThgJ//Akx6uxPh06E4pS+iKPyRj/ZTP+ABgQNPOQx 8/vps2cnyJ9iTPGdeb47dOlrzEWxMW5uRK83h+hem3XKhoiimoeQwBRkWU2ZomsaIrw22wRoVHd /jr1ndvB1s53AtyJzCasjJ/kIM0+pqaG+l5mWL+v+ZjvknmPbj1U4pdciQprcHOroOLPG6scQQZ /L5LcOdowEj2115j0LTMFuoIIQA3SCMHuE3ixpv6jJYq5UkxIVh121kF3jwDKWg1B0xkVqlZuz3 8XKsd/AGdZsssHMwAIiziHdUi8fDRymqF1m5MvIE9tsNwcMXz5Ulq81v1FeWXV3joAc4hJIDod0 SelsXQkWve0JR5A== X-Mailer: b4 0.14.3 Message-ID: <20260320-gpuvm-rust-v5-2-76fd44f17a87@google.com> Subject: [PATCH v5 2/6] rust: helpers: Add bindings/wrappers for dma_resv_lock From: Alice Ryhl To: Danilo Krummrich , Daniel Almeida Cc: Boris Brezillon , Janne Grunau , Matthew Brost , "=?utf-8?q?Thomas_Hellstr=C3=B6m?=" , Lyude Paul , Asahi Lina , Sumit Semwal , "=?utf-8?q?Christian_K=C3=B6nig?=" , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Asahi Lina This is just for basic usage in the DRM shmem abstractions for implied locking, not intended as a full DMA Reservation abstraction yet. Cc: Sumit Semwal Cc: Christian K=C3=B6nig Signed-off-by: Asahi Lina Signed-off-by: Daniel Almeida Reviewed-by: Alice Ryhl Signed-off-by: Lyude Paul Reviewed-by: Janne Grunau Reviewed-by: Daniel Almeida Signed-off-by: Alice Ryhl --- Taken from: https://lore.kernel.org/all/20251202220924.520644-3-lyude@redhat.com/ with __rust_helper and MAINTAINERS file added. --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/dma-resv.c | 14 ++++++++++++++ rust/helpers/helpers.c | 1 + 4 files changed, 17 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index b40923a18066..5d013a36f46f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7534,6 +7534,7 @@ F: include/linux/*fence.h F: include/linux/dma-buf.h F: include/linux/dma-buf/ F: include/linux/dma-resv.h +F: rust/helpers/dma-resv.c K: \bdma_(?:buf|fence|resv)\b =20 DMA GENERIC OFFLOAD ENGINE SUBSYSTEM diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index ef0f010c268b..47279e993934 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/dma-resv.c b/rust/helpers/dma-resv.c new file mode 100644 index 000000000000..71914d8241e2 --- /dev/null +++ b/rust/helpers/dma-resv.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +__rust_helper +int rust_helper_dma_resv_lock(struct dma_resv *obj, struct ww_acquire_ctx = *ctx) +{ + return dma_resv_lock(obj, ctx); +} + +__rust_helper void rust_helper_dma_resv_unlock(struct dma_resv *obj) +{ + dma_resv_unlock(obj); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index a83060f8500c..fa56d62c9014 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -28,6 +28,7 @@ #include "cred.c" #include "device.c" #include "dma.c" +#include "dma-resv.c" #include "drm.c" #include "drm_gpuvm.c" #include "err.c" --=20 2.53.0.959.g497ff81fa9-goog From nobody Thu Apr 2 17:44:30 2026 Received: from mail-wr1-f73.google.com (mail-wr1-f73.google.com [209.85.221.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C11BC3CF051 for ; Fri, 20 Mar 2026 16:08:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022925; cv=none; b=ifEDii9VS2WJVJmhA1fRqP8SeEfd1Vq72mjmFdIH7MDREmHoX2t+H0BvbKfWHBVWHLbovqq2k4wuPozPWWCBKay3pL2gv6a8yKdJNnt9ChebK1L67qNvVsVfN77tU+YcZEe3ehFNbjmrVbX+REaCgM0Jx7AOnj19dKoyVhF1Wu0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022925; c=relaxed/simple; bh=n3i27r8Uj1K9Jua9JJ5lqChjmq+OSy3eVxOiFxWTgKs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=KCKDcbgz4CUe9E/GJJ1JPLojhIZNAEFkjSWva8dJY1MSlqoAhV2tUWOggLh0K+SE9wck/SR8vC1St8gn81QoHaBgzFMXs5NNinIIwrY4lnr5a+K/7J9TiqZkXj7Kvxx0WCuKoE/pwzb7HHkN8H9iIY5o75HJwbMW6F872mNnAo4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=XxkchYkR; arc=none smtp.client-ip=209.85.221.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="XxkchYkR" Received: by mail-wr1-f73.google.com with SMTP id ffacd0b85a97d-43b4730dad8so1900465f8f.3 for ; Fri, 20 Mar 2026 09:08:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1774022921; x=1774627721; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Igsa35Q/abhqbKOPMwTPoyThyZXwfOe3ZKKrgGUnE38=; b=XxkchYkR/J7/OMrN3UVqc4Xbyawk2nspqNhc+KCuyhTOZDRkYro7PxcT5wX4npEXIy V/BWdVY42UoEmWMdZ+ox+Jd4c11abyjP/wtv1GjU+41t+r4jmyO2UZ6gKlmnhTjk4+Bi vfAIlw0eAHuiHRjhScCsn9L8dvh/bb//vrmThFnWJ//SWynKYScFCsiXpm2Op/POeo5E mlS7fWjC1oeSm25TlDE6tc2HfJO0+o0TYJC2Ez58ISLSmxqwYO42crrQBlgY7K4aK8DT PFVsyJBsEoXVFYCMcSM6NGInsZstEHCQT/KvoCsVyAanFDOFPN/JKEpfVVFK147fqb4Q vD0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774022921; x=1774627721; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Igsa35Q/abhqbKOPMwTPoyThyZXwfOe3ZKKrgGUnE38=; b=sxIHmVcj4N1XLrAy6hY8uk6wS87529LMQ3fUjZBFKeLdd5oST/s68bMyxhvxqGT9Op 7Zac4kn6/8rdVAK4HELgTrWbx+ctQoMF6UvRn5tsRKH3liIcYjfFB2QhB2N+o6Gc1SMk tW5IqddmBEszZrRioP2AqynxFjPJaF/9CtEqVkEdWQBVqgaZlTtmaO8GC1JzDDrDfY8D n6XOgKsOeeHHpD54oq0ryIIiqCdYdJ1rNnimAEATmko+4O7vS6c9Da8Cd9N7AjnU9FCo 3u1A/8zxpy5vZoQ0F/stAOF9bQW+UojwB2ICqTU3wQPztuu43IJsDc0KemgUaoIJh2Fn ELtA== X-Forwarded-Encrypted: i=1; AJvYcCXlRyUAQwrbQRfBFxcjU/NzdEoNVXyGhFFE3Vnv9X4FzJm+eH65RPtjst5ZF+iqWNRKmscFBf92meS9IeY=@vger.kernel.org X-Gm-Message-State: AOJu0YzieZ7M0+v0v5Wyqj8210P4rGjMcHVoisDciGnpDCJXVBOGMNia 3P5Jyg2DfIr2Wf1rZnTYaMAxGOi18mL+QubVik2q8jAiD+nIcsqGHBDnQwMaQN4NGnvzl4dq3Rm CtvpROiwfUYBQfc0pjQ== X-Received: from wrwn9.prod.google.com ([2002:a5d:67c9:0:b0:439:b81f:ad99]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6000:2f84:b0:43b:4d2e:a004 with SMTP id ffacd0b85a97d-43b64242f00mr6010280f8f.10.1774022920836; Fri, 20 Mar 2026 09:08:40 -0700 (PDT) Date: Fri, 20 Mar 2026 16:08:27 +0000 In-Reply-To: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=13926; i=aliceryhl@google.com; h=from:subject:message-id; bh=n3i27r8Uj1K9Jua9JJ5lqChjmq+OSy3eVxOiFxWTgKs=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpvXECzP7ZocGSSzsHAKUlGZh0W8Rs/EEFE/Chd WOKGnKJG8iJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCab1xAgAKCRAEWL7uWMY5 RgmcD/9ALk+plFEngOFLXrpDHniFei2T0g9ABTC9MLlSIJ6fuG1nrGGmK2jue4BtBf/fWyeIGHZ LPc209/72ZbAm/HZ9EiqHp48Mc5FUKOABRxou1BsTDrf6XIN3kzfURJ/rNQRcyaz6m5yRI/esO/ sX1EOx03qxdzcSTeFTGmxCWa3JOotn8TeQDIpkoZmeaCqqJIzhfP0fLGcBAPPcSqV062QXpKoD2 Xc8UTJeu4aA2bZ6OyCjLKkqcfz1NM35NdLEHw7XJQEfiLEe3L9XeEEHrZeleP9TP6p3fOkq6l+l ST7PFiehIi92TV5F9qyUCtz2BuoZ9yr/F3vy16w2gA1kROVTF4h4h5YCJLv+ejMsSpgH8flbjqP 00x7o9bH9omusaUUqF+yyZCW+lKzvzvjLBT3AR/h+aPYAEsCy4nAYGRPwko9UuYShHHJulfiBNS sbD5i6mG0wger1TN2kkZguMuk9G3JOY3WHmMSpsRxcDqgddY/RO7CZoglQFPswqiP3VXHV84prC VqgBmiP3CdQlvKyP21830kFP0qf/DYldsqQDuCjaKli0i/8voCLPFLZhkiFpxQ8ssROLBPnwgMW T7oUxbMSahs2lv+133PxrdvxBQLG5nfKwCfIl6toybf3nObQV34roAZORnLbF8EBad2MlF1jQJ0 aOUsab+c842ZU9A== X-Mailer: b4 0.14.3 Message-ID: <20260320-gpuvm-rust-v5-3-76fd44f17a87@google.com> Subject: [PATCH v5 3/6] rust: gpuvm: add GpuVm::obtain() From: Alice Ryhl To: Danilo Krummrich , Daniel Almeida Cc: Boris Brezillon , Janne Grunau , Matthew Brost , "=?utf-8?q?Thomas_Hellstr=C3=B6m?=" , Lyude Paul , Asahi Lina , Sumit Semwal , "=?utf-8?q?Christian_K=C3=B6nig?=" , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This provides a mechanism to create (or look up) VMBO instances, which represent the mapping between GPUVM and GEM objects. The GpuVmBoRegistered type can be considered like ARef>, except that no way to increment the refcount is provided. The GpuVmBoAlloc type is more akin to a pre-allocated GpuVmBo, so it's not really a GpuVmBo yet. Its destructor could call drm_gpuvm_bo_destroy_not_in_lists(), but as the type is currently private and never called anywhere, this perf optimization does not need to happen now. Pre-allocating and obtaining the gpuvm_bo object is exposed as a single step. This could theoretically be a problem if one wanted to call drm_gpuvm_bo_obtain_prealloc() during the fence signalling critical path, but that's not a possibility because: 1. Adding the BO to the extobj list requires the resv lock, so it cannot happen during the fence signalling critical path. 2. obtain() requires that the BO is not in the extobj list, so obtain() must be called before adding the BO to the extobj list. Thus, drm_gpuvm_bo_obtain_prealloc() cannot be called during the fence signalling critical path. (For extobjs at least.) Reviewed-by: Daniel Almeida Signed-off-by: Alice Ryhl --- rust/helpers/drm_gpuvm.c | 6 ++ rust/kernel/drm/gpuvm/mod.rs | 32 +++++- rust/kernel/drm/gpuvm/vm_bo.rs | 227 +++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 262 insertions(+), 3 deletions(-) diff --git a/rust/helpers/drm_gpuvm.c b/rust/helpers/drm_gpuvm.c index 18cf104a8bc7..ca959d9a66f6 100644 --- a/rust/helpers/drm_gpuvm.c +++ b/rust/helpers/drm_gpuvm.c @@ -4,6 +4,12 @@ =20 #include =20 +__rust_helper +struct drm_gpuvm_bo *rust_helper_drm_gpuvm_bo_get(struct drm_gpuvm_bo *vm_= bo) +{ + return drm_gpuvm_bo_get(vm_bo); +} + __rust_helper struct drm_gpuvm *rust_helper_drm_gpuvm_get(struct drm_gpuvm *obj) { diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs index 5086625bbbe7..40446e679844 100644 --- a/rust/kernel/drm/gpuvm/mod.rs +++ b/rust/kernel/drm/gpuvm/mod.rs @@ -25,13 +25,20 @@ =20 use core::{ cell::UnsafeCell, + mem::ManuallyDrop, ops::{ Deref, Range, // }, - ptr::NonNull, // + ptr::{ + self, + NonNull, // + }, // }; =20 +mod vm_bo; +pub use self::vm_bo::*; + /// A DRM GPU VA manager. /// /// This object is refcounted, but the locations of mapped ranges may only= be accessed or changed @@ -73,8 +80,8 @@ const fn vtable() -> &'static bindings::drm_gpuvm_ops { vm_free: Some(Self::vm_free), op_alloc: None, op_free: None, - vm_bo_alloc: None, - vm_bo_free: None, + vm_bo_alloc: GpuVmBo::::ALLOC_FN, + vm_bo_free: GpuVmBo::::FREE_FN, vm_bo_validate: None, sm_step_map: None, sm_step_unmap: None, @@ -173,6 +180,16 @@ pub fn va_range(&self) -> Range { Range { start, end } } =20 + /// Get or create the [`GpuVmBo`] for this gem object. + #[inline] + pub fn obtain( + &self, + obj: &T::Object, + data: impl PinInit, + ) -> Result>, AllocError> { + Ok(GpuVmBoAlloc::new(self, obj, data)?.obtain()) + } + /// Clean up buffer objects that are no longer used. #[inline] pub fn deferred_cleanup(&self) { @@ -198,6 +215,12 @@ pub fn is_extobj(&self, obj: &T::Object) -> bool { // SAFETY: By type invariants we can free it when refcount hits ze= ro. drop(unsafe { KBox::from_raw(me) }) } + + #[inline] + fn raw_resv(&self) -> *mut bindings::dma_resv { + // SAFETY: `r_obj` is immutable and valid for duration of GPUVM. + unsafe { (*(*self.as_raw()).r_obj).resv } + } } =20 /// The manager for a GPUVM. @@ -207,6 +230,9 @@ pub trait DriverGpuVm: Sized { =20 /// The kind of GEM object stored in this GPUVM. type Object: IntoGEMObject; + + /// Data stored with each `struct drm_gpuvm_bo`. + type VmBoData; } =20 /// The core of the DRM GPU VA manager. diff --git a/rust/kernel/drm/gpuvm/vm_bo.rs b/rust/kernel/drm/gpuvm/vm_bo.rs new file mode 100644 index 000000000000..b51c6f106185 --- /dev/null +++ b/rust/kernel/drm/gpuvm/vm_bo.rs @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +use super::*; + +/// Represents that a given GEM object has at least one mapping on this [`= GpuVm`] instance. +/// +/// Does not assume that GEM lock is held. +/// +/// # Invariants +/// +/// * Allocated with `kmalloc` and refcounted via `inner`. +/// * Is present in the gem list. +#[repr(C)] +#[pin_data] +pub struct GpuVmBo { + #[pin] + inner: Opaque, + #[pin] + data: T::VmBoData, +} + +// SAFETY: By type invariants, the allocation is managed by the refcount i= n `self.inner`. +unsafe impl AlwaysRefCounted for GpuVmBo { + fn inc_ref(&self) { + // SAFETY: By type invariants, the allocation is managed by the re= fcount in `self.inner`. + unsafe { bindings::drm_gpuvm_bo_get(self.inner.get()) }; + } + + unsafe fn dec_ref(obj: NonNull) { + // CAST: `drm_gpuvm_bo` is first field of repr(C) struct. + // SAFETY: By type invariants, the allocation is managed by the re= fcount in `self.inner`. + // This GPUVM instance uses immediate mode, so we may put the refc= ount using the deferred + // mechanism. + unsafe { bindings::drm_gpuvm_bo_put_deferred(obj.as_ptr().cast()) = }; + } +} + +impl GpuVmBo { + /// The function pointer for allocating a GpuVmBo stored in the gpuvm = vtable. + /// + /// Allocation is always implemented according to [`Self::vm_bo_alloc`= ], but it is set to + /// `None` if the default gpuvm behavior is the same as `vm_bo_alloc`. + /// + /// This may be `Some` even if `FREE_FN` is `None`, or vice-versa. + pub(super) const ALLOC_FN: Option *mut bindi= ngs::drm_gpuvm_bo> =3D { + use core::alloc::Layout; + let base =3D Layout::new::(); + let rust =3D Layout::new::(); + assert!(base.size() <=3D rust.size()); + if base.size() !=3D rust.size() || base.align() !=3D rust.align() { + Some(Self::vm_bo_alloc) + } else { + // This causes GPUVM to allocate a `GpuVmBo` with `kzalloc(= sizeof(drm_gpuvm_bo))`. + None + } + }; + + /// The function pointer for freeing a GpuVmBo stored in the gpuvm vta= ble. + /// + /// Freeing is always implemented according to [`Self::vm_bo_free`], b= ut it is set to `None` if + /// the default gpuvm behavior is the same as `vm_bo_free`. + /// + /// This may be `Some` even if `ALLOC_FN` is `None`, or vice-versa. + pub(super) const FREE_FN: Option =3D { + if core::mem::needs_drop::() { + Some(Self::vm_bo_free) + } else { + // This causes GPUVM to free a `GpuVmBo` with `kfree`. + None + } + }; + + /// Custom function for allocating a `drm_gpuvm_bo`. + /// + /// # Safety + /// + /// Always safe to call. + unsafe extern "C" fn vm_bo_alloc() -> *mut bindings::drm_gpuvm_bo { + let raw_ptr =3D KBox::::new_uninit(GFP_KERNEL | __GFP_ZERO) + .map(KBox::into_raw) + .unwrap_or(ptr::null_mut()); + + // CAST: `drm_gpuvm_bo` is first field of `Self`. + raw_ptr.cast() + } + + /// Custom function for freeing a `drm_gpuvm_bo`. + /// + /// # Safety + /// + /// The pointer must have been allocated with [`GpuVmBo::ALLOC_FN`], a= nd must not be used after + /// this call. + unsafe extern "C" fn vm_bo_free(ptr: *mut bindings::drm_gpuvm_bo) { + // CAST: `drm_gpuvm_bo` is first field of `Self`. + // SAFETY: + // * The ptr was allocated from kmalloc with the layout of `GpuVmB= o`. + // * `ptr->inner` has no destructor. + // * `ptr->data` contains a valid `T::VmBoData` that we can drop. + drop(unsafe { KBox::::from_raw(ptr.cast()) }); + } + + /// Access this [`GpuVmBo`] from a raw pointer. + /// + /// # Safety + /// + /// For the duration of `'a`, the pointer must reference a valid `drm_= gpuvm_bo` associated with + /// a [`GpuVm`]. The BO must also be present in the GEM list. + #[inline] + #[expect(dead_code)] + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm_bo) ->= &'a Self { + // SAFETY: `drm_gpuvm_bo` is first field and `repr(C)`. + unsafe { &*ptr.cast() } + } + + /// Returns a raw pointer to underlying C value. + #[inline] + pub fn as_raw(&self) -> *mut bindings::drm_gpuvm_bo { + self.inner.get() + } + + /// The [`GpuVm`] that this GEM object is mapped in. + #[inline] + pub fn gpuvm(&self) -> &GpuVm { + // SAFETY: The `obj` pointer is guaranteed to be valid. + unsafe { GpuVm::::from_raw((*self.inner.get()).vm) } + } + + /// The [`drm_gem_object`](crate::gem::Object) for these mappings. + #[inline] + pub fn obj(&self) -> &T::Object { + // SAFETY: The `obj` pointer is guaranteed to be valid. + unsafe { ::from_raw((*self.inner.get()= ).obj) } + } + + /// The driver data with this buffer object. + #[inline] + pub fn data(&self) -> &T::VmBoData { + &self.data + } +} + +/// A pre-allocated [`GpuVmBo`] object. +/// +/// # Invariants +/// +/// Points at a `drm_gpuvm_bo` that contains a valid `T::VmBoData`, has a = refcount of one, and is +/// absent from any gem, extobj, or evict lists. +pub(super) struct GpuVmBoAlloc(NonNull>); + +impl GpuVmBoAlloc { + /// Create a new pre-allocated [`GpuVmBo`]. + /// + /// It's intentional that the initializer is infallible because `drm_g= puvm_bo_put` will call + /// drop on the data, so we don't have a way to free it when the data = is missing. + #[inline] + pub(super) fn new( + gpuvm: &GpuVm, + gem: &T::Object, + value: impl PinInit, + ) -> Result, AllocError> { + // CAST: `GpuVmBoAlloc::vm_bo_alloc` ensures that this memory was = allocated with the layout + // of `GpuVmBo`. The type is repr(C), so `container_of` is not = required. + // SAFETY: The provided gpuvm and gem ptrs are valid for the durat= ion of this call. + let raw_ptr =3D unsafe { + bindings::drm_gpuvm_bo_create(gpuvm.as_raw(), gem.as_raw()).ca= st::>() + }; + let ptr =3D NonNull::new(raw_ptr).ok_or(AllocError)?; + // SAFETY: `ptr->data` is a valid pinned location. + let Ok(()) =3D unsafe { value.__pinned_init(&raw mut (*raw_ptr).da= ta) }; + // INVARIANTS: We just created the vm_bo so it's absent from lists= , and the data is valid + // as we just initialized it. + Ok(GpuVmBoAlloc(ptr)) + } + + /// Returns a raw pointer to underlying C value. + #[inline] + pub(super) fn as_raw(&self) -> *mut bindings::drm_gpuvm_bo { + // SAFETY: The pointer references a valid `drm_gpuvm_bo`. + unsafe { (*self.0.as_ptr()).inner.get() } + } + + /// Look up whether there is an existing [`GpuVmBo`] for this gem obje= ct. + #[inline] + pub(super) fn obtain(self) -> ARef> { + let me =3D ManuallyDrop::new(self); + // SAFETY: Valid `drm_gpuvm_bo` not already in the lists. + let ptr =3D unsafe { bindings::drm_gpuvm_bo_obtain_prealloc(me.as_= raw()) }; + + // Add the vm_bo to the extobj list if it's an external object, an= d if the vm_bo does not + // already exist. (If we are using an existing vm_bo, it's already= in the extobj list.) + if ptr::eq(ptr, me.as_raw()) && me.gpuvm().is_extobj(me.obj()) { + let resv_lock =3D me.gpuvm().raw_resv(); + // TODO: Use a proper lock guard here once a dma_resv lock abs= traction exists. + // SAFETY: The GPUVM is still alive, so its resv lock is too. + unsafe { bindings::dma_resv_lock(resv_lock, ptr::null_mut()) }; + // SAFETY: We hold the GPUVMs resv lock. + unsafe { bindings::drm_gpuvm_bo_extobj_add(ptr) }; + // SAFETY: We took the lock, so we can unlock it. + unsafe { bindings::dma_resv_unlock(resv_lock) }; + } + + // SAFETY: `drm_gpuvm_bo_obtain_prealloc` always returns a non-nul= l ptr + let ptr =3D unsafe { NonNull::new_unchecked(ptr.cast()) }; + + // INVARIANTS: `drm_gpuvm_bo_obtain_prealloc` ensures that the bo = is in the GEM list. + // SAFETY: We received one refcount from `drm_gpuvm_bo_obtain_prea= lloc`. + unsafe { ARef::from_raw(ptr) } + } +} + +impl Deref for GpuVmBoAlloc { + type Target =3D GpuVmBo; + #[inline] + fn deref(&self) -> &GpuVmBo { + // SAFETY: By the type invariants we may deref while `Self` exists. + unsafe { self.0.as_ref() } + } +} + +impl Drop for GpuVmBoAlloc { + #[inline] + fn drop(&mut self) { + // TODO: Call drm_gpuvm_bo_destroy_not_in_lists() directly. + // SAFETY: It's safe to perform a deferred put in any context. + unsafe { bindings::drm_gpuvm_bo_put_deferred(self.as_raw()) }; + } +} --=20 2.53.0.959.g497ff81fa9-goog From nobody Thu Apr 2 17:44:30 2026 Received: from mail-ed1-f73.google.com (mail-ed1-f73.google.com [209.85.208.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 727F43CF678 for ; Fri, 20 Mar 2026 16:08:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022926; cv=none; b=fUA2loBMzfPl1/4Vj8zhukYtywk/6X7HjNNpWzWfFp/eyi5G90TfXBE8feCyVznkxddWEXmvLJRzyGaiDzqrBDZUwb3HdwWNgEq6mNXy1P+X7RvLKvyP3lHf0/DojkUi7IZ434j1FsZNH1wl1ScXW41k4qgQnioR61VdBT+1NgY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022926; c=relaxed/simple; bh=fADi6ter3jMPbMHo9dDtabnQY4l2WMhxMN/lP/zhOhA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=aWXSop0ygBs2W+zjkDdlkUHek8BlkD3ENc6B7cBnuB9pewtjbgKV1ztI1AWq2uuR1m2YDNRkwHMfu7RGYNQQ9AfDGyvX4D1/D2I2HGlOYiV/cIMcsl/POHHXVkwABHKjAXH5LESmelokCDpsPM+ZB8fW35ara/mPGIokKv8F2rk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=KKewb4S4; arc=none smtp.client-ip=209.85.208.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="KKewb4S4" Received: by mail-ed1-f73.google.com with SMTP id 4fb4d7f45d1cf-667d8f30c50so3423814a12.0 for ; Fri, 20 Mar 2026 09:08:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1774022923; x=1774627723; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=jqhplbxZwVhhYVaVxWgCf5aaHOc0xQLlVk0KfnrtVUA=; b=KKewb4S4G1G+23HeQZj7IYviCIN/3hXFS6mz7NL0CN3uLokl0n3BMJtfb8E9t6M5rk AkJxjpKw1XUi22mHCsV1HI+mN25KrpMsK9LRW7Ph0Gr/gv14T6X/ZDMhIov6EXwRHZLc nNVF30vrSa32Ot/qQGnwq81H9oVqRKjufQtEWkD9MFGSTq0eJ31OUW9H7dOi92UvFdob tgFt0CcDk9FdiSDWIRYfHZGOqWy9sj4tTmXGZMO5JE5jIdA24nYc5dUrfF0iXwMhnGZk IV1dARe2ctUIa6GvXvLenn2BMWRcjam2zgD1omuaUasY1VBpAKbv8K+Aa9Pl8L0uKXCi rVpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774022923; x=1774627723; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=jqhplbxZwVhhYVaVxWgCf5aaHOc0xQLlVk0KfnrtVUA=; b=XuK4flZXzc4a/ATeLOwspgLo0wUfsXlBaR4DWuaiSHxmSVBuuMGEx4tPFN/7LkEk07 iIHytgDjxY6TWsoQy4/tkQP+02b7D1oa5V4zY2do+meQrLcj/25d625Em3+6dpcXz6LL TbKKDZIrJ+FAfL8yLJYRA9RQ+Ymc2mPT12TeaoNg4W9tEeP2dBCn+CtkPRJjnNewCwjY BZbO/RlzvDlPuMX/GlubkS3I3I0tZlTADzsGuH0sUDQ639QpiKV6FB8aijWrCnCJt7G8 u3VFHC+n3nkyioRmheNIH1ExU2c0oLZ1b+MiFryi+i70xBmq/7Kn+7NkLioqdWEloXkT +oTg== X-Forwarded-Encrypted: i=1; AJvYcCWJmyQIKTy/445y09MbmA6l1TBxmBd3qQywK016RfBgVp/W9iPlqz1Up/oQrUbSmPwedwKi8Jzf6/uzeyo=@vger.kernel.org X-Gm-Message-State: AOJu0Yzz0t6Lv0wd0Jd840TlK1FmIdqxZxbWy2NqehABbc27VjI/8S6v pLDfCazC6013JffgteTWTi98FIB8sMOd1h7L+WzBwG4oNxz9nWGlklcENAq9z1qPl6o3RkTq1ar wmq/es0Nmhyute93UdQ== X-Received: from edxi7.prod.google.com ([2002:a05:6402:547:b0:663:a62b:3359]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a50:ec98:0:b0:669:226a:1259 with SMTP id 4fb4d7f45d1cf-669226a2079mr1041411a12.13.1774022922558; Fri, 20 Mar 2026 09:08:42 -0700 (PDT) Date: Fri, 20 Mar 2026 16:08:28 +0000 In-Reply-To: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7532; i=aliceryhl@google.com; h=from:subject:message-id; bh=fADi6ter3jMPbMHo9dDtabnQY4l2WMhxMN/lP/zhOhA=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpvXECys/Z9rTmCwrkx59VTk1IXxqskMfwTQVQy 5ufSs2LtiSJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCab1xAgAKCRAEWL7uWMY5 RtqgEACdA9tVleu5vZrva3QP1MV7UkB6PgqjgLT4Jr2ZpnZmd+Tv7kc+q8KWYkJzFIxpn0xyGe9 NAOLl61R7RWASl10i/14Db1c7J/JO2bqwo9aqlttALoFfl9Dbv2KOqzWL5IrQ2Y2zrwly4nG80O QAoS+qjin8xsP3jLnCIzGdZIpZmo8b+4qQDMbFuPYVVqDd4XtnyTwcrDxAxwMnwAQaL4rAGrJLc yMNSc1nEUNtMVXyEtSo3TOFet8YGpAFyphVOa6ZC++tJ3ogwzlLytWzLtXycoytgyTb+hkbBr42 fQ0gwDt23jWBH3xNsfXxEs0rXSFMEpQTx9fUVpN4uRUW4nWuxzcwDFAGYyCpSsr/2KhHCZXs6DP vac0aZX5VGErvRKsnWpsxiefrgvqG8Hmu/sqn9K02zP2mjUDNv/av7gulbfgMPGz2o3r1jqKY6m AlMoobmcrjMs0AHb1647Ssyqi7bBFunlYuT4rnRyAoljGHuObtmtFRoFgKYHkoQ5zpbF204oq1f 8wJ8xV95BiGt8cmi+M6EjQ3bKhpVlkqE3hQUZ5t4QmlcNGFCKhUR31y9WgPsIGNGR/T+oCg1C4G 5EhoNyiu7e3k2r6Uq9UBxrLXWJxI67rCUJ4C6h7HPrYLtVnJ9lQKW0rpWg5RoEkttntJJM6qGgR Lp+EJH8QAzA5x8A== X-Mailer: b4 0.14.3 Message-ID: <20260320-gpuvm-rust-v5-4-76fd44f17a87@google.com> Subject: [PATCH v5 4/6] rust: gpuvm: add GpuVa struct From: Alice Ryhl To: Danilo Krummrich , Daniel Almeida Cc: Boris Brezillon , Janne Grunau , Matthew Brost , "=?utf-8?q?Thomas_Hellstr=C3=B6m?=" , Lyude Paul , Asahi Lina , Sumit Semwal , "=?utf-8?q?Christian_K=C3=B6nig?=" , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This struct will be used to keep track of individual mapped ranges in the GPU's virtual memory. Co-developed-by: Asahi Lina Signed-off-by: Asahi Lina Co-developed-by: Daniel Almeida Signed-off-by: Daniel Almeida Reviewed-by: Daniel Almeida Signed-off-by: Alice Ryhl --- rust/kernel/drm/gpuvm/mod.rs | 17 ++++- rust/kernel/drm/gpuvm/va.rs | 151 +++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/drm/gpuvm/vm_bo.rs | 1 - 3 files changed, 166 insertions(+), 3 deletions(-) diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs index 40446e679844..2eb4fb0f9839 100644 --- a/rust/kernel/drm/gpuvm/mod.rs +++ b/rust/kernel/drm/gpuvm/mod.rs @@ -11,7 +11,10 @@ //! C header: [`include/drm/drm_gpuvm.h`](srctree/include/drm/drm_gpuvm.h) =20 use kernel::{ - alloc::AllocError, + alloc::{ + AllocError, + Flags as AllocFlags, // + }, bindings, drm, drm::gem::IntoGEMObject, @@ -25,9 +28,13 @@ =20 use core::{ cell::UnsafeCell, - mem::ManuallyDrop, + mem::{ + ManuallyDrop, + MaybeUninit, // + }, ops::{ Deref, + DerefMut, Range, // }, ptr::{ @@ -36,6 +43,9 @@ }, // }; =20 +mod va; +pub use self::va::*; + mod vm_bo; pub use self::vm_bo::*; =20 @@ -231,6 +241,9 @@ pub trait DriverGpuVm: Sized { /// The kind of GEM object stored in this GPUVM. type Object: IntoGEMObject; =20 + /// Data stored with each `struct drm_gpuva`. + type VaData; + /// Data stored with each `struct drm_gpuvm_bo`. type VmBoData; } diff --git a/rust/kernel/drm/gpuvm/va.rs b/rust/kernel/drm/gpuvm/va.rs new file mode 100644 index 000000000000..540f525bb56d --- /dev/null +++ b/rust/kernel/drm/gpuvm/va.rs @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +#![expect(dead_code)] +use super::*; + +/// Represents that a range of a GEM object is mapped in this [`GpuVm`] in= stance. +/// +/// Does not assume that GEM lock is held. +/// +/// # Invariants +/// +/// This is a valid `drm_gpuva` that is resident in the [`GpuVm`] instance= . The associated +/// [`GpuVmBo`] is part of the GEM list. +#[repr(C)] +#[pin_data] +pub struct GpuVa { + #[pin] + inner: Opaque, + #[pin] + data: T::VaData, +} + +impl GpuVa { + /// Access this [`GpuVa`] from a raw pointer. + /// + /// # Safety + /// + /// For the duration of `'a`, the pointer must reference a valid `drm_= gpuva` associated with a + /// [`GpuVm`]. Its parent [`GpuVmBo`] must be in the GEM list. + #[inline] + pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuva) -> &'a Self { + // SAFETY: `drm_gpuva` is first field and `repr(C)`. + unsafe { &*ptr.cast() } + } + + /// Returns a raw pointer to underlying C value. + #[inline] + pub fn as_raw(&self) -> *mut bindings::drm_gpuva { + self.inner.get() + } + + /// Returns the address of this mapping in the GPU virtual address spa= ce. + #[inline] + pub fn addr(&self) -> u64 { + // SAFETY: The `va.addr` field of `drm_gpuva` is immutable. + unsafe { (*self.as_raw()).va.addr } + } + + /// Returns the length of this mapping. + #[inline] + pub fn length(&self) -> u64 { + // SAFETY: The `va.range` field of `drm_gpuva` is immutable. + unsafe { (*self.as_raw()).va.range } + } + + /// Returns `addr..addr+length`. + #[inline] + pub fn range(&self) -> Range { + let addr =3D self.addr(); + addr..addr + self.length() + } + + /// Returns the offset within the GEM object. + #[inline] + pub fn gem_offset(&self) -> u64 { + // SAFETY: The `gem.offset` field of `drm_gpuva` is immutable. + unsafe { (*self.as_raw()).gem.offset } + } + + /// Returns the GEM object. + #[inline] + pub fn obj(&self) -> &T::Object { + // SAFETY: The `gem.offset` field of `drm_gpuva` is immutable. + unsafe { ::from_raw((*self.as_raw()).g= em.obj) } + } + + /// Returns the underlying [`GpuVmBo`] object that backs this [`GpuVa`= ]. + #[inline] + pub fn vm_bo(&self) -> &GpuVmBo { + // SAFETY: The `vm_bo` field has been set and is immutable for the= duration in which this + // `drm_gpuva` is resident in the VM. By the type invariants, the = BO is part of the GEM + // list. + unsafe { GpuVmBo::from_raw((*self.as_raw()).vm_bo) } + } +} + +/// A pre-allocated [`GpuVa`] object. +/// +/// # Invariants +/// +/// The memory is zeroed. +pub struct GpuVaAlloc(KBox>>); + +impl GpuVaAlloc { + /// Pre-allocate a [`GpuVa`] object. + pub fn new(flags: AllocFlags) -> Result, AllocError> { + // INVARIANTS: Memory allocated with __GFP_ZERO. + Ok(GpuVaAlloc(KBox::new_uninit(flags | __GFP_ZERO)?)) + } + + /// Prepare this `drm_gpuva` for insertion into the GPUVM. + pub(super) fn prepare(mut self, va_data: impl PinInit) -> *= mut bindings::drm_gpuva { + let va_ptr =3D MaybeUninit::as_mut_ptr(&mut self.0); + // SAFETY: The `data` field is pinned. + let Ok(()) =3D unsafe { va_data.__pinned_init(&raw mut (*va_ptr).d= ata) }; + KBox::into_raw(self.0).cast() + } +} + +/// A [`GpuVa`] object that has been removed. +/// +/// # Invariants +/// +/// The `drm_gpuva` is not resident in the [`GpuVm`]. +pub struct GpuVaRemoved(KBox>); + +impl GpuVaRemoved { + /// Convert a raw pointer into a [`GpuVaRemoved`]. + /// + /// # Safety + /// + /// Must have been removed from a [`GpuVm`]. + pub(super) unsafe fn from_raw(ptr: *mut bindings::drm_gpuva) -> Self { + // SAFETY: Since it has been removed we can take ownership of allo= cation. + GpuVaRemoved(unsafe { KBox::from_raw(ptr.cast()) }) + } + + /// Take ownership of the VA data. + pub fn into_inner(self) -> T::VaData + where + T::VaData: Unpin, + { + KBox::into_inner(self.0).data + } +} + +impl Deref for GpuVaRemoved { + type Target =3D T::VaData; + fn deref(&self) -> &T::VaData { + &self.0.data + } +} + +impl DerefMut for GpuVaRemoved +where + T::VaData: Unpin, +{ + fn deref_mut(&mut self) -> &mut T::VaData { + &mut self.0.data + } +} diff --git a/rust/kernel/drm/gpuvm/vm_bo.rs b/rust/kernel/drm/gpuvm/vm_bo.rs index b51c6f106185..7fd1d301fd41 100644 --- a/rust/kernel/drm/gpuvm/vm_bo.rs +++ b/rust/kernel/drm/gpuvm/vm_bo.rs @@ -106,7 +106,6 @@ impl GpuVmBo { /// For the duration of `'a`, the pointer must reference a valid `drm_= gpuvm_bo` associated with /// a [`GpuVm`]. The BO must also be present in the GEM list. #[inline] - #[expect(dead_code)] pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm_bo) ->= &'a Self { // SAFETY: `drm_gpuvm_bo` is first field and `repr(C)`. unsafe { &*ptr.cast() } --=20 2.53.0.959.g497ff81fa9-goog From nobody Thu Apr 2 17:44:30 2026 Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EC8E2361DDB for ; Fri, 20 Mar 2026 16:08:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022928; cv=none; b=ZuxiR/u04WvzaYAo6+4Hrqi9da+RBCz+tSJpPYqp9UgcZVAYPjnpMh7ITfJkyURFHvG7IOEct2z+h+UiXdTCEs0beu2vH4sJKtg7iqzsYkBjGsjtpJI2bdFCrm01+WR8FhDPbURioUDoAZ85dynWla97x8nrg+1NAOrWwK1FuIM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022928; c=relaxed/simple; bh=YP9pNPeNfXJOzTxZQ1Z8+iVttV0lADSArhe2qrwpJas=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=rZ+foj5YAcuCBIMm2vy5VLzQV7aeVHJPKh9xqsdXlemyMGG8PVjn/7iy/92OL4noeQI14ekbgEPdgujm5R5w6m1zFDipfSV3HkFwkDmR1Dt8Mo6c0hpPxJ+CFRFR8rhvg184td8qxW42qp0F40+U2Ja9JQrBFcBsZnoYoruXFRc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=N0hOuk1q; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="N0hOuk1q" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-48539bda3dcso20204265e9.2 for ; Fri, 20 Mar 2026 09:08:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1774022924; x=1774627724; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=AouGft0bx+9AXi5MVuNtcImb+EKH7/hONBpgoj18nJk=; b=N0hOuk1q4O9UJdP+IbcrKHHN6e+MPuif6gA3waebsvbipRh2wQzeyDeTUhA6TSrwPh WrMoBp85kymVVF8Mjjn7QooETdhSpWx28L5mNwLi/QCmR+Hs0lsTkd4Dfj7JO/wv7QfC exo8Akaz0wYfoYNjf9ahZOLtl/5mx9rnR+yNgL0vc59Btyp5ZUtRnZlmkaWFY3n4r+wx XHoumqRts0RYviyiBcD5lAbM7OC9lBRBrGufvNsgGz/FQMUkkJtjWlllNcVcN401g+x9 zlzGKNYTqH/ihUERLawAhhyzxIAtKq0bvhv7xN/QqApXswRPZp6jw1QsmR6yVtfK/hKR S1LA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774022924; x=1774627724; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=AouGft0bx+9AXi5MVuNtcImb+EKH7/hONBpgoj18nJk=; b=ZpBiVHGyucbaB0KldqZIwPxmzS0hdvucyoFKvCXhnsMDpXwV7mesx0l2mVBAuuSxli nc+Wwl6c/uyJezwsA2eBiYEMWkDbPFwtIq1hjeN7IdhHZwPyO4blU2ojnGmo5cAQ72Se FPa0wmce9qtOy/KGHVqqAYyC2Z7QIrtW6ELl1XMnqd1AwblZLKSeDV39Dxxqo0jBl3EF zrfwnkTXAMhpYAU0g1xhlRiJIuuFt2uC01hwDFnWIzuEC8T+ZInIxgZUuv54V8GqTm85 ppCt+aYr8z86rWilDl3d9GgjAlO4ZRaAHBrW8Dcs4Ywx8a1AA1AE3FmiwWGfjxYINUqA Rn/g== X-Forwarded-Encrypted: i=1; AJvYcCVXqbqH2hvG4g8aBNGkohaakOSaZO/C9B+pmqZH+Yq1QvWg2+lQ19An0sqQI+FhLn6h4Oa5n6rSmdOOslw=@vger.kernel.org X-Gm-Message-State: AOJu0YxF/t3WuIbBXL43xI38w8gJ6pRboTXeejRzH6+E/jw2feDwlTiu ZURAPSFH5HBKE+I9vAPMeMU9uQE26tF9IidNrA1CJKWcrBSP/vxA3GRgkp9pNm4Y/l4TRSZlRc5 hn67SGEG4rypPM++3Gw== X-Received: from wmpd23.prod.google.com ([2002:a05:600c:4c17:b0:483:29f4:26b8]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:c111:b0:485:3f30:6250 with SMTP id 5b1f17b1804b1-486ff024da8mr37521875e9.20.1774022924188; Fri, 20 Mar 2026 09:08:44 -0700 (PDT) Date: Fri, 20 Mar 2026 16:08:29 +0000 In-Reply-To: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=14053; i=aliceryhl@google.com; h=from:subject:message-id; bh=YP9pNPeNfXJOzTxZQ1Z8+iVttV0lADSArhe2qrwpJas=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpvXECqvWiWpo3fTo0JRHXwoFOtJtUNNuNs2rWb i2LKx40mPKJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCab1xAgAKCRAEWL7uWMY5 RkaPD/94IQtMcgikWvw0IY0sGoaz8Vzi6w2cqmyeTOs7uA+UXeVputHDSN5edrYzjsAdL8cu/Cc K2l+/C+eHFz28EiYM9nh0onoLIEg12g6l+G7DWypuMFOzlJhmX3gxDwuFaQGZdaL3cG999ROX62 PzSNZqBMOoZ/38TjDG+Ucc7dxBDMoRLwr8ZT/uAW2Aa+r1WzGTNbbzQjpuP3MBbnEGJ54P9+SAG ZUQS9GiPL9xfv8diFyVlOoee0fffqAcQDt/SqTY0gMmnl/zenTwvU8VbQ7nAYzoDCkba/F3YZJK f9TGaE79HQbnQp9NtUtrtTp6tP/tOdQDlqKNDFbmAtG/5+Mx2YPP2DVHTxSbxKto5mKUmG2B8b/ 3ATUiqerGWTLF+1rHbmZC2taA/NnGyq8LYim/qChpOOZi8sh/Tv2MAt6/7fIPphOGvljAqvgi7t MFQZZGWYjz3QLo93drSDpmsnkUas9X/+um7+ckHg7qi8CwJv0h035k+Lkz2sK5AxpLjUJaUJjr6 D5bOmrTa8kbqEwnrEW8++dmrPFWxa3htbkZoecU+NMgHHpNv6oSoIIgeaHhL4wNuirssfgJY6fB jEdhCOLIKLEG28+HW4Rkz4fZHsHl18ZgyyMT7TR1hpBEDLjR8wCe+huDXaA4qeG0kg8n92gfgrN MoFRI464M5pXUYw== X-Mailer: b4 0.14.3 Message-ID: <20260320-gpuvm-rust-v5-5-76fd44f17a87@google.com> Subject: [PATCH v5 5/6] rust: gpuvm: add GpuVmCore::sm_unmap() From: Alice Ryhl To: Danilo Krummrich , Daniel Almeida Cc: Boris Brezillon , Janne Grunau , Matthew Brost , "=?utf-8?q?Thomas_Hellstr=C3=B6m?=" , Lyude Paul , Asahi Lina , Sumit Semwal , "=?utf-8?q?Christian_K=C3=B6nig?=" , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add the entrypoint for unmapping ranges in the GPUVM, and provide callbacks and VA types for the implementation. Co-developed-by: Asahi Lina Signed-off-by: Asahi Lina Reviewed-by: Daniel Almeida Signed-off-by: Alice Ryhl --- rust/kernel/drm/gpuvm/mod.rs | 30 ++++- rust/kernel/drm/gpuvm/sm_ops.rs | 270 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/drm/gpuvm/va.rs | 1 - rust/kernel/drm/gpuvm/vm_bo.rs | 8 ++ 4 files changed, 304 insertions(+), 5 deletions(-) diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs index 2eb4fb0f9839..dca72e7b2b89 100644 --- a/rust/kernel/drm/gpuvm/mod.rs +++ b/rust/kernel/drm/gpuvm/mod.rs @@ -18,6 +18,7 @@ bindings, drm, drm::gem::IntoGEMObject, + error::to_result, prelude::*, sync::aref::{ ARef, @@ -28,6 +29,7 @@ =20 use core::{ cell::UnsafeCell, + marker::PhantomData, mem::{ ManuallyDrop, MaybeUninit, // @@ -43,12 +45,15 @@ }, // }; =20 -mod va; -pub use self::va::*; +mod sm_ops; +pub use self::sm_ops::*; =20 mod vm_bo; pub use self::vm_bo::*; =20 +mod va; +pub use self::va::*; + /// A DRM GPU VA manager. /// /// This object is refcounted, but the locations of mapped ranges may only= be accessed or changed @@ -94,8 +99,8 @@ const fn vtable() -> &'static bindings::drm_gpuvm_ops { vm_bo_free: GpuVmBo::::FREE_FN, vm_bo_validate: None, sm_step_map: None, - sm_step_unmap: None, - sm_step_remap: None, + sm_step_unmap: Some(Self::sm_step_unmap), + sm_step_remap: Some(Self::sm_step_remap), } } =20 @@ -246,6 +251,23 @@ pub trait DriverGpuVm: Sized { =20 /// Data stored with each `struct drm_gpuvm_bo`. type VmBoData; + + /// The private data passed to callbacks. + type SmContext<'ctx>; + + /// Indicates that an existing mapping should be removed. + fn sm_step_unmap<'op, 'ctx>( + &mut self, + op: OpUnmap<'op, Self>, + context: &mut Self::SmContext<'ctx>, + ) -> Result, Error>; + + /// Indicates that an existing mapping should be split up. + fn sm_step_remap<'op, 'ctx>( + &mut self, + op: OpRemap<'op, Self>, + context: &mut Self::SmContext<'ctx>, + ) -> Result, Error>; } =20 /// The core of the DRM GPU VA manager. diff --git a/rust/kernel/drm/gpuvm/sm_ops.rs b/rust/kernel/drm/gpuvm/sm_ops= .rs new file mode 100644 index 000000000000..ab802a07e4be --- /dev/null +++ b/rust/kernel/drm/gpuvm/sm_ops.rs @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +use super::*; + +/// The actual data that gets threaded through the callbacks. +struct SmData<'a, 'ctx, T: DriverGpuVm> { + gpuvm: &'a mut UniqueRefGpuVm, + user_context: &'a mut T::SmContext<'ctx>, +} + +/// Represents an `sm_step_unmap` operation that has not yet been complete= d. +pub struct OpUnmap<'op, T: DriverGpuVm> { + op: &'op bindings::drm_gpuva_op_unmap, + // This ensures that 'op is invariant, so that `OpUnmap<'long, T>` doe= s not + // coerce to `OpUnmap<'short, T>`. This ensures that the user can't re= turn the + // wrong`OpUnmapped` value. + _invariant: PhantomData<*mut &'op mut T>, +} + +impl<'op, T: DriverGpuVm> OpUnmap<'op, T> { + /// Indicates whether this [`GpuVa`] is physically contiguous with the + /// original mapping request. + /// + /// Optionally, if `keep` is set, drivers may keep the actual page tab= le + /// mappings for this `drm_gpuva`, adding the missing page table entri= es + /// only and update the `drm_gpuvm` accordingly. + pub fn keep(&self) -> bool { + self.op.keep + } + + /// The range being unmapped. + pub fn va(&self) -> &GpuVa { + // SAFETY: This is a valid va. + unsafe { GpuVa::::from_raw(self.op.va) } + } + + /// Remove the VA. + pub fn remove(self) -> (OpUnmapped<'op, T>, GpuVaRemoved) { + // SAFETY: The op references a valid drm_gpuva in the GPUVM. + unsafe { bindings::drm_gpuva_unmap(self.op) }; + // SAFETY: The va is no longer in the interval tree so we may unli= nk it. + unsafe { bindings::drm_gpuva_unlink_defer(self.op.va) }; + + // SAFETY: We just removed this va from the `GpuVm`. + let va =3D unsafe { GpuVaRemoved::from_raw(self.op.va) }; + + ( + OpUnmapped { + _invariant: self._invariant, + }, + va, + ) + } +} + +/// Represents a completed [`OpUnmap`] operation. +pub struct OpUnmapped<'op, T> { + _invariant: PhantomData<*mut &'op mut T>, +} + +/// Represents an `sm_step_remap` operation that has not yet been complete= d. +pub struct OpRemap<'op, T: DriverGpuVm> { + op: &'op bindings::drm_gpuva_op_remap, + // This ensures that 'op is invariant, so that `OpRemap<'long, T>` doe= s not + // coerce to `OpRemap<'short, T>`. This ensures that the user can't re= turn the + // wrong`OpRemapped` value. + _invariant: PhantomData<*mut &'op mut T>, +} + +impl<'op, T: DriverGpuVm> OpRemap<'op, T> { + /// The preceding part of a split mapping. + #[inline] + pub fn prev(&self) -> Option<&OpRemapMapData> { + // SAFETY: We checked for null, so the pointer must be valid. + NonNull::new(self.op.prev).map(|ptr| unsafe { OpRemapMapData::from= _raw(ptr) }) + } + + /// The subsequent part of a split mapping. + #[inline] + pub fn next(&self) -> Option<&OpRemapMapData> { + // SAFETY: We checked for null, so the pointer must be valid. + NonNull::new(self.op.next).map(|ptr| unsafe { OpRemapMapData::from= _raw(ptr) }) + } + + /// Indicates whether the `drm_gpuva` being removed is physically cont= iguous with the original + /// mapping request. + /// + /// Optionally, if `keep` is set, drivers may keep the actual page tab= le mappings for this + /// `drm_gpuva`, adding the missing page table entries only and update= the `drm_gpuvm` + /// accordingly. + #[inline] + pub fn keep(&self) -> bool { + // SAFETY: The unmap pointer is always valid. + unsafe { (*self.op.unmap).keep } + } + + /// The range being unmapped. + #[inline] + pub fn va_to_unmap(&self) -> &GpuVa { + // SAFETY: This is a valid va. + unsafe { GpuVa::::from_raw((*self.op.unmap).va) } + } + + /// The [`drm_gem_object`](crate::gem::Object) whose VA is being remap= ped. + #[inline] + pub fn obj(&self) -> &T::Object { + self.va_to_unmap().obj() + } + + /// The [`GpuVmBo`] that is being remapped. + #[inline] + pub fn vm_bo(&self) -> &GpuVmBo { + self.va_to_unmap().vm_bo() + } + + /// Update the GPUVM to perform the remapping. + pub fn remap( + self, + va_alloc: [GpuVaAlloc; 2], + prev_data: impl PinInit, + next_data: impl PinInit, + ) -> (OpRemapped<'op, T>, OpRemapRet) { + let [va1, va2] =3D va_alloc; + + let mut unused_va =3D None; + let mut prev_ptr =3D ptr::null_mut(); + let mut next_ptr =3D ptr::null_mut(); + if self.prev().is_some() { + prev_ptr =3D va1.prepare(prev_data); + } else { + unused_va =3D Some(va1); + } + if self.next().is_some() { + next_ptr =3D va2.prepare(next_data); + } else { + unused_va =3D Some(va2); + } + + // SAFETY: the pointers are non-null when required + unsafe { bindings::drm_gpuva_remap(prev_ptr, next_ptr, self.op) }; + + let gpuva_guard =3D self.vm_bo().lock_gpuva(); + if !prev_ptr.is_null() { + // SAFETY: The prev_ptr is a valid drm_gpuva prepared for inse= rtion. The vm_bo is still + // valid as the not-yet-unlinked gpuva holds a refcount on the= vm_bo. + unsafe { bindings::drm_gpuva_link(prev_ptr, self.vm_bo().as_ra= w()) }; + } + if !next_ptr.is_null() { + // SAFETY: The next_ptr is a valid drm_gpuva prepared for inse= rtion. The vm_bo is still + // valid as the not-yet-unlinked gpuva holds a refcount on the= vm_bo. + unsafe { bindings::drm_gpuva_link(next_ptr, self.vm_bo().as_ra= w()) }; + } + drop(gpuva_guard); + + // SAFETY: The va is no longer in the interval tree so we may unli= nk it. + unsafe { bindings::drm_gpuva_unlink_defer((*self.op.unmap).va) }; + + ( + OpRemapped { + _invariant: self._invariant, + }, + OpRemapRet { + // SAFETY: We just removed this va from the `GpuVm`. + unmapped_va: unsafe { GpuVaRemoved::from_raw((*self.op.unm= ap).va) }, + unused_va, + }, + ) + } +} + +/// Part of an [`OpRemap`] that represents a new mapping. +#[repr(transparent)] +pub struct OpRemapMapData(bindings::drm_gpuva_op_map); + +impl OpRemapMapData { + /// # Safety + /// Must reference a valid `drm_gpuva_op_map` for duration of `'a`. + unsafe fn from_raw<'a>(ptr: NonNull) -> &'= a Self { + // SAFETY: ok per safety requirements + unsafe { ptr.cast().as_ref() } + } + + /// The base address of the new mapping. + pub fn addr(&self) -> u64 { + self.0.va.addr + } + + /// The length of the new mapping. + pub fn length(&self) -> u64 { + self.0.va.range + } + + /// The offset within the [`drm_gem_object`](crate::gem::Object). + pub fn gem_offset(&self) -> u64 { + self.0.gem.offset + } +} + +/// Struct containing objects removed or not used by [`OpRemap::remap`]. +pub struct OpRemapRet { + /// The `drm_gpuva` that was removed. + pub unmapped_va: GpuVaRemoved, + /// If the remap did not split the region into two pieces, then the un= used `drm_gpuva` is + /// returned here. + pub unused_va: Option>, +} + +/// Represents a completed [`OpRemap`] operation. +pub struct OpRemapped<'op, T> { + _invariant: PhantomData<*mut &'op mut T>, +} + +impl UniqueRefGpuVm { + /// Remove any mappings in the given region. + /// + /// Internally calls [`DriverGpuVm::sm_step_unmap`] for ranges entirel= y contained within the + /// given range, and [`DriverGpuVm::sm_step_remap`] for ranges that ov= erlap with the range. + #[inline] + pub fn sm_unmap(&mut self, addr: u64, length: u64, context: &mut T::Sm= Context<'_>) -> Result { + let gpuvm =3D self.as_raw(); + let mut p =3D SmData { + gpuvm: self, + user_context: context, + }; + // SAFETY: + // * raw_request() creates a valid request. + // * The private data is valid to be interpreted as SmData. + to_result(unsafe { bindings::drm_gpuvm_sm_unmap(gpuvm, (&raw mut p= ).cast(), addr, length) }) + } +} + +impl GpuVm { + /// # Safety + /// Must be called from `sm_unmap` with a pointer to `SmData`. + pub(super) unsafe extern "C" fn sm_step_unmap( + op: *mut bindings::drm_gpuva_op, + p: *mut c_void, + ) -> c_int { + // SAFETY: The caller provides a pointer to `SmData`. + let p =3D unsafe { &mut *p.cast::>() }; + let op =3D OpUnmap { + // SAFETY: sm_step_unmap is called with an unmap operation. + op: unsafe { &(*op).__bindgen_anon_1.unmap }, + _invariant: PhantomData, + }; + match p.gpuvm.data().sm_step_unmap(op, p.user_context) { + Ok(OpUnmapped { .. }) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// Must be called from `sm_unmap` with a pointer to `SmData`. + pub(super) unsafe extern "C" fn sm_step_remap( + op: *mut bindings::drm_gpuva_op, + p: *mut c_void, + ) -> c_int { + // SAFETY: The caller provides a pointer to `SmData`. + let p =3D unsafe { &mut *p.cast::>() }; + let op =3D OpRemap { + // SAFETY: sm_step_remap is called with a remap operation. + op: unsafe { &(*op).__bindgen_anon_1.remap }, + _invariant: PhantomData, + }; + match p.gpuvm.data().sm_step_remap(op, p.user_context) { + Ok(OpRemapped { .. }) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } +} diff --git a/rust/kernel/drm/gpuvm/va.rs b/rust/kernel/drm/gpuvm/va.rs index 540f525bb56d..654e5ca09bcb 100644 --- a/rust/kernel/drm/gpuvm/va.rs +++ b/rust/kernel/drm/gpuvm/va.rs @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT =20 -#![expect(dead_code)] use super::*; =20 /// Represents that a range of a GEM object is mapped in this [`GpuVm`] in= stance. diff --git a/rust/kernel/drm/gpuvm/vm_bo.rs b/rust/kernel/drm/gpuvm/vm_bo.rs index 7fd1d301fd41..098dbd7f9aa4 100644 --- a/rust/kernel/drm/gpuvm/vm_bo.rs +++ b/rust/kernel/drm/gpuvm/vm_bo.rs @@ -136,6 +136,14 @@ pub fn obj(&self) -> &T::Object { pub fn data(&self) -> &T::VmBoData { &self.data } + + pub(super) fn lock_gpuva(&self) -> crate::sync::MutexGuard<'_, ()> { + // SAFETY: The GEM object is valid. + let ptr =3D unsafe { &raw mut (*self.obj().as_raw()).gpuva.lock }; + // SAFETY: The GEM object is valid, so the mutex is properly initi= alized. + let mutex =3D unsafe { crate::sync::Mutex::from_raw(ptr) }; + mutex.lock() + } } =20 /// A pre-allocated [`GpuVmBo`] object. --=20 2.53.0.959.g497ff81fa9-goog From nobody Thu Apr 2 17:44:30 2026 Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 461033D0914 for ; Fri, 20 Mar 2026 16:08:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022929; cv=none; b=SPrz97MJkO9KMSrv3jLPa3plxLkxl0Fiin9FxI8N06k0SzqJC9sSmXIX0jh6YxCeIi0NflyQzIqNe/mFMX1PrckAcv4imO5R/Zrpl9Wzd76wblnx748We2LiiwsVqUVc3iV7AtKjvz1MFyHXVAF+5xN75taxxvOlmvg/KwzBMLA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774022929; c=relaxed/simple; bh=pJHrLXIHGhItCYI8EF29aToSZy8W3VqMaEzP+Dsyx3o=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=kkmQ3EPiRRtsxG2RwDIp15a4p1WZpR/6HQQ7g/xE/SDRMfaUWI6SFslgQRCXkRwL0KSbMspt/u18GLawqN4N5XBTnHb1HoLbjD8zyDrIiR2iWs7Eh2mkowYtM3mn9qeG4CM6N+nt4LtkENJ3m4KCJpYvhY143OpKgJj5yfKCrKc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=UQ8WUVjI; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="UQ8WUVjI" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-486fb29a8b8so15875935e9.0 for ; Fri, 20 Mar 2026 09:08:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1774022925; x=1774627725; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=PEPN1CCLI1GKK6sV/j4L1DMhnYo+L22QHlnStf4ccEg=; b=UQ8WUVjI89qpekgl10z/m2nrk6xN+0p8CLuzvJwWGO/O8kab6+6mFe/WezrM3Q8e9B v+Q24cUn++Hc00EGhv2KE9rqawKlF0awhwk1+VFxbCnHqHbHuYDoKAltHpJYaz9MfyFy kbILb4/Y2xRzQp/CJ7GA3yeyn+MORq15Ji+VxE9IVcbf8JHWT04dLEW3Vjmo90xcZlP5 TH/hfZwMZqkHnoEKETE86H26kW5qF6BNJ8L1c2R7u8wx16g8QJAQZwKpYt6wojIVZ8ya 1p87K66DD7/DxsgM051FI7RTJIdjcW/QwQIofA/z+abAxNIF932CQrQkTf8nwf/JeMu1 dugw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774022925; x=1774627725; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=PEPN1CCLI1GKK6sV/j4L1DMhnYo+L22QHlnStf4ccEg=; b=JcvjofML4YC24F5IT+vyHCg0a/lRGkd4pfk/ZM74YOeGGC5W0Qwdbtmkcjyamnz0Ei O3+p45weDWo916Ze6e/9REWjoDjSSqQmt0ZQ/xxPOl6AfLmo8DtNUMJX1c7mOYF5m5I0 FCmGAM0fMHk8iG2Ze7uXAkEUj8ycOBzwXeRzgg8A0pnxM/NwEC+FEU8H8fQqe641cVo1 WKZLbCPUK8hWm5oratJLzrZvmvUsOzXj0m6lvICfg1CXroDedVolnX4R0x6hf/HuX54i Db3HVnMM8J6kf4QXJB3IuKE7kRbNAtqkn5eDH6a/TXcE9euMVx14jmaKSSmiArPXFdo0 Zygg== X-Forwarded-Encrypted: i=1; AJvYcCW2j0aCsiq+N6qqI9caeOo0Q06oPMJneg/LhZi0SCIX7JvHnc6IqJtmdsuqca8yna9YxCdJJPsE34i+dWA=@vger.kernel.org X-Gm-Message-State: AOJu0Yx1BA1PkVE+8k1glV7jrcKbZnAIVTX2RFwR5dEcSSxKujnnnRW8 JVHw7NlIuz/2DJ5+aGe2RJGF/9AAyeqbglvE0ef5wiQuOkJ/VdPD3Ke3/wcJCAI6EmY8KC/Yqmm yre3eiADbpsnLS/WT3A== X-Received: from wmjq10.prod.google.com ([2002:a7b:ce8a:0:b0:485:2cf0:2668]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:46ce:b0:485:2f4a:6ae6 with SMTP id 5b1f17b1804b1-486febb56demr52608445e9.6.1774022925504; Fri, 20 Mar 2026 09:08:45 -0700 (PDT) Date: Fri, 20 Mar 2026 16:08:30 +0000 In-Reply-To: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260320-gpuvm-rust-v5-0-76fd44f17a87@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=9670; i=aliceryhl@google.com; h=from:subject:message-id; bh=pJHrLXIHGhItCYI8EF29aToSZy8W3VqMaEzP+Dsyx3o=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpvXECjoN67yqz9QA1puINwCTzOntqXbsNrU+4a RodE6QNb/aJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCab1xAgAKCRAEWL7uWMY5 RszxD/9kMoNIG/ohpvcMrtMbuodzOfNpRDtOMbn+49YSrdubOITnMFZDMqS9JMJP0rQVila++8n RaOKiB9lo7YF93n1rbdv1BNxEemrQfZhf3Rrs7vPwcWD1NarPNqVSusCgi6o03Msl3V9Z54WT4z 9TqPKTVHsVNErS8yP4CMUHBWpOs8nkI5JLzDThMAO4g0qI79V/1lU6sfxRzwUxb0i03QcaA8Fs4 zHunoHK8LhoOb6dDV2Rv5vHqVIeG5izarcwR5Fipa7melfQ6J1GFe7GeN9fA/sN5XIWDml1MUtm peWrxGkDpPr5UgaMQ81ji7cAO3emiUcmewbkFSOo+NuSQmCwnfMl1SX7NpKAUJkIvq7rRi2uJob mSpeD9AyJTzFfK5wsT8IV1aFT5bZFoF0pf/vDV9Xj5XBfH2j9D9tZSO+IGqXeMEUFsO2xaNmUKb cTrTT9DJ72mbEguMKKxPg/+8oryabvUEVVDsibUQz79IRcKwsFnIGZ5ccjhe8W4IYZLl8gVcobd W7ZAEm8OIvjNsfRpAdS7M5mWdpW3rGPvVE+Zcj1/E6ixSiljitdxsA5+BJ4VVidsbUaxtzt6sWY uK7gMVDk14+Wb29RMoiVYzbOgAaYgnRUrywHoIeHCOU+4WxbU7GqEE3WPWzcPFoEr5QsK5C1YLe 3rwCvMdtFHl8lYA== X-Mailer: b4 0.14.3 Message-ID: <20260320-gpuvm-rust-v5-6-76fd44f17a87@google.com> Subject: [PATCH v5 6/6] rust: gpuvm: add GpuVmCore::sm_map() From: Alice Ryhl To: Danilo Krummrich , Daniel Almeida Cc: Boris Brezillon , Janne Grunau , Matthew Brost , "=?utf-8?q?Thomas_Hellstr=C3=B6m?=" , Lyude Paul , Asahi Lina , Sumit Semwal , "=?utf-8?q?Christian_K=C3=B6nig?=" , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Finally also add the operation for creating new mappings. Mapping operations need extra data in the context since they involve a vm_bo coming from the outside. Co-developed-by: Asahi Lina Signed-off-by: Asahi Lina Reviewed-by: Daniel Almeida Signed-off-by: Alice Ryhl --- rust/kernel/drm/gpuvm/mod.rs | 9 ++- rust/kernel/drm/gpuvm/sm_ops.rs | 157 ++++++++++++++++++++++++++++++++++++= ++-- 2 files changed, 160 insertions(+), 6 deletions(-) diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs index dca72e7b2b89..64a614d14f09 100644 --- a/rust/kernel/drm/gpuvm/mod.rs +++ b/rust/kernel/drm/gpuvm/mod.rs @@ -98,7 +98,7 @@ const fn vtable() -> &'static bindings::drm_gpuvm_ops { vm_bo_alloc: GpuVmBo::::ALLOC_FN, vm_bo_free: GpuVmBo::::FREE_FN, vm_bo_validate: None, - sm_step_map: None, + sm_step_map: Some(Self::sm_step_map), sm_step_unmap: Some(Self::sm_step_unmap), sm_step_remap: Some(Self::sm_step_remap), } @@ -255,6 +255,13 @@ pub trait DriverGpuVm: Sized { /// The private data passed to callbacks. type SmContext<'ctx>; =20 + /// Indicates that a new mapping should be created. + fn sm_step_map<'op, 'ctx>( + &mut self, + op: OpMap<'op, Self>, + context: &mut Self::SmContext<'ctx>, + ) -> Result, Error>; + /// Indicates that an existing mapping should be removed. fn sm_step_unmap<'op, 'ctx>( &mut self, diff --git a/rust/kernel/drm/gpuvm/sm_ops.rs b/rust/kernel/drm/gpuvm/sm_ops= .rs index ab802a07e4be..1d07e84a8062 100644 --- a/rust/kernel/drm/gpuvm/sm_ops.rs +++ b/rust/kernel/drm/gpuvm/sm_ops.rs @@ -8,6 +8,103 @@ struct SmData<'a, 'ctx, T: DriverGpuVm> { user_context: &'a mut T::SmContext<'ctx>, } =20 +#[repr(C)] +struct SmMapData<'a, 'ctx, T: DriverGpuVm> { + sm_data: SmData<'a, 'ctx, T>, + vm_bo: ARef>, +} + +/// The argument for [`GpuVmCore::sm_map`]. +pub struct OpMapRequest<'a, 'ctx, T: DriverGpuVm> { + /// Address in GPU virtual address space. + pub addr: u64, + /// Length of mapping to create. + pub range: u64, + /// Offset in GEM object. + pub gem_offset: u64, + /// The GEM object to map. + pub vm_bo: ARef>, + /// The user-provided context type. + pub context: &'a mut T::SmContext<'ctx>, +} + +impl<'a, 'ctx, T: DriverGpuVm> OpMapRequest<'a, 'ctx, T> { + fn raw_request(&self) -> bindings::drm_gpuvm_map_req { + bindings::drm_gpuvm_map_req { + map: bindings::drm_gpuva_op_map { + va: bindings::drm_gpuva_op_map__bindgen_ty_1 { + addr: self.addr, + range: self.range, + }, + gem: bindings::drm_gpuva_op_map__bindgen_ty_2 { + offset: self.gem_offset, + obj: self.vm_bo.obj().as_raw(), + }, + }, + } + } +} + +/// Represents an `sm_step_map` operation that has not yet been completed. +pub struct OpMap<'op, T: DriverGpuVm> { + op: &'op bindings::drm_gpuva_op_map, + // Since these abstractions are designed for immediate mode, the VM BO= needs to be + // pre-allocated, so we always have it available when we reach this po= int. + vm_bo: &'op GpuVmBo, + // This ensures that 'op is invariant, so that `OpMap<'long, T>` does = not + // coerce to `OpMap<'short, T>`. This ensures that the user can't retu= rn + // the wrong `OpMapped` value. + _invariant: PhantomData<*mut &'op mut T>, +} + +impl<'op, T: DriverGpuVm> OpMap<'op, T> { + /// The base address of the new mapping. + pub fn addr(&self) -> u64 { + self.op.va.addr + } + + /// The length of the new mapping. + pub fn length(&self) -> u64 { + self.op.va.range + } + + /// The offset within the [`drm_gem_object`](crate::gem::Object). + pub fn gem_offset(&self) -> u64 { + self.op.gem.offset + } + + /// The [`drm_gem_object`](crate::gem::Object) to map. + pub fn obj(&self) -> &T::Object { + // SAFETY: The `obj` pointer is guaranteed to be valid. + unsafe { ::from_raw(self.op.gem.obj) } + } + + /// The [`GpuVmBo`] that the new VA will be associated with. + pub fn vm_bo(&self) -> &GpuVmBo { + self.vm_bo + } + + /// Use the pre-allocated VA to carry out this map operation. + pub fn insert(self, va: GpuVaAlloc, va_data: impl PinInit) -> OpMapped<'op, T> { + let va =3D va.prepare(va_data); + // SAFETY: By the type invariants we may access the interval tree. + unsafe { bindings::drm_gpuva_map(self.vm_bo.gpuvm().as_raw(), va, = self.op) }; + + let _gpuva_guard =3D self.vm_bo().lock_gpuva(); + // SAFETY: The va is prepared for insertion, and we hold the GEM l= ock. + unsafe { bindings::drm_gpuva_link(va, self.vm_bo.as_raw()) }; + + OpMapped { + _invariant: self._invariant, + } + } +} + +/// Represents a completed [`OpMap`] operation. +pub struct OpMapped<'op, T> { + _invariant: PhantomData<*mut &'op mut T>, +} + /// Represents an `sm_step_unmap` operation that has not yet been complete= d. pub struct OpUnmap<'op, T: DriverGpuVm> { op: &'op bindings::drm_gpuva_op_unmap, @@ -211,6 +308,30 @@ pub struct OpRemapped<'op, T> { } =20 impl UniqueRefGpuVm { + /// Create a mapping, removing or remapping anything that overlaps. + /// + /// Internally calls the [`DriverGpuVm`] callbacks similar to [`Self::= sm_unmap`], except that + /// the [`DriverGpuVm::sm_step_map`] is called once to create the requ= ested mapping. + #[inline] + pub fn sm_map(&mut self, req: OpMapRequest<'_, '_, T>) -> Result { + let gpuvm =3D self.as_raw(); + let raw_req =3D req.raw_request(); + let mut p =3D SmMapData { + sm_data: SmData { + gpuvm: self, + user_context: req.context, + }, + vm_bo: req.vm_bo, + }; + // SAFETY: + // * raw_request() creates a valid request. + // * The private data is valid to be interpreted as both SmData an= d SmMapData since the + // first field of SmMapData is SmData. + to_result(unsafe { + bindings::drm_gpuvm_sm_map(gpuvm, (&raw mut p).cast(), &raw co= nst raw_req) + }) + } + /// Remove any mappings in the given region. /// /// Internally calls [`DriverGpuVm::sm_step_unmap`] for ranges entirel= y contained within the @@ -224,19 +345,45 @@ pub fn sm_unmap(&mut self, addr: u64, length: u64, co= ntext: &mut T::SmContext<'_ }; // SAFETY: // * raw_request() creates a valid request. - // * The private data is valid to be interpreted as SmData. + // * The private data is a valid SmData. to_result(unsafe { bindings::drm_gpuvm_sm_unmap(gpuvm, (&raw mut p= ).cast(), addr, length) }) } } =20 impl GpuVm { /// # Safety - /// Must be called from `sm_unmap` with a pointer to `SmData`. + /// Must be called from `sm_map` with a pointer to `SmMapData`. + pub(super) unsafe extern "C" fn sm_step_map( + op: *mut bindings::drm_gpuva_op, + p: *mut c_void, + ) -> c_int { + // SAFETY: If we reach `sm_step_map` then we were called from `sm_= map` which always passes + // an `SmMapData` as private data. + let p =3D unsafe { &mut *p.cast::>() }; + let op =3D OpMap { + // SAFETY: sm_step_map is called with a map operation. + op: unsafe { &(*op).__bindgen_anon_1.map }, + vm_bo: &p.vm_bo, + _invariant: PhantomData, + }; + match p + .sm_data + .gpuvm + .data() + .sm_step_map(op, p.sm_data.user_context) + { + Ok(OpMapped { .. }) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// Must be called from `sm_map` or `sm_unmap` with a pointer to `SmMa= pData` or `SmData`. pub(super) unsafe extern "C" fn sm_step_unmap( op: *mut bindings::drm_gpuva_op, p: *mut c_void, ) -> c_int { - // SAFETY: The caller provides a pointer to `SmData`. + // SAFETY: The caller provides a pointer that can be treated as `S= mData`. let p =3D unsafe { &mut *p.cast::>() }; let op =3D OpUnmap { // SAFETY: sm_step_unmap is called with an unmap operation. @@ -250,12 +397,12 @@ impl GpuVm { } =20 /// # Safety - /// Must be called from `sm_unmap` with a pointer to `SmData`. + /// Must be called from `sm_map` or `sm_unmap` with a pointer to `SmMa= pData` or `SmData`. pub(super) unsafe extern "C" fn sm_step_remap( op: *mut bindings::drm_gpuva_op, p: *mut c_void, ) -> c_int { - // SAFETY: The caller provides a pointer to `SmData`. + // SAFETY: The caller provides a pointer that can be treated as `S= mData`. let p =3D unsafe { &mut *p.cast::>() }; let op =3D OpRemap { // SAFETY: sm_step_remap is called with a remap operation. --=20 2.53.0.959.g497ff81fa9-goog