Commit | Line | Data |
---|---|---|
8afa13a0 ZR |
1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ |
2 | /* | |
09881d29 | 3 | * Copyright 2021-2023 VMware, Inc. |
8afa13a0 ZR |
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 | ||
09881d29 | 27 | #include "vmwgfx_bo.h" |
8afa13a0 ZR |
28 | #include "vmwgfx_drv.h" |
29 | ||
30 | #include "drm/drm_prime.h" | |
31 | #include "drm/drm_gem_ttm_helper.h" | |
32 | ||
8afa13a0 ZR |
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); | |
668b2066 | 36 | if (bo) |
8afa13a0 | 37 | ttm_bo_put(bo); |
8afa13a0 ZR |
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); | |
09881d29 | 54 | struct vmw_bo *vbo = to_vmw_bo(obj); |
8afa13a0 ZR |
55 | int ret; |
56 | ||
57 | ret = ttm_bo_reserve(bo, false, false, NULL); | |
58 | if (unlikely(ret != 0)) | |
59 | goto err; | |
60 | ||
61 | vmw_bo_pin_reserved(vbo, 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, true); | |
73 | } | |
74 | ||
75 | static void vmw_gem_object_unpin(struct drm_gem_object *obj) | |
76 | { | |
77 | vmw_gem_pin_private(obj, 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(obj->dev, vmw_tt->dma_ttm.pages, vmw_tt->dma_ttm.num_pages); | |
90 | } | |
91 | ||
9da2957f ZR |
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 | }; | |
8afa13a0 ZR |
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, | |
9da2957f | 111 | .vm_ops = &vmw_vm_ops, |
8afa13a0 ZR |
112 | }; |
113 | ||
91398b41 ZR |
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(vmw, params, 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 | ||
8afa13a0 ZR |
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, | |
09881d29 | 132 | struct vmw_bo **p_vbo) |
8afa13a0 ZR |
133 | { |
134 | int ret; | |
668b2066 ZR |
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 | }; | |
8afa13a0 | 142 | |
91398b41 | 143 | ret = vmw_gem_object_create(dev_priv, ¶ms, p_vbo); |
8afa13a0 ZR |
144 | if (ret != 0) |
145 | goto out_no_bo; | |
146 | ||
668b2066 | 147 | ret = drm_gem_handle_create(filp, &(*p_vbo)->tbo.base, handle); |
8afa13a0 ZR |
148 | out_no_bo: |
149 | return ret; | |
150 | } | |
151 | ||
152 | ||
153 | int vmw_gem_object_create_ioctl(struct drm_device *dev, void *data, | |
154 | struct drm_file *filp) | |
155 | { | |
156 | struct vmw_private *dev_priv = vmw_priv(dev); | |
157 | union drm_vmw_alloc_dmabuf_arg *arg = | |
158 | (union drm_vmw_alloc_dmabuf_arg *)data; | |
159 | struct drm_vmw_alloc_dmabuf_req *req = &arg->req; | |
160 | struct drm_vmw_dmabuf_rep *rep = &arg->rep; | |
09881d29 | 161 | struct vmw_bo *vbo; |
8afa13a0 ZR |
162 | uint32_t handle; |
163 | int ret; | |
164 | ||
165 | ret = vmw_gem_object_create_with_handle(dev_priv, filp, | |
166 | req->size, &handle, &vbo); | |
167 | if (ret) | |
168 | goto out_no_bo; | |
169 | ||
170 | rep->handle = handle; | |
668b2066 | 171 | rep->map_handle = drm_vma_node_offset_addr(&vbo->tbo.base.vma_node); |
8afa13a0 ZR |
172 | rep->cur_gmr_id = handle; |
173 | rep->cur_gmr_offset = 0; | |
9ef8d83e ZR |
174 | /* drop reference from allocate - handle holds it now */ |
175 | drm_gem_object_put(&vbo->tbo.base); | |
8afa13a0 ZR |
176 | out_no_bo: |
177 | return ret; | |
178 | } | |
179 | ||
180 | #if defined(CONFIG_DEBUG_FS) | |
181 | ||
09881d29 | 182 | static void vmw_bo_print_info(int id, struct vmw_bo *bo, struct seq_file *m) |
8afa13a0 ZR |
183 | { |
184 | const char *placement; | |
185 | const char *type; | |
186 | ||
668b2066 | 187 | switch (bo->tbo.resource->mem_type) { |
8afa13a0 ZR |
188 | case TTM_PL_SYSTEM: |
189 | placement = " CPU"; | |
190 | break; | |
191 | case VMW_PL_GMR: | |
192 | placement = " GMR"; | |
193 | break; | |
194 | case VMW_PL_MOB: | |
195 | placement = " MOB"; | |
196 | break; | |
197 | case VMW_PL_SYSTEM: | |
198 | placement = "VCPU"; | |
199 | break; | |
200 | case TTM_PL_VRAM: | |
201 | placement = "VRAM"; | |
202 | break; | |
203 | default: | |
204 | placement = "None"; | |
205 | break; | |
206 | } | |
207 | ||
668b2066 | 208 | switch (bo->tbo.type) { |
8afa13a0 ZR |
209 | case ttm_bo_type_device: |
210 | type = "device"; | |
211 | break; | |
212 | case ttm_bo_type_kernel: | |
213 | type = "kernel"; | |
214 | break; | |
215 | case ttm_bo_type_sg: | |
216 | type = "sg "; | |
217 | break; | |
218 | default: | |
219 | type = "none "; | |
220 | break; | |
221 | } | |
222 | ||
72345114 | 223 | seq_printf(m, "\t\t0x%08x: %12zu bytes %s, type = %s", |
668b2066 | 224 | id, bo->tbo.base.size, placement, type); |
8afa13a0 | 225 | seq_printf(m, ", priority = %u, pin_count = %u, GEM refs = %d, TTM refs = %d", |
668b2066 ZR |
226 | bo->tbo.priority, |
227 | bo->tbo.pin_count, | |
228 | kref_read(&bo->tbo.base.refcount), | |
229 | kref_read(&bo->tbo.kref)); | |
8afa13a0 ZR |
230 | seq_puts(m, "\n"); |
231 | } | |
232 | ||
233 | static int vmw_debugfs_gem_info_show(struct seq_file *m, void *unused) | |
234 | { | |
235 | struct vmw_private *vdev = (struct vmw_private *)m->private; | |
236 | struct drm_device *dev = &vdev->drm; | |
237 | struct drm_file *file; | |
238 | int r; | |
239 | ||
240 | r = mutex_lock_interruptible(&dev->filelist_mutex); | |
241 | if (r) | |
242 | return r; | |
243 | ||
244 | list_for_each_entry(file, &dev->filelist, lhead) { | |
245 | struct task_struct *task; | |
246 | struct drm_gem_object *gobj; | |
247 | int id; | |
248 | ||
249 | /* | |
250 | * Although we have a valid reference on file->pid, that does | |
251 | * not guarantee that the task_struct who called get_pid() is | |
252 | * still alive (e.g. get_pid(current) => fork() => exit()). | |
253 | * Therefore, we need to protect this ->comm access using RCU. | |
254 | */ | |
255 | rcu_read_lock(); | |
4230cea8 | 256 | task = pid_task(file->pid, PIDTYPE_TGID); |
8afa13a0 ZR |
257 | seq_printf(m, "pid %8d command %s:\n", pid_nr(file->pid), |
258 | task ? task->comm : "<unknown>"); | |
259 | rcu_read_unlock(); | |
260 | ||
261 | spin_lock(&file->table_lock); | |
262 | idr_for_each_entry(&file->object_idr, gobj, id) { | |
09881d29 | 263 | struct vmw_bo *bo = to_vmw_bo(gobj); |
8afa13a0 ZR |
264 | |
265 | vmw_bo_print_info(id, bo, m); | |
266 | } | |
267 | spin_unlock(&file->table_lock); | |
268 | } | |
269 | ||
270 | mutex_unlock(&dev->filelist_mutex); | |
271 | return 0; | |
272 | } | |
273 | ||
274 | DEFINE_SHOW_ATTRIBUTE(vmw_debugfs_gem_info); | |
275 | ||
276 | #endif | |
277 | ||
278 | void vmw_debugfs_gem_init(struct vmw_private *vdev) | |
279 | { | |
280 | #if defined(CONFIG_DEBUG_FS) | |
281 | struct drm_minor *minor = vdev->drm.primary; | |
282 | struct dentry *root = minor->debugfs_root; | |
283 | ||
284 | debugfs_create_file("vmwgfx_gem_info", 0444, root, vdev, | |
285 | &vmw_debugfs_gem_info_fops); | |
286 | #endif | |
287 | } |