1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ |
2 | /* |
3 | * Copyright 2021-2023 VMware, Inc. |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person |
6 | * obtaining a copy of this software and associated documentation |
7 | * files (the "Software"), to deal in the Software without |
8 | * restriction, including without limitation the rights to use, copy, |
9 | * modify, merge, publish, distribute, sublicense, and/or sell copies |
10 | * of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be |
14 | * included in all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
20 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
21 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | * SOFTWARE. |
24 | * |
25 | */ |
26 | |
27 | #include "vmwgfx_bo.h" |
28 | #include "vmwgfx_drv.h" |
29 | |
30 | #include "drm/drm_prime.h" |
31 | #include "drm/drm_gem_ttm_helper.h" |
32 | |
33 | static void vmw_gem_object_free(struct drm_gem_object *gobj) |
34 | { |
35 | struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(gobj); |
36 | if (bo) |
37 | ttm_bo_put(bo); |
38 | } |
39 | |
40 | static int vmw_gem_object_open(struct drm_gem_object *obj, |
41 | struct drm_file *file_priv) |
42 | { |
43 | return 0; |
44 | } |
45 | |
46 | static void vmw_gem_object_close(struct drm_gem_object *obj, |
47 | struct drm_file *file_priv) |
48 | { |
49 | } |
50 | |
51 | static int vmw_gem_pin_private(struct drm_gem_object *obj, bool do_pin) |
52 | { |
53 | struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(obj); |
54 | struct vmw_bo *vbo = to_vmw_bo(gobj: obj); |
55 | int ret; |
56 | |
57 | ret = ttm_bo_reserve(bo, interruptible: false, no_wait: false, NULL); |
58 | if (unlikely(ret != 0)) |
59 | goto err; |
60 | |
61 | vmw_bo_pin_reserved(bo: vbo, pin: do_pin); |
62 | |
63 | ttm_bo_unreserve(bo); |
64 | |
65 | err: |
66 | return ret; |
67 | } |
68 | |
69 | |
70 | static int vmw_gem_object_pin(struct drm_gem_object *obj) |
71 | { |
72 | return vmw_gem_pin_private(obj, do_pin: true); |
73 | } |
74 | |
75 | static void vmw_gem_object_unpin(struct drm_gem_object *obj) |
76 | { |
77 | vmw_gem_pin_private(obj, do_pin: false); |
78 | } |
79 | |
80 | static struct sg_table *vmw_gem_object_get_sg_table(struct drm_gem_object *obj) |
81 | { |
82 | struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(obj); |
83 | struct vmw_ttm_tt *vmw_tt = |
84 | container_of(bo->ttm, struct vmw_ttm_tt, dma_ttm); |
85 | |
86 | if (vmw_tt->vsgt.sgt) |
87 | return vmw_tt->vsgt.sgt; |
88 | |
89 | return drm_prime_pages_to_sg(dev: obj->dev, pages: vmw_tt->dma_ttm.pages, nr_pages: vmw_tt->dma_ttm.num_pages); |
90 | } |
91 | |
92 | static const struct vm_operations_struct vmw_vm_ops = { |
93 | .pfn_mkwrite = vmw_bo_vm_mkwrite, |
94 | .page_mkwrite = vmw_bo_vm_mkwrite, |
95 | .fault = vmw_bo_vm_fault, |
96 | .open = ttm_bo_vm_open, |
97 | .close = ttm_bo_vm_close, |
98 | }; |
99 | |
100 | static const struct drm_gem_object_funcs vmw_gem_object_funcs = { |
101 | .free = vmw_gem_object_free, |
102 | .open = vmw_gem_object_open, |
103 | .close = vmw_gem_object_close, |
104 | .print_info = drm_gem_ttm_print_info, |
105 | .pin = vmw_gem_object_pin, |
106 | .unpin = vmw_gem_object_unpin, |
107 | .get_sg_table = vmw_gem_object_get_sg_table, |
108 | .vmap = drm_gem_ttm_vmap, |
109 | .vunmap = drm_gem_ttm_vunmap, |
110 | .mmap = drm_gem_ttm_mmap, |
111 | .vm_ops = &vmw_vm_ops, |
112 | }; |
113 | |
114 | int vmw_gem_object_create(struct vmw_private *vmw, |
115 | struct vmw_bo_params *params, |
116 | struct vmw_bo **p_vbo) |
117 | { |
118 | int ret = vmw_bo_create(dev_priv: vmw, params, p_bo: p_vbo); |
119 | |
120 | if (ret != 0) |
121 | goto out_no_bo; |
122 | |
123 | (*p_vbo)->tbo.base.funcs = &vmw_gem_object_funcs; |
124 | out_no_bo: |
125 | return ret; |
126 | } |
127 | |
128 | int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv, |
129 | struct drm_file *filp, |
130 | uint32_t size, |
131 | uint32_t *handle, |
132 | struct vmw_bo **p_vbo) |
133 | { |
134 | int ret; |
135 | struct vmw_bo_params params = { |
136 | .domain = (dev_priv->has_mob) ? VMW_BO_DOMAIN_SYS : VMW_BO_DOMAIN_VRAM, |
137 | .busy_domain = VMW_BO_DOMAIN_SYS, |
138 | .bo_type = ttm_bo_type_device, |
139 | .size = size, |
140 | .pin = false |
141 | }; |
142 | |
143 | ret = vmw_gem_object_create(vmw: dev_priv, params: ¶ms, p_vbo); |
144 | if (ret != 0) |
145 | goto out_no_bo; |
146 | |
147 | ret = drm_gem_handle_create(file_priv: filp, obj: &(*p_vbo)->tbo.base, handlep: handle); |
148 | out_no_bo: |
149 | return ret; |
150 | } |
151 | |
152 | struct drm_gem_object *vmw_prime_import_sg_table(struct drm_device *dev, |
153 | struct dma_buf_attachment *attach, |
154 | struct sg_table *table) |
155 | { |
156 | int ret; |
157 | struct vmw_private *dev_priv = vmw_priv(dev); |
158 | struct drm_gem_object *gem = NULL; |
159 | struct vmw_bo *vbo; |
160 | struct vmw_bo_params params = { |
161 | .domain = (dev_priv->has_mob) ? VMW_BO_DOMAIN_SYS : VMW_BO_DOMAIN_VRAM, |
162 | .busy_domain = VMW_BO_DOMAIN_SYS, |
163 | .bo_type = ttm_bo_type_sg, |
164 | .size = attach->dmabuf->size, |
165 | .pin = false, |
166 | .resv = attach->dmabuf->resv, |
167 | .sg = table, |
168 | |
169 | }; |
170 | |
171 | dma_resv_lock(obj: params.resv, NULL); |
172 | |
173 | ret = vmw_bo_create(dev_priv, params: ¶ms, p_bo: &vbo); |
174 | if (ret != 0) |
175 | goto out_no_bo; |
176 | |
177 | vbo->tbo.base.funcs = &vmw_gem_object_funcs; |
178 | |
179 | gem = &vbo->tbo.base; |
180 | out_no_bo: |
181 | dma_resv_unlock(obj: params.resv); |
182 | return gem; |
183 | } |
184 | |
185 | int vmw_gem_object_create_ioctl(struct drm_device *dev, void *data, |
186 | struct drm_file *filp) |
187 | { |
188 | struct vmw_private *dev_priv = vmw_priv(dev); |
189 | union drm_vmw_alloc_dmabuf_arg *arg = |
190 | (union drm_vmw_alloc_dmabuf_arg *)data; |
191 | struct drm_vmw_alloc_dmabuf_req *req = &arg->req; |
192 | struct drm_vmw_dmabuf_rep *rep = &arg->rep; |
193 | struct vmw_bo *vbo; |
194 | uint32_t handle; |
195 | int ret; |
196 | |
197 | ret = vmw_gem_object_create_with_handle(dev_priv, filp, |
198 | size: req->size, handle: &handle, p_vbo: &vbo); |
199 | if (ret) |
200 | goto out_no_bo; |
201 | |
202 | rep->handle = handle; |
203 | rep->map_handle = drm_vma_node_offset_addr(node: &vbo->tbo.base.vma_node); |
204 | rep->cur_gmr_id = handle; |
205 | rep->cur_gmr_offset = 0; |
206 | /* drop reference from allocate - handle holds it now */ |
207 | drm_gem_object_put(obj: &vbo->tbo.base); |
208 | out_no_bo: |
209 | return ret; |
210 | } |
211 | |
212 | #if defined(CONFIG_DEBUG_FS) |
213 | |
214 | static void vmw_bo_print_info(int id, struct vmw_bo *bo, struct seq_file *m) |
215 | { |
216 | const char *placement; |
217 | const char *type; |
218 | |
219 | switch (bo->tbo.resource->mem_type) { |
220 | case TTM_PL_SYSTEM: |
221 | placement = " CPU" ; |
222 | break; |
223 | case VMW_PL_GMR: |
224 | placement = " GMR" ; |
225 | break; |
226 | case VMW_PL_MOB: |
227 | placement = " MOB" ; |
228 | break; |
229 | case VMW_PL_SYSTEM: |
230 | placement = "VCPU" ; |
231 | break; |
232 | case TTM_PL_VRAM: |
233 | placement = "VRAM" ; |
234 | break; |
235 | default: |
236 | placement = "None" ; |
237 | break; |
238 | } |
239 | |
240 | switch (bo->tbo.type) { |
241 | case ttm_bo_type_device: |
242 | type = "device" ; |
243 | break; |
244 | case ttm_bo_type_kernel: |
245 | type = "kernel" ; |
246 | break; |
247 | case ttm_bo_type_sg: |
248 | type = "sg " ; |
249 | break; |
250 | default: |
251 | type = "none " ; |
252 | break; |
253 | } |
254 | |
255 | seq_printf(m, fmt: "\t\t0x%08x: %12zu bytes %s, type = %s" , |
256 | id, bo->tbo.base.size, placement, type); |
257 | seq_printf(m, fmt: ", priority = %u, pin_count = %u, GEM refs = %d, TTM refs = %d" , |
258 | bo->tbo.priority, |
259 | bo->tbo.pin_count, |
260 | kref_read(kref: &bo->tbo.base.refcount), |
261 | kref_read(kref: &bo->tbo.kref)); |
262 | seq_puts(m, s: "\n" ); |
263 | } |
264 | |
265 | static int vmw_debugfs_gem_info_show(struct seq_file *m, void *unused) |
266 | { |
267 | struct vmw_private *vdev = (struct vmw_private *)m->private; |
268 | struct drm_device *dev = &vdev->drm; |
269 | struct drm_file *file; |
270 | int r; |
271 | |
272 | r = mutex_lock_interruptible(&dev->filelist_mutex); |
273 | if (r) |
274 | return r; |
275 | |
276 | list_for_each_entry(file, &dev->filelist, lhead) { |
277 | struct task_struct *task; |
278 | struct drm_gem_object *gobj; |
279 | struct pid *pid; |
280 | int id; |
281 | |
282 | /* |
283 | * Although we have a valid reference on file->pid, that does |
284 | * not guarantee that the task_struct who called get_pid() is |
285 | * still alive (e.g. get_pid(current) => fork() => exit()). |
286 | * Therefore, we need to protect this ->comm access using RCU. |
287 | */ |
288 | rcu_read_lock(); |
289 | pid = rcu_dereference(file->pid); |
290 | task = pid_task(pid, PIDTYPE_TGID); |
291 | seq_printf(m, fmt: "pid %8d command %s:\n" , pid_nr(pid), |
292 | task ? task->comm : "<unknown>" ); |
293 | rcu_read_unlock(); |
294 | |
295 | spin_lock(lock: &file->table_lock); |
296 | idr_for_each_entry(&file->object_idr, gobj, id) { |
297 | struct vmw_bo *bo = to_vmw_bo(gobj); |
298 | |
299 | vmw_bo_print_info(id, bo, m); |
300 | } |
301 | spin_unlock(lock: &file->table_lock); |
302 | } |
303 | |
304 | mutex_unlock(lock: &dev->filelist_mutex); |
305 | return 0; |
306 | } |
307 | |
308 | DEFINE_SHOW_ATTRIBUTE(vmw_debugfs_gem_info); |
309 | |
310 | #endif |
311 | |
312 | void vmw_debugfs_gem_init(struct vmw_private *vdev) |
313 | { |
314 | #if defined(CONFIG_DEBUG_FS) |
315 | struct drm_minor *minor = vdev->drm.primary; |
316 | struct dentry *root = minor->debugfs_root; |
317 | |
318 | debugfs_create_file(name: "vmwgfx_gem_info" , mode: 0444, parent: root, data: vdev, |
319 | fops: &vmw_debugfs_gem_info_fops); |
320 | #endif |
321 | } |
322 | |