From nobody Sat Feb 7 21:08:18 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 C7FC63D4139 for ; Wed, 21 Jan 2026 11:31:53 +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=1768995116; cv=none; b=kTYc0RRPpLJkyyylUpK9xDf4W3krhz7JjcsUzMGHzDdUFUAK/EG1TQ+s/pB1iyVaHGpEZH/qVhHyQvHXsEeCVVU29cetxhUbkxrZC/28/eINyyhs3nPDgTsOo2vbUQ9DnDXjClCu5cBJP0eY6MqxcH+5LuFN/bhJ80sxAqeacmI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768995116; c=relaxed/simple; bh=QT598xKSltbEtm8vXGNQ8SdDthsUesBeRaBTzJqOa44=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=WrDWjENUFt0dhN+PnjVFCRVRPaPnvhsah9NsZTJ+hXaZB2ntJJDbBIiNKM7ATgcLnEywNmOFLM2Gi4dD/KpDaWGSG46H+6Sdd/Q6+sJ6NsPyKJ/IYl8HPyWeMmFa77T46ic66j98/JKk0/vq23/bmZxdZAtZcURz8337z9BmWlE= 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=FU5VlZgZ; 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="FU5VlZgZ" Received: by mail-wr1-f73.google.com with SMTP id ffacd0b85a97d-430f57cd2caso5183584f8f.0 for ; Wed, 21 Jan 2026 03:31:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1768995112; x=1769599912; 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=EGHQDH6BoNVeJd4cDefcw3Iw7Y0ea3fsAXQePB2iyUs=; b=FU5VlZgZAbmHoHz+GecPDAiFk+jCyST92tHlHnBPqG2IRC/nikKZG9Od8o/svBoqtx yctXRwQTJ74fPA6EQMGXHqEKlDCb8TGnuNgvIlRlPXHOD+OyiX6I+K5wZkAzXENR4dZs 9aG4aJHqwuWa7RlcjVg6J5DqZnda//uEf2Vo/bKHBdxrefcO1vMVsjCWiJPUzNaIzVVZ g/Ij7qGvypBl4+p8WuYxjE/lM8L5rXX3FLiyHrn0o0gZCYRfK4+M5d5RAVicatOrFVuK daqUM3A/aM1RBFfxU3E7rzW2sR+ZrQZmMzzKMekjlN8sKIHvMRn7n2t61vKCGKGx9dr7 EPyg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768995112; x=1769599912; 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=EGHQDH6BoNVeJd4cDefcw3Iw7Y0ea3fsAXQePB2iyUs=; b=SMPAaGHT02LBOj/nI9arAQ7BE5cYrWc0tmWOapp/eFmqZbLE7MzkiSRnfgqBJSe80N oByZQR4So+y07r5ROHlKY2V5873sNUK/ZB9eF917qfiwOQdCfN6iPyLgDm/SziMy/kg6 rcERYflgsTq3wK5GAaDiUO5euB6Dpy5HWxrnvKJT9ELqF6saZVbIe2geBpM/AiH18v97 CNu2X2WJgms6ek2PnzVG7u2XvDHt8S0sAQwCu8+GfYQY087Tz2Z0TAYf3u9MUeLPVTuE oNVSwIex5wz5utsLlSScv1K7887jCsFsMorJ2+IvRfclaAywqVLKrbnsVMlo+e8WSQUS nVzQ== X-Forwarded-Encrypted: i=1; AJvYcCVHdk+4suZK7xVpvIdEqGXkBya+fj7W1ZbmQLGy4iPuN37ep//SYpWDUjcdi7aH34k24JF9txyim/+E/jc=@vger.kernel.org X-Gm-Message-State: AOJu0YwHaJSj6aA6jiIfK8kY2eRLvbzh+t6TspeG+LvcJZu1LTP/S4bC CiQNwvIM0p60CyN+/5bIjp05TM4Or9YDWprfIIl2a+rxmwZwyXjgNbpE+7gYtml1RLziFX3DER1 Pubhc7aXt6Qx7BmtGzA== X-Received: from wrf23.prod.google.com ([2002:a05:6000:43d7:b0:435:97fd:ed65]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a5d:5f48:0:b0:431:6ba:38ac with SMTP id ffacd0b85a97d-4356a033204mr24644212f8f.4.1768995112164; Wed, 21 Jan 2026 03:31:52 -0800 (PST) Date: Wed, 21 Jan 2026 11:31:17 +0000 In-Reply-To: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=11911; i=aliceryhl@google.com; h=from:subject:message-id; bh=gsy08/pi46UtQSQFtePT4mRLZ3dgJLIJxW9dRSa7MUo=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpcLkdA2PFraLMJbUWQJH5xRZjLi6fWKC0BrD/3 XHW50OrXCiJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXC5HQAKCRAEWL7uWMY5 Rk5hEACmArh2p5dOffCaFjbbfF8wAjZnLbci4H5HAv4AF3AEUcc5WGUOB8Yl7pPt0+IK/L5ORnX L/RjBB9ZLWE20bUW1Ctk7KifXTFEdGnZfwfF6YyTs60kgMsX/5qt2YU3nUnhiDksZXLn/wCrBTS dByMUpzrO7voCxJZWqmLOmaMLesyMBS9d3CM/JUhPdf6imGGYZKZ/2G1Fx6Oo9r+ndjJfH4tXXk ps4dAoehsaVZaS25GPjGOzGRb8sJv/oagR8IdK3KAleipMqkRv1PVwgQkhQzbxqZ19/t1mlxkKn DXg6z8p2zaGfTg5WRasTNN86c/0FxYguq6biDSC2tzhzRqdWzrc6iM4H0h3CjG74gCjHypLADhz 59ka0Ca5JHDvgoSRTKm4ShnvTsye+gr9wQMnwasvcw13PHtcACvhHD2ZX5b2+fdMXFarUna4zVh y8MXsNddRE+8BiUJSSHC2/UM9uleykIf7luXAol3vXS2ZYTZ9nb+HUEvjCB7sSIkeZL877w4HvS bFTDjcOekP5gYQUMziF3aaq24TSshRtMU0QQHXA3dXZzyVpMCJzLcPvOFslRUF0/0fv9sdr7E9M iU1E6i6cZBGdEFAqUjOvCGdyiIj7B9FI9vetG9RGFTY/vVjBy7gJOdzTnneY+JLKbXWDEkUXoXb bfK7jXi3WobDwMw== X-Mailer: b4 0.14.2 Message-ID: <20260121-gpuvm-rust-v3-1-dd95c04aec35@google.com> Subject: [PATCH v3 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 , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@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 GpuVmCore, 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 Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Reviewed-by: Daniel Almeida --- MAINTAINERS | 2 + rust/bindings/bindings_helper.h | 1 + rust/helpers/drm_gpuvm.c | 18 ++++ rust/helpers/helpers.c | 1 + rust/kernel/drm/gpuvm/mod.rs | 231 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/drm/mod.rs | 1 + 6 files changed, 254 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3b84ad595e226f231b256d24f0da6bac459e93a8..618becae72985b9dfdca8469ee4= 8d4752fd0ca41 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8720,6 +8720,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 a067038b4b422b4256f4a2b75fe644d47e6e82c8..dd60a5c6b142ec2c5fd6df80279= ab6813163791c 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 0000000000000000000000000000000000000000..d1471e5844ec81f994af9252d90= 54053ab13f352 --- /dev/null +++ b/rust/helpers/drm_gpuvm.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +#ifdef CONFIG_DRM_GPUVM + +#include + +struct drm_gpuvm *rust_helper_drm_gpuvm_get(struct drm_gpuvm *obj) +{ + return drm_gpuvm_get(obj); +} + +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 79c72762ad9c4b473971e6210c9577860d2e2b08..0943d589b7578d3c0e207937f63= a5e02719c6146 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -26,6 +26,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 0000000000000000000000000000000000000000..81b5e767885d8258c44086444b1= 53c91961ffabc --- /dev/null +++ b/rust/kernel/drm/gpuvm/mod.rs @@ -0,0 +1,231 @@ +// 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 "core" is only accessible using a s= pecial unique handle. The +/// core consists of the `core` field and the GPUVM's interval tree. +/// +/// # Invariants +/// +/// * Stored in an allocation managed by the refcount in `self.vm`. +/// * Access to `data` and the gpuvm interval tree is controlled via the [= `GpuVmCore`] type. +#[pin_data] +pub struct GpuVm { + #[pin] + vm: Opaque, + /// Accessed only through the [`GpuVmCore`] reference. + data: UnsafeCell, +} + +// 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. + Ok(GpuVmCore(unsafe { + ARef::from_raw(NonNull::new_unchecked(KBox::into_raw( + Pin::into_inner_unchecked(obj), + ))) + })) + } + + /// 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 `GpuVmCore` reference. +pub struct GpuVmCore(ARef>); + +impl GpuVmCore { + /// 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 GpuVmCore { + 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 1b82b6945edf25b947afc08300e211bd97150d6b..a4b6c5430198571ec701af2ef45= 2cc9ac55870e6 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.52.0.457.g6b5491de43-goog From nobody Sat Feb 7 21:08:18 2026 Received: from mail-ed1-f74.google.com (mail-ed1-f74.google.com [209.85.208.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 77AEA3ECBE4 for ; Wed, 21 Jan 2026 11:31:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768995118; cv=none; b=Y4smyyUCaj7GASJ/rClT3lfqPXZLDMb+swPlk6m1ssf0lcT7vsK8yJic0Sazg1lEn3EBndsfLKe7/qQUXUoM27K2mKr0YRl0SRnfFTdEeu6D5Dk5x3uEYK3DNEmMhE32HPv/rru9GyY/0quyndFrcuNFkEME4o7vZQIBMjXoYdg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768995118; c=relaxed/simple; bh=P5wAIaRKPh85zJzKZEFPqIhdMETe8VR8WR1ZOlPakyA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=GVXtGeo+kmkD3rHztQmoFI62jWUIkL1bSobZa9zn3YluIYtzaVtRndBx73aENvrgMi1k4Ku6MQbegLgQQ8weACK0vAq6DY5fuNGUySdt7rEhG6YiHsNlNckvUf3QRGXrscgNiV90hjT1SKXt13CtkBdmxrKnW1n/WPzbKB4SiuM= 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=K5enx0H4; arc=none smtp.client-ip=209.85.208.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="K5enx0H4" Received: by mail-ed1-f74.google.com with SMTP id 4fb4d7f45d1cf-6582e841d15so61478a12.1 for ; Wed, 21 Jan 2026 03:31:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1768995114; x=1769599914; 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=n0kCZZM6p+E0DmFtSo+zls4mReeu1EP5jRXjRI1bQ8M=; b=K5enx0H4dNKr4RxxOpbiKDoyFwfURaBaITEcKyT/Z93mer5uNTkRNpJRtlAkriqwR/ Nw6PpwCzB/FYGYtAgvex/OEQsh0bUByurI5L6RWWZvWhpIOA8yEiVyyiFT395WJttXjb XCP1VnhFO+u0Q3ohGNdtF/X62zxfrewXdZZYGsMMPEM5T2U5tv1k/xd7jMxL1e0z5spo BfiTz1+pZlNcwM0ButWGsL7sNpoDeiPvu69A3jN/GWFvc9aBSX+TESQUQdPq1JAlRN7d IQWQwk8rkqkDc1vLYfjabnZtEpwVuDqh/ZTjTFFxWhTxx6/sH4bd78tCdck3G4oxCErT YzIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768995114; x=1769599914; 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=n0kCZZM6p+E0DmFtSo+zls4mReeu1EP5jRXjRI1bQ8M=; b=jc/RXTE1YfgN7YLwPVFB0WxYglL7mIDcG0oBvZjVRi8VZIiylH5ThQ4nIXzpANIaK0 eEKwtC4Z/vt3Lg94Rz+kcUsQ0+kBL42uO64py7uJtHXV5gYEmrvpixr1ZJRjIAjOdHez jfEJyGKCbjvBj0iUuuvKaaItC8L4xk2pacN6Us6Xnq7p6VEKK4g5wU4vvcbL1cbSsv5e 0PFRH1bRF3i0CsgtdUEwy1XduNIRelEga9IdywAv/pMdcS5wB0XWeyZ3Xe6D+6BuaBk8 WLl3D7YlQZ8UhSrluGvvmlOtgVjhFs/S/K1//pGx6Xll/TG748TD/XnAYx7dOIUhX9bQ EJAg== X-Forwarded-Encrypted: i=1; AJvYcCULFQg1j8CKhnYzdf+xOpOYne8qVv8XppUAIHcJo+WXN7KfDWeBkVX3gocpG3p+kYkRtkrBPksGnBAj42o=@vger.kernel.org X-Gm-Message-State: AOJu0Yz+vL/xjGMISI8M5VwLUVcOrzQum0RI26uJwKOPoFU6EFf8M5wm ArjH6Aj8JOMWnPnKmoyXlR0OC6xDk5M9NG6o/AC8eBEXbQOQcMwOBykuOZeaPWgk5g6t/R09HgC x1oNw6TD72JFNunYtpQ== X-Received: from ejcb5.prod.google.com ([2002:a17:906:36c5:b0:b7c:eb5d:caa1]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a17:906:6a1b:b0:b80:4108:f826 with SMTP id a640c23a62f3a-b8796aee516mr1388441366b.36.1768995114622; Wed, 21 Jan 2026 03:31:54 -0800 (PST) Date: Wed, 21 Jan 2026 11:31:18 +0000 In-Reply-To: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=2156; i=aliceryhl@google.com; h=from:subject:message-id; bh=1z0yLHaRNnuMNmurQRoqGqVcNNO3MBwRXrJ8DuvVV9s=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpcLkdj+BPjbRCtGqjLAHynwm2GJjp67dFSVfjd anNEQ9bBDqJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXC5HQAKCRAEWL7uWMY5 RjsRD/9Qa7uaYEVUkWWEcDspFV55uc+SZPuy6VeJHSgm6lIpAoBeIe9TRWGXbVwsPtXR8atAqUZ 6t9+o5o1CyDD4mxImiOl9XaIBPgEFnb7f8p7sk0MdZz1VWVeHT4fb7g8kMaxvixsDBpMKnb6Zn3 GDiOCx/DeiJc7Iiht2GwFqMhCi1BgX2HBCO6eAcEu2NhHCRU4PgSFRX7Cl0eLt2CDyyPR8wexgE yCT7T/XAqsyY2SYt/GEQxk9WadlOQvthMwJR8i3lmjVvcxjpbFHw2NWddFSiW2qASIuV5ld8+ZE SJYBqqjFi/6uGnQpMAMP/sTNAI/hkvDgtMUQNVSjlgkBAQ9QBQlNc9tYA66lUpNU0zeHhw4RM3x SFjSKvc2VsEeaEjc1StrUJNlI9arTrrTPdDj+/+0XwKNhhSo1BDXO7Lw72bQybhP7rBMyKbJlx7 kHoswT/MmLHqMOHzcIRkAWrxkXPoFdNeaSoYqZn9DLXnZvC4fonDYEWKRawkmsODIpGtGSiY9PQ oxV1f1uEHAXQIbOqd7IVHN6TsgxkuXrtdD9WQ58ZdwaceTVxIIwyTSaYluDseGgYhj5nMnqXgjd pEl7iNsF7M9408d6h0GmH2rLYiRVlATdI4a6zywi6m+JA6V2ujhW87RI892nJJDfjwqGryE607x yBAC3Wr9u1PDhqw== X-Mailer: b4 0.14.2 Message-ID: <20260121-gpuvm-rust-v3-2-dd95c04aec35@google.com> Subject: [PATCH v3 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 , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@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. Signed-off-by: Asahi Lina Signed-off-by: Daniel Almeida Reviewed-by: Alice Ryhl Signed-off-by: Lyude Paul Reviewed-by: Janne Grunau Signed-off-by: Alice Ryhl Reviewed-by: Daniel Almeida --- Taken from: https://lore.kernel.org/all/20251202220924.520644-3-lyude@redhat.com/ --- rust/bindings/bindings_helper.h | 1 + rust/helpers/dma-resv.c | 13 +++++++++++++ rust/helpers/helpers.c | 1 + 3 files changed, 15 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index dd60a5c6b142ec2c5fd6df80279ab6813163791c..ed80dd8088bc60c67b02f7666e6= 02e33158bb962 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 0000000000000000000000000000000000000000..05501cb814513b483afd0b7f220= 230d867863c2f --- /dev/null +++ b/rust/helpers/dma-resv.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +int rust_helper_dma_resv_lock(struct dma_resv *obj, struct ww_acquire_ctx = *ctx) +{ + return dma_resv_lock(obj, ctx); +} + +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 0943d589b7578d3c0e207937f63a5e02719c6146..aae78c938b0b5848b1740fb3f2d= c4b7f93b1a760 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -25,6 +25,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.52.0.457.g6b5491de43-goog From nobody Sat Feb 7 21:08:18 2026 Received: from mail-ej1-f74.google.com (mail-ej1-f74.google.com [209.85.218.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 329683EF0C7 for ; Wed, 21 Jan 2026 11:31:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768995122; cv=none; b=k7inw2QIGWRoDTGOeSUp961GMFa6kStl1NC20czpsu8pYcGniGGMoiPuXsjsenhSyKAxnj41q+MuPzreilre8JWjU6dgoM0gNyM1ltPGCFIjvu8ebO81KrOLHqdyg3RkpZivpyOcs6ty5KC/QcPGYwv/oXlTjT3D/yDT8pNn1ug= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768995122; c=relaxed/simple; bh=vC3+05kWBzpETYWstiU3G/6khCsB+5+BSNGaApB13E4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Go7mbDgStNmxrxqb22Wh+8fOrssnAgYLCyMknL8NfFo/T7UY9vRd5QcH+oBtvkVjZfJi5/EKZjsPbQfr4CoUpDPZZqwuhqpDdNFntiOHIMIJ9l4lnOfd0NKLilYkVdb23gchZjtn8btv+hYrGWlSMClm4qKUOwUJ9iqG/EKUScc= 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=EtfEzInK; arc=none smtp.client-ip=209.85.218.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="EtfEzInK" Received: by mail-ej1-f74.google.com with SMTP id a640c23a62f3a-b7ff8a27466so692621566b.3 for ; Wed, 21 Jan 2026 03:31:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1768995117; x=1769599917; 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=byZUHYbnD2FmVZ/gZogcTNsh5E8bfWqnPKh+s4Vmwt8=; b=EtfEzInK4/t2h10rGgLiQssa089McmNw60/wgzNxX7aKCdglWmmVnXXkb1AGwjcHDa rQWRcRjXfMlYGiTc9/lEtvKZJJm6PA4pWeaGFtRi5j+98IFNmpdegsM8I0u1tVdp5Pt0 aiU6C9av/Hql729avhNQTZFcBO+EqUtxkG7lxA1joOsMWD8mpL3/OxAm4JzO3i87ikOE n420/PgsJxau09bmQLefskkcaw5I9KXcqFrQOi42IWp+TlXLj+AbSojzPLlz7xrErR+W 3UBjopCM6DMBDUJiQQtfSfU5PHbFo4Ec5c/JTTmQ2hUqKcSifBhmVtOST4359SlXXEp8 Lm9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768995117; x=1769599917; 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=byZUHYbnD2FmVZ/gZogcTNsh5E8bfWqnPKh+s4Vmwt8=; b=Bz+Iy028uyGWjtYPKHmr/nbiVAWlKddT+Zw2xLb36uc1vV16yMaUqHq9W1yTep7HzW 9Eaan9vyU8mTRR2SoK5uTrfX2L5j9nqU7pgIu98TL7W41uRS2jIxuVYg46A3/TWJ7e8O Yq16c3/+xAe9YmAfKWvcWC9VwQmO32kd22ZvIQgqkhfeRilM8aitWZ0ejF/WImf5a2kV FWGGKVxrINlfaYFkf7xcVTc26zftgaEjRZyeDhs/sFbAsVKb0gq+nuHwGkKs7oeWTtVo h1jUbgVCocV9OJL+yst7TJbzDxPg1iqvBjsjIpOmme+uyIdic9hGUkEmd1rkOqY5ZOvJ WoVg== X-Forwarded-Encrypted: i=1; AJvYcCV0bD6kUF5f9FOyjnTNp4oUjFLucS4q2mEw7pj2fIp/qiM2Gx/qOcYpd+Hs21j0gZxjKQ8d+dcj/tQ+hKM=@vger.kernel.org X-Gm-Message-State: AOJu0Yx9uipy4ffWhVf/fB9zX8AO3dmSTDoVWNlf0qerVVGt2zMLMkUx 03w3/3C/pFjo7br5J8WiYof2NYyEXV5M/qEQUdwYX+BeQYE+NSld3bGM9OR65C9DKj6SA68klwY 8aAwX7mVUSJt6rNfVAA== X-Received: from edbdd11.prod.google.com ([2002:a05:6402:312b:b0:64b:3f32:3786]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a17:907:6e94:b0:b87:b87:cdb7 with SMTP id a640c23a62f3a-b8796bb8150mr1416704566b.64.1768995117333; Wed, 21 Jan 2026 03:31:57 -0800 (PST) Date: Wed, 21 Jan 2026 11:31:19 +0000 In-Reply-To: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=12444; i=aliceryhl@google.com; h=from:subject:message-id; bh=vC3+05kWBzpETYWstiU3G/6khCsB+5+BSNGaApB13E4=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpcLkeY/nC9ZSN2Gx/9Wqo+rhNRfyujfDk6sqU1 4zTWUsEvkmJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXC5HgAKCRAEWL7uWMY5 RrsSD/98ZQrAREUlwb6bADb0Qq7wWc1HRyEh6cfIy4SBiTTBOfiZV3NQSB7U8E1uHK8IxD3V63O bVFxHBWm9vqHOwgR4J6bOX8xnZtPhnKryncQ9T/+4TkCLEcdbVa1SXONUcGdYBF8teWiTAmhv14 wRhQpd6T4MVzn849dcKRPKXPvZ1iT+SVCcQG7mGQgyHEG8ODy1wytogfXFimu3WOWBlrFKHzSKB GdAJ8aGPS0dJ9Bjx+6XvVyVfExP/OdUN75iAtMdlTZM18FSVK1mmCrbKi7D4uF2vVoJBwHyKvOO J6hLhrFvOGpXkcwVj3Ad44vJ1ELJuqoIhIbnzvet2qoWpR0Roo4BJKvx0zFJxgVJh9LkYxZBnwz jnwXMgQRdcdRc8FPFgnvcHETtBzZE/gjL9r01QQ8u6CWDczHCQhBqYZD+72Qt+pHLjAZcvoAu5R z4jGqin3HBpNOBxLuqCwSVyAwhh7lF47aThBi/Z6/hlAy2XbE2NBKQBUo264QPtTJVp/gAp/OiN 5djuBmd+A88sbu3vvTtkEquYh1jxWXsuckpMzU0LqrcigA7gfaIQUZOBG+bBFTr3vVgk+UefYNG lrBREXzHW22ixWIUI0/0Mc3mQbHDgCLHTT09atS0hYaoK1Kqylz//zGRw8/UNdR1ovZSSYfiRFU onO6tV5El5h4Zcg== X-Mailer: b4 0.14.2 Message-ID: <20260121-gpuvm-rust-v3-3-dd95c04aec35@google.com> Subject: [PATCH v3 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 , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@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 GpuVmBoResident 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 GpuVm, so it's not really a GpuVm 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.) Signed-off-by: Alice Ryhl Reviewed-by: Daniel Almeida --- rust/kernel/drm/gpuvm/mod.rs | 32 +++++- rust/kernel/drm/gpuvm/vm_bo.rs | 219 +++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 248 insertions(+), 3 deletions(-) diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs index 81b5e767885d8258c44086444b153c91961ffabc..cb576a7ffa07bc108704e008b7f= 87de52a837930 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 "core" is only accessible using a s= pecial unique handle. The @@ -68,8 +75,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, @@ -166,6 +173,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) { @@ -191,6 +208,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_lock(&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. @@ -200,6 +223,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 0000000000000000000000000000000000000000..310fa569b5bd43f0f872ff859b3= 624377b93d651 --- /dev/null +++ b/rust/kernel/drm/gpuvm/vm_bo.rs @@ -0,0 +1,219 @@ +// 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. +#[repr(C)] +#[pin_data] +pub struct GpuVmBo { + #[pin] + inner: Opaque, + #[pin] + data: T::VmBoData, +} + +impl GpuVmBo { + 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 + } + }; + + 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 to match function pointer type in C struct. + unsafe extern "C" fn vm_bo_alloc() -> *mut bindings::drm_gpuvm_bo { + KBox::::new_uninit(GFP_KERNEL | __GFP_ZERO) + .map(KBox::into_raw) + .unwrap_or(ptr::null_mut()) + .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) { + // 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`]. + #[inline] + pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm_bo) -> &'a Se= lf { + // 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) -> GpuVmBoResident { + 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()) }; + + // If the vm_bo does not already exist, ensure that it's in the ex= tobj list. + if ptr::eq(ptr, me.as_raw()) && me.gpuvm().is_extobj(me.obj()) { + let resv_lock =3D me.gpuvm().raw_resv_lock(); + // 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) }; + } + + // INVARIANTS: Valid `drm_gpuvm_bo` in the GEM list. + // SAFETY: `drm_gpuvm_bo_obtain_prealloc` always returns a non-nul= l ptr + GpuVmBoResident(unsafe { NonNull::new_unchecked(ptr.cast()) }) + } +} + +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()) }; + } +} + +/// A [`GpuVmBo`] object in the GEM list. +/// +/// # Invariants +/// +/// Points at a `drm_gpuvm_bo` that contains a valid `T::VmBoData` and is = present in the gem list. +pub struct GpuVmBoResident(NonNull>); + +impl GpuVmBoResident { + /// Returns a raw pointer to underlying C value. + #[inline] + pub 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() } + } +} + +impl Deref for GpuVmBoResident { + 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 GpuVmBoResident { + #[inline] + fn drop(&mut self) { + // SAFETY: It's safe to perform a deferred put in any context. + unsafe { bindings::drm_gpuvm_bo_put_deferred(self.as_raw()) }; + } +} --=20 2.52.0.457.g6b5491de43-goog From nobody Sat Feb 7 21:08:18 2026 Received: from mail-wr1-f74.google.com (mail-wr1-f74.google.com [209.85.221.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 13D7B3BC4CA for ; Wed, 21 Jan 2026 11:32:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768995124; cv=none; b=WY6SKFP4T6Kfdmd0je/n+lKsBUO3MY/OrUUmv8XTnm9rSqql53VZi2wy1sq/IF3QSd8VtRA433nhCCNhwTobDEnzFs0Sv9O2tcOFWCjYVljSwZgaL+K4ex0SAOQbe6ZdMsxjJZtskqzKWY6a+a6cc+vxaxl+IlaVCtZQqcECnC0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768995124; c=relaxed/simple; bh=eaJbZT8B2Xja2kdiGi1Ws869kj8AJxUSIKK3G5I4Mvs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=UwP3sxirKzeeFRLbYJe3jVliMNDoc5EqOmYa9iih1RKeJqeBRO8Muc6OefXD+44gDROg2IZpGMdfWj8tAVXmGVkgd51pKPvzpmADT5s+ygHutflxf+PnKmzwVZjK8PGPv/gLPCmZ4DU4jjCpcLA/52hbFycaOZoQDJ0RfZSTBv8= 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=P57C4fTe; arc=none smtp.client-ip=209.85.221.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="P57C4fTe" Received: by mail-wr1-f74.google.com with SMTP id ffacd0b85a97d-432db1a9589so4243118f8f.0 for ; Wed, 21 Jan 2026 03:32:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1768995120; x=1769599920; 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=LdNXhntJkj2eNG88xbizt69UmEV/pDIxWww84BjGwBY=; b=P57C4fTeTd99SG84pHdsOuV9WvjKcmRPFL4OZVwOS0jq9VMEN9kvlDLpC38QkFdbTd Z+ljPPPHz8ISoEYd2ydAbUeBblW3zUVclzYNuFYXCAjYtCd3gwtnTS4ojZ+6xbCPEsXX ildL3S4s+2vFn0/WWiPOluPRPz09/yjh5jvy9RWYXH9vAsHrZ1jqB4CnAkCktPGC+rpb oXGp8GgmqG8VSuEq8K4B5wEbW7+IvTTCZsBsKD6lkgXknTiEXMf4oys0EVnluxa8tX6B SIxO1EuFcUO6uvv5pjr3uHX6U2giQaNHuSsWyxeFrRitNhwr2wqO/Q6Sskaph5J1h3/b GRHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768995120; x=1769599920; 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=LdNXhntJkj2eNG88xbizt69UmEV/pDIxWww84BjGwBY=; b=Vs1X53KRPgXEH+Kqq97hOxegfS8LBu9PvJsEbqru3bYuHl8xlMjNu1QpR4JlljWTpU mnKq8a04xVgXQV+iNJSO7HZz4CDbSNNM67rXjYwZ5DcB57FqgJHKcqBgqvmg1TGBJwQ1 YyffTNsCNF2h8xC0IAtW931HbIskk+7LPa7VZ19G7v+H5BTpuQl1X5c7V6Ge3OZCbSxf DCmK3HxNF06QZ5jdg55VEh0CjAg4PPzMxZUXcHKDGEHaLWLv0xx6Nar/sbgAVCzOQ3pL AtDTO/uK4uR2FPMC8tkyRhnEx7350OhTbSuKYrL9DWR4wWQBwLexWELY5H8ySJkCK09Q CRuA== X-Forwarded-Encrypted: i=1; AJvYcCX9mlqWHF0ekMLCAwOnSJCXcVbiQBKsh4GRuE9MfxVQXYzSR/vS51p1ylTyzkv457lijb77XTgzANRS1WE=@vger.kernel.org X-Gm-Message-State: AOJu0Yx1OwrU2w3dMQIWyww78NKsvvQEndAMCj0ATg3LXAzrjBXHKJx0 VTEJRWWzoGUiMVE3HhpUWl/23m/a3EBh0TNu+cU9vKquAQzpH8zJ/vQ92/XPbAQrGUmhDWWylEH zu0rVdO4+EtzRpTjKgg== X-Received: from wrbex1.prod.google.com ([2002:a05:6000:2481:b0:432:88c5:9eae]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a5d:4a92:0:b0:435:94dc:8c78 with SMTP id ffacd0b85a97d-43594dc8d0emr6022606f8f.40.1768995119989; Wed, 21 Jan 2026 03:31:59 -0800 (PST) Date: Wed, 21 Jan 2026 11:31:20 +0000 In-Reply-To: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6712; i=aliceryhl@google.com; h=from:subject:message-id; bh=eaJbZT8B2Xja2kdiGi1Ws869kj8AJxUSIKK3G5I4Mvs=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpcLke0bv1AU0YLFOT8myV74XrcFtjRhhP+57oB ChfIUqWAWqJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXC5HgAKCRAEWL7uWMY5 RhWED/wJPKLEvMvMMtru2PUKQVwm4UuBejGc+CN4McSACM9zZJwCKrd/n9DghJpF6mqL5LOw5/B wqCtvPBTrKSrFM+FeZcXYTHxP7EGoIP10vS2n16clXWUf6d4SDVDcH9Jn3vnIg0dTpDXOqh1H6B uKTJVQ8bFavjXWZCY3MfbCUc/+O5L4GH5FbURaSS50AxMjNdYmMv5X0nfo3uestPY5s+i7m+69/ BAiINrxfezJkRNptUzFarzZS1gWhq619MBF3lfTvVhYcXuHwvuGXJPFXVY3EvEv+URxQvSghqtE V3gAZEN8mExr12z3mG+WbVQOinRQfpRfiQDFb4u3bv9RdDvjz0mmmYlTAM3MF3ZCEfqF0QR0rnL 4zXnejcVgHSe1f/GqJQrGAtMzleRokvwkD/dhOLE2qxE8easwlCYdbm6BDIAwU9XgYZBmPvh9po Bqs6LFioU1HVyvbDx+aDFo49y0mEZ58GZ4RRueCCul3tA/CIyRghS3m/85tEmNQZv1Fv2DTRrIf IDm7LGruBN/rOw3uZ2UcTTFAPY6GznEzKKlSmUNiUWihc9a8ZMMyf4XEKqM27Gsn4sedzvoaMxQ /zb8EFdpmFeqZaNAeaIm1OnP/NOFCJJSKMzqvAVCZL5bc8jOMsPDc+8ZKh3NZIEm7XsEnFRK/ll /vv3cmCcK72U+jA== X-Mailer: b4 0.14.2 Message-ID: <20260121-gpuvm-rust-v3-4-dd95c04aec35@google.com> Subject: [PATCH v3 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 , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@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 Signed-off-by: Alice Ryhl Reviewed-by: Daniel Almeida --- rust/kernel/drm/gpuvm/mod.rs | 17 ++++- rust/kernel/drm/gpuvm/va.rs | 149 +++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 164 insertions(+), 2 deletions(-) diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs index cb576a7ffa07bc108704e008b7f87de52a837930..2179ddd717d8728bbe231bd94ea= 7b5d1e2652543 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 @@ -224,6 +234,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 0000000000000000000000000000000000000000..c96796a6b2c8c7c4b5475324562= 968ca0f07fd09 --- /dev/null +++ b/rust/kernel/drm/gpuvm/va.rs @@ -0,0 +1,149 @@ +// 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. +#[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`]. + #[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. + 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 + } +} --=20 2.52.0.457.g6b5491de43-goog From nobody Sat Feb 7 21:08:18 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 645F33EFD3B for ; Wed, 21 Jan 2026 11:32:04 +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=1768995126; cv=none; b=LuHNToBRHv0+m6UJ8NSoSbc4C0nm0rG3LNem4THEhJ2Hd92WBu8wI+69zu0HomVozyVHGhCF5Bn/JDI8/4Xcs+sPSRvA1nQDCIcwZ3lTs+b8+f0nf2uao6qUdl0LrzDaOAyE6rh3L6zj/njRikS/Yamg5Ua81EIE5cGejt3W/gU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768995126; c=relaxed/simple; bh=WHjn7ojmZ651+H3xURlrDU/WE1FVfLNqkTEkLpEkBeQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=TQYU74DOIAseCXEV5CjZXO7KlFjI69rmPu3rVQRY31T15wCwh1rikAo6NPwwa2qxZZcDbJT8OMUTrRdhWQPAQMyLiVHgaAYQ8mK2N5y1XWzcoKXSHwNX7+xOVeDE/34FK/X92buc8ywwEHeU0HmdTekHrPrVqK20C4bOFhBjSD0= 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=CvaBd6WN; 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="CvaBd6WN" Received: by mail-wr1-f73.google.com with SMTP id ffacd0b85a97d-4325cc15176so4451481f8f.1 for ; Wed, 21 Jan 2026 03:32:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1768995123; x=1769599923; 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=63bt8+7K/MSaVF9qHfILB+WRhvet6RgRj7D7Rrko1lw=; b=CvaBd6WNPrJu+AT4qo8D15nA13VoMZZebRkDR6KmIQrN56dYNHu7bcfLx3H4uEHnt1 Cg7LpR/Xc4aZdxFd9s/2c19GsWbG+/Xln/itvrkWd3fPqyvai3shvhdfw1PQ2FaEwvVj nCjJyX9+1zm7akpCSfXPmGakLTWH543mj1Ve1jbUy6e5RWUW9QWusuxLzg7dDTpRoYkJ 6vYOUCooh2H2sTxPzTwT0c0JDv/8YF0vw2OD3hz1nXdsIBtYHX1QERcOpeW9VvBTRSqQ 2i9h6GhTHHUZE8ZHZEmWcxsWPxBA18ERDcBoLFBUyHnR/kg/L++zgjl+s/i4xGlWjOh7 7cHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768995123; x=1769599923; 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=63bt8+7K/MSaVF9qHfILB+WRhvet6RgRj7D7Rrko1lw=; b=C1lcdx7OQHsws9SOvOgWJR/eWKCSt+lp4DeLgZ0K4FOA8FiGcnoQoWYvZgQ0mvHMve A6FI4Fy6eAfvtpiSxLVDHXXzduCGFKgBVZF0jR3dWIcoLgIwtEU+xtv7w39Nj/O3xjzC IM63BgVNBScqtf+/pvCGlKl8L2PdzFS0WQMSDlhgiMDY0t1whgTvL9UaKNdp7xmVCJ08 YVsIdn4boFkwGl2ugkPl6RLPXKBDTmYM2XoRkLtoSyznQl4RvaM/8pc0j6qfvNZvhnds EIDrVRyosPiCUdzF1Rki1EROpcMQepP7NK9pi/1vW+wTDsXkuEr837FgHHi5sevwyFXN U1ug== X-Forwarded-Encrypted: i=1; AJvYcCX4ocIuBS2rulP2TgAFxyqXJ9BUUZdnqf/NKrSbcUgpdPboRk4bjLIeNqQT1SIaTFpi6oUBEJEBNLH4eQg=@vger.kernel.org X-Gm-Message-State: AOJu0YzkoWVlvCfhq3tMDUKODuLYdGUZuvSofs1JsAZELu6SkV/W9+1L uG3RSN8rbTawbmQCKJCAt1WLfqq1OLk6pjrzNNBgf9OgFGTJcXWRt1AJ7mrCX4Q/q8ynyU6KMeV 1X5CKZWtrfq5VSXX5lA== X-Received: from wrbgx18.prod.google.com ([2002:a05:6000:4712:b0:435:9759:ea24]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6000:2007:b0:430:fd84:3175 with SMTP id ffacd0b85a97d-4358ff813bemr8336856f8f.38.1768995122676; Wed, 21 Jan 2026 03:32:02 -0800 (PST) Date: Wed, 21 Jan 2026 11:31:21 +0000 In-Reply-To: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=13809; i=aliceryhl@google.com; h=from:subject:message-id; bh=WHjn7ojmZ651+H3xURlrDU/WE1FVfLNqkTEkLpEkBeQ=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpcLkeVPvVaozDdcE0o+0u8/qNsxrLd7OYtF7qS NnBfoyHXROJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXC5HgAKCRAEWL7uWMY5 RvzoD/9QH4Ch24HbF2aIYYteAsT+nLP8QizW1oxiu5oUznN8+jmcnmFQFNC4N2G1DCKVV4vRzfb +EXugWukA9mMk5q2RqnvL7mD57OXvFwmLAj91xPFAoAlC+hhgFO4b8QYnMgdw5++D0jAwO4rpWI x7jXc3nyEFVOdv0sH1YN9/vczUTyVDfk3sHbmvC1A76+hmHRvaPVI/FRIA5ZJT2l8l5MJkiQQAC 4dFzAoxuB42/LuBER0gTTckvyudbxVyO4JxFN6QeiEODEcNgweJmRzJWqXq/kZzKzhXFrquqt+c bRaeYN/fb65PayGtHSQr/naJcHBpZbU4+bbihp3zhUeZh9qxMxiRfbmfiu9hqgodruzzEWiQjNv LVnHHVanb0dhWcnFZn9h0xH2STUJv9/q/epG2ImBrit3PT1zIgvOyKM+iPBWi7M+CrF1k/sZlYG AF0sykLk9Qb4TrpVMRk8jIcov+2T/ih/99v0laTmue5RsZ1AeTJ3MdJMWLQ5Lr0Nr5jIE3ZrjxP 0GeQCf2IYOiEYuQID0Wx7LDOmd5uD1V2+AEOF3EZ2wwSTBNqylM5TCCC0ReOD103Ruft1X1MfPK qmpBf/0WXjDAkMMQQPrL1ocLJgghmSac95s5sYguw01Wt+PPWi2ZgHbaVZ3q71lPYzweftaAtiB jsEcsol+DYS3PbQ== X-Mailer: b4 0.14.2 Message-ID: <20260121-gpuvm-rust-v3-5-dd95c04aec35@google.com> Subject: [PATCH v3 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 , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@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 Signed-off-by: Alice Ryhl Reviewed-by: Daniel Almeida --- rust/kernel/drm/gpuvm/mod.rs | 30 ++++- rust/kernel/drm/gpuvm/sm_ops.rs | 264 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/drm/gpuvm/va.rs | 1 - rust/kernel/drm/gpuvm/vm_bo.rs | 8 ++ 4 files changed, 298 insertions(+), 5 deletions(-) diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs index 2179ddd717d8728bbe231bd94ea7b5d1e2652543..165a25666ccc3d62e59b73483d4= eedff044423e9 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 "core" is only accessible using a s= pecial unique handle. The @@ -89,8 +94,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 @@ -239,6 +244,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 0000000000000000000000000000000000000000..3c29d10d63f0b0a1976c714a86d= 486948ba81a15 --- /dev/null +++ b/rust/kernel/drm/gpuvm/sm_ops.rs @@ -0,0 +1,264 @@ +// 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 GpuVmCore, + 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, + _invariant: PhantomData<*mut &'op mut T>, +} + +impl<'op, T: DriverGpuVm> OpUnmap<'op, T> { + /// Indicates whether this `drm_gpuva` is physically contiguous with t= he + /// 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, + _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 GpuVmCore { + /// 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 c96796a6b2c8c7c4b5475324562968ca0f07fd09..a31122ff22282186a1d76d4bb08= 5714f6465722b 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 310fa569b5bd43f0f872ff859b3624377b93d651..f600dfb15379233111b5893f6aa= 85a12e6c9e131 100644 --- a/rust/kernel/drm/gpuvm/vm_bo.rs +++ b/rust/kernel/drm/gpuvm/vm_bo.rs @@ -101,6 +101,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.52.0.457.g6b5491de43-goog From nobody Sat Feb 7 21:08:18 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 2671640B6ED for ; Wed, 21 Jan 2026 11:32:06 +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=1768995129; cv=none; b=lEStL7IAO1fdBoMy1Xzv2jhQCMwaNcGGTlo7J/rXkcDk2uQtX8vsIHm/S0f0TPXPQGD+oV1Mh9eZub+SA/HPqQtsL+CMU8pBm3oQPEqQWc56x/qauM5PQtonGuFDPz1L90v51n9VacsjU5y5m7BegPD/MF+oKuy0VOB9FidP3B0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768995129; c=relaxed/simple; bh=puxOKvWZSmMilkgdnuRja+EvIpEfKakk94O5pIyZrME=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Gsx7wkFJrI1cH9xLALiT74cCuFAI48usHtgdWiWZdRlEo0jnKWynYbaBPTXRCU1D5aouWkRW19EHZW3x0X9MLqmWExzN1fRVqig8jyAmLJdDE6VZ1JQFIOYwa+aJO8R8+KZ258IgjZNc6KwtgsZGAdZV6nGIu3Gr0rC828zcJSk= 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=EtyxTym5; 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="EtyxTym5" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-4803e8b6007so11583045e9.0 for ; Wed, 21 Jan 2026 03:32:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1768995125; x=1769599925; 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=ADHnj9pTGTbd43O9T51NFX85JNRLR0rvNZITUD0GxdM=; b=EtyxTym5EhqKEAFYdNO+AqgmSuxzUVbY7O6IeacedicvavqwLIWz/ZcZnOKhJ5gzee 8kolfOLJFjKACLaFSjnP2oW/7OklWPgsTTnTPRgkYtapC8IrsJsr9g9ET9C1yDJppwsy 4P19g/TlhvsJnRpt3YOGWflEcMjBwt3JP5I33GimL790Ioc0YrVKm6tVOQdt7+gjGxpm CWjw/wTD+o4by67RuV38c71scaELQxL6+Nx2yzOW7CtpmtDDFN7NAnnDTouSVzHw57r6 lELoRTdG4vy1GUi/Wc0R7NQVr2AIlTc8xzbs9wjTEmXia/cPgUdT7NF08wbukuJRyvFb 6VFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768995125; x=1769599925; 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=ADHnj9pTGTbd43O9T51NFX85JNRLR0rvNZITUD0GxdM=; b=v9OLfOsAJJXC2+MzQbwZJxXWN2R8AmwlVYncarqfYpo4zz7xAqTZIYjXQfrUr57iTW 3p7K8SIXpDcLr6qsd6G4ZdWV9d4IgHpUHVufXG6aIsOgPQ1vb0jJ39ZDyvytBOSm6/k2 E4ZkwYBtqYHfToE20dJjt4rygP/jfyDpg1ri1qFzkBGUYjSqWJ8Ox4P2CO7sDM5tz+3f 0kg3gug6c69Rr4uhHZBk13Km2v5cpcaj50NnzRAnD+kvDe98WhX7UmfJ8KiTmI0Dxldz +2pj8NnCfpxCnqhRNHAdM1d0pqcfYrYNmV7weQJ9qd9DWw3MKHgbL74KAGuG7Dn5aQt2 mNPA== X-Forwarded-Encrypted: i=1; AJvYcCVR49RNUgGytKs/nC0Twykofsg1H8BoV9lPuyN0vJXT32gCeOGC9G+3vnS/q50jvgCfUT0Dd6qGy2v4pjI=@vger.kernel.org X-Gm-Message-State: AOJu0Yy675ibx++z9aTWC5eewCEt1eRBYA1rRSy56xfLl5x69Ty9CzsX 6k3pdkYOQnquqD7Myr7yWrqM0TnC6SpMBKeE0DRq9pJ89HL0tGWPyokqKnPEvzuMb/G0gsnlWXm jqz54yuoY7aUJL5+hOQ== X-Received: from wmbhb6.prod.google.com ([2002:a05:600c:8686:b0:475:dadb:c8f2]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:1d14:b0:477:73cc:82c2 with SMTP id 5b1f17b1804b1-4803e7a4051mr69580645e9.9.1768995125342; Wed, 21 Jan 2026 03:32:05 -0800 (PST) Date: Wed, 21 Jan 2026 11:31:22 +0000 In-Reply-To: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260121-gpuvm-rust-v3-0-dd95c04aec35@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=9517; i=aliceryhl@google.com; h=from:subject:message-id; bh=puxOKvWZSmMilkgdnuRja+EvIpEfKakk94O5pIyZrME=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpcLkezgTn6a3N4qhkQEjM7d2JXqI0C+4VoWJDj kUFK5DuqY6JAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXC5HgAKCRAEWL7uWMY5 RlLXD/9c5Be8fBOxYmcDNrxEJd5yQFMEUqjllsfSNRz1dvTepH1SiaSzi7glFTAdjp5An2S5to8 WhLUFtBdx0mZ/+NCKYDrUemcCJAQlKU/PQqnxyxUt6enja6LaLEJFM4iQ48jnmy6g4DrWyYjT8W WggqmFpNwL6y9QUQd8oRaTWTbUJ1m8Ij1yZxX945p87X1qTDvHOKy93sX5F2hVwtbbEpvUhkI6y ZOVUfZd1siQc2Qmlp26VbdZiGOtEqHWIzCl7zY/Fm/7mhMbh1WfQSfOO7LirdOQy9RbKwJpCvWB YdZLjD2FlvFbt3niguznRhBm3X+/xNIBki3nyOumowMvKCv3M9w6zYPjzphkbOKFQOzPDLN9kUT xXo+6eyPa7IWdPSyFCyBenLiPaHTaBBXaZc1K/NLOTn4xvQBMEibtH832F05Oq8WsH7GuMF/lCt Q9fgNg4bEtONTzB8XZncoG1Es2DolQQgoDsc8TlwMHuXs3x6cKwITDBYrSk++P9N5dCCbcRYZhZ /9kYoeiK2TNx24QP4O98/OrqEK6dcqrEjajqu5UAmGoTuXeSKc1JbfDXCr42ttyGhpa+ORAj+sa w0wQQfG2ZrvyVhWXSUD7l6PwFreHEK1it3mwvOqYvgvgek6no/VjxXnw0LJVOunmhCdBj+OBKT9 6dJQszz7C6VtFrA== X-Mailer: b4 0.14.2 Message-ID: <20260121-gpuvm-rust-v3-6-dd95c04aec35@google.com> Subject: [PATCH v3 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 , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@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 Signed-off-by: Alice Ryhl Reviewed-by: Daniel Almeida --- rust/kernel/drm/gpuvm/mod.rs | 9 ++- rust/kernel/drm/gpuvm/sm_ops.rs | 154 ++++++++++++++++++++++++++++++++++++= ++-- 2 files changed, 157 insertions(+), 6 deletions(-) diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs index 165a25666ccc3d62e59b73483d4eedff044423e9..557c0d629eec912a97fc4ef1849= 5d5bf0807db0a 100644 --- a/rust/kernel/drm/gpuvm/mod.rs +++ b/rust/kernel/drm/gpuvm/mod.rs @@ -93,7 +93,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), } @@ -248,6 +248,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 3c29d10d63f0b0a1976c714a86d486948ba81a15..5f3c5d3918147a6962e5658443c= 343835baa10b8 100644 --- a/rust/kernel/drm/gpuvm/sm_ops.rs +++ b/rust/kernel/drm/gpuvm/sm_ops.rs @@ -8,6 +8,100 @@ 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: GpuVmBoResident, +} + +/// 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 offset: u64, + /// The GEM object to map. + pub vm_bo: GpuVmBoResident, + /// 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.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, + _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, @@ -205,6 +299,30 @@ pub struct OpRemapped<'op, T> { } =20 impl GpuVmCore { + /// 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 @@ -218,19 +336,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. @@ -244,12 +388,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.52.0.457.g6b5491de43-goog