This struct will be used to keep track of individual mapped ranges in
the GPU's virtual memory.
Co-developed-by: Asahi Lina <lina+kernel@asahilina.net>
Signed-off-by: Asahi Lina <lina+kernel@asahilina.net>
Co-developed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
rust/kernel/drm/gpuvm/mod.rs | 17 ++++-
rust/kernel/drm/gpuvm/va.rs | 149 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 164 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs
index cb576a7ffa07bc108704e008b7f87de52a837930..2179ddd717d8728bbe231bd94ea7b5d1e2652543 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)
use kernel::{
- alloc::AllocError,
+ alloc::{
+ AllocError,
+ Flags as AllocFlags, //
+ },
bindings,
drm,
drm::gem::IntoGEMObject,
@@ -25,9 +28,13 @@
use core::{
cell::UnsafeCell,
- mem::ManuallyDrop,
+ mem::{
+ ManuallyDrop,
+ MaybeUninit, //
+ },
ops::{
Deref,
+ DerefMut,
Range, //
},
ptr::{
@@ -36,6 +43,9 @@
}, //
};
+mod va;
+pub use self::va::*;
+
mod vm_bo;
pub use self::vm_bo::*;
@@ -224,6 +234,9 @@ pub trait DriverGpuVm: Sized {
/// The kind of GEM object stored in this GPUVM.
type Object: IntoGEMObject;
+ /// 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..c96796a6b2c8c7c4b5475324562968ca0f07fd09
--- /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`] instance.
+///
+/// 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<T: DriverGpuVm> {
+ #[pin]
+ inner: Opaque<bindings::drm_gpuva>,
+ #[pin]
+ data: T::VaData,
+}
+
+impl<T: DriverGpuVm> GpuVa<T> {
+ /// 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<T>`].
+ #[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 space.
+ #[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<u64> {
+ let addr = 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 { <T::Object as IntoGEMObject>::from_raw((*self.as_raw()).gem.obj) }
+ }
+
+ /// Returns the underlying [`GpuVmBo`] object that backs this [`GpuVa`].
+ #[inline]
+ pub fn vm_bo(&self) -> &GpuVmBo<T> {
+ // 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<T: DriverGpuVm>(KBox<MaybeUninit<GpuVa<T>>>);
+
+impl<T: DriverGpuVm> GpuVaAlloc<T> {
+ /// Pre-allocate a [`GpuVa`] object.
+ pub fn new(flags: AllocFlags) -> Result<GpuVaAlloc<T>, 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<T::VaData>) -> *mut bindings::drm_gpuva {
+ let va_ptr = MaybeUninit::as_mut_ptr(&mut self.0);
+ // SAFETY: The `data` field is pinned.
+ let Ok(()) = unsafe { va_data.__pinned_init(&raw mut (*va_ptr).data) };
+ 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<T: DriverGpuVm>(KBox<GpuVa<T>>);
+
+impl<T: DriverGpuVm> GpuVaRemoved<T> {
+ /// Convert a raw pointer into a [`GpuVaRemoved`].
+ ///
+ /// # Safety
+ ///
+ /// Must have been removed from a [`GpuVm<T>`].
+ pub(super) unsafe fn from_raw(ptr: *mut bindings::drm_gpuva) -> Self {
+ // SAFETY: Since it has been removed we can take ownership of allocation.
+ 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<T: DriverGpuVm> Deref for GpuVaRemoved<T> {
+ type Target = T::VaData;
+ fn deref(&self) -> &T::VaData {
+ &self.0.data
+ }
+}
+
+impl<T: DriverGpuVm> DerefMut for GpuVaRemoved<T>
+where
+ T::VaData: Unpin,
+{
+ fn deref_mut(&mut self) -> &mut T::VaData {
+ &mut self.0.data
+ }
+}
--
2.52.0.457.g6b5491de43-goog
> On 21 Jan 2026, at 08:31, Alice Ryhl <aliceryhl@google.com> wrote:
>
> This struct will be used to keep track of individual mapped ranges in
> the GPU's virtual memory.
>
> Co-developed-by: Asahi Lina <lina+kernel@asahilina.net>
> Signed-off-by: Asahi Lina <lina+kernel@asahilina.net>
> Co-developed-by: Daniel Almeida <daniel.almeida@collabora.com>
> Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
> rust/kernel/drm/gpuvm/mod.rs | 17 ++++-
> rust/kernel/drm/gpuvm/va.rs | 149 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 164 insertions(+), 2 deletions(-)
>
> diff --git a/rust/kernel/drm/gpuvm/mod.rs b/rust/kernel/drm/gpuvm/mod.rs
> index cb576a7ffa07bc108704e008b7f87de52a837930..2179ddd717d8728bbe231bd94ea7b5d1e2652543 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)
>
> use kernel::{
> - alloc::AllocError,
> + alloc::{
> + AllocError,
> + Flags as AllocFlags, //
> + },
> bindings,
> drm,
> drm::gem::IntoGEMObject,
> @@ -25,9 +28,13 @@
>
> use core::{
> cell::UnsafeCell,
> - mem::ManuallyDrop,
> + mem::{
> + ManuallyDrop,
> + MaybeUninit, //
> + },
> ops::{
> Deref,
> + DerefMut,
> Range, //
> },
> ptr::{
> @@ -36,6 +43,9 @@
> }, //
> };
>
> +mod va;
> +pub use self::va::*;
> +
> mod vm_bo;
> pub use self::vm_bo::*;
>
> @@ -224,6 +234,9 @@ pub trait DriverGpuVm: Sized {
> /// The kind of GEM object stored in this GPUVM.
> type Object: IntoGEMObject;
>
> + /// 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..c96796a6b2c8c7c4b5475324562968ca0f07fd09
> --- /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`] instance.
> +///
> +/// 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<T: DriverGpuVm> {
> + #[pin]
> + inner: Opaque<bindings::drm_gpuva>,
> + #[pin]
> + data: T::VaData,
> +}
> +
> +impl<T: DriverGpuVm> GpuVa<T> {
> + /// 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<T>`].
> + #[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 space.
> + #[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<u64> {
> + let addr = 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 { <T::Object as IntoGEMObject>::from_raw((*self.as_raw()).gem.obj) }
> + }
> +
> + /// Returns the underlying [`GpuVmBo`] object that backs this [`GpuVa`].
> + #[inline]
> + pub fn vm_bo(&self) -> &GpuVmBo<T> {
> + // 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<T: DriverGpuVm>(KBox<MaybeUninit<GpuVa<T>>>);
> +
> +impl<T: DriverGpuVm> GpuVaAlloc<T> {
> + /// Pre-allocate a [`GpuVa`] object.
> + pub fn new(flags: AllocFlags) -> Result<GpuVaAlloc<T>, 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<T::VaData>) -> *mut bindings::drm_gpuva {
> + let va_ptr = MaybeUninit::as_mut_ptr(&mut self.0);
> + // SAFETY: The `data` field is pinned.
> + let Ok(()) = unsafe { va_data.__pinned_init(&raw mut (*va_ptr).data) };
> + 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<T: DriverGpuVm>(KBox<GpuVa<T>>);
> +
> +impl<T: DriverGpuVm> GpuVaRemoved<T> {
> + /// Convert a raw pointer into a [`GpuVaRemoved`].
> + ///
> + /// # Safety
> + ///
> + /// Must have been removed from a [`GpuVm<T>`].
> + pub(super) unsafe fn from_raw(ptr: *mut bindings::drm_gpuva) -> Self {
> + // SAFETY: Since it has been removed we can take ownership of allocation.
> + 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<T: DriverGpuVm> Deref for GpuVaRemoved<T> {
> + type Target = T::VaData;
> + fn deref(&self) -> &T::VaData {
> + &self.0.data
> + }
> +}
> +
> +impl<T: DriverGpuVm> DerefMut for GpuVaRemoved<T>
> +where
> + T::VaData: Unpin,
> +{
> + fn deref_mut(&mut self) -> &mut T::VaData {
> + &mut self.0.data
> + }
> +}
>
> --
> 2.52.0.457.g6b5491de43-goog
>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
© 2016 - 2026 Red Hat, Inc.