Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rust: drm: gem: Add GEM object abstraction

DRM GEM is the DRM memory management subsystem used by most modern
drivers; add a Rust abstraction for DRM GEM.

This includes the BaseObject trait, which contains operations shared by
all GEM object classes.

Signed-off-by: Asahi Lina <lina@asahilina.net>
Reviewed-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Link: https://lore.kernel.org/r/20250410235546.43736-8-dakr@kernel.org
[ Rework of GEM object abstractions
* switch to the Opaque<T> type
* fix (mutable) references to struct drm_gem_object (which in this
context is UB)
* drop all custom reference types in favor of AlwaysRefCounted
* bunch of minor changes and simplifications (e.g. IntoGEMObject
trait)
* write and fix safety and invariant comments
* remove necessity for and convert 'as' casts
* original source archive: https://archive.is/dD5SL

- Danilo ]
[ Fix missing CONFIG_DRM guards in rust/helpers/drm.c. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

Asahi Lina and committed by
Danilo Krummrich
c284d3e4 a98a73be

+351 -2
+2
rust/bindings/bindings_helper.h
··· 9 9 #include <drm/drm_device.h> 10 10 #include <drm/drm_drv.h> 11 11 #include <drm/drm_file.h> 12 + #include <drm/drm_gem.h> 12 13 #include <drm/drm_ioctl.h> 13 14 #include <kunit/test.h> 14 15 #include <linux/auxiliary_bus.h> ··· 61 60 const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM; 62 61 const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN; 63 62 const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL; 63 + const fop_flags_t RUST_CONST_HELPER_FOP_UNSIGNED_OFFSET = FOP_UNSIGNED_OFFSET;
+23
rust/helpers/drm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <drm/drm_gem.h> 4 + #include <drm/drm_vma_manager.h> 5 + 6 + #ifdef CONFIG_DRM 7 + 8 + void rust_helper_drm_gem_object_get(struct drm_gem_object *obj) 9 + { 10 + drm_gem_object_get(obj); 11 + } 12 + 13 + void rust_helper_drm_gem_object_put(struct drm_gem_object *obj) 14 + { 15 + drm_gem_object_put(obj); 16 + } 17 + 18 + __u64 rust_helper_drm_vma_node_offset_addr(struct drm_vma_offset_node *node) 19 + { 20 + return drm_vma_node_offset_addr(node); 21 + } 22 + 23 + #endif
+1
rust/helpers/helpers.c
··· 15 15 #include "cpumask.c" 16 16 #include "cred.c" 17 17 #include "device.c" 18 + #include "drm.c" 18 19 #include "err.c" 19 20 #include "fs.c" 20 21 #include "io.c"
+3 -1
rust/kernel/drm/device.rs
··· 89 89 driver_features: drm::driver::FEAT_GEM, 90 90 ioctls: T::IOCTLS.as_ptr(), 91 91 num_ioctls: T::IOCTLS.len() as i32, 92 - fops: core::ptr::null_mut() as _, 92 + fops: &Self::GEM_FOPS as _, 93 93 }; 94 + 95 + const GEM_FOPS: bindings::file_operations = drm::gem::create_fops(); 94 96 95 97 /// Create a new `drm::Device` for a `drm::Driver`. 96 98 pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<ARef<Self>> {
+1 -1
rust/kernel/drm/driver.rs
··· 88 88 } 89 89 90 90 /// Trait for memory manager implementations. Implemented internally. 91 - pub trait AllocImpl: super::private::Sealed { 91 + pub trait AllocImpl: super::private::Sealed + drm::gem::IntoGEMObject { 92 92 /// The C callback operations for this memory manager. 93 93 const ALLOC_OPS: AllocOps; 94 94 }
+320
rust/kernel/drm/gem/mod.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 OR MIT 2 + 3 + //! DRM GEM API 4 + //! 5 + //! C header: [`include/linux/drm/drm_gem.h`](srctree/include/linux/drm/drm_gem.h) 6 + 7 + use crate::{ 8 + alloc::flags::*, 9 + bindings, drm, 10 + drm::driver::{AllocImpl, AllocOps}, 11 + error::{to_result, Result}, 12 + prelude::*, 13 + types::{ARef, Opaque}, 14 + }; 15 + use core::{mem, ops::Deref, ptr, ptr::NonNull}; 16 + 17 + /// GEM object functions, which must be implemented by drivers. 18 + pub trait BaseDriverObject<T: BaseObject>: Sync + Send + Sized { 19 + /// Create a new driver data object for a GEM object of a given size. 20 + fn new(dev: &drm::Device<T::Driver>, size: usize) -> impl PinInit<Self, Error>; 21 + 22 + /// Open a new handle to an existing object, associated with a File. 23 + fn open( 24 + _obj: &<<T as IntoGEMObject>::Driver as drm::Driver>::Object, 25 + _file: &drm::File<<<T as IntoGEMObject>::Driver as drm::Driver>::File>, 26 + ) -> Result { 27 + Ok(()) 28 + } 29 + 30 + /// Close a handle to an existing object, associated with a File. 31 + fn close( 32 + _obj: &<<T as IntoGEMObject>::Driver as drm::Driver>::Object, 33 + _file: &drm::File<<<T as IntoGEMObject>::Driver as drm::Driver>::File>, 34 + ) { 35 + } 36 + } 37 + 38 + /// Trait that represents a GEM object subtype 39 + pub trait IntoGEMObject: Sized + super::private::Sealed { 40 + /// Owning driver for this type 41 + type Driver: drm::Driver; 42 + 43 + /// Returns a reference to the raw `drm_gem_object` structure, which must be valid as long as 44 + /// this owning object is valid. 45 + #[allow(clippy::wrong_self_convention)] 46 + fn into_gem_obj(&self) -> &Opaque<bindings::drm_gem_object>; 47 + 48 + /// Converts a pointer to a `struct drm_gem_object` into a pointer to `Self`. 49 + fn from_gem_obj(obj: *mut bindings::drm_gem_object) -> *mut Self; 50 + } 51 + 52 + /// Trait which must be implemented by drivers using base GEM objects. 53 + pub trait DriverObject: BaseDriverObject<Object<Self>> { 54 + /// Parent `Driver` for this object. 55 + type Driver: drm::Driver; 56 + } 57 + 58 + extern "C" fn open_callback<T: BaseDriverObject<U>, U: BaseObject>( 59 + raw_obj: *mut bindings::drm_gem_object, 60 + raw_file: *mut bindings::drm_file, 61 + ) -> core::ffi::c_int { 62 + // SAFETY: `open_callback` is only ever called with a valid pointer to a `struct drm_file`. 63 + let file = unsafe { 64 + drm::File::<<<U as IntoGEMObject>::Driver as drm::Driver>::File>::as_ref(raw_file) 65 + }; 66 + let obj = 67 + <<<U as IntoGEMObject>::Driver as drm::Driver>::Object as IntoGEMObject>::from_gem_obj( 68 + raw_obj, 69 + ); 70 + 71 + // SAFETY: `from_gem_obj()` returns a valid pointer as long as the type is correct and the 72 + // `raw_obj` we got is valid. 73 + match T::open(unsafe { &*obj }, file) { 74 + Err(e) => e.to_errno(), 75 + Ok(()) => 0, 76 + } 77 + } 78 + 79 + extern "C" fn close_callback<T: BaseDriverObject<U>, U: BaseObject>( 80 + raw_obj: *mut bindings::drm_gem_object, 81 + raw_file: *mut bindings::drm_file, 82 + ) { 83 + // SAFETY: `open_callback` is only ever called with a valid pointer to a `struct drm_file`. 84 + let file = unsafe { 85 + drm::File::<<<U as IntoGEMObject>::Driver as drm::Driver>::File>::as_ref(raw_file) 86 + }; 87 + let obj = 88 + <<<U as IntoGEMObject>::Driver as drm::Driver>::Object as IntoGEMObject>::from_gem_obj( 89 + raw_obj, 90 + ); 91 + 92 + // SAFETY: `from_gem_obj()` returns a valid pointer as long as the type is correct and the 93 + // `raw_obj` we got is valid. 94 + T::close(unsafe { &*obj }, file); 95 + } 96 + 97 + impl<T: DriverObject> IntoGEMObject for Object<T> { 98 + type Driver = T::Driver; 99 + 100 + fn into_gem_obj(&self) -> &Opaque<bindings::drm_gem_object> { 101 + &self.obj 102 + } 103 + 104 + fn from_gem_obj(obj: *mut bindings::drm_gem_object) -> *mut Self { 105 + // SAFETY: All of our objects are Object<T>. 106 + unsafe { crate::container_of!(obj, Object<T>, obj).cast_mut() } 107 + } 108 + } 109 + 110 + /// Base operations shared by all GEM object classes 111 + pub trait BaseObject 112 + where 113 + Self: crate::types::AlwaysRefCounted + IntoGEMObject, 114 + { 115 + /// Returns the size of the object in bytes. 116 + fn size(&self) -> usize { 117 + // SAFETY: `self.into_gem_obj()` is guaranteed to be a pointer to a valid `struct 118 + // drm_gem_object`. 119 + unsafe { (*self.into_gem_obj().get()).size } 120 + } 121 + 122 + /// Creates a new handle for the object associated with a given `File` 123 + /// (or returns an existing one). 124 + fn create_handle( 125 + &self, 126 + file: &drm::File<<<Self as IntoGEMObject>::Driver as drm::Driver>::File>, 127 + ) -> Result<u32> { 128 + let mut handle: u32 = 0; 129 + // SAFETY: The arguments are all valid per the type invariants. 130 + to_result(unsafe { 131 + bindings::drm_gem_handle_create( 132 + file.as_raw().cast(), 133 + self.into_gem_obj().get(), 134 + &mut handle, 135 + ) 136 + })?; 137 + Ok(handle) 138 + } 139 + 140 + /// Looks up an object by its handle for a given `File`. 141 + fn lookup_handle( 142 + file: &drm::File<<<Self as IntoGEMObject>::Driver as drm::Driver>::File>, 143 + handle: u32, 144 + ) -> Result<ARef<Self>> { 145 + // SAFETY: The arguments are all valid per the type invariants. 146 + let ptr = unsafe { bindings::drm_gem_object_lookup(file.as_raw().cast(), handle) }; 147 + let ptr = <Self as IntoGEMObject>::from_gem_obj(ptr); 148 + let ptr = NonNull::new(ptr).ok_or(ENOENT)?; 149 + 150 + // SAFETY: We take ownership of the reference of `drm_gem_object_lookup()`. 151 + Ok(unsafe { ARef::from_raw(ptr) }) 152 + } 153 + 154 + /// Creates an mmap offset to map the object from userspace. 155 + fn create_mmap_offset(&self) -> Result<u64> { 156 + // SAFETY: The arguments are valid per the type invariant. 157 + to_result(unsafe { bindings::drm_gem_create_mmap_offset(self.into_gem_obj().get()) })?; 158 + 159 + // SAFETY: The arguments are valid per the type invariant. 160 + Ok(unsafe { 161 + bindings::drm_vma_node_offset_addr(ptr::addr_of_mut!( 162 + (*self.into_gem_obj().get()).vma_node 163 + )) 164 + }) 165 + } 166 + } 167 + 168 + impl<T> BaseObject for T where Self: crate::types::AlwaysRefCounted + IntoGEMObject {} 169 + 170 + /// A base GEM object. 171 + /// 172 + /// Invariants 173 + /// 174 + /// - `self.obj` is a valid instance of a `struct drm_gem_object`. 175 + /// - `self.dev` is always a valid pointer to a `struct drm_device`. 176 + #[repr(C)] 177 + #[pin_data] 178 + pub struct Object<T: DriverObject + Send + Sync> { 179 + obj: Opaque<bindings::drm_gem_object>, 180 + dev: *const drm::Device<T::Driver>, 181 + #[pin] 182 + data: T, 183 + } 184 + 185 + impl<T: DriverObject> Object<T> { 186 + /// The size of this object's structure. 187 + pub const SIZE: usize = mem::size_of::<Self>(); 188 + 189 + const OBJECT_FUNCS: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs { 190 + free: Some(Self::free_callback), 191 + open: Some(open_callback::<T, Object<T>>), 192 + close: Some(close_callback::<T, Object<T>>), 193 + print_info: None, 194 + export: None, 195 + pin: None, 196 + unpin: None, 197 + get_sg_table: None, 198 + vmap: None, 199 + vunmap: None, 200 + mmap: None, 201 + status: None, 202 + vm_ops: core::ptr::null_mut(), 203 + evict: None, 204 + rss: None, 205 + }; 206 + 207 + /// Create a new GEM object. 208 + pub fn new(dev: &drm::Device<T::Driver>, size: usize) -> Result<ARef<Self>> { 209 + let obj: Pin<KBox<Self>> = KBox::pin_init( 210 + try_pin_init!(Self { 211 + obj: Opaque::new(bindings::drm_gem_object::default()), 212 + data <- T::new(dev, size), 213 + // INVARIANT: The drm subsystem guarantees that the `struct drm_device` will live 214 + // as long as the GEM object lives. 215 + dev, 216 + }), 217 + GFP_KERNEL, 218 + )?; 219 + 220 + // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above. 221 + unsafe { (*obj.as_raw()).funcs = &Self::OBJECT_FUNCS }; 222 + 223 + // SAFETY: The arguments are all valid per the type invariants. 224 + to_result(unsafe { bindings::drm_gem_object_init(dev.as_raw(), obj.obj.get(), size) })?; 225 + 226 + // SAFETY: We never move out of `Self`. 227 + let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(obj) }); 228 + 229 + // SAFETY: `ptr` comes from `KBox::into_raw` and hence can't be NULL. 230 + let ptr = unsafe { NonNull::new_unchecked(ptr) }; 231 + 232 + // SAFETY: We take over the initial reference count from `drm_gem_object_init()`. 233 + Ok(unsafe { ARef::from_raw(ptr) }) 234 + } 235 + 236 + /// Returns the `Device` that owns this GEM object. 237 + pub fn dev(&self) -> &drm::Device<T::Driver> { 238 + // SAFETY: The DRM subsystem guarantees that the `struct drm_device` will live as long as 239 + // the GEM object lives, hence the pointer must be valid. 240 + unsafe { &*self.dev } 241 + } 242 + 243 + fn as_raw(&self) -> *mut bindings::drm_gem_object { 244 + self.obj.get() 245 + } 246 + 247 + extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { 248 + // SAFETY: All of our objects are of type `Object<T>`. 249 + let this = unsafe { crate::container_of!(obj, Self, obj) }.cast_mut(); 250 + 251 + // SAFETY: The C code only ever calls this callback with a valid pointer to a `struct 252 + // drm_gem_object`. 253 + unsafe { bindings::drm_gem_object_release(obj) }; 254 + 255 + // SAFETY: All of our objects are allocated via `KBox`, and we're in the 256 + // free callback which guarantees this object has zero remaining references, 257 + // so we can drop it. 258 + let _ = unsafe { KBox::from_raw(this) }; 259 + } 260 + } 261 + 262 + // SAFETY: Instances of `Object<T>` are always reference-counted. 263 + unsafe impl<T: DriverObject> crate::types::AlwaysRefCounted for Object<T> { 264 + fn inc_ref(&self) { 265 + // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero. 266 + unsafe { bindings::drm_gem_object_get(self.as_raw()) }; 267 + } 268 + 269 + unsafe fn dec_ref(obj: NonNull<Self>) { 270 + // SAFETY: `obj` is a valid pointer to an `Object<T>`. 271 + let obj = unsafe { obj.as_ref() }; 272 + 273 + // SAFETY: The safety requirements guarantee that the refcount is non-zero. 274 + unsafe { bindings::drm_gem_object_put(obj.as_raw()) } 275 + } 276 + } 277 + 278 + impl<T: DriverObject> super::private::Sealed for Object<T> {} 279 + 280 + impl<T: DriverObject> Deref for Object<T> { 281 + type Target = T; 282 + 283 + fn deref(&self) -> &Self::Target { 284 + &self.data 285 + } 286 + } 287 + 288 + impl<T: DriverObject> AllocImpl for Object<T> { 289 + const ALLOC_OPS: AllocOps = AllocOps { 290 + gem_create_object: None, 291 + prime_handle_to_fd: None, 292 + prime_fd_to_handle: None, 293 + gem_prime_import: None, 294 + gem_prime_import_sg_table: None, 295 + dumb_create: None, 296 + dumb_map_offset: None, 297 + }; 298 + } 299 + 300 + pub(super) const fn create_fops() -> bindings::file_operations { 301 + // SAFETY: As by the type invariant, it is safe to initialize `bindings::file_operations` 302 + // zeroed. 303 + let mut fops: bindings::file_operations = unsafe { core::mem::zeroed() }; 304 + 305 + fops.owner = core::ptr::null_mut(); 306 + fops.open = Some(bindings::drm_open); 307 + fops.release = Some(bindings::drm_release); 308 + fops.unlocked_ioctl = Some(bindings::drm_ioctl); 309 + #[cfg(CONFIG_COMPAT)] 310 + { 311 + fops.compat_ioctl = Some(bindings::drm_compat_ioctl); 312 + } 313 + fops.poll = Some(bindings::drm_poll); 314 + fops.read = Some(bindings::drm_read); 315 + fops.llseek = Some(bindings::noop_llseek); 316 + fops.mmap = Some(bindings::drm_gem_mmap); 317 + fops.fop_flags = bindings::FOP_UNSIGNED_OFFSET; 318 + 319 + fops 320 + }
+1
rust/kernel/drm/mod.rs
··· 5 5 pub mod device; 6 6 pub mod driver; 7 7 pub mod file; 8 + pub mod gem; 8 9 pub mod ioctl; 9 10 10 11 pub use self::device::Device;