From nobody Sat Feb 7 15:48:22 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 6AD6B322B8F for ; Fri, 30 Jan 2026 14:24:25 +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=1769783067; cv=none; b=ejZ/Jfv9iu0UipjFnN1j/9Uuz3PdqqIc1xAttbkuJi1rPfOkBM6ErwPNBAjscALyiF2jVsIUjHl3QRLp9AhIytLGsQBL+4WogU8F9BXFHcGgUb2MwY/4UWlhUMRbnhqpjAC1WoECTtGTQhsWefYmebPVQRYHjUvir325T9K5bc8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769783067; c=relaxed/simple; bh=p6q7ABPVf1V7n4SSGe1o9ggHL5+0T5eSDdsnUVTtoVQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=UzOF5IwbTMKCSJd8dhwto9lqMwh82tTDEHkp3FRASWRB4J7TfDvMxkBzKDAcQhDUA3eKbpqkKocgN9gBk1M6pE9lqc7Nydc2rQMTleqq7Yp/wdEqL2jIUrbMmjRv+me9JcQf6IDPvrTN8wXBn746NJHXDPSrWyiX/7+ed+eyC3s= 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=P5nFjEdu; 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="P5nFjEdu" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-47d4029340aso38762795e9.3 for ; Fri, 30 Jan 2026 06:24:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769783064; x=1770387864; 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=ppUfKt0GlMufYciF/ftyvL04mIsDf9JeAbFlI9mEvvA=; b=P5nFjEduJDu2rMcn72SQiaPS124x7xWFnAt4EIqf87z9mQI491fClMqG5CvTPe+xN/ wjVwmE1O1NmVBSRv4RCcEQj4/fJa3wLtaYpnN+uvDY8SDaRp3RqR5WlHNb9kqJtuz8rS 0NxSazfvGFuPBNVIufXrLW8l/LhH/Mf/9HhMGoS4OvOrObtukSVDw1hi+Fmg7AfWsqne yARZxp2VDPmKVKXLjUp7HqzRaKjY7m8hzyyePRS6qTTHT8VUAiPi1N8gbkgLB7ipx1DG BaiAJNYxlx6jGDbCu5C9p5rL/9lEbKQ8ZK5NrR5mMwddQEXGMYRp1g0WRYy/xtaYDTE+ eHmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769783064; x=1770387864; 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=ppUfKt0GlMufYciF/ftyvL04mIsDf9JeAbFlI9mEvvA=; b=mZCqlwWeNPd7sjeq5j1QR+K9WhxYB8jZGcEba/t2MMQyP/tp1SyU6v4uxRFWEKwrMu oJVrOYptqH8vS+kOpb0RhX4DLNJcfi2WtmEPOmED2eQSftRTrb1ipTT5SjCmpuZhmOZj GMlfw6axUH23NHsEH5gjLgsAbJpJPDCPaIjxDJwQR+bm/7TGnqHsCvonM5qBwWCMvZeq d+SjSzhlP7gK64lhFtvciYTTzO+1R4VQ6i8wBu/trNy4Zt7Kxt3NnN0p6Lhe3fXWtRHJ nKLjQQzYLwVB4NPjzKj4l6SzApkPFopD4EGJqfRrikA0UMpO83Zc1lx1ic5Kr+qG0kBd Thhw== X-Forwarded-Encrypted: i=1; AJvYcCVWi9UAYDQ+/tiSfCtr1muJy1VPJv9AzOVFSddif5bQnOMmsPalSMyAWgdCaJqE6/LlfTumWe8ZL9hfT5U=@vger.kernel.org X-Gm-Message-State: AOJu0Yxh12BYFGb2X/2njpPWYntz3pJQDUIFp1nXUeda02e9FbGdbXZH fSF4iB/zFf7+NAOTgwHKaE4kiZIboON/1qDyYVp6AHXOwjW7YA6Q4M20F9u7+pvxborzbBrz+CR qgX23D2DJ5SUIS/PGQw== X-Received: from wmbjp21.prod.google.com ([2002:a05:600c:5595:b0:47e:da9a:d7da]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:34ca:b0:480:3b4e:41ba with SMTP id 5b1f17b1804b1-482db48d595mr34991155e9.18.1769783063901; Fri, 30 Jan 2026 06:24:23 -0800 (PST) Date: Fri, 30 Jan 2026 14:24:10 +0000 In-Reply-To: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=11998; i=aliceryhl@google.com; h=from:subject:message-id; bh=sB6yEvHJS8rqhFVL17ijf9/pm4c8+sT/fWcd41WFMgU=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpfL8S3JTmpmG1lqNYg6FwVI/4Ks5CfnsjvVg/f mL2AQzUofGJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXy/EgAKCRAEWL7uWMY5 RkPUEACYJ5n97LQYXabI1mnBL41ofB7vcoESU2dNaWIx722lo42b7p2abJ66Am0wxuyCdU8ewE2 voPmWHhVwwI3ciaRKdRDMenBtKewU6CfQnSWRCia9vNTe3KEgBbqzRd0FJyRKcnmWTKPH/QZafe PD/nnqOb8/2ROjy47lbPgBj3E6Llq6Oo+WgDFLVYZyua0eX9/A0bVVvlCSgQ99z5XHmaNJ2WObq l7/Tk1NoNbNLVBvDwEhg9s65VGx4l764c8htUUAy4PGrAi0Dir0pInFhlIRJzZ762zSG/Rx4BMI S6Im+dLI0kP/sjO1VawaN0/Umpf9WaONMlDgrobrV6NuslYhpntWdY7CdpnP5xNq4JS6BkYvOqY zL4tjFai7brd28vJWQuQ9pM+6WLAHU27SksbXirO+GshZMzh5oq0PwZ8clY0BqQhVaT8jHOV4MJ 3DM7S6zsGDaq6A5hI9VMOAAqFBsUkI+cPAVogCSUGyetqVZOU8vU0XAZ4xqJThTA37t9uoAO1vW kBXyIQObIZYSMxo+aB0l/otaWF8NGFFhr18XrwLCEvGN+wxNyY+CHeN14ZqsDJhTTL52W4KLy2u T9sLcU+3+nfkWhP3OGHpruNj9MygYzUP/ZIHseddJLyz5Rx1uWl8qKpTE8zg8lQ19h2c25UodYV 6vV4dbkA1ieRWFQ== X-Mailer: b4 0.14.2 Message-ID: <20260130-gpuvm-rust-v4-1-8364d104ff40@google.com> Subject: [PATCH v4 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 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 | 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..dcb1fccc766115c6a0ca03bda57= 8e3f3e5791492 --- /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.53.0.rc1.225.gd81095ad13-goog From nobody Sat Feb 7 15:48:22 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 2F71230EF64 for ; Fri, 30 Jan 2026 14:24:27 +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=1769783069; cv=none; b=PHOBMz3ZrTkR8GxMlEFNGlosNFDZ6CKl+WdNbVKd7xSzOF4VJbWjMPGFdntDKHJLMrOqoh/GDNdxUP3ASsAWbcxaSLzD79NrkESlmHkzUaa+qs2TgIBjEbCuKJRn2WhJ1rKxdSlTIfsWUt6bLHeEKn8i9crc0wxX18FSxsTXxgE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769783069; c=relaxed/simple; bh=Pps4IBNQOEPIeVXcRuKPdqW1iuZzmjtBAm5yYAEp4ig=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=lnr05fpcGF/5b0ikv716T5yCtLhZeonCUjJNJWRYGH7FFbiBB1dwUvHizwV4Ivgf9JXSOdmnyEjaKeXwRnvGl/mMrXLtsYZX5Uzd0T8Vw9hev0ErEQxzITrebGt9omdT1IaLMfbVjG31S32Ynm6FE7Fai6eW8gSSDRpwr0o75nc= 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=b4lNEPux; 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="b4lNEPux" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-4801d21c280so17100145e9.1 for ; Fri, 30 Jan 2026 06:24:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769783066; x=1770387866; 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=Fh9S7yV54xV/K3laFzFwLiYnpQQhQ4Nm8f/AYMtVUuA=; b=b4lNEPuxse4hozUz0YfHaQC2vZqc/Yg1dCHV/RWRjeOBegiJ+N/r3Oyq6/uEGq6dXl W9XaTI2K2/yadX4nAlBeBs4N2WJOPES/1/XXQezSsANw5nxx4Ya/yeu28F5zqzrzMUro b0435oR/35M/aGDxNRR8A6F5PvTsGE9+5OwKL4hlwbkadb2Yej5teSFtdDW/NCwkIg21 VtrcCYWP5xeYPViWDrm8jUsU+fiGQ48U6PNS83ZspwU2h5siPZVe3cpvtxYe2wAkRuRW YMKXj3+AaYEoO4L4ofoNMOQm6vnharHQXr3nXF1XHtUKnYBlSC0kkhA1vbH0NDWsTz8n 4JIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769783066; x=1770387866; 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=Fh9S7yV54xV/K3laFzFwLiYnpQQhQ4Nm8f/AYMtVUuA=; b=sD2HUFmTQnYOIHGLi8mMdKTC79i4I98pDMiYxPMnIDYHx9v8//sq4hvWfwMHX2uxPy oOsWirB4NI7OR3yy+fycVQ16hwZR4QZ0Av/YQBxfil6cuG7iKijYEZNxTSVHtIqzwq92 klcqSe/5AMSvczka2QUnq6UmJW6+XISUqXEVBelPZCGo+MerezvcEaJZPatRzuyNJErQ fHRsPbcobSCXyG8eNqU1b5kBwJyiv/2QKPmU+cgCf8lgzkSErxRsVkBbEcn/KTSR/hSo TWu+CvjgSKRXMysQRVZ3v7nemPL7I1hTapaL23tfC/PKjjqFTP8hXzelkwcsW3Juzd6/ NQNA== X-Forwarded-Encrypted: i=1; AJvYcCVpdHTU3/e9qkQqGKnBAHwwqGQkdosyr5PY7p9JkQ64xEYFEXtFMrYtFjlnrmLgieXVyYghRdSm6+0e6ws=@vger.kernel.org X-Gm-Message-State: AOJu0YywEdU/Qi9kxP5ugctzX4iX+ozDhZ9vbgK7CXJWjMJoH5TFqIZZ XP+hIWys722/dmF7jJ1NUtzWhStdMgyVpEWQudzZxaS9jV6t1TV2hoZevWEkJpeqXtytTqiZZWw RJV7b+BhZIU3iDWGHnw== X-Received: from wmbhi15.prod.google.com ([2002:a05:600c:534f:b0:480:6b05:6b8e]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:8b2f:b0:47f:f952:d207 with SMTP id 5b1f17b1804b1-482db48c8a2mr36951215e9.19.1769783066519; Fri, 30 Jan 2026 06:24:26 -0800 (PST) Date: Fri, 30 Jan 2026 14:24:11 +0000 In-Reply-To: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=2790; i=aliceryhl@google.com; h=from:subject:message-id; bh=/ynqDc2nL4j3GmY/nB7jtyhKdp90gurfdQg25PJaHYI=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpfL8SCH4OjJAHyDSphTaLdJMNdcF+hUYl/QxDy Jl9WyDfD3CJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXy/EgAKCRAEWL7uWMY5 RpAAD/9aqNKc1ryV2T6NERDr2GzUvpb5oetrSoGiMEv2TW+otQ+VvrJMKBTBLkXOpz56mIZ3R8X aSvRLQlYOL4DxXUVf7DICLvVF4lzEWsfc4NGGE9dMJkY98AJ5z+Xr3mGFDnftzX8wU/w/UaDZjl lTvxvdIrtRoXX7/awedOx/WGXHqyn+x1jQMgflEwaFlexQBcp1lkTjzMp4e8x090xPeZ9YZk5XM kiahskQQlPWCdaiX9iqQ1E2GTPYBsZto2PXYrzjeYwTELo0ebcVUjI86asMqAGesaDy9HQj/+6n rnN3+3rEvNJTMm4fgxaPtFItLFgQrLnWtdNjLfGXf6ayH/ZMq1MZM1tsNov1XJ+xo8mRiHs1nSQ gk9jk1TebJegcNhvp6RnyudUCZpYFKEbREe6BaOtqGgV6hXBG7nPo9Nr9PlCjVO7kA51rsL9cPb ri/PptsrdKIIMNV9bRzNK5+EVBVoO85H3SghP5OcOOgTucFjREpXzrZAiYFS5bHe9bk+9ksZFWZ E+HVuLxRbwXdy5oCUdr/dlQYsLU9Ds101wWgIWG6vNYS6134JuqIPzVnafKUhs4l0Jq9hrT3Sch nriDL7JwNU04A964jcfOziktOxTw42Miiykz9s/4hCx/M3xpdyS4PfDqdzxXa51k1wY3yLpuKvk n0pxGwBXwACvH8A== X-Mailer: b4 0.14.2 Message-ID: <20260130-gpuvm-rust-v4-2-8364d104ff40@google.com> Subject: [PATCH v4 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 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 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 618becae72985b9dfdca8469ee48d4752fd0ca41..8d44728261ffa82fc36fa0c5df3= b11a5bfb4339b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7467,6 +7467,7 @@ L: rust-for-linux@vger.kernel.org S: Supported W: https://rust-for-linux.com T: git git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-co= re.git +F: rust/helpers/dma-resv.c F: rust/helpers/dma.c F: rust/helpers/scatterlist.c F: rust/kernel/dma.rs 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..71914d8241e297511fdf7770336= 756c3e953a4f4 --- /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 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.53.0.rc1.225.gd81095ad13-goog From nobody Sat Feb 7 15:48:22 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 68583329C67 for ; Fri, 30 Jan 2026 14:24:31 +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=1769783073; cv=none; b=MLcvXs01vHC8ODrK3gOKEX3jXI5JZ6tNZ1MZheS+5xIjxNkm7WQz8J+GUK4vNHU5EbJUAYVGTF6H8f/swRt805cZVLc+5UPhZ0Qv6hkfNf6l6MntfaCBpLHa3b7zOraeqszMRxhpIb2iZ/mepiwEtTv/52nabYSfSSCUunHM0c4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769783073; c=relaxed/simple; bh=jwVB73Goy1eFiXhQV9XQGXXaSu7p/Z7zsbGnU3h3g48=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=SWxKh0zgy/l79IUqH/mnnmU90+V/bbeXZWJGkMAdrHqqXPY/WdbvJw78HFtLQpXP/tmfZTiJBE1Uc9GHBxq5i2wpG+HJsrv0yt0W+f1v6POo54BvQQCnY2tlbckwIXyPKVdaPxyWQzHnThOq39pfSNQTV8JrmvIlpMbq3Uueagc= 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=qEt/EAUB; 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="qEt/EAUB" Received: by mail-wr1-f73.google.com with SMTP id ffacd0b85a97d-42fdbba545fso1716306f8f.0 for ; Fri, 30 Jan 2026 06:24:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769783070; x=1770387870; 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=yhJUSoGORcPmFs/1pDmni+0CTscySpgtXwx1t8RNgwY=; b=qEt/EAUBHKm8wc3cRx0JAIz7mO7b6y+vn1rCZekGGYk88bsXYiRxcjkKMgoBSrOyDO eH8AbRqD8ecureE7ji8RSbu34fpk/D5r1apW7UJRbw5IqhF63IUmuNMGhCFaTyFq2m6a 3+NrXMJBs5I94MOr/Eu//jHT3ZYdJRxP9f425XiTePfKAe/RksSeD22k+OMWREQQpD9E HGOmM2lPoMdDxU9I/dORodXjpUT1uOvhJFhLPEWriegGE/JvA5HxbYXQ6IeumBKQcHpe lwe+IqBJvF3+COlTJst5IyO2CQZ0sPJwpS0+gB7eko4uX34Uf9SUDaHonuUgiKJ8jsh5 /eug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769783070; x=1770387870; 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=yhJUSoGORcPmFs/1pDmni+0CTscySpgtXwx1t8RNgwY=; b=fpp2ORSMk7S5MssG5VWbTJUCat7QHvJmWkLGun0UNQj/+Q9sx1xWvNyQiU5c6LkO8q kMwJCmXEDi4WyfDzYBdPUFr4gtXHrm4paFx4BBn/rSRrvGOVt9XZMQDmvkebWciLaVuk S/GsmgQEUWmUgcdMK4qeXrRSwIOetMvNgJMv0R4EGXW5qBBZvXKtmUE64ZY1YeC7W3bH 6mF9sKPTaEn0FTWPTxhlEkTSEZMZW1WHU5JCDTtBEyod9131RnBaw8DFUtFGdX5gtqCa 4POEA6gNbhI5jt+7d1Vnao9QUyu1u1I2o53PzL3mZUsh0r7NGfMXySIA9Cn1EoQxnZ1l cRUw== X-Forwarded-Encrypted: i=1; AJvYcCWKhShNBZe8Mn0uSKsxaNQVciY9HAulIq8pHQTnQVBgWa+nffVU1LcsZ/ujvIIN//nH8e1V8KvH66u7quc=@vger.kernel.org X-Gm-Message-State: AOJu0Yx/Q3UrfLrqL4jQGRytapoFRPS7R/3gDl0+1vEhtoCBXSbrNXYy UgA8xc+qbMcYT17SwqKOCfBDy/6be83iEjcwgEnk3qiT1Ez+uz9DHSVTvEXj6wp06kR0ptaw6Xf VkIS0Em5yg5XHsAlFDw== X-Received: from wmcn27.prod.google.com ([2002:a05:600c:c0db:b0:47e:e414:b8fa]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:45cb:b0:475:ddad:c3a9 with SMTP id 5b1f17b1804b1-482db47de28mr43665845e9.13.1769783069709; Fri, 30 Jan 2026 06:24:29 -0800 (PST) Date: Fri, 30 Jan 2026 14:24:12 +0000 In-Reply-To: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=12569; i=aliceryhl@google.com; h=from:subject:message-id; bh=jwVB73Goy1eFiXhQV9XQGXXaSu7p/Z7zsbGnU3h3g48=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpfL8T2SZ+JHz8VbiIpmjFQwf4kvpvo8SUk/bbn 698aC/2w3iJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXy/EwAKCRAEWL7uWMY5 Rs/mD/wL/K/DMPXG147iK8vo68tuxIcmVPxg1tfp6/1KirgIFOLK9SSckmoEkvm27WDyGIGF2F8 92J3bfxN4kDa30TXw5r4nPq3iBf8S8BYSRup0CNPcCXfiGmM0fuTkHUdQ0Q2fknZ9j4PcgaD88v gbdB/lkecuHE0X//wKuYbngb8gequI8qbAkpQxKaAfb799/tkG5/Z8xmWwmwIc+JhB7KTveV/xm 9ExWFiaKrxs2GpXXEGLwXCu/rJHCuq7QfHATISX0XxDqWrUl7bnEyE5G6mIpLNSgK1okmQUaa6p 8+2kQ06n5c3fsn5oLsPv9QrSN1ri/8E69lxrpV3A9JAaj4KaCD9wwrCehSJrAi6CbzPse46Ps+O d1vMw45s6yxr+HC06iKP0G+qMBB8KdFOJktncT/oRddMbv/3usH8jueWtpaf+YGYDsjw0cFQsIX m23u0MCSKn/b99hYz5H+0qxvfl7QGoZbuwa8zian1CjL2SiwXXRftiBasbquQ0uL/oRmSdFwTZs M/c+h9Guf4LAtZvQe6mbgr23WIJjc4UqxrW23pMxWx1NLIy+pS29VdLS91KC3bPAbVb/7i+NXOK 1ToZvBSTPRvL3vbBFHq0w2iSFU2BBUbj0Sv0tcQ7vuybR/0hPq7cAjITeBI685ZTAsVcdqQRH+O He3dQZkNn3/ErNA== X-Mailer: b4 0.14.2 Message-ID: <20260130-gpuvm-rust-v4-3-8364d104ff40@google.com> Subject: [PATCH v4 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 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/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 dcb1fccc766115c6a0ca03bda578e3f3e5791492..8f2f1c135e9dd071fd4b4ad0762= a3e79dc922eea 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(&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..272e1a83c2d5f43c42dbdd9e09f= 51394a1e855b6 --- /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 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) -> GpuVmBoRegistered { + 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(); + // 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 + GpuVmBoRegistered(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 GpuVmBoRegistered(NonNull>); + +impl GpuVmBoRegistered { + /// 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 GpuVmBoRegistered { + 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 GpuVmBoRegistered { + #[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.53.0.rc1.225.gd81095ad13-goog From nobody Sat Feb 7 15:48:22 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 C273233066E for ; Fri, 30 Jan 2026 14:24:32 +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=1769783074; cv=none; b=JzH/jmYGBcKfgPKUKOruC1B/ArHLoIRsYvzm93sTWaSfT9BkFh9/Tw1F5DlUoo01gh2uTjqOvTIQCN+V+0sSc+Ezk4NlNey+i3U6qlN9xYXnFkSKeB30nb94Srw+Cp8b1aCMUriX5lNUKq/na2ejyg1j3Ae9M1KoIs9l/JxHfU8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769783074; c=relaxed/simple; bh=eLhNisHxVldbjSAORK0H/oAbA90dI0faTM2nrjJzZY8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=F3sVaF7+sop07pEjyE1kby5bi2PNeozHCdOG0mp8ZU/aZmLoaDNrEGGaKH9fFBL+n65ZG6f3iSgovQrOJ4oTRlQSiCzeDtI66i+FJA+SZjQaVoc9KU/j+CAYqWHfjDbMaPkx+sxNs40/GHgNu5I447vO8qJNfdJmkrhMBdmWXI0= 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=xUSPiMY0; 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="xUSPiMY0" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-4804157a3c9so27712245e9.1 for ; Fri, 30 Jan 2026 06:24:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769783071; x=1770387871; 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=3944QNBiimcmSBU0CWJuvRuEeF8KgtSwRq2Snhbdh2o=; b=xUSPiMY0UI4x/aJc0PVWPKz9YFYbGSgLX1cZLgXQZ1Nc4/eM/jwSEud5MdvABKJr60 K6kGwnZmDVwmDpJ4qqfu6WCPvijP/twkRV6Px/Uag5PfcsdDgBmZnGuBC7V26S2BH94I aTvXm9aa7XFv7KktYw+50h6Hvjoo3wb+cxi6GNoDJpnCjXpwptHPyt95C/CHTR2/dt37 MqN4Y9kMxxo0zCi5vnRaMAGWvTUDNHiL9LK3Nrnk7BOwqZsv0LE+lR3EMfsv01GzCsfl ErksrhKfN4H3PSNkcvkHelP5zSpd1Pf19XJ2L2T8tvVfYsY1SXxC7R61tRh9FAMcxqK9 ibMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769783071; x=1770387871; 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=3944QNBiimcmSBU0CWJuvRuEeF8KgtSwRq2Snhbdh2o=; b=uFC1Rz2AdHpdPh5JEM1X4aZe1axTz6+5s2Lf+8Lkg0NeXJUAXWbeI2MFgA+ChvJmI0 Y/lL8HGDb4LBuPsHq+XfddnkqHMRq7oKUlgmiG/xY57mXBJreqcWccF3ZQc6MSScQNMY mDniZ7aNFKmOKDj8H5DXQTH2ZtVuMlNMpyjQYcKxaBGu9l8fPbVQf1HZArkpuTSVjRj5 Y6hyXqTdMawriBzvkt2XnTC0Z3WF6f/yYaWEIGaG77BWPtFKpoTpPiFbQLROukq3Z39e e5tZ+nifMIccb3o1w3vbdfckLnjvJD48kECHF3bdThI8WqcoHeMkgHElR1bhs991GxYb ntgw== X-Forwarded-Encrypted: i=1; AJvYcCV87IVubwm+kqV3LYm+3FAOUdZZ47bR9tpPE6Q/kIXaUiwP7+rMI1o3eBEwS0ON9oVup/K8gpMG3E2Pk5E=@vger.kernel.org X-Gm-Message-State: AOJu0Yz5fS5kSV8iq68F0pXCB9Tbt/ynZcIsIxkSKbxbaH0lPWy1l/iC qgl0OgXhuIaIDEIBq5F0CFkJ1GxwFRHen+O/hLjqNpt+uSu2/BRewARyD20HmSQ48HIl+9fMuj+ 35K9g6CpzRO1lkxtr0A== X-Received: from wmbil22.prod.google.com ([2002:a05:600c:a596:b0:47d:50d8:6af4]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:34c7:b0:47e:e48b:506d with SMTP id 5b1f17b1804b1-482db47413emr41588745e9.16.1769783071255; Fri, 30 Jan 2026 06:24:31 -0800 (PST) Date: Fri, 30 Jan 2026 14:24:13 +0000 In-Reply-To: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6776; i=aliceryhl@google.com; h=from:subject:message-id; bh=eLhNisHxVldbjSAORK0H/oAbA90dI0faTM2nrjJzZY8=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpfL8Ttv+SXxrvnn5ZalEbpepz5kw5ggYMm3jh6 1tzLUbHgT+JAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXy/EwAKCRAEWL7uWMY5 RtE3EACjdIjwMBgmn4NIM88dT1GBf79ISVz8nY9qSmipE30ianz59B8nc2sAmNr9IvJtOoNON8F 6SJmvVzN5EiY/A2cdWyWtn3CIcCKJd8v7EwKyykGpAH7D9YywoxXJHSAIjGwAUBb7YLSaSpwrmp 4DtP+puAeAwfoCPpJQn5r/IVYJepTqd4lXVCBOg03pwVjqnP/kOF2dgVOQozH3OsWCNjASXnEal ni/apkFE+e0yvebe+Y81C3JCQgzHfX6jJqNACmr3SB61ITQYh1f+FJHUZSdF/Xn1c4RmuZ0Y6Px 09VAwkRcCQ67pHAqmY8wbedplUq0+60BfGrVaoM+4tfrQB7jZdpQ3B/Q5jI16lovNu8sisvsETb G2vB/EZhP2ptbmrDCRPioU3HHfWFJXvfCqsRQWHsDu8v59/02ULcQni8ZuSV4X31JRi7V6sZQg6 Hys5aU9QnQfZNMvuRUSOyxF+Npcc6htXfYoz2QdyD2quSKjZP58C0h2gdVS+Q+YAgMc4OeGS0bA ExTW3ogtNOzViVuHoee46j9XMWWkznsPOH1vO1MmRXO+3PICyf5OosmiK+vrN/V/jInAzXXHJpM Hg/wAbC1sECTAtY7J87seuAvu0/KXpefdpnCjvAq+As/ZisdkkRCuvve7zgL+LR6+ZIsb+oVowU i1bd43kkm+nTvpg== X-Mailer: b4 0.14.2 Message-ID: <20260130-gpuvm-rust-v4-4-8364d104ff40@google.com> Subject: [PATCH v4 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 Reviewed-by: Daniel Almeida Signed-off-by: Alice Ryhl --- 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 8f2f1c135e9dd071fd4b4ad0762a3e79dc922eea..c8c024ec47b0053d9941465858c= 0597f0dfd4950 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.53.0.rc1.225.gd81095ad13-goog From nobody Sat Feb 7 15:48:22 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 8371F36CE16 for ; Fri, 30 Jan 2026 14:24:37 +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=1769783079; cv=none; b=CnQvYiTAJCSBr12NvJLlKmVVVBP8fryuQacnO+B7p1ptR0RGTqh7nb4FzDoxpEMh8vIFrfhuMsV35tuDuRptMWfnSGPLPjhQ28jUJzlFheOH4VtNaN4AlqMk37uvuuU3oiCeDAWwJ7SYsAi+cxHOwbH4RtaXdrZsJC+UNVoIIAk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769783079; c=relaxed/simple; bh=jgzhuuggRvgfvFa+UYkqzqI+ERjQOxfIuIZZ+mOO67g=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=tMvjpik/5e+O9YE1UTkiyRW81Np2WXr7QsWqRW0NP3n32JhXJmSyjc403hOGUCO885uAyOCWLypvX/tDZuNhSlpVsq8pLTOfWWPt7EkZQMtuFE3k6uP4XuDDJCceu6i4v+mj9Wv+cXtd/clvMmyf7GlFRgJP9OtB/arH9qg9LSo= 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=yCPhcWni; 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="yCPhcWni" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-4801c1056c7so11927175e9.2 for ; Fri, 30 Jan 2026 06:24:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769783076; x=1770387876; 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=27748GJnY6LQ5US9SZ5tQooCOHDDmElDfpB/MH1p8m8=; b=yCPhcWniQv6O/x0syQpv/4aXNqvgYGJwGCb3pvbzT5Ahb5zzkMQu+dktdoGL5Ib7fP nE1Y2qgjaSwQLVGHbJmj2yD4LFeoWarOpQXCdOKwAzNbdfhsupAEtx61l0pzBjb79ZAQ 7aFCzeaJV7hlWmhDU7NgfT8gKTzLTq75uJKwJeXOK2xev4nOxSOPDRvFWkvdiDguhQMw 6Q9oHCQK1bI72KA8+J95E7/FePGgLNNpRRdW89W8cVeptedXt1B9CJUqUHb+ztjHlx36 cnPsKNAflh2lkM0sbKJbjfXuQNLuJpItFECWYiv1/OtR7SnASEiIeoBT/Nsv/HnoPCBX 9wjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769783076; x=1770387876; 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=27748GJnY6LQ5US9SZ5tQooCOHDDmElDfpB/MH1p8m8=; b=RInI0mxmSxg8mAJ6xE+avwI97tO807PAqHL/FCrAxoqZzEkADmlR8YL5GD69OtVQZU Ej46/DZH49Vwxjx/KxC17ApAhMvdepk1RcpTfM79pVJDxpZM/9zOdvqBT8vY1ZbKv2j5 CM8chA8RKIWiukb/OrIzLn7rwbxzAj/fe8faPj8XDfSy9bmDB5cUWIUH1VyBOb3bTugR kWwjt6gO9RxLgKpUuvnoJsQrUphaFvUCt8WY0K3xne/OC3UCkzr3VT6Fm0xoCdDKLXeb VuBKwHwp65dfq5Ev8mC4F7JM8FXDarNC7pwfYexxRetlMQwH6vcy8kdia7Fot0v/bq9U EkBw== X-Forwarded-Encrypted: i=1; AJvYcCVuKLwqxFJb6/iDA9qslQSP6o4Lug3D+yeK4o4dXGNeX560c+EQH5gnpub/omo6H5JnEGyWqWH94bbuY44=@vger.kernel.org X-Gm-Message-State: AOJu0Yy4Ct/AluuY/4ti7EodDhDEOQyhcNUUsWCiQ6z5wX3Jz3o2QsxP fE+soj9naE1BvTXtNnq4uUAyXD3AIQt9V8ZBHpq3ZQAe9L6p1A77hbr7KWNBbGEvmkARqpcsoin uheTY3BQsFKoPxgjJkg== X-Received: from wmop18.prod.google.com ([2002:a05:600c:4692:b0:477:a4d4:607a]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:1d8c:b0:47b:deb9:15fb with SMTP id 5b1f17b1804b1-482db4970e2mr39979535e9.33.1769783075895; Fri, 30 Jan 2026 06:24:35 -0800 (PST) Date: Fri, 30 Jan 2026 14:24:14 +0000 In-Reply-To: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=14341; i=aliceryhl@google.com; h=from:subject:message-id; bh=jgzhuuggRvgfvFa+UYkqzqI+ERjQOxfIuIZZ+mOO67g=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpfL8T2HejB1v/RawIRXHvsE/4tmAvzaeR/cz7h qqxkrd7s5uJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXy/EwAKCRAEWL7uWMY5 Rnh9D/9SohCFHKBAzPMJGvZ4Eo7T9AfaGaSgl5/8DEs/8nQTZ5djjoRp6uiNarsYWm5viaqrcey Ubh33pzzSS9s0T/8VhoRTrCC4PTgflduQMLGsMzADpmkdXYcrC6BhLL/dtSWUu0bK7FBXN+zX6x YuHbNTl2CKG9zRs4q4/3m80L9F3vQyT52nRAH41pVAOqF2sx1iiXzoCU9YgUZ+/DyfOQnntKMzi yLLr9dnZabUvWkPSGw4aQO4vBkhrK7jYLmnf9po2zt/ZH+XGCm5mEXywBlRlnJksTfAMR8873uQ V+6hvQe9X4Q/FA/2v///K2TxZ3TGL/SmT69rR7u1VdZoS8+LFSgyRvzDAq8KJYB/TorPTAfFu+K gDW5xxwuP0gYc37aUfZrDQIi1RC5zd9rpmbmWjPmpeijz+BN2YxG8BOLfhvmibjpSU21oMVTVwI E52km3d3+W7l16T0ph2Z02M1BgpdYxUfFu8DNctYEp18EWNp/5JpH9QJY9j99v47/oGIyRfxClF 1XoZNEsUrwQXWUIeuTH0wHKB//1PrM42szF88wvZs6+XbMK1OIi7xo/1bX5NOEyLchb0Jyjbc3w qUIZIPTKbjdw8hmXIoJsAnif9iQrXn0a8039KdaBrMO9LLD3j7GAaPYXqXR/uYf8DuRSes0FwBF qajHgyVfzhtiWiQ== X-Mailer: b4 0.14.2 Message-ID: <20260130-gpuvm-rust-v4-5-8364d104ff40@google.com> Subject: [PATCH v4 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 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 c8c024ec47b0053d9941465858c0597f0dfd4950..fd4c662f84a4830515c2ddd18d5= d503e4ee9fc8f 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..3f345bce14a18ae88ce525629e3= e5b76820e97a6 --- /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 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, + // 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 fn(&'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 fn(&'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 fn(&'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 fn(&'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 272e1a83c2d5f43c42dbdd9e09f51394a1e855b6..e8570f321c107be44fe2e463c88= e2760fa197dfa 100644 --- a/rust/kernel/drm/gpuvm/vm_bo.rs +++ b/rust/kernel/drm/gpuvm/vm_bo.rs @@ -100,6 +100,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.rc1.225.gd81095ad13-goog From nobody Sat Feb 7 15:48:22 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 8299D328B69 for ; Fri, 30 Jan 2026 14:24: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=1769783082; cv=none; b=DbClSNDAAc0UkmwGb9+xhM7PMl3My776N00gkhpLoRHqTRYRXzXgACf0a4rhfMRoPkne9/8a2kHRjPjPs4L/vGiGW8SaThX/WBGfRF4dCTROI2aJF+MNsFS5yVzOvHJvfOmuIoOCn2jpmnvunqXv+aPTEBs2MTVPxaXVZ4/Lqcc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769783082; c=relaxed/simple; bh=OocxZt4c/n9d8K9jPoS6cMKUoromwSnEs6v794Vpv6Q=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=McTkmdufmvV1xe0OsLarxHb78fzhcNpjStVtP0hMNB3HMWpTbkq72Ca3v2OZlWusF0tZli/eQAcqRCXYleSq0N52GX/qxU2a9fJWKuyYPOtAqPqSkcfRTIgHH4ekknu1Blc2u6xD9zPyOsyBbT80g4uANWI2jxjmVFjjLovYWik= 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=xS+pAIDw; 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="xS+pAIDw" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-4804157a3c9so27713315e9.1 for ; Fri, 30 Jan 2026 06:24:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769783078; x=1770387878; 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=wyrtNzxygY0xuisDnGfl0mQkqGyjjT25fNVtDfZ2la0=; b=xS+pAIDwK6J6RI2Q86sqRouiHl3+H5OoED1wMXAG/FZ1pk6gWlDbreLddU7P0YfumZ /cjzHhzkeOV5yR7XUm+rATWo7rrKdvz55hbC+gvtHOFtGKUoSMSVS/GJY1LXRCNOdSuH ofW8umS81g9sHw1VDXjvobuzfnUkdBBbm1xoVZU+6QTbw+0uuDx/Ceh5z2EFu0bHATLw q80rqZ0ttbhJC7HXNu5FoJAIrEFi7/5fRnysSwhoCLWS8vKuD1Wr5F9cOYoe19wnq54d uVqj9Wu2ejfyutK6Kk1e21sal1WxHv3WVfN1cdwNGGjR4U8rGbyf0MpCdu3lE+pA48k2 fX2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769783078; x=1770387878; 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=wyrtNzxygY0xuisDnGfl0mQkqGyjjT25fNVtDfZ2la0=; b=Ql6ooHa+Y8KqmH4MqiTMueBO432yDBTK56ssd33cFIkYbIWOLxAcFayfyedubfT0Ga NzTcF6eF338Duj0IvMWfiLBO9luYYLyIUFit5rgj727bLHDGo4tLiSvM3cuZhVRid6uL SgfPetJDpFDgZMXw0pE0yCBBpXIfd8MxVQTUE6iBieBFTmYdzVVCxgATPSHm2hSg65og Q3DgVdAaZj7UPcqkXdApcmr68Trx1VwwHKstH66dmICa4Hh2/A3jZeIrbfSWwKUlY5lL k2Pt+w49eU0JIYG+9rx8z1kuiEGgWgt8H3DPgovX/uASIhFH5C5uj0IBSYYu+hzBgIYR LX7g== X-Forwarded-Encrypted: i=1; AJvYcCVizFDwYXeC1Fm/qrF9p5CXKIodLu26MkSjuWp1DL+W7/iUzgioJdQA2/2DWKzKW3v3zchf7xuOaAzNQK8=@vger.kernel.org X-Gm-Message-State: AOJu0YyL48HTvZgckWOLGO/ZXVGgFdLALReMmwM3PAyK3ob6iuG+0ewn KPL+qOOePsc/y5MDCEISkALLUusOeJzEeq7DSdJQz0a3obuyV3z+b4ldPJ0Eu1SW18I5Mf6Rm+0 q4Rzob0eDQw5+zlhNNQ== X-Received: from wmi5.prod.google.com ([2002:a05:600c:205:b0:477:a43b:b532]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:4f16:b0:480:1d16:2538 with SMTP id 5b1f17b1804b1-482db493999mr45696685e9.23.1769783078083; Fri, 30 Jan 2026 06:24:38 -0800 (PST) Date: Fri, 30 Jan 2026 14:24:15 +0000 In-Reply-To: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260130-gpuvm-rust-v4-0-8364d104ff40@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=9806; i=aliceryhl@google.com; h=from:subject:message-id; bh=OocxZt4c/n9d8K9jPoS6cMKUoromwSnEs6v794Vpv6Q=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpfL8TNpDBJAXz4MdOGdrah1kFDKuqNiYZ8JDv4 wOSBWdUsAyJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaXy/EwAKCRAEWL7uWMY5 Rqn4D/9uCpt6PkFti3PDllr1YBzn1GDezaPhq6OfTs2nn/w/MF34t4O/isAOjNNTtWlHUOJlddK xKSSKTuSZH87IJ7G3kodJHZHxcTPYK4cbxylXVxbYynQDEo+qatfIrdX7ZMr0RJ4SEU3ylg9vC0 dRxoVCwZxIV7n4EWzUJU9+J9tn34y+AgiACY7a5KpwukIbMLmptbV18IR7vpUAhC78zmYILpKXK r4NdWP1YXZfF1pNZbnvm61LPEY8x6Peeumo5QJElfWcmjwAsCud6TkG7n6GslLXSFDmSsQ6qoDG 7+e2NG7pdV4ueQAc/ORi9Vmm4RvD7oeizk0My9Qi19uxoFiSGXzv3l2NaIAcE5axRL3Sxk/NyJz lFaNisYsxk10HxLQ79AfNWUnQhpy5mttheTTYEpfqVzj1vsUus6tfcZE+Ud+kedyWUXurVyK5DB cM3T3dCr3C2+yzcIwe/0DugPPONTRazWMgLr1nr/puZ0DqCtvYof6nAWpQkdl2Gv8+xih0D9lsh xJ7QN6NOgArGBQaKsjgtOt3Bd2bMiEcwEf2couvAHG3qakc+O2/9Dgjs3UorszFmM317ZfOPj6r fIxErZfYRlBAkC3I++e/k17Yx4ldwIYD1uogdjp0pd/I19vQ4dqAElVh9nvD2k6GAPsFZhYDa9n qGRIT7lw0MvFI1Q== X-Mailer: b4 0.14.2 Message-ID: <20260130-gpuvm-rust-v4-6-8364d104ff40@google.com> Subject: [PATCH v4 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 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 fd4c662f84a4830515c2ddd18d5d503e4ee9fc8f..20e512842dfc6f2bd461cd3d223= 61ef8bff2f204 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 3f345bce14a18ae88ce525629e3e5b76820e97a6..6ad741364b1856b3863b118a1d5= 581c54bb98ea9 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: GpuVmBoRegistered, +} + +/// 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: GpuVmBoRegistered, + /// 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 fn(&'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 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 @@ -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.rc1.225.gd81095ad13-goog