1 | // SPDX-License-Identifier: GPL-2.0 OR MIT |
2 | /************************************************************************** |
3 | * |
4 | * Copyright © 2018-2023 VMware, Inc., Palo Alto, CA., USA |
5 | * All Rights Reserved. |
6 | * |
7 | * Permission is hereby granted, free of charge, to any person obtaining a |
8 | * copy of this software and associated documentation files (the |
9 | * "Software"), to deal in the Software without restriction, including |
10 | * without limitation the rights to use, copy, modify, merge, publish, |
11 | * distribute, sub license, and/or sell copies of the Software, and to |
12 | * permit persons to whom the Software is furnished to do so, subject to |
13 | * the following conditions: |
14 | * |
15 | * The above copyright notice and this permission notice (including the |
16 | * next paragraph) shall be included in all copies or substantial portions |
17 | * of the Software. |
18 | * |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
22 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, |
23 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
24 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
25 | * USE OR OTHER DEALINGS IN THE SOFTWARE. |
26 | * |
27 | **************************************************************************/ |
28 | |
29 | #include "vmwgfx_binding.h" |
30 | #include "vmwgfx_bo.h" |
31 | #include "vmwgfx_drv.h" |
32 | #include "vmwgfx_resource_priv.h" |
33 | |
34 | #include <drm/ttm/ttm_placement.h> |
35 | |
36 | /** |
37 | * struct vmw_dx_streamoutput - Streamoutput resource metadata. |
38 | * @res: Base resource struct. |
39 | * @ctx: Non-refcounted context to which @res belong. |
40 | * @cotable: Refcounted cotable holding this Streamoutput. |
41 | * @cotable_head: List head for cotable-so_res list. |
42 | * @id: User-space provided identifier. |
43 | * @size: User-space provided mob size. |
44 | * @committed: Whether streamoutput is actually created or pending creation. |
45 | */ |
46 | struct vmw_dx_streamoutput { |
47 | struct vmw_resource res; |
48 | struct vmw_resource *ctx; |
49 | struct vmw_resource *cotable; |
50 | struct list_head cotable_head; |
51 | u32 id; |
52 | u32 size; |
53 | bool committed; |
54 | }; |
55 | |
56 | static int vmw_dx_streamoutput_create(struct vmw_resource *res); |
57 | static int vmw_dx_streamoutput_bind(struct vmw_resource *res, |
58 | struct ttm_validate_buffer *val_buf); |
59 | static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback, |
60 | struct ttm_validate_buffer *val_buf); |
61 | static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res, |
62 | enum vmw_cmdbuf_res_state state); |
63 | |
64 | static const struct vmw_res_func vmw_dx_streamoutput_func = { |
65 | .res_type = vmw_res_streamoutput, |
66 | .needs_guest_memory = true, |
67 | .may_evict = false, |
68 | .type_name = "DX streamoutput" , |
69 | .domain = VMW_BO_DOMAIN_MOB, |
70 | .busy_domain = VMW_BO_DOMAIN_MOB, |
71 | .create = vmw_dx_streamoutput_create, |
72 | .destroy = NULL, /* Command buffer managed resource. */ |
73 | .bind = vmw_dx_streamoutput_bind, |
74 | .unbind = vmw_dx_streamoutput_unbind, |
75 | .commit_notify = vmw_dx_streamoutput_commit_notify, |
76 | }; |
77 | |
78 | static inline struct vmw_dx_streamoutput * |
79 | vmw_res_to_dx_streamoutput(struct vmw_resource *res) |
80 | { |
81 | return container_of(res, struct vmw_dx_streamoutput, res); |
82 | } |
83 | |
84 | /** |
85 | * vmw_dx_streamoutput_unscrub - Reattach the MOB to streamoutput. |
86 | * @res: The streamoutput resource. |
87 | * |
88 | * Return: 0 on success, negative error code on failure. |
89 | */ |
90 | static int vmw_dx_streamoutput_unscrub(struct vmw_resource *res) |
91 | { |
92 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
93 | struct vmw_private *dev_priv = res->dev_priv; |
94 | struct { |
95 | SVGA3dCmdHeader ; |
96 | SVGA3dCmdDXBindStreamOutput body; |
97 | } *cmd; |
98 | |
99 | if (!list_empty(head: &so->cotable_head) || !so->committed ) |
100 | return 0; |
101 | |
102 | cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id); |
103 | if (!cmd) |
104 | return -ENOMEM; |
105 | |
106 | cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT; |
107 | cmd->header.size = sizeof(cmd->body); |
108 | cmd->body.soid = so->id; |
109 | cmd->body.mobid = res->guest_memory_bo->tbo.resource->start; |
110 | cmd->body.offsetInBytes = res->guest_memory_offset; |
111 | cmd->body.sizeInBytes = so->size; |
112 | vmw_cmd_commit(dev_priv, bytes: sizeof(*cmd)); |
113 | |
114 | vmw_cotable_add_resource(ctx: so->cotable, head: &so->cotable_head); |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static int vmw_dx_streamoutput_create(struct vmw_resource *res) |
120 | { |
121 | struct vmw_private *dev_priv = res->dev_priv; |
122 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
123 | int ret = 0; |
124 | |
125 | WARN_ON_ONCE(!so->committed); |
126 | |
127 | if (vmw_resource_mob_attached(res)) { |
128 | mutex_lock(&dev_priv->binding_mutex); |
129 | ret = vmw_dx_streamoutput_unscrub(res); |
130 | mutex_unlock(lock: &dev_priv->binding_mutex); |
131 | } |
132 | |
133 | res->id = so->id; |
134 | |
135 | return ret; |
136 | } |
137 | |
138 | static int vmw_dx_streamoutput_bind(struct vmw_resource *res, |
139 | struct ttm_validate_buffer *val_buf) |
140 | { |
141 | struct vmw_private *dev_priv = res->dev_priv; |
142 | struct ttm_buffer_object *bo = val_buf->bo; |
143 | int ret; |
144 | |
145 | if (WARN_ON(bo->resource->mem_type != VMW_PL_MOB)) |
146 | return -EINVAL; |
147 | |
148 | mutex_lock(&dev_priv->binding_mutex); |
149 | ret = vmw_dx_streamoutput_unscrub(res); |
150 | mutex_unlock(lock: &dev_priv->binding_mutex); |
151 | |
152 | return ret; |
153 | } |
154 | |
155 | /** |
156 | * vmw_dx_streamoutput_scrub - Unbind the MOB from streamoutput. |
157 | * @res: The streamoutput resource. |
158 | * |
159 | * Return: 0 on success, negative error code on failure. |
160 | */ |
161 | static int vmw_dx_streamoutput_scrub(struct vmw_resource *res) |
162 | { |
163 | struct vmw_private *dev_priv = res->dev_priv; |
164 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
165 | struct { |
166 | SVGA3dCmdHeader ; |
167 | SVGA3dCmdDXBindStreamOutput body; |
168 | } *cmd; |
169 | |
170 | if (list_empty(head: &so->cotable_head)) |
171 | return 0; |
172 | |
173 | WARN_ON_ONCE(!so->committed); |
174 | |
175 | cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id); |
176 | if (!cmd) |
177 | return -ENOMEM; |
178 | |
179 | cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT; |
180 | cmd->header.size = sizeof(cmd->body); |
181 | cmd->body.soid = res->id; |
182 | cmd->body.mobid = SVGA3D_INVALID_ID; |
183 | cmd->body.offsetInBytes = 0; |
184 | cmd->body.sizeInBytes = so->size; |
185 | vmw_cmd_commit(dev_priv, bytes: sizeof(*cmd)); |
186 | |
187 | res->id = -1; |
188 | list_del_init(entry: &so->cotable_head); |
189 | |
190 | return 0; |
191 | } |
192 | |
193 | static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback, |
194 | struct ttm_validate_buffer *val_buf) |
195 | { |
196 | struct vmw_private *dev_priv = res->dev_priv; |
197 | struct vmw_fence_obj *fence; |
198 | int ret; |
199 | |
200 | if (WARN_ON(res->guest_memory_bo->tbo.resource->mem_type != VMW_PL_MOB)) |
201 | return -EINVAL; |
202 | |
203 | mutex_lock(&dev_priv->binding_mutex); |
204 | ret = vmw_dx_streamoutput_scrub(res); |
205 | mutex_unlock(lock: &dev_priv->binding_mutex); |
206 | |
207 | if (ret) |
208 | return ret; |
209 | |
210 | (void) vmw_execbuf_fence_commands(NULL, dev_priv, p_fence: &fence, NULL); |
211 | vmw_bo_fence_single(bo: val_buf->bo, fence); |
212 | |
213 | if (fence != NULL) |
214 | vmw_fence_obj_unreference(fence_p: &fence); |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res, |
220 | enum vmw_cmdbuf_res_state state) |
221 | { |
222 | struct vmw_private *dev_priv = res->dev_priv; |
223 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
224 | |
225 | if (state == VMW_CMDBUF_RES_ADD) { |
226 | mutex_lock(&dev_priv->binding_mutex); |
227 | vmw_cotable_add_resource(ctx: so->cotable, head: &so->cotable_head); |
228 | so->committed = true; |
229 | res->id = so->id; |
230 | mutex_unlock(lock: &dev_priv->binding_mutex); |
231 | } else { |
232 | mutex_lock(&dev_priv->binding_mutex); |
233 | list_del_init(entry: &so->cotable_head); |
234 | so->committed = false; |
235 | res->id = -1; |
236 | mutex_unlock(lock: &dev_priv->binding_mutex); |
237 | } |
238 | } |
239 | |
240 | /** |
241 | * vmw_dx_streamoutput_lookup - Do a streamoutput resource lookup by user key. |
242 | * @man: Command buffer managed resource manager for current context. |
243 | * @user_key: User-space identifier for lookup. |
244 | * |
245 | * Return: Valid refcounted vmw_resource on success, error pointer on failure. |
246 | */ |
247 | struct vmw_resource * |
248 | vmw_dx_streamoutput_lookup(struct vmw_cmdbuf_res_manager *man, |
249 | u32 user_key) |
250 | { |
251 | return vmw_cmdbuf_res_lookup(man, res_type: vmw_cmdbuf_res_streamoutput, |
252 | user_key); |
253 | } |
254 | |
255 | static void vmw_dx_streamoutput_res_free(struct vmw_resource *res) |
256 | { |
257 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
258 | |
259 | vmw_resource_unreference(p_res: &so->cotable); |
260 | kfree(objp: so); |
261 | } |
262 | |
263 | static void vmw_dx_streamoutput_hw_destroy(struct vmw_resource *res) |
264 | { |
265 | /* Destroyed by user-space cmd buf or as part of context takedown. */ |
266 | res->id = -1; |
267 | } |
268 | |
269 | /** |
270 | * vmw_dx_streamoutput_add - Add a streamoutput as a cmd buf managed resource. |
271 | * @man: Command buffer managed resource manager for current context. |
272 | * @ctx: Pointer to context resource. |
273 | * @user_key: The identifier for this streamoutput. |
274 | * @list: The list of staged command buffer managed resources. |
275 | * |
276 | * Return: 0 on success, negative error code on failure. |
277 | */ |
278 | int vmw_dx_streamoutput_add(struct vmw_cmdbuf_res_manager *man, |
279 | struct vmw_resource *ctx, u32 user_key, |
280 | struct list_head *list) |
281 | { |
282 | struct vmw_dx_streamoutput *so; |
283 | struct vmw_resource *res; |
284 | struct vmw_private *dev_priv = ctx->dev_priv; |
285 | int ret; |
286 | |
287 | so = kmalloc(size: sizeof(*so), GFP_KERNEL); |
288 | if (!so) { |
289 | return -ENOMEM; |
290 | } |
291 | |
292 | res = &so->res; |
293 | so->ctx = ctx; |
294 | so->cotable = vmw_resource_reference |
295 | (res: vmw_context_cotable(ctx, cotable_type: SVGA_COTABLE_STREAMOUTPUT)); |
296 | so->id = user_key; |
297 | so->committed = false; |
298 | INIT_LIST_HEAD(list: &so->cotable_head); |
299 | ret = vmw_resource_init(dev_priv, res, delay_id: true, |
300 | res_free: vmw_dx_streamoutput_res_free, |
301 | func: &vmw_dx_streamoutput_func); |
302 | if (ret) |
303 | goto out_resource_init; |
304 | |
305 | ret = vmw_cmdbuf_res_add(man, res_type: vmw_cmdbuf_res_streamoutput, user_key, |
306 | res, list); |
307 | if (ret) |
308 | goto out_resource_init; |
309 | |
310 | res->id = so->id; |
311 | res->hw_destroy = vmw_dx_streamoutput_hw_destroy; |
312 | |
313 | out_resource_init: |
314 | vmw_resource_unreference(p_res: &res); |
315 | |
316 | return ret; |
317 | } |
318 | |
319 | /** |
320 | * vmw_dx_streamoutput_set_size - Sets streamoutput mob size in res struct. |
321 | * @res: The streamoutput res for which need to set size. |
322 | * @size: The size provided by user-space to set. |
323 | */ |
324 | void vmw_dx_streamoutput_set_size(struct vmw_resource *res, u32 size) |
325 | { |
326 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
327 | |
328 | so->size = size; |
329 | } |
330 | |
331 | /** |
332 | * vmw_dx_streamoutput_remove - Stage streamoutput for removal. |
333 | * @man: Command buffer managed resource manager for current context. |
334 | * @user_key: The identifier for this streamoutput. |
335 | * @list: The list of staged command buffer managed resources. |
336 | * |
337 | * Return: 0 on success, negative error code on failure. |
338 | */ |
339 | int vmw_dx_streamoutput_remove(struct vmw_cmdbuf_res_manager *man, |
340 | u32 user_key, |
341 | struct list_head *list) |
342 | { |
343 | struct vmw_resource *r; |
344 | |
345 | return vmw_cmdbuf_res_remove(man, res_type: vmw_cmdbuf_res_streamoutput, |
346 | user_key: (u32)user_key, list, res: &r); |
347 | } |
348 | |
349 | /** |
350 | * vmw_dx_streamoutput_cotable_list_scrub - cotable unbind_func callback. |
351 | * @dev_priv: Device private. |
352 | * @list: The list of cotable resources. |
353 | * @readback: Whether the call was part of a readback unbind. |
354 | */ |
355 | void vmw_dx_streamoutput_cotable_list_scrub(struct vmw_private *dev_priv, |
356 | struct list_head *list, |
357 | bool readback) |
358 | { |
359 | struct vmw_dx_streamoutput *entry, *next; |
360 | |
361 | lockdep_assert_held_once(&dev_priv->binding_mutex); |
362 | |
363 | list_for_each_entry_safe(entry, next, list, cotable_head) { |
364 | WARN_ON(vmw_dx_streamoutput_scrub(&entry->res)); |
365 | if (!readback) |
366 | entry->committed =false; |
367 | } |
368 | } |
369 | |