Skip to content

Commit d590578

Browse files
committed
rust: gpuvm: add GpuVa struct
This struct will be used to keep track of individual mapped ranges in the GPU's virtual memory. Sparse VAs are not yet supported. Co-developed-by: Asahi Lina <[email protected]> Signed-off-by: Asahi Lina <[email protected]> Co-developed-by: Daniel Almeida <[email protected]> Signed-off-by: Daniel Almeida <[email protected]> Reviewed-by: Daniel Almeida <[email protected]> Signed-off-by: Alice Ryhl <[email protected]>
1 parent 09852b9 commit d590578

3 files changed

Lines changed: 185 additions & 4 deletions

File tree

rust/kernel/drm/gpuvm/mod.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
//! C header: [`include/drm/drm_gpuvm.h`](srctree/include/drm/drm_gpuvm.h)
1212
1313
use kernel::{
14-
alloc::AllocError,
14+
alloc::{
15+
AllocError,
16+
Flags as AllocFlags, //
17+
},
1518
bindings,
1619
drm,
1720
drm::gem::IntoGEMObject,
@@ -25,9 +28,13 @@ use kernel::{
2528

2629
use core::{
2730
cell::UnsafeCell,
28-
mem::ManuallyDrop,
31+
mem::{
32+
ManuallyDrop,
33+
MaybeUninit, //
34+
},
2935
ops::{
3036
Deref,
37+
DerefMut,
3138
Range, //
3239
},
3340
ptr::{
@@ -36,6 +43,9 @@ use core::{
3643
}, //
3744
};
3845

46+
mod va;
47+
pub use self::va::*;
48+
3949
mod vm_bo;
4050
pub use self::vm_bo::*;
4151

@@ -48,7 +58,7 @@ pub use self::vm_bo::*;
4858
///
4959
/// * Stored in an allocation managed by the refcount in `self.vm`.
5060
/// * Access to `data` and the gpuvm interval tree is controlled via the [`UniqueRefGpuVm`] type.
51-
/// * Does not contain any sparse `GpuVa` instances.
61+
/// * Does not contain any sparse [`GpuVa<T>`] instances.
5262
#[pin_data]
5363
pub struct GpuVm<T: DriverGpuVm> {
5464
#[pin]
@@ -242,6 +252,9 @@ pub trait DriverGpuVm: Sized + Send {
242252
/// The kind of GEM object stored in this GPUVM.
243253
type Object: IntoGEMObject;
244254

255+
/// Data stored with each [`struct drm_gpuva`](struct@GpuVa).
256+
type VaData;
257+
245258
/// Data stored with each [`struct drm_gpuvm_bo`](struct@GpuVmBo).
246259
type VmBoData;
247260
}

rust/kernel/drm/gpuvm/va.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR MIT
2+
3+
#![expect(dead_code)]
4+
use super::*;
5+
6+
/// Represents that a range of a GEM object is mapped in this [`GpuVm`] instance.
7+
///
8+
/// Does not assume that GEM lock is held.
9+
///
10+
/// # Invariants
11+
///
12+
/// * This is a valid `drm_gpuva` object that is resident in a [`GpuVm<T>`] instance.
13+
/// * It is associated with a [`GpuVmBo<T>`]. Or in other words, it's not an
14+
/// `gpuvm->kernel_alloc_node` and `DRM_GPUVA_SPARSE` is not set.
15+
/// * The associated [`GpuVmBo<T>`] is part of the GEM list.
16+
#[repr(C)]
17+
#[pin_data]
18+
pub struct GpuVa<T: DriverGpuVm> {
19+
#[pin]
20+
inner: Opaque<bindings::drm_gpuva>,
21+
#[pin]
22+
data: T::VaData,
23+
}
24+
25+
impl<T: DriverGpuVm> PartialEq for GpuVa<T> {
26+
#[inline]
27+
fn eq(&self, other: &Self) -> bool {
28+
core::ptr::eq(self.as_raw(), other.as_raw())
29+
}
30+
}
31+
impl<T: DriverGpuVm> Eq for GpuVa<T> {}
32+
33+
impl<T: DriverGpuVm> GpuVa<T> {
34+
/// Access this [`GpuVa`] from a raw pointer.
35+
///
36+
/// # Safety
37+
///
38+
/// * For the duration of `'a`, the pointer must reference a valid `drm_gpuva` associated with
39+
/// a [`GpuVm<T>`].
40+
/// * It must be associated with a [`GpuVmBo<T>`].
41+
/// * The associated [`GpuVmBo<T>`] is part of the GEM list.
42+
#[inline]
43+
pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuva) -> &'a Self {
44+
// CAST: `drm_gpuva` is first field and `repr(C)`.
45+
// SAFETY: The safety requirements match the invariants of `GpuVa`.
46+
unsafe { &*ptr.cast() }
47+
}
48+
49+
/// Returns a raw pointer to underlying C value.
50+
#[inline]
51+
pub fn as_raw(&self) -> *mut bindings::drm_gpuva {
52+
self.inner.get()
53+
}
54+
55+
/// Returns the address of this mapping in the GPU virtual address space.
56+
#[inline]
57+
pub fn addr(&self) -> u64 {
58+
// SAFETY: The `va.addr` field of `drm_gpuva` is immutable.
59+
unsafe { (*self.as_raw()).va.addr }
60+
}
61+
62+
/// Returns the length of this mapping.
63+
#[inline]
64+
pub fn length(&self) -> u64 {
65+
// SAFETY: The `va.range` field of `drm_gpuva` is immutable.
66+
unsafe { (*self.as_raw()).va.range }
67+
}
68+
69+
/// Returns `addr..addr+length`.
70+
#[inline]
71+
pub fn range(&self) -> Range<u64> {
72+
let addr = self.addr();
73+
addr..addr + self.length()
74+
}
75+
76+
/// Returns the offset within the GEM object.
77+
#[inline]
78+
pub fn gem_offset(&self) -> u64 {
79+
// SAFETY: The `gem.offset` field of `drm_gpuva` is immutable.
80+
unsafe { (*self.as_raw()).gem.offset }
81+
}
82+
83+
/// Returns the GEM object.
84+
#[inline]
85+
pub fn obj(&self) -> &T::Object {
86+
// SAFETY: The `gem.obj` field of `drm_gpuva` is immutable. We know that it's not null
87+
// because this VA is associated with a `GpuVmBo<T>`.
88+
unsafe { <T::Object as IntoGEMObject>::from_raw((*self.as_raw()).gem.obj) }
89+
}
90+
91+
/// Returns the underlying [`GpuVmBo`] object that backs this [`GpuVa`].
92+
#[inline]
93+
pub fn vm_bo(&self) -> &GpuVmBo<T> {
94+
// SAFETY: The `vm_bo` field of `drm_gpuva` is immutable. We know that it's not null
95+
// because this VA is associated with a `GpuVmBo<T>`. The BO is in the GEM list by the type
96+
// invariants.
97+
unsafe { GpuVmBo::from_raw((*self.as_raw()).vm_bo) }
98+
}
99+
}
100+
101+
/// A pre-allocated [`GpuVa`] object.
102+
///
103+
/// # Invariants
104+
///
105+
/// The memory is zeroed.
106+
pub struct GpuVaAlloc<T: DriverGpuVm>(KBox<MaybeUninit<GpuVa<T>>>);
107+
108+
impl<T: DriverGpuVm> GpuVaAlloc<T> {
109+
/// Pre-allocate a [`GpuVa`] object.
110+
pub fn new(flags: AllocFlags) -> Result<GpuVaAlloc<T>, AllocError> {
111+
// INVARIANTS: Memory allocated with __GFP_ZERO.
112+
Ok(GpuVaAlloc(KBox::new_uninit(flags | __GFP_ZERO)?))
113+
}
114+
115+
/// Prepare this `drm_gpuva` for insertion into the GPUVM.
116+
#[must_use]
117+
pub(super) fn prepare(mut self, va_data: impl PinInit<T::VaData>) -> *mut bindings::drm_gpuva {
118+
let va_ptr = MaybeUninit::as_mut_ptr(&mut self.0);
119+
// SAFETY: The `data` field is pinned.
120+
let Ok(()) = unsafe { va_data.__pinned_init(&raw mut (*va_ptr).data) };
121+
KBox::into_raw(self.0).cast()
122+
}
123+
}
124+
125+
/// A [`GpuVa`] object that has been removed.
126+
///
127+
/// # Invariants
128+
///
129+
/// The `drm_gpuva` is not resident in the [`GpuVm`].
130+
pub struct GpuVaRemoved<T: DriverGpuVm>(KBox<GpuVa<T>>);
131+
132+
impl<T: DriverGpuVm> GpuVaRemoved<T> {
133+
/// Convert a raw pointer into a [`GpuVaRemoved`].
134+
///
135+
/// # Safety
136+
///
137+
/// * Must have been removed from a [`GpuVm<T>`].
138+
/// * It must not be a `gpuvm->kernel_alloc_node` va.
139+
pub(super) unsafe fn from_raw(ptr: *mut bindings::drm_gpuva) -> Self {
140+
// SAFETY: Since it used to be a VA in a `GpuVm<T>` and it's not a kernel_alloc_node, this
141+
// pointer references a `GpuVa<T>` with a valid `T::VaData`. Since it has been removed, we
142+
// can take ownership of the allocation.
143+
GpuVaRemoved(unsafe { KBox::from_raw(ptr.cast()) })
144+
}
145+
146+
/// Take ownership of the VA data.
147+
pub fn into_inner(self) -> T::VaData
148+
where
149+
T::VaData: Unpin,
150+
{
151+
KBox::into_inner(self.0).data
152+
}
153+
}
154+
155+
impl<T: DriverGpuVm> Deref for GpuVaRemoved<T> {
156+
type Target = T::VaData;
157+
fn deref(&self) -> &T::VaData {
158+
&self.0.data
159+
}
160+
}
161+
162+
impl<T: DriverGpuVm> DerefMut for GpuVaRemoved<T>
163+
where
164+
T::VaData: Unpin,
165+
{
166+
fn deref_mut(&mut self) -> &mut T::VaData {
167+
&mut self.0.data
168+
}
169+
}

rust/kernel/drm/gpuvm/vm_bo.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ impl<T: DriverGpuVm> GpuVmBo<T> {
114114
/// For the duration of `'a`, the pointer must reference a valid `drm_gpuvm_bo` associated with
115115
/// a [`GpuVm<T>`]. The BO must also be present in the GEM list.
116116
#[inline]
117-
#[expect(dead_code)]
118117
pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm_bo) -> &'a Self {
119118
// SAFETY: `drm_gpuvm_bo` is first field and `repr(C)`.
120119
unsafe { &*ptr.cast() }

0 commit comments

Comments
 (0)