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: shmem: Add DRM shmem helper abstraction

The DRM shmem helper includes common code useful for drivers which
allocate GEM objects as anonymous shmem. Add a Rust abstraction for
this. Drivers can choose the raw GEM implementation or the shmem layer,
depending on their needs.

Signed-off-by: Asahi Lina <lina@asahilina.net>
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Janne Grunau <j@jananu.net>
Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com>
Link: https://patch.msgid.link/20260316211646.650074-6-lyude@redhat.com
[ * DRM_GEM_SHMEM_HELPER is a tristate; when a module driver selects it,
it becomes =m. The Rust kernel crate and its C helpers are always
built into vmlinux and can't reference symbols from a module,
causing link errors.

Thus, add RUST_DRM_GEM_SHMEM_HELPER bool Kconfig that selects
DRM_GEM_SHMEM_HELPER, forcing it built-in when Rust drivers need it;
use cfg(CONFIG_RUST_DRM_GEM_SHMEM_HELPER) for the shmem module.

* Add cfg_attr(not(CONFIG_RUST_DRM_GEM_SHMEM_HELPER), expect(unused))
on pub(crate) use impl_aref_for_gem_obj and BaseObjectPrivate, so
that unused warnings are suppressed when shmem is not enabled.

* Enable const_refs_to_static (stabilized in 1.83) to prevent build
errors with older compilers.

* Use &raw const for bindings::drm_gem_shmem_vm_ops and add
#[allow(unused_unsafe, reason = "Safe since Rust 1.82.0")].

* Fix incorrect C Header path and minor spelling and formatting
issues.

* Drop shmem::Object::sg_table() as the current implementation is
unsound.

- Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

Asahi Lina and committed by
Danilo Krummrich
80df573a 89b4964c

+299 -2
+7
drivers/gpu/drm/Kconfig
··· 258 258 help 259 259 Choose this if you need the GEM shmem helper functions 260 260 261 + config RUST_DRM_GEM_SHMEM_HELPER 262 + bool 263 + depends on DRM && MMU 264 + select DRM_GEM_SHMEM_HELPER 265 + help 266 + Choose this if you need the GEM shmem helper functions In Rust 267 + 261 268 config DRM_SUBALLOC_HELPER 262 269 tristate 263 270 depends on DRM
+2
rust/bindings/bindings_helper.h
··· 34 34 #include <drm/drm_drv.h> 35 35 #include <drm/drm_file.h> 36 36 #include <drm/drm_gem.h> 37 + #include <drm/drm_gem_shmem_helper.h> 37 38 #include <drm/drm_ioctl.h> 38 39 #include <kunit/test.h> 39 40 #include <linux/auxiliary_bus.h> ··· 64 63 #include <linux/interrupt.h> 65 64 #include <linux/io-pgtable.h> 66 65 #include <linux/ioport.h> 66 + #include <linux/iosys-map.h> 67 67 #include <linux/jiffies.h> 68 68 #include <linux/jump_label.h> 69 69 #include <linux/mdio.h>
+55 -1
rust/helpers/drm.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 3 #include <drm/drm_gem.h> 4 + #include <drm/drm_gem_shmem_helper.h> 4 5 #include <drm/drm_vma_manager.h> 5 6 6 7 #ifdef CONFIG_DRM ··· 22 21 return drm_vma_node_offset_addr(node); 23 22 } 24 23 25 - #endif 24 + #ifdef CONFIG_DRM_GEM_SHMEM_HELPER 25 + __rust_helper void 26 + rust_helper_drm_gem_shmem_object_free(struct drm_gem_object *obj) 27 + { 28 + return drm_gem_shmem_object_free(obj); 29 + } 30 + 31 + __rust_helper void 32 + rust_helper_drm_gem_shmem_object_print_info(struct drm_printer *p, unsigned int indent, 33 + const struct drm_gem_object *obj) 34 + { 35 + drm_gem_shmem_object_print_info(p, indent, obj); 36 + } 37 + 38 + __rust_helper int 39 + rust_helper_drm_gem_shmem_object_pin(struct drm_gem_object *obj) 40 + { 41 + return drm_gem_shmem_object_pin(obj); 42 + } 43 + 44 + __rust_helper void 45 + rust_helper_drm_gem_shmem_object_unpin(struct drm_gem_object *obj) 46 + { 47 + drm_gem_shmem_object_unpin(obj); 48 + } 49 + 50 + __rust_helper struct sg_table * 51 + rust_helper_drm_gem_shmem_object_get_sg_table(struct drm_gem_object *obj) 52 + { 53 + return drm_gem_shmem_object_get_sg_table(obj); 54 + } 55 + 56 + __rust_helper int 57 + rust_helper_drm_gem_shmem_object_vmap(struct drm_gem_object *obj, 58 + struct iosys_map *map) 59 + { 60 + return drm_gem_shmem_object_vmap(obj, map); 61 + } 62 + 63 + __rust_helper void 64 + rust_helper_drm_gem_shmem_object_vunmap(struct drm_gem_object *obj, 65 + struct iosys_map *map) 66 + { 67 + drm_gem_shmem_object_vunmap(obj, map); 68 + } 69 + 70 + __rust_helper int 71 + rust_helper_drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) 72 + { 73 + return drm_gem_shmem_object_mmap(obj, vma); 74 + } 75 + 76 + #endif /* CONFIG_DRM_GEM_SHMEM_HELPER */ 77 + #endif /* CONFIG_DRM */
+6 -1
rust/kernel/drm/gem/mod.rs
··· 26 26 ptr::NonNull, // 27 27 }; 28 28 29 + #[cfg(CONFIG_RUST_DRM_GEM_SHMEM_HELPER)] 30 + pub mod shmem; 31 + 29 32 /// A macro for implementing [`AlwaysRefCounted`] for any GEM object type. 30 33 /// 31 34 /// Since all GEM objects use the same refcounting scheme. ··· 63 60 } 64 61 }; 65 62 } 63 + #[cfg_attr(not(CONFIG_RUST_DRM_GEM_SHMEM_HELPER), allow(unused))] 64 + pub(crate) use impl_aref_for_gem_obj; 66 65 67 66 /// A type alias for retrieving a [`Driver`]s [`DriverFile`] implementation from its 68 67 /// [`DriverObject`] implementation. ··· 221 216 impl<T: IntoGEMObject> BaseObject for T {} 222 217 223 218 /// Crate-private base operations shared by all GEM object classes. 224 - #[expect(unused)] 219 + #[cfg_attr(not(CONFIG_RUST_DRM_GEM_SHMEM_HELPER), expect(unused))] 225 220 pub(crate) trait BaseObjectPrivate: IntoGEMObject { 226 221 /// Return a pointer to this object's dma_resv. 227 222 fn raw_dma_resv(&self) -> *mut bindings::dma_resv {
+228
rust/kernel/drm/gem/shmem.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + //! DRM GEM shmem helper objects 4 + //! 5 + //! C header: [`include/linux/drm/drm_gem_shmem_helper.h`](srctree/include/drm/drm_gem_shmem_helper.h) 6 + 7 + // TODO: 8 + // - There are a number of spots here that manually acquire/release the DMA reservation lock using 9 + // dma_resv_(un)lock(). In the future we should add support for ww mutex, expose a method to 10 + // acquire a reference to the WwMutex, and then use that directly instead of the C functions here. 11 + 12 + use crate::{ 13 + container_of, 14 + drm::{ 15 + device, 16 + driver, 17 + gem, 18 + private::Sealed, // 19 + }, 20 + error::to_result, 21 + prelude::*, 22 + types::{ 23 + ARef, 24 + Opaque, // 25 + }, // 26 + }; 27 + use core::{ 28 + ops::{ 29 + Deref, 30 + DerefMut, // 31 + }, 32 + ptr::NonNull, 33 + }; 34 + use gem::{ 35 + BaseObjectPrivate, 36 + DriverObject, 37 + IntoGEMObject, // 38 + }; 39 + 40 + /// A struct for controlling the creation of shmem-backed GEM objects. 41 + /// 42 + /// This is used with [`Object::new()`] to control various properties that can only be set when 43 + /// initially creating a shmem-backed GEM object. 44 + #[derive(Default)] 45 + pub struct ObjectConfig<'a, T: DriverObject> { 46 + /// Whether to set the write-combine map flag. 47 + pub map_wc: bool, 48 + 49 + /// Reuse the DMA reservation from another GEM object. 50 + /// 51 + /// The newly created [`Object`] will hold an owned refcount to `parent_resv_obj` if specified. 52 + pub parent_resv_obj: Option<&'a Object<T>>, 53 + } 54 + 55 + /// A shmem-backed GEM object. 56 + /// 57 + /// # Invariants 58 + /// 59 + /// `obj` contains a valid initialized `struct drm_gem_shmem_object` for the lifetime of this 60 + /// object. 61 + #[repr(C)] 62 + #[pin_data] 63 + pub struct Object<T: DriverObject> { 64 + #[pin] 65 + obj: Opaque<bindings::drm_gem_shmem_object>, 66 + /// Parent object that owns this object's DMA reservation object. 67 + parent_resv_obj: Option<ARef<Object<T>>>, 68 + #[pin] 69 + inner: T, 70 + } 71 + 72 + super::impl_aref_for_gem_obj!(impl<T> for Object<T> where T: DriverObject); 73 + 74 + // SAFETY: All GEM objects are thread-safe. 75 + unsafe impl<T: DriverObject> Send for Object<T> {} 76 + 77 + // SAFETY: All GEM objects are thread-safe. 78 + unsafe impl<T: DriverObject> Sync for Object<T> {} 79 + 80 + impl<T: DriverObject> Object<T> { 81 + /// `drm_gem_object_funcs` vtable suitable for GEM shmem objects. 82 + const VTABLE: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs { 83 + free: Some(Self::free_callback), 84 + open: Some(super::open_callback::<T>), 85 + close: Some(super::close_callback::<T>), 86 + print_info: Some(bindings::drm_gem_shmem_object_print_info), 87 + export: None, 88 + pin: Some(bindings::drm_gem_shmem_object_pin), 89 + unpin: Some(bindings::drm_gem_shmem_object_unpin), 90 + get_sg_table: Some(bindings::drm_gem_shmem_object_get_sg_table), 91 + vmap: Some(bindings::drm_gem_shmem_object_vmap), 92 + vunmap: Some(bindings::drm_gem_shmem_object_vunmap), 93 + mmap: Some(bindings::drm_gem_shmem_object_mmap), 94 + status: None, 95 + rss: None, 96 + #[allow(unused_unsafe, reason = "Safe since Rust 1.82.0")] 97 + // SAFETY: `drm_gem_shmem_vm_ops` is a valid, static const on the C side. 98 + vm_ops: unsafe { &raw const bindings::drm_gem_shmem_vm_ops }, 99 + evict: None, 100 + }; 101 + 102 + /// Return a raw pointer to the embedded drm_gem_shmem_object. 103 + fn as_raw_shmem(&self) -> *mut bindings::drm_gem_shmem_object { 104 + self.obj.get() 105 + } 106 + 107 + /// Create a new shmem-backed DRM object of the given size. 108 + /// 109 + /// Additional config options can be specified using `config`. 110 + pub fn new( 111 + dev: &device::Device<T::Driver>, 112 + size: usize, 113 + config: ObjectConfig<'_, T>, 114 + args: T::Args, 115 + ) -> Result<ARef<Self>> { 116 + let new: Pin<KBox<Self>> = KBox::try_pin_init( 117 + try_pin_init!(Self { 118 + obj <- Opaque::init_zeroed(), 119 + parent_resv_obj: config.parent_resv_obj.map(|p| p.into()), 120 + inner <- T::new(dev, size, args), 121 + }), 122 + GFP_KERNEL, 123 + )?; 124 + 125 + // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above. 126 + unsafe { (*new.as_raw()).funcs = &Self::VTABLE }; 127 + 128 + // SAFETY: The arguments are all valid via the type invariants. 129 + to_result(unsafe { bindings::drm_gem_shmem_init(dev.as_raw(), new.as_raw_shmem(), size) })?; 130 + 131 + // SAFETY: We never move out of `self`. 132 + let new = KBox::into_raw(unsafe { Pin::into_inner_unchecked(new) }); 133 + 134 + // SAFETY: We're taking over the owned refcount from `drm_gem_shmem_init`. 135 + let obj = unsafe { ARef::from_raw(NonNull::new_unchecked(new)) }; 136 + 137 + // Start filling out values from `config` 138 + if let Some(parent_resv) = config.parent_resv_obj { 139 + // SAFETY: We have yet to expose the new gem object outside of this function, so it is 140 + // safe to modify this field. 141 + unsafe { (*obj.obj.get()).base.resv = parent_resv.raw_dma_resv() }; 142 + } 143 + 144 + // SAFETY: We have yet to expose this object outside of this function, so we're guaranteed 145 + // to have exclusive access - thus making this safe to hold a mutable reference to. 146 + let shmem = unsafe { &mut *obj.as_raw_shmem() }; 147 + shmem.set_map_wc(config.map_wc); 148 + 149 + Ok(obj) 150 + } 151 + 152 + /// Returns the `Device` that owns this GEM object. 153 + pub fn dev(&self) -> &device::Device<T::Driver> { 154 + // SAFETY: `dev` will have been initialized in `Self::new()` by `drm_gem_shmem_init()`. 155 + unsafe { device::Device::from_raw((*self.as_raw()).dev) } 156 + } 157 + 158 + extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { 159 + // SAFETY: 160 + // - DRM always passes a valid gem object here 161 + // - We used drm_gem_shmem_create() in our create_gem_object callback, so we know that 162 + // `obj` is contained within a drm_gem_shmem_object 163 + let this = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) }; 164 + 165 + // SAFETY: 166 + // - We're in free_callback - so this function is safe to call. 167 + // - We won't be using the gem resources on `this` after this call. 168 + unsafe { bindings::drm_gem_shmem_release(this) }; 169 + 170 + // SAFETY: 171 + // - We verified above that `obj` is valid, which makes `this` valid 172 + // - This function is set in AllocOps, so we know that `this` is contained within a 173 + // `Object<T>` 174 + let this = unsafe { container_of!(Opaque::cast_from(this), Self, obj) }.cast_mut(); 175 + 176 + // SAFETY: We're recovering the Kbox<> we created in gem_create_object() 177 + let _ = unsafe { KBox::from_raw(this) }; 178 + } 179 + } 180 + 181 + impl<T: DriverObject> Deref for Object<T> { 182 + type Target = T; 183 + 184 + fn deref(&self) -> &Self::Target { 185 + &self.inner 186 + } 187 + } 188 + 189 + impl<T: DriverObject> DerefMut for Object<T> { 190 + fn deref_mut(&mut self) -> &mut Self::Target { 191 + &mut self.inner 192 + } 193 + } 194 + 195 + impl<T: DriverObject> Sealed for Object<T> {} 196 + 197 + impl<T: DriverObject> gem::IntoGEMObject for Object<T> { 198 + fn as_raw(&self) -> *mut bindings::drm_gem_object { 199 + // SAFETY: 200 + // - Our immutable reference is proof that this is safe to dereference. 201 + // - `obj` is always a valid drm_gem_shmem_object via our type invariants. 202 + unsafe { &raw mut (*self.obj.get()).base } 203 + } 204 + 205 + unsafe fn from_raw<'a>(obj: *mut bindings::drm_gem_object) -> &'a Object<T> { 206 + // SAFETY: The safety contract of from_gem_obj() guarantees that `obj` is contained within 207 + // `Self` 208 + unsafe { 209 + let obj = Opaque::cast_from(container_of!(obj, bindings::drm_gem_shmem_object, base)); 210 + 211 + &*container_of!(obj, Object<T>, obj) 212 + } 213 + } 214 + } 215 + 216 + impl<T: DriverObject> driver::AllocImpl for Object<T> { 217 + type Driver = T::Driver; 218 + 219 + const ALLOC_OPS: driver::AllocOps = driver::AllocOps { 220 + gem_create_object: None, 221 + prime_handle_to_fd: None, 222 + prime_fd_to_handle: None, 223 + gem_prime_import: None, 224 + gem_prime_import_sg_table: Some(bindings::drm_gem_shmem_prime_import_sg_table), 225 + dumb_create: Some(bindings::drm_gem_shmem_dumb_create), 226 + dumb_map_offset: None, 227 + }; 228 + }
+1
rust/kernel/lib.rs
··· 38 38 #![feature(const_option)] 39 39 #![feature(const_ptr_write)] 40 40 #![feature(const_refs_to_cell)] 41 + #![feature(const_refs_to_static)] 41 42 // 42 43 // Stable since Rust 1.84.0. 43 44 #![feature(strict_provenance)]