xref: /linux/drivers/gpu/drm/tyr/gem.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
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