From nobody Thu Dec 18 14:26:22 2025 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7F0A7261396; Mon, 24 Mar 2025 15:14:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742829297; cv=pass; b=KA+SeResZJgVTtZBYPwWcxipw0By3VNtqC0xRnV49AbRo87WN0DniUdopx/sa5X9f8fg6M/4zhqaTmaQKsWv/Alft+macSRoFj3ybRny/yFmH1czMMhIZ+ZMDrBqw1KVmKSfd/+oBlrj2w7j3tckSdRteDoZI0msqwCuM9+pBY8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742829297; c=relaxed/simple; bh=yyWBoVIdgM9yd55s/XqWCPB+EHlT41sHUz5jV+dNppk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Sr24SyvIPzumxiGfDq23COO/mGjYSGKILayWr+wIfiN68h5/E9zHudIf44I7Oe2YOFuE4vC5L2fi0UjKc3BF4fyEgBj/6kSgFBfBTV8jFpMHsVz5PtstaqzDmV9yGjHiqKZNvQWHXBOUb+a/hdqGySYSSSgaYmZLaE9FPd6gKUE= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b=My3I3Mp9; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b="My3I3Mp9" ARC-Seal: i=1; a=rsa-sha256; t=1742829273; cv=none; d=zohomail.com; s=zohoarc; b=FXPdAeI3f+wrKHfylHGK4lXWlXx1asWt6oTR6MWN+rR5EBKONwlGVzk0Ne6VXnSwW138eA8Uh33Dwwko3lXCJgXNOTRpGw7ihhj/+4FuWDa6OFRuW6CmnuwsOTOugYxPvvNZketVzPaDRlB/EugCvd9tfq0cmyaKWTCX2CQSiP4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1742829273; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=0GC8XAU9kSZizGu7pirgOvAlkT/SjNy72/DTHBMZsj8=; b=bp+xEitzIzdPSgM7ciQCzhUyVZ4fojqWQGfAqBO3aAOk9eVXLFsbUEFHNkDj+Q0mM8PAVfqRWTmKSy0unQqOAAsBf5Minb0wyy1uehGadw+TOM+cpgDN61Shr1Rn7gH4D3kFKhcNB8nJ8l4VeP398L7B7aXCWgXaEicMo6qrTzE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=daniel.almeida@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1742829273; s=zohomail; d=collabora.com; i=daniel.almeida@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=0GC8XAU9kSZizGu7pirgOvAlkT/SjNy72/DTHBMZsj8=; b=My3I3Mp9qHOHIPnuXmVVMRivjE55FPuydbJuD5lBmm9xPwHDIxUiavN+N9SJBROk 8aWEuPnmOJ7JZ7dNJK5Qta6C6fRsOCoxWyP5i1kVaVtJkgxwTSEFEtZfCvPEU5y0fnp 8xsm/rwbKJZ+AO+OeBYP0r0xBo1gQalPIDN+S6aA= Received: by mx.zohomail.com with SMTPS id 1742829270854331.91542821528924; Mon, 24 Mar 2025 08:14:30 -0700 (PDT) From: Daniel Almeida Date: Mon, 24 Mar 2025 12:13:54 -0300 Subject: [PATCH 1/2] rust: helpers: Add bindings/wrappers for dma_resv Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250324-gpuvm-v1-1-7f8213eebb56@collabora.com> References: <20250324-gpuvm-v1-0-7f8213eebb56@collabora.com> In-Reply-To: <20250324-gpuvm-v1-0-7f8213eebb56@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Sumit Semwal , =?utf-8?q?Christian_K=C3=B6nig?= , Boris Brezillon Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org, Asahi Lina X-Mailer: b4 0.14.2 X-ZohoMailClient: External 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 --- rust/bindings/bindings_helper.h | 4 +++- rust/helpers/dma-resv.c | 13 +++++++++++++ rust/helpers/helpers.c | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index e6020ba5b00237a08402fbd609c7fba27b970dd9..53111b5b35588541eca05e574a8= d24cdafbf1dd6 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -53,5 +54,6 @@ const gfp_t RUST_CONST_HELPER_GFP_NOWAIT =3D GFP_NOWAIT; const gfp_t RUST_CONST_HELPER___GFP_ZERO =3D __GFP_ZERO; const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM =3D ___GFP_HIGHMEM; const gfp_t RUST_CONST_HELPER___GFP_NOWARN =3D ___GFP_NOWARN; -const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL =3D BLK_FEAT_RO= TATIONAL; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL =3D + BLK_FEAT_ROTATIONAL; const fop_flags_t RUST_CONST_HELPER_FOP_UNSIGNED_OFFSET =3D FOP_UNSIGNED_O= FFSET; 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 7a06d6bc48537248c8a3ec4243b37d8fb2b1cb26..c5e536d688bc35c7b348daa61e8= 68c91a7bdbd23 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -14,6 +14,7 @@ #include "build_bug.c" #include "cred.c" #include "device.c" +#include "dma-resv.c" #include "drm.c" #include "err.c" #include "fs.c" --=20 2.48.1 From nobody Thu Dec 18 14:26:22 2025 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7FD5D42A99; Mon, 24 Mar 2025 15:15:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742829308; cv=pass; b=U8BEDw9cZvk9N0gTKrm7keef9xv9NMalFpWdR+srMxJ/KgP/VB+HZY1+65J+6GurkH8E9+cN01jQ64ecuMwxpvH6eIaGaB5pK5VUkeFc47qSEakWDwntLlfxgK7q65kEjjx6tb6pndLth6h9Y9qNV1FArHDxC0qiQ/SkYbKrbdk= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742829308; c=relaxed/simple; bh=iwXHDCvRRhvcdl2K43+2WWSmunLylk22g+U39RjQ5oM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gJCyTvLdu+BBpHZluS7W+lF7jvKJdZzQwmYdlIIrKPHqUR24tf3uxpGG1rMarITONP0es/fVxxW1jjfrKZGa7zJTLzcXWIDQFgkV2fcBODbtnGTJbxrH5Q5cECIv1vP1RDAoId74W7lm+I9pt3IHocAcVgj++DAQPc8BctYj91w= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b=ZYEmTdDW; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b="ZYEmTdDW" ARC-Seal: i=1; a=rsa-sha256; t=1742829277; cv=none; d=zohomail.com; s=zohoarc; b=ZSiHoueYqVlM6RLV8T0w/HOAdNFSW8n+nVzxj6PwuHGPQxlY7ITDmkDMOzU4ZMXSKTYZxjhSviF1Ig2/4W8bePhJKm2cZaLMZNKuxtA17oX491xYy0WgxOtzTHWN9uIApxGm0XOW2kMBxERR2fxfDgN4hyCQxE44kfCNbe8d1XM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1742829277; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=nUC7537CjzGVE7RZJlY7zU8rxribHh/4rAje2iLHfBg=; b=HP8ElI1zUhV12XoS0DD0Zpn1RtCkADt7f8U/ZgACx2etTYoVBQkliomO/yYcTBaHkLD+7fb23sDIbHig+fzIVfvJJyjZ3SYivS/BtnuP+qOYJt7VL//tf0ysu44jgQo9BnbNQASbPWD25KAyOGvDauo82ggHD3D2Iwgw8gznY/Y= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=daniel.almeida@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1742829277; s=zohomail; d=collabora.com; i=daniel.almeida@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=nUC7537CjzGVE7RZJlY7zU8rxribHh/4rAje2iLHfBg=; b=ZYEmTdDWCIWNKyxKhGZfaVdYc7H8D1FOBzQLP5p04OMbI+KETeNnwQLL/CshbhcZ +LsfuSLC2O7aBcvnD2gFvJ9+VAmoxHSZzmi/5gB3UyMiojKU+xYxiVAgH+Nm19lfqRX SdfXZLttEHNL2FUNUYZKj1mFVDQ+nBbkz3Xo7yWk= Received: by mx.zohomail.com with SMTPS id 1742829275293182.35144515663887; Mon, 24 Mar 2025 08:14:35 -0700 (PDT) From: Daniel Almeida Date: Mon, 24 Mar 2025 12:13:55 -0300 Subject: [PATCH 2/2] rust: drm: Add GPUVM abstraction Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250324-gpuvm-v1-2-7f8213eebb56@collabora.com> References: <20250324-gpuvm-v1-0-7f8213eebb56@collabora.com> In-Reply-To: <20250324-gpuvm-v1-0-7f8213eebb56@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Sumit Semwal , =?utf-8?q?Christian_K=C3=B6nig?= , Boris Brezillon Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org, Asahi Lina , Daniel Almeida X-Mailer: b4 0.14.2 X-ZohoMailClient: External 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 mapping's backing GEM buffers. This initial version only support synchronous operations. In other words, we do not support the use case where the operations are pre-built and handed over to the driver. We also do not support using a driver-specific lock in order to serialize the gpuva list for a given GEM object. This has to be the GEM object resv, which is the default behavior in C. Similarly to the C code, locking is left to the driver. This means that the driver should make sure that the VM's interval tree is protected against concurrent access. In Rust, we encode this requirement by requiring a generic Guard type in GpuVm::lock(), which is a similar approach as the one taken by CondVar. It is up to drivers to make sure that the guard indeed provides the required locking. Any operations that modifies the interval tree is only available after the VM has been locked as per above. Signed-off-by: Asahi Lina Co-developed-by: Daniel Almeida Signed-off-by: Daniel Almeida --- rust/bindings/bindings_helper.h | 1 + rust/helpers/drm_gpuvm.c | 29 ++ rust/helpers/helpers.c | 1 + rust/kernel/drm/gpuvm.rs | 790 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/drm/mod.rs | 2 + 5 files changed, 823 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 53111b5b35588541eca05e574a8d24cdafbf1dd6..0152f87bfeaccfca4fd5d58c45f= e4d9f81f05d7b 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -10,6 +10,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..7a074d2c2160ebc5f92909236c5= aaecb1853e45d --- /dev/null +++ b/rust/helpers/drm_gpuvm.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +#include + +#ifdef CONFIG_DRM +#ifdef CONFIG_DRM_GPUVM + +struct drm_gpuvm *rust_helper_drm_gpuvm_get(struct drm_gpuvm *obj) +{ + return drm_gpuvm_get(obj); +} + +void rust_helper_drm_gpuva_init_from_op(struct drm_gpuva *va, struct drm_g= puva_op_map *op) +{ + drm_gpuva_init_from_op(va, op); +} + +struct drm_gpuvm_bo *rust_helper_drm_gpuvm_bo_get(struct drm_gpuvm_bo *vm_= bo) +{ + return drm_gpuvm_bo_get(vm_bo); +} + +bool rust_helper_drm_gpuvm_is_extobj(struct drm_gpuvm *gpuvm, struct drm_g= em_object *obj) +{ + return drm_gpuvm_is_extobj(gpuvm, obj); +} + +#endif /* CONFIG_DRM_GPUVM */ +#endif /* CONFIG_DRM */ diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index c5e536d688bc35c7b348daa61e868c91a7bdbd23..a013cc91020ae5094a04a2e4b93= 993cf0b149885 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -16,6 +16,7 @@ #include "device.c" #include "dma-resv.c" #include "drm.c" +#include "drm_gpuvm.c" #include "err.c" #include "fs.c" #include "io.c" diff --git a/rust/kernel/drm/gpuvm.rs b/rust/kernel/drm/gpuvm.rs new file mode 100644 index 0000000000000000000000000000000000000000..98553aaea650c800d40bb483dbc= f35c67e696114 --- /dev/null +++ b/rust/kernel/drm/gpuvm.rs @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! GPUVM abstractions. +//! +//! Only synchronous operations are supported, and the GEM's `dma_resv` is= used +//! as the GEM's gpuva lock. +//! +//! C header: [`include/drm/drm_gpuvm.h`](srctree/include/drm/drm_gpuvm.h) + +use core::cell::UnsafeCell; +use core::marker::{PhantomData, PhantomPinned}; +use core::ops::{Deref, DerefMut, Range}; +use core::ptr::NonNull; + +use crate::bindings; +use crate::drm::device; +use crate::drm::drv; +use crate::drm::gem::IntoGEMObject; +use crate::error::code::ENOMEM; +use crate::error::from_result; +use crate::error::to_result; +use crate::error::Result; +use crate::init; +use crate::init::pin_init_from_closure; +use crate::prelude::*; +use crate::sync::lock::Backend; +use crate::sync::lock::Guard; +use crate::types::ARef; +use crate::types::AlwaysRefCounted; +use crate::types::Opaque; + +// SAFETY: This type is safe to zero-initialize. +unsafe impl init::Zeroable for bindings::drm_gpuvm_bo {} + +#[allow(type_alias_bounds)] +// A convenience type for the driver's GEM object. +type DriverObject =3D ::Object; + +/// Trait that must be implemented by DRM drivers to represent a DRM GpuVm= (a GPU address space). +pub trait DriverGpuVm: Sized { + /// The parent `Driver` implementation for this `DriverGpuVm`. + type Driver: drv::Driver; + + /// The driver-specific GpuVa type. + type GpuVa: DriverGpuVa; + + /// The driver-specific GpuVmBo type. + type GpuVmBo: DriverGpuVmBo; + + /// The driver-specific context that is kept through the map, unmap and + /// remap steps. + type StepContext; + + /// Implements the map step for the driver. + fn step_map( + self: &mut UpdatingGpuVm<'_, Self>, + op: &mut OpMap, + ctx: &mut Self::StepContext, + ) -> Result; + + /// Implements the unmap step for the driver. + fn step_unmap( + self: &mut UpdatingGpuVm<'_, Self>, + op: &mut OpUnMap, + ctx: &mut Self::StepContext, + ) -> Result; + + /// Implements the remap step for the driver. + fn step_remap( + self: &mut UpdatingGpuVm<'_, Self>, + op: &mut OpReMap, + vm_bo: &GpuVmBo, + ctx: &mut Self::StepContext, + ) -> Result; +} + +/// The driver-specific context that is passed through the map, unmap and = remap +/// steps. +struct StepContext<'a, T: DriverGpuVm> { + gpuvm: &'a GpuVm, + ctx: &'a mut T::StepContext, +} + +/// Trait that must be implemented by DRM drivers to represent a DRM GpuVa= (a mapping in GPU address space). +pub trait DriverGpuVa: Sized {} + +/// Trait that must be implemented by DRM drivers to represent a DRM GpuVm= Bo (a connection between a BO and a VM). +pub trait DriverGpuVmBo: Sized { + /// Initializes the GpuVmBo object. + fn new() -> impl PinInit; +} + +/// Provide a default implementation for trivial types +impl DriverGpuVmBo for T { + fn new() -> impl PinInit { + // SAFETY: + // - Ok(()) is always returned. + // - The slot is never moved. + unsafe { + pin_init_from_closure(|slot| { + *slot =3D Self::default(); + Ok(()) + }) + } + } +} + +#[repr(transparent)] +/// A transparent wrapper over `drm_gpuva_op_map`. +/// +/// This encodes the map operation to be carried out by the driver. +pub struct OpMap(bindings::drm_gpuva_op_map, PhantomData); + +#[repr(transparent)] +/// Represents a single remap operation generated by [`GpuVm`]. +/// +/// A remap operation is generated when an existing GPU VA mapping is spli= t by +/// inserting a new one or by partially unmapping existing mappings. Hence= , it +/// consists of a maximum of two maps and one unmap operation, +/// +/// The unmap operation takes care of removing the original existing mappi= ng. +/// The `prev` field is used to remap the preceding part and `next` is use= d to +/// remap the subsequent part. +/// +/// If the start address of the new mapping aligns with the start address = of the +/// old mapping, `prev` will be `None`. Similarly, if the end address of t= he new +/// mapping aligns with the end address of the old mapping, `next` will be +/// `None`. + +/// Note: the reason for a dedicated remap operation, rather than arbitrary +/// unmap and map operations, is to give drivers the chance of extracting = driver +/// specific data for creating the new mappings from the unmap operations's +/// [`GpuVa`] structure which typically is embedded in larger driver speci= fic +/// structures. +pub struct OpReMap(bindings::drm_gpuva_op_remap, PhantomDa= ta); + +impl OpMap { + #[inline] + /// Returns the base address of the new mapping. + pub fn addr(&self) -> u64 { + self.0.va.addr + } + + #[inline] + /// Returns the range of the new mapping. + pub fn range(&self) -> u64 { + self.0.va.range + } + + #[inline] + /// Returns the offset within the GEM object. + pub fn offset(&self) -> u64 { + self.0.gem.offset + } + + #[inline] + /// Returns the GEM object to map. + pub fn object(&self) -> &::Object { + let p =3D <::Object as IntoGEMObject>::f= rom_gem_obj(self.0.gem.obj); + // SAFETY: The GEM object has an active reference for the lifetime= of this op + unsafe { &*p } + } + + /// A helper function to map and link a GpuVa to a GpuVmBo. + pub fn map_and_link_va( + &mut self, + gpuvm: &mut UpdatingGpuVm<'_, T>, + gpuva: Pin>>, + gpuvmbo: &ARef>, + ) -> Result<(), Pin>>> { + // SAFETY: We are handing off the GpuVa ownership and it will not = be moved. + let p =3D KBox::leak(unsafe { Pin::into_inner_unchecked(gpuva) }); + // SAFETY: These C functions are called with the correct invariants + unsafe { + bindings::drm_gpuva_init_from_op(&mut p.gpuva, &mut self.0); + if bindings::drm_gpuva_insert(gpuvm.0.gpuvm() as *mut _, &mut = p.gpuva) !=3D 0 { + // EEXIST, return the GpuVa to the caller as an error + return Err(Pin::new_unchecked(KBox::from_raw(p))); + }; + // SAFETY: This takes a new reference to the gpuvmbo. + bindings::drm_gpuva_link(&mut p.gpuva, &gpuvmbo.bo as *const _= as *mut _); + } + Ok(()) + } +} + +#[repr(transparent)] +/// Represents a single unmap operation generated by [`GpuVm`]. +pub struct OpUnMap(bindings::drm_gpuva_op_unmap, PhantomDa= ta); + +impl OpUnMap { + #[inline] + /// Returns the GPU VA to unmap. + pub fn va(&self) -> Option<&GpuVa> { + if self.0.va.is_null() { + return None; + } + // SAFETY: Container invariant is guaranteed for ops structs creat= ed for our types. + let p =3D unsafe { crate::container_of!(self.0.va, GpuVa, gpuva= ) as *mut GpuVa }; + // SAFETY: The GpuVa object reference is valid per the op_unmap co= ntract + Some(unsafe { &*p }) + } + + #[inline] + /// Indicates whether this GPU VA is physically contiguous with the or= iginal + /// mapping request. + /// + /// Optionally, if `keep` is set, drivers may keep the actual page tab= le + /// mappings for this GPU VA, adding the missing page table entries on= ly and + /// subsequently updating the VM accordingly. + pub fn keep(&self) -> bool { + self.0.keep + } + + /// A helper to unmap and unlink a GpuVa from a GpuVmBo. + pub fn unmap_and_unlink_va(&mut self) -> Option>>> { + if self.0.va.is_null() { + return None; + } + // SAFETY: Container invariant is guaranteed for ops structs creat= ed for our types. + let p =3D unsafe { crate::container_of!(self.0.va, GpuVa, gpuva= ) as *mut GpuVa }; + + // SAFETY: The GpuVa object reference is valid per the op_unmap co= ntract + unsafe { + bindings::drm_gpuva_unmap(&mut self.0); + bindings::drm_gpuva_unlink(self.0.va); + } + + // Unlinking/unmapping relinquishes ownership of the GpuVa object, + // so clear the pointer + self.0.va =3D core::ptr::null_mut(); + // SAFETY: The GpuVa object reference is valid per the op_unmap co= ntract + Some(unsafe { Pin::new_unchecked(KBox::from_raw(p)) }) + } +} + +impl OpReMap { + #[inline] + /// Returns the preceding part of a split mapping, if any. + pub fn prev_map(&mut self) -> Option<&mut OpMap> { + // SAFETY: The prev pointer must be valid if not-NULL per the op_r= emap contract + unsafe { (self.0.prev as *mut OpMap).as_mut() } + } + + #[inline] + /// Returns the subsequent part of a split mapping, if any. + pub fn next_map(&mut self) -> Option<&mut OpMap> { + // SAFETY: The next pointer must be valid if not-NULL per the op_r= emap contract + unsafe { (self.0.next as *mut OpMap).as_mut() } + } + + #[inline] + /// Returns the unmap operation for the original existing mapping. + pub fn unmap(&mut self) -> &mut OpUnMap { + // SAFETY: The unmap pointer is always valid per the op_remap cont= ract + unsafe { (self.0.unmap as *mut OpUnMap).as_mut().unwrap() } + } +} + +#[repr(C)] +#[pin_data] +/// A GPU VA range. +/// +/// Drivers can use `inner` to store additional data. +pub struct GpuVa { + #[pin] + gpuva: bindings::drm_gpuva, + #[pin] + inner: T::GpuVa, + #[pin] + _p: PhantomPinned, +} + +// SAFETY: This type is safe to zero-init (as far as C is concerned). +unsafe impl init::Zeroable for bindings::drm_gpuva {} + +impl GpuVa { + /// Creates a new GPU VA. + pub fn new(inner: impl PinInit) -> Result>>> + where + Error: From, + { + KBox::try_pin_init( + try_pin_init!(Self { + gpuva <- init::zeroed(), + inner <- inner, + _p: PhantomPinned + }), + GFP_KERNEL, + ) + } + + #[inline] + /// Returns the start address of the GPU VA range. + pub fn addr(&self) -> u64 { + self.gpuva.va.addr + } + + #[inline] + /// Returns the range of the GPU VA. + pub fn range(&self) -> u64 { + self.gpuva.va.range + } + + #[inline] + /// Returns the offset within the GEM object. + pub fn offset(&self) -> u64 { + self.gpuva.gem.offset + } +} + +#[repr(C)] +#[pin_data] +/// The connection between a GEM object and a VM. +pub struct GpuVmBo { + #[pin] + bo: bindings::drm_gpuvm_bo, + #[pin] + inner: T::GpuVmBo, + #[pin] + _p: PhantomPinned, +} + +impl GpuVmBo { + /// Return a reference to the inner driver data for this GpuVmBo + pub fn inner(&self) -> &T::GpuVmBo { + &self.inner + } +} + +// SAFETY: DRM GpuVmBo objects are always reference counted and the get/pu= t functions +// satisfy the requirements. +unsafe impl AlwaysRefCounted for GpuVmBo { + fn inc_ref(&self) { + // SAFETY: The drm_gpuvm_get function satisfies the requirements f= or inc_ref(). + unsafe { bindings::drm_gpuvm_bo_get(&self.bo as *const _ as *mut _= ) }; + } + + unsafe fn dec_ref(mut obj: NonNull) { + // SAFETY: drm_gpuvm_bo_put() requires holding the gpuva lock, whi= ch is the dma_resv lock by default. + // The drm_gpuvm_put function satisfies the requirements for dec_r= ef(). + // (We do not support custom locks yet.) + unsafe { + let resv =3D (*obj.as_mut().bo.obj).resv; + bindings::dma_resv_lock(resv, core::ptr::null_mut()); + bindings::drm_gpuvm_bo_put(&mut obj.as_mut().bo); + bindings::dma_resv_unlock(resv); + } + } +} + +/// The DRM GPU VA Manager. +/// +/// It keeps track of a GPU's virtual address space by using maple tree +/// structures. +/// +/// Typically, an instance of [`GpuVm`] is embedded bigger, driver-specific +/// structures. +/// +/// Drivers can pass addresses and ranges in arbitrary units, e.g.: bytes = or +/// pages. +/// +/// There should be one manager instance per GPU virtual address space. +#[repr(C)] +#[pin_data] +pub struct GpuVm { + #[pin] + gpuvm: Opaque, + #[pin] + inner: UnsafeCell, + #[pin] + _p: PhantomPinned, +} + +/// # Safety +/// +/// This function is only safe to be called from the GPUVM C code. +unsafe extern "C" fn vm_free_callback(raw_gpuvm: *mut bind= ings::drm_gpuvm) { + // SAFETY: Container invariant is guaranteed for objects using our cal= lback. + let p =3D unsafe { + crate::container_of!( + raw_gpuvm as *mut Opaque, + GpuVm, + gpuvm + ) as *mut GpuVm + }; + + // SAFETY: p is guaranteed to be valid for drm_gpuvm objects using thi= s callback. + unsafe { drop(KBox::from_raw(p)) }; +} + +/// # Safety +/// +/// This function is only safe to be called from the GPUVM C code. +unsafe extern "C" fn vm_bo_alloc_callback() -> *mut bindin= gs::drm_gpuvm_bo { + let obj: Result>>> =3D KBox::try_pin_init( + try_pin_init!(GpuVmBo:: { + bo <- init::zeroed(), + inner <- T::GpuVmBo::new(), + _p: PhantomPinned + }), + GFP_KERNEL, + ); + + match obj { + Ok(obj) =3D> + // SAFETY: The DRM core will keep this object pinned + unsafe { + let p =3D KBox::leak(Pin::into_inner_unchecked(obj)); + &mut p.bo + }, + Err(_) =3D> core::ptr::null_mut(), + } +} + +/// # Safety +/// +/// This function is only safe to be called from the GPUVM C code. +unsafe extern "C" fn vm_bo_free_callback(raw_vm_bo: *mut b= indings::drm_gpuvm_bo) { + // SAFETY: Container invariant is guaranteed for objects using this ca= llback. + let p =3D unsafe { crate::container_of!(raw_vm_bo, GpuVmBo, bo) as = *mut GpuVmBo }; + + // SAFETY: p is guaranteed to be valid for drm_gpuvm_bo objects using = this callback. + unsafe { drop(KBox::from_raw(p)) }; +} + +/// # Safety +/// +/// This function is only safe to be called from the GPUVM C code. +unsafe extern "C" fn step_map_callback( + op: *mut bindings::drm_gpuva_op, + _priv: *mut core::ffi::c_void, +) -> core::ffi::c_int { + // SAFETY: We know this is a map op, and OpMap is a transparent wrappe= r. + let map =3D unsafe { &mut *((&mut (*op).__bindgen_anon_1.map) as *mut = _ as *mut OpMap) }; + // SAFETY: This is a pointer to a StepContext created inline in sm_map= (), which is + // guaranteed to outlive this function. + let ctx =3D unsafe { &mut *(_priv as *mut StepContext<'_, T>) }; + + from_result(|| { + UpdatingGpuVm(ctx.gpuvm).step_map(map, ctx.ctx)?; + Ok(0) + }) +} + +/// # Safety +/// +/// This function is only safe to be called from the GPUVM C code. +unsafe extern "C" fn step_remap_callback( + op: *mut bindings::drm_gpuva_op, + _priv: *mut core::ffi::c_void, +) -> core::ffi::c_int { + // SAFETY: We know this is a map op, and OpReMap is a transparent wrap= per. + let remap =3D unsafe { &mut *((&mut (*op).__bindgen_anon_1.remap) as *= mut _ as *mut OpReMap) }; + // SAFETY: This is a pointer to a StepContext created inline in sm_map= (), which is + // guaranteed to outlive this function. + let ctx =3D unsafe { &mut *(_priv as *mut StepContext<'_, T>) }; + + let p_vm_bo =3D remap.unmap().va().unwrap().gpuva.vm_bo; + + let res =3D { + // SAFETY: vm_bo pointer must be valid and non-null by the step_re= map invariants. + // Since we grab a ref, this reference's lifetime is until the dec= ref. + let vm_bo_ref =3D unsafe { + bindings::drm_gpuvm_bo_get(p_vm_bo); + &*(crate::container_of!(p_vm_bo, GpuVmBo, bo) as *mut GpuVm= Bo) + }; + + from_result(|| { + UpdatingGpuVm(ctx.gpuvm).step_remap(remap, vm_bo_ref, ctx.ctx)= ?; + Ok(0) + }) + }; + + // SAFETY: We incremented the refcount above, and the Rust reference w= e took is + // no longer in scope. + unsafe { bindings::drm_gpuvm_bo_put(p_vm_bo) }; + + res +} + +/// # Safety +/// +/// This function is only safe to be called from the GPUVM C code. +unsafe extern "C" fn step_unmap_callback( + op: *mut bindings::drm_gpuva_op, + _priv: *mut core::ffi::c_void, +) -> core::ffi::c_int { + // SAFETY: We know this is a map op, and OpUnMap is a transparent wrap= per. + let unmap =3D unsafe { &mut *((&mut (*op).__bindgen_anon_1.unmap) as *= mut _ as *mut OpUnMap) }; + // SAFETY: This is a pointer to a StepContext created inline in sm_map= (), which is + // guaranteed to outlive this function. + let ctx =3D unsafe { &mut *(_priv as *mut StepContext<'_, T>) }; + + from_result(|| { + UpdatingGpuVm(ctx.gpuvm).step_unmap(unmap, ctx.ctx)?; + Ok(0) + }) +} + +impl GpuVm { + const OPS: bindings::drm_gpuvm_ops =3D bindings::drm_gpuvm_ops { + vm_free: Some(vm_free_callback::), + op_alloc: None, + op_free: None, + vm_bo_alloc: Some(vm_bo_alloc_callback::), + vm_bo_free: Some(vm_bo_free_callback::), + vm_bo_validate: None, + sm_step_map: Some(step_map_callback::), + sm_step_remap: Some(step_remap_callback::), + sm_step_unmap: Some(step_unmap_callback::), + }; + + fn gpuvm(&self) -> *const bindings::drm_gpuvm { + self.gpuvm.get() + } + + /// Creates a GPUVM instance. + pub fn new( + name: &'static CStr, + dev: &device::Device, + r_obj: &::Object, + range: Range, + reserve_range: Range, + inner: impl PinInit, + ) -> Result>> + where + Error: From, + { + let obj: Pin> =3D KBox::try_pin_init( + try_pin_init!(Self { + // SAFETY: drm_gpuvm_init cannot fail and always initializ= es the member + gpuvm <- unsafe { + init::pin_init_from_closure(move |slot: *mut Opaque | { + // Zero-init required by drm_gpuvm_init + *slot =3D core::mem::zeroed(); + bindings::drm_gpuvm_init( + Opaque::raw_get(slot), + name.as_char_ptr(), + 0, + dev.as_raw(), + r_obj.gem_obj() as *const _ as *mut _, + range.start, + range.end - range.start, + reserve_range.start, + reserve_range.end - reserve_range.start, + &Self::OPS + ); + Ok(()) + }) + }, + // SAFETY: Just passing through to the initializer argument + inner <- unsafe { + init::pin_init_from_closure(move |slot: *mut UnsafeCel= l | { + inner.__pinned_init(slot as *mut _) + }) + }, + _p: PhantomPinned + }), + GFP_KERNEL, + )?; + + // SAFETY: We never move out of the object + let vm_ref =3D unsafe { + ARef::from_raw(NonNull::new_unchecked(KBox::leak( + Pin::into_inner_unchecked(obj), + ))) + }; + + Ok(vm_ref) + } + + /// Locks the VM, protecting its interval tree against concurrent acce= sses. + /// + /// Callers must prove that they have exclusive access to the VM by ho= lding + /// some guard type. This encodes the driver-specific locking requirem= ents. + /// + /// It is up to the caller to ensure that the guard indeed provides the + /// required locking. + pub fn lock(&self, _guard: &mut Guard<'_, U, B>= ) -> LockedGpuVm<'_, T> { + LockedGpuVm { gpuvm: self } + } + + /// Returns true if the given object is external to the GPUVM, i.e.: i= f it + /// does not share the DMA reservation object of the GPUVM. + pub fn is_extobj(&self, obj: &impl IntoGEMObject) -> bool { + let gem =3D obj.gem_obj() as *const _ as *mut _; + // SAFETY: This is safe to call as long as the arguments are valid= pointers. + unsafe { bindings::drm_gpuvm_is_extobj(self.gpuvm() as *mut _, gem= ) } + } +} + +// SAFETY: DRM GpuVm objects are always reference counted and the get/put = functions +// satisfy the requirements. +unsafe impl AlwaysRefCounted for GpuVm { + fn inc_ref(&self) { + // SAFETY: The drm_gpuvm_get function satisfies the requirements f= or inc_ref(). + unsafe { bindings::drm_gpuvm_get(&self.gpuvm as *const _ as *mut _= ) }; + } + + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: The drm_gpuvm_put function satisfies the requirements f= or dec_ref(). + unsafe { bindings::drm_gpuvm_put(Opaque::raw_get(&(*obj.as_ptr()).= gpuvm)) }; + } +} + +/// The object returned after a call to [`GpuVm::lock`]. +/// +/// This object has access to operations that modify the VM's interval tre= e. +pub struct LockedGpuVm<'a, T: DriverGpuVm> { + gpuvm: &'a GpuVm, +} + +impl LockedGpuVm<'_, T> { + /// Finds the [`GpuVmBo`] object that connects `obj` to this VM. + /// + /// If found, increases the reference count of the GpuVmBo object + /// accordingly. + pub fn find_bo(&mut self, obj: &DriverObject) -> Option>> { + // SAFETY: LockedGpuVm implies the right locks are held. + let p =3D unsafe { + bindings::drm_gpuvm_bo_find( + self.gpuvm.gpuvm() as *mut _, + obj.gem_obj() as *const _ as *mut _, + ) + }; + if p.is_null() { + None + } else { + // SAFETY: All the drm_gpuvm_bo objects in this GpuVm are alwa= ys allocated by us as GpuVmBo. + let p =3D unsafe { crate::container_of!(p, GpuVmBo, bo) as = *mut GpuVmBo }; + // SAFETY: We checked for NULL above, and the types ensure that + // this object was created by vm_bo_alloc_callback. + Some(unsafe { ARef::from_raw(NonNull::new_unchecked(p)) }) + } + } + + /// Obtains the [`GpuVmBo`] object that connects `obj` to this VM. + /// + /// This connection is unique, so an instane of [`GpuVmBo`] will be + /// allocated for `obj` once, and that instance will be returned from = that + /// point forward. + pub fn obtain_bo(&mut self, obj: &DriverObject) -> Result>> { + // SAFETY: LockedGpuVm implies the right locks are held. + let p =3D unsafe { + bindings::drm_gpuvm_bo_obtain( + self.gpuvm.gpuvm() as *mut _, + obj.gem_obj() as *const _ as *mut _, + ) + }; + if p.is_null() { + Err(ENOMEM) + } else { + // SAFETY: Container invariant is guaranteed for GpuVmBo objec= ts for this GpuVm. + let p =3D unsafe { crate::container_of!(p, GpuVmBo, bo) as = *mut GpuVmBo }; + // SAFETY: We checked for NULL above, and the types ensure that + // this object was created by vm_bo_alloc_callback. + Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(p)) }) + } + } + + /// Iterates the given range of the GPU VA space. It utilizes + /// [`DriverGpuVm`] to call back into the driver providing the split a= nd + /// merge steps. + /// + /// A sequence of callbacks can contain map, unmap and remap operation= s, but + /// the sequence of callbacks might also be empty if no operation is + /// required, e.g. if the requested mapping already exists in the exac= t same + /// way. + /// + /// There can be an arbitrary amount of unmap operations, a maximum of= two + /// remap operations and a single map operation. The latter one repres= ents + /// the original map operation requested by the caller. + /// + /// # Arguments + /// + /// - `ctx`: A driver-specific context. + /// - `req_obj`: The GEM object to map. + /// - `req_addr`: The start address of the new mapping. + /// - `req_range`: The range of the mapping. + /// - `req_offset`: The offset into the GEM object. + pub fn sm_map( + &mut self, + ctx: &mut T::StepContext, + req_obj: &DriverObject, + req_addr: u64, + req_range: u64, + req_offset: u64, + ) -> Result { + let mut ctx =3D StepContext { + ctx, + gpuvm: self.gpuvm, + }; + + // SAFETY: LockedGpuVm implies the right locks are held. + to_result(unsafe { + bindings::drm_gpuvm_sm_map( + self.gpuvm.gpuvm() as *mut _, + &mut ctx as *mut _ as *mut _, + req_addr, + req_range, + req_obj.gem_obj() as *const _ as *mut _, + req_offset, + ) + }) + } + + /// Iterates the given range of the GPU VA space. It utilizes + /// [`DriverGpuVm`] to call back into the driver providing the operati= ons to + /// unmap and, if required, split existent mappings. + /// + /// A sequence of callbacks can contain unmap and remap operations, + /// depending on whether there are actual overlapping mappings to spli= t. + /// + /// There can be an arbitrary amount of unmap operations and a maximum= of + /// two remap operations. + /// + /// # Arguments + /// + /// - `ctx`: A driver-specific context. + /// - `req_addr`: The start address of the range to unmap. + /// - `req_range`: The range of the mappings to unmap. + pub fn sm_unmap(&mut self, ctx: &mut T::StepContext, req_addr: u64, re= q_range: u64) -> Result { + let mut ctx =3D StepContext { + ctx, + gpuvm: self.gpuvm, + }; + + // SAFETY: LockedGpuVm implies the right locks are held. + to_result(unsafe { + bindings::drm_gpuvm_sm_unmap( + self.gpuvm.gpuvm() as *mut _, + &mut ctx as *mut _ as *mut _, + req_addr, + req_range, + ) + }) + } +} + +impl Deref for LockedGpuVm<'_, T> { + type Target =3D T; + + fn deref(&self) -> &T { + // SAFETY: The existence of this LockedGpuVm implies the lock is h= eld, + // so this is the only reference + unsafe { &*self.gpuvm.inner.get() } + } +} + +impl DerefMut for LockedGpuVm<'_, T> { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: The existence of this UpdatingGpuVm implies the lock is= held, + // so this is the only reference + unsafe { &mut *self.gpuvm.inner.get() } + } +} + +/// A state representing a GPU VM that is being updated. +pub struct UpdatingGpuVm<'a, T: DriverGpuVm>(&'a GpuVm); + +impl UpdatingGpuVm<'_, T> {} + +impl Deref for UpdatingGpuVm<'_, T> { + type Target =3D T; + + fn deref(&self) -> &T { + // SAFETY: The existence of this UpdatingGpuVm implies the lock is= held, + // so this is the only reference + unsafe { &*self.0.inner.get() } + } +} + +impl DerefMut for UpdatingGpuVm<'_, T> { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: The existence of this UpdatingGpuVm implies the lock is= held, + // so this is the only reference + unsafe { &mut *self.0.inner.get() } + } +} + +// SAFETY: All our trait methods take locks +unsafe impl Sync for GpuVm {} +// SAFETY: All our trait methods take locks +unsafe impl Send for GpuVm {} + +// SAFETY: All our trait methods take locks +unsafe impl Sync for GpuVmBo {} +// SAFETY: All our trait methods take locks +unsafe impl Send for GpuVmBo {} diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs index c44760a1332fa1ef875939b48e7af450f7372020..849dc1e577f15bfada11d6739df= f48ac33813326 100644 --- a/rust/kernel/drm/mod.rs +++ b/rust/kernel/drm/mod.rs @@ -6,4 +6,6 @@ pub mod drv; pub mod file; pub mod gem; +#[cfg(CONFIG_DRM_GPUVM =3D "y")] +pub mod gpuvm; pub mod ioctl; --=20 2.48.1