|
| 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/linux/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::{device, driver, gem, private::Sealed}, |
| 15 | + error::{from_err_ptr, to_result}, |
| 16 | + prelude::*, |
| 17 | + scatterlist, |
| 18 | + types::{ARef, Opaque}, |
| 19 | +}; |
| 20 | +use core::{ |
| 21 | + ops::{Deref, DerefMut}, |
| 22 | + ptr::NonNull, |
| 23 | +}; |
| 24 | +use gem::{BaseObjectPrivate, DriverObject, IntoGEMObject}; |
| 25 | + |
| 26 | +/// A struct for controlling the creation of shmem-backed GEM objects. |
| 27 | +/// |
| 28 | +/// This is used with [`Object::new()`] to control various properties that can only be set when |
| 29 | +/// initially creating a shmem-backed GEM object. |
| 30 | +#[derive(Default)] |
| 31 | +pub struct ObjectConfig<'a, T: DriverObject> { |
| 32 | + /// Whether to set the write-combine map flag. |
| 33 | + pub map_wc: bool, |
| 34 | + |
| 35 | + /// Reuse the DMA reservation from another GEM object. |
| 36 | + /// |
| 37 | + /// The newly created [`Object`] will hold an owned refcount to `parent_resv_obj` if specified. |
| 38 | + pub parent_resv_obj: Option<&'a Object<T>>, |
| 39 | +} |
| 40 | + |
| 41 | +/// A shmem-backed GEM object. |
| 42 | +/// |
| 43 | +/// # Invariants |
| 44 | +/// |
| 45 | +/// `obj` contains a valid initialized `struct drm_gem_shmem_object` for the lifetime of this |
| 46 | +/// object. |
| 47 | +#[repr(C)] |
| 48 | +#[pin_data] |
| 49 | +pub struct Object<T: DriverObject> { |
| 50 | + #[pin] |
| 51 | + obj: Opaque<bindings::drm_gem_shmem_object>, |
| 52 | + // Parent object that owns this object's DMA reservation object |
| 53 | + parent_resv_obj: Option<ARef<Object<T>>>, |
| 54 | + #[pin] |
| 55 | + inner: T, |
| 56 | +} |
| 57 | + |
| 58 | +super::impl_aref_for_gem_obj!(impl<T> for Object<T> where T: DriverObject); |
| 59 | + |
| 60 | +impl<T: DriverObject> Object<T> { |
| 61 | + /// `drm_gem_object_funcs` vtable suitable for GEM shmem objects. |
| 62 | + const VTABLE: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs { |
| 63 | + free: Some(Self::free_callback), |
| 64 | + open: Some(super::open_callback::<T>), |
| 65 | + close: Some(super::close_callback::<T>), |
| 66 | + print_info: Some(bindings::drm_gem_shmem_object_print_info), |
| 67 | + export: None, |
| 68 | + pin: Some(bindings::drm_gem_shmem_object_pin), |
| 69 | + unpin: Some(bindings::drm_gem_shmem_object_unpin), |
| 70 | + get_sg_table: Some(bindings::drm_gem_shmem_object_get_sg_table), |
| 71 | + vmap: Some(bindings::drm_gem_shmem_object_vmap), |
| 72 | + vunmap: Some(bindings::drm_gem_shmem_object_vunmap), |
| 73 | + mmap: Some(bindings::drm_gem_shmem_object_mmap), |
| 74 | + status: None, |
| 75 | + rss: None, |
| 76 | + // SAFETY: `drm_gem_shmem_vm_ops` is static const on the C side, so immutable references are |
| 77 | + // safe here and such references shall be valid forever |
| 78 | + vm_ops: unsafe { &bindings::drm_gem_shmem_vm_ops }, |
| 79 | + evict: None, |
| 80 | + }; |
| 81 | + |
| 82 | + /// Return a raw pointer to the embedded drm_gem_shmem_object. |
| 83 | + fn as_shmem(&self) -> *mut bindings::drm_gem_shmem_object { |
| 84 | + self.obj.get() |
| 85 | + } |
| 86 | + |
| 87 | + /// Create a new shmem-backed DRM object of the given size. |
| 88 | + /// |
| 89 | + /// Additional config options can be specified using `config`. |
| 90 | + pub fn new( |
| 91 | + dev: &device::Device<T::Driver>, |
| 92 | + size: usize, |
| 93 | + config: ObjectConfig<'_, T>, |
| 94 | + args: T::Args, |
| 95 | + ) -> Result<ARef<Self>> { |
| 96 | + let new: Pin<KBox<Self>> = KBox::try_pin_init( |
| 97 | + try_pin_init!(Self { |
| 98 | + obj <- Opaque::init_zeroed(), |
| 99 | + parent_resv_obj: config.parent_resv_obj.map(|p| p.into()), |
| 100 | + inner <- T::new(dev, size, args), |
| 101 | + }), |
| 102 | + GFP_KERNEL, |
| 103 | + )?; |
| 104 | + |
| 105 | + // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above. |
| 106 | + unsafe { (*new.as_raw()).funcs = &Self::VTABLE }; |
| 107 | + |
| 108 | + // SAFETY: The arguments are all valid via the type invariants. |
| 109 | + to_result(unsafe { bindings::drm_gem_shmem_init(dev.as_raw(), new.as_shmem(), size) })?; |
| 110 | + |
| 111 | + // SAFETY: We never move out of `self`. |
| 112 | + let new = KBox::into_raw(unsafe { Pin::into_inner_unchecked(new) }); |
| 113 | + |
| 114 | + // SAFETY: We're taking over the owned refcount from `drm_gem_shmem_init`. |
| 115 | + let obj = unsafe { ARef::from_raw(NonNull::new_unchecked(new)) }; |
| 116 | + |
| 117 | + // Start filling out values from `config` |
| 118 | + if let Some(parent_resv) = config.parent_resv_obj { |
| 119 | + // SAFETY: We have yet to expose the new gem object outside of this function, so it is |
| 120 | + // safe to modify this field. |
| 121 | + unsafe { (*obj.obj.get()).base.resv = parent_resv.raw_dma_resv() }; |
| 122 | + } |
| 123 | + |
| 124 | + // SAFETY: We have yet to expose this object outside of this function, so we're guaranteed |
| 125 | + // to have exclusive access - thus making this safe to hold a mutable reference to. |
| 126 | + let shmem = unsafe { &mut *obj.as_shmem() }; |
| 127 | + shmem.set_map_wc(config.map_wc); |
| 128 | + |
| 129 | + Ok(obj) |
| 130 | + } |
| 131 | + |
| 132 | + /// Returns the `Device` that owns this GEM object. |
| 133 | + pub fn dev(&self) -> &device::Device<T::Driver> { |
| 134 | + // SAFETY: `dev` will have been initialized in `Self::new()` by `drm_gem_shmem_init()`. |
| 135 | + unsafe { device::Device::from_raw((*self.as_raw()).dev) } |
| 136 | + } |
| 137 | + |
| 138 | + extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { |
| 139 | + // SAFETY: |
| 140 | + // - DRM always passes a valid gem object here |
| 141 | + // - We used drm_gem_shmem_create() in our create_gem_object callback, so we know that |
| 142 | + // `obj` is contained within a drm_gem_shmem_object |
| 143 | + let this = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) }; |
| 144 | + |
| 145 | + // SAFETY: |
| 146 | + // - We're in free_callback - so this function is safe to call. |
| 147 | + // - We won't be using the gem resources on `this` after this call. |
| 148 | + unsafe { bindings::drm_gem_shmem_release(this) }; |
| 149 | + |
| 150 | + // SAFETY: |
| 151 | + // - We verified above that `obj` is valid, which makes `this` valid |
| 152 | + // - This function is set in AllocOps, so we know that `this` is contained within a |
| 153 | + // `Object<T>` |
| 154 | + let this = unsafe { container_of!(Opaque::cast_from(this), Self, obj) }.cast_mut(); |
| 155 | + |
| 156 | + // SAFETY: We're recovering the Kbox<> we created in gem_create_object() |
| 157 | + let _ = unsafe { KBox::from_raw(this) }; |
| 158 | + } |
| 159 | + |
| 160 | + /// Creates (if necessary) and returns an immutable reference to a scatter-gather table of DMA |
| 161 | + /// pages for this object. |
| 162 | + /// |
| 163 | + /// This will pin the object in memory. |
| 164 | + #[inline] |
| 165 | + pub fn sg_table(&self) -> Result<&scatterlist::SGTable> { |
| 166 | + // SAFETY: |
| 167 | + // - drm_gem_shmem_get_pages_sgt is thread-safe. |
| 168 | + // - drm_gem_shmem_get_pages_sgt returns either a valid pointer to a scatterlist, or an |
| 169 | + // error pointer. |
| 170 | + let sgt = from_err_ptr(unsafe { bindings::drm_gem_shmem_get_pages_sgt(self.as_shmem()) })?; |
| 171 | + |
| 172 | + // SAFETY: We checked above that `sgt` is not an error pointer, so it must be a valid |
| 173 | + // pointer to a scatterlist |
| 174 | + Ok(unsafe { scatterlist::SGTable::from_raw(sgt) }) |
| 175 | + } |
| 176 | +} |
| 177 | + |
| 178 | +impl<T: DriverObject> Deref for Object<T> { |
| 179 | + type Target = T; |
| 180 | + |
| 181 | + fn deref(&self) -> &Self::Target { |
| 182 | + &self.inner |
| 183 | + } |
| 184 | +} |
| 185 | + |
| 186 | +impl<T: DriverObject> DerefMut for Object<T> { |
| 187 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 188 | + &mut self.inner |
| 189 | + } |
| 190 | +} |
| 191 | + |
| 192 | +impl<T: DriverObject> Sealed for Object<T> {} |
| 193 | + |
| 194 | +impl<T: DriverObject> gem::IntoGEMObject for Object<T> { |
| 195 | + fn as_raw(&self) -> *mut bindings::drm_gem_object { |
| 196 | + // SAFETY: |
| 197 | + // - Our immutable reference is proof that this is safe to dereference. |
| 198 | + // - `obj` is always a valid drm_gem_shmem_object via our type invariants. |
| 199 | + unsafe { &raw mut (*self.obj.get()).base } |
| 200 | + } |
| 201 | + |
| 202 | + unsafe fn from_raw<'a>(obj: *mut bindings::drm_gem_object) -> &'a Object<T> { |
| 203 | + // SAFETY: The safety contract of from_gem_obj() guarantees that `obj` is contained within |
| 204 | + // `Self` |
| 205 | + unsafe { |
| 206 | + let obj = Opaque::cast_from(container_of!(obj, bindings::drm_gem_shmem_object, base)); |
| 207 | + |
| 208 | + &*container_of!(obj, Object<T>, obj) |
| 209 | + } |
| 210 | + } |
| 211 | +} |
| 212 | + |
| 213 | +impl<T: DriverObject> driver::AllocImpl for Object<T> { |
| 214 | + type Driver = T::Driver; |
| 215 | + |
| 216 | + const ALLOC_OPS: driver::AllocOps = driver::AllocOps { |
| 217 | + gem_create_object: None, |
| 218 | + prime_handle_to_fd: None, |
| 219 | + prime_fd_to_handle: None, |
| 220 | + gem_prime_import: None, |
| 221 | + gem_prime_import_sg_table: Some(bindings::drm_gem_shmem_prime_import_sg_table), |
| 222 | + dumb_create: Some(bindings::drm_gem_shmem_dumb_create), |
| 223 | + dumb_map_offset: None, |
| 224 | + }; |
| 225 | +} |
0 commit comments