1 // SPDX-License-Identifier: GPL-2.0 or MIT 2 //! GEM buffer object management for the Tyr driver. 3 //! 4 //! This module provides buffer object (BO) management functionality using 5 //! DRM's GEM subsystem with shmem backing. 6 7 use core::ops::Range; 8 9 use kernel::{ 10 drm::gem::{ 11 self, 12 shmem, // 13 }, 14 prelude::*, 15 sync::{ 16 aref::ARef, 17 Arc, // 18 }, // 19 }; 20 21 use crate::{ 22 driver::{ 23 TyrDrmDevice, 24 TyrDrmDriver, // 25 }, 26 vm::{ 27 Vm, 28 VmMapFlags, // 29 }, 30 }; 31 32 /// Tyr's DriverObject type for GEM objects. 33 #[pin_data] 34 pub(crate) struct BoData { 35 flags: u32, 36 } 37 38 /// Provides a way to pass arguments when creating BoData 39 /// as required by the gem::DriverObject trait. 40 pub(crate) struct BoCreateArgs { 41 flags: u32, 42 } 43 44 impl gem::DriverObject for BoData { 45 type Driver = TyrDrmDriver; 46 type Args = BoCreateArgs; 47 48 fn new(_dev: &TyrDrmDevice, _size: usize, args: BoCreateArgs) -> impl PinInit<Self, Error> { 49 try_pin_init!(Self { flags: args.flags }) 50 } 51 } 52 53 /// Type alias for Tyr GEM buffer objects. 54 pub(crate) type Bo = gem::shmem::Object<BoData>; 55 56 /// Creates a dummy GEM object to serve as the root of a GPUVM. 57 pub(crate) fn new_dummy_object(ddev: &TyrDrmDevice) -> Result<ARef<Bo>> { 58 let bo = Bo::new( 59 ddev, 60 4096, 61 shmem::ObjectConfig { 62 map_wc: true, 63 parent_resv_obj: None, 64 }, 65 BoCreateArgs { flags: 0 }, 66 )?; 67 68 Ok(bo) 69 } 70 71 /// Specifies how to choose a GPU virtual address for a [`KernelBo`]. 72 /// An automatic VA allocation strategy will be added in the future. 73 pub(crate) enum KernelBoVaAlloc { 74 /// Explicit VA address specified by the caller. 75 Explicit(u64), 76 } 77 78 /// A kernel-owned buffer object with automatic GPU virtual address mapping. 79 /// 80 /// This structure represents a buffer object that is created and managed entirely 81 /// by the kernel driver, as opposed to userspace-created GEM objects. It combines 82 /// a GEM object with automatic GPU virtual address (VA) space mapping and cleanup. 83 /// 84 /// When dropped, the buffer is automatically unmapped from the GPU VA space. 85 pub(crate) struct KernelBo<'drm> { 86 /// The underlying GEM buffer object. 87 bo: ARef<Bo>, 88 /// The GPU VM this buffer is mapped into. 89 vm: Arc<Vm<'drm>>, 90 /// The GPU VA range occupied by this buffer. 91 va_range: Range<u64>, 92 } 93 94 impl<'drm> KernelBo<'drm> { 95 /// Creates a new kernel-owned buffer object and maps it into GPU VA space. 96 /// 97 /// This function allocates a new shmem-backed GEM object and immediately maps 98 /// it into the specified GPU virtual memory space. The mapping is automatically 99 /// cleaned up when the [`KernelBo`] is dropped. 100 pub(crate) fn new( 101 ddev: &TyrDrmDevice, 102 vm: Arc<Vm<'drm>>, 103 size: u64, 104 va_alloc: KernelBoVaAlloc, 105 flags: VmMapFlags, 106 ) -> Result<Self> { 107 if size == 0 { 108 dev_err!(vm.dev(), "Cannot create KernelBo with size 0"); 109 return Err(EINVAL); 110 } 111 112 let KernelBoVaAlloc::Explicit(va) = va_alloc; 113 114 let bo_size = usize::try_from(size).map_err(|_| EOVERFLOW)?; 115 let va_end = va.checked_add(size).ok_or(EINVAL)?; 116 117 let bo = Bo::new( 118 ddev, 119 bo_size, 120 shmem::ObjectConfig { 121 map_wc: true, 122 parent_resv_obj: None, 123 }, 124 BoCreateArgs { flags: 0 }, 125 )?; 126 127 vm.map_bo_range(&bo, 0, size, va, flags)?; 128 129 Ok(KernelBo { 130 bo, 131 vm, 132 va_range: va..va_end, 133 }) 134 } 135 136 pub(crate) fn bo(&self) -> &Bo { 137 &self.bo 138 } 139 } 140 141 impl Drop for KernelBo<'_> { 142 fn drop(&mut self) { 143 let va = self.va_range.start; 144 let size = self.va_range.end - self.va_range.start; 145 146 if let Err(e) = self.vm.unmap_range(va, size) { 147 // If unmap_range fails, it is still safe to drop the 148 // KernelBo and its ARef to the GEM buffer object because 149 // GPUVM also holds a reference to the GEM buffer object. 150 // The physical pages won't be freed or reallocated. 151 dev_err!( 152 self.vm.dev(), 153 "Failed to unmap KernelBo range {:#x}..{:#x}: {:?}", 154 self.va_range.start, 155 self.va_range.end, 156 e 157 ); 158 } 159 } 160 } 161