diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_shader.c')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 812 |
1 files changed, 812 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c new file mode 100644 index 000000000000..ee3856578a12 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | |||
@@ -0,0 +1,812 @@ | |||
1 | /************************************************************************** | ||
2 | * | ||
3 | * Copyright © 2009-2012 VMware, Inc., Palo Alto, CA., USA | ||
4 | * All Rights Reserved. | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the | ||
8 | * "Software"), to deal in the Software without restriction, including | ||
9 | * without limitation the rights to use, copy, modify, merge, publish, | ||
10 | * distribute, sub license, and/or sell copies of the Software, and to | ||
11 | * permit persons to whom the Software is furnished to do so, subject to | ||
12 | * the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the | ||
15 | * next paragraph) shall be included in all copies or substantial portions | ||
16 | * of the Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
21 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | ||
22 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
23 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||
24 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
25 | * | ||
26 | **************************************************************************/ | ||
27 | |||
28 | #include "vmwgfx_drv.h" | ||
29 | #include "vmwgfx_resource_priv.h" | ||
30 | #include "ttm/ttm_placement.h" | ||
31 | |||
32 | #define VMW_COMPAT_SHADER_HT_ORDER 12 | ||
33 | |||
34 | struct vmw_shader { | ||
35 | struct vmw_resource res; | ||
36 | SVGA3dShaderType type; | ||
37 | uint32_t size; | ||
38 | }; | ||
39 | |||
40 | struct vmw_user_shader { | ||
41 | struct ttm_base_object base; | ||
42 | struct vmw_shader shader; | ||
43 | }; | ||
44 | |||
45 | /** | ||
46 | * enum vmw_compat_shader_state - Staging state for compat shaders | ||
47 | */ | ||
48 | enum vmw_compat_shader_state { | ||
49 | VMW_COMPAT_COMMITED, | ||
50 | VMW_COMPAT_ADD, | ||
51 | VMW_COMPAT_DEL | ||
52 | }; | ||
53 | |||
54 | /** | ||
55 | * struct vmw_compat_shader - Metadata for compat shaders. | ||
56 | * | ||
57 | * @handle: The TTM handle of the guest backed shader. | ||
58 | * @tfile: The struct ttm_object_file the guest backed shader is registered | ||
59 | * with. | ||
60 | * @hash: Hash item for lookup. | ||
61 | * @head: List head for staging lists or the compat shader manager list. | ||
62 | * @state: Staging state. | ||
63 | * | ||
64 | * The structure is protected by the cmdbuf lock. | ||
65 | */ | ||
66 | struct vmw_compat_shader { | ||
67 | u32 handle; | ||
68 | struct ttm_object_file *tfile; | ||
69 | struct drm_hash_item hash; | ||
70 | struct list_head head; | ||
71 | enum vmw_compat_shader_state state; | ||
72 | }; | ||
73 | |||
74 | /** | ||
75 | * struct vmw_compat_shader_manager - Compat shader manager. | ||
76 | * | ||
77 | * @shaders: Hash table containing staged and commited compat shaders | ||
78 | * @list: List of commited shaders. | ||
79 | * @dev_priv: Pointer to a device private structure. | ||
80 | * | ||
81 | * @shaders and @list are protected by the cmdbuf mutex for now. | ||
82 | */ | ||
83 | struct vmw_compat_shader_manager { | ||
84 | struct drm_open_hash shaders; | ||
85 | struct list_head list; | ||
86 | struct vmw_private *dev_priv; | ||
87 | }; | ||
88 | |||
89 | static void vmw_user_shader_free(struct vmw_resource *res); | ||
90 | static struct vmw_resource * | ||
91 | vmw_user_shader_base_to_res(struct ttm_base_object *base); | ||
92 | |||
93 | static int vmw_gb_shader_create(struct vmw_resource *res); | ||
94 | static int vmw_gb_shader_bind(struct vmw_resource *res, | ||
95 | struct ttm_validate_buffer *val_buf); | ||
96 | static int vmw_gb_shader_unbind(struct vmw_resource *res, | ||
97 | bool readback, | ||
98 | struct ttm_validate_buffer *val_buf); | ||
99 | static int vmw_gb_shader_destroy(struct vmw_resource *res); | ||
100 | |||
101 | static uint64_t vmw_user_shader_size; | ||
102 | |||
103 | static const struct vmw_user_resource_conv user_shader_conv = { | ||
104 | .object_type = VMW_RES_SHADER, | ||
105 | .base_obj_to_res = vmw_user_shader_base_to_res, | ||
106 | .res_free = vmw_user_shader_free | ||
107 | }; | ||
108 | |||
109 | const struct vmw_user_resource_conv *user_shader_converter = | ||
110 | &user_shader_conv; | ||
111 | |||
112 | |||
113 | static const struct vmw_res_func vmw_gb_shader_func = { | ||
114 | .res_type = vmw_res_shader, | ||
115 | .needs_backup = true, | ||
116 | .may_evict = true, | ||
117 | .type_name = "guest backed shaders", | ||
118 | .backup_placement = &vmw_mob_placement, | ||
119 | .create = vmw_gb_shader_create, | ||
120 | .destroy = vmw_gb_shader_destroy, | ||
121 | .bind = vmw_gb_shader_bind, | ||
122 | .unbind = vmw_gb_shader_unbind | ||
123 | }; | ||
124 | |||
125 | /** | ||
126 | * Shader management: | ||
127 | */ | ||
128 | |||
129 | static inline struct vmw_shader * | ||
130 | vmw_res_to_shader(struct vmw_resource *res) | ||
131 | { | ||
132 | return container_of(res, struct vmw_shader, res); | ||
133 | } | ||
134 | |||
135 | static void vmw_hw_shader_destroy(struct vmw_resource *res) | ||
136 | { | ||
137 | (void) vmw_gb_shader_destroy(res); | ||
138 | } | ||
139 | |||
140 | static int vmw_gb_shader_init(struct vmw_private *dev_priv, | ||
141 | struct vmw_resource *res, | ||
142 | uint32_t size, | ||
143 | uint64_t offset, | ||
144 | SVGA3dShaderType type, | ||
145 | struct vmw_dma_buffer *byte_code, | ||
146 | void (*res_free) (struct vmw_resource *res)) | ||
147 | { | ||
148 | struct vmw_shader *shader = vmw_res_to_shader(res); | ||
149 | int ret; | ||
150 | |||
151 | ret = vmw_resource_init(dev_priv, res, true, | ||
152 | res_free, &vmw_gb_shader_func); | ||
153 | |||
154 | |||
155 | if (unlikely(ret != 0)) { | ||
156 | if (res_free) | ||
157 | res_free(res); | ||
158 | else | ||
159 | kfree(res); | ||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | res->backup_size = size; | ||
164 | if (byte_code) { | ||
165 | res->backup = vmw_dmabuf_reference(byte_code); | ||
166 | res->backup_offset = offset; | ||
167 | } | ||
168 | shader->size = size; | ||
169 | shader->type = type; | ||
170 | |||
171 | vmw_resource_activate(res, vmw_hw_shader_destroy); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static int vmw_gb_shader_create(struct vmw_resource *res) | ||
176 | { | ||
177 | struct vmw_private *dev_priv = res->dev_priv; | ||
178 | struct vmw_shader *shader = vmw_res_to_shader(res); | ||
179 | int ret; | ||
180 | struct { | ||
181 | SVGA3dCmdHeader header; | ||
182 | SVGA3dCmdDefineGBShader body; | ||
183 | } *cmd; | ||
184 | |||
185 | if (likely(res->id != -1)) | ||
186 | return 0; | ||
187 | |||
188 | ret = vmw_resource_alloc_id(res); | ||
189 | if (unlikely(ret != 0)) { | ||
190 | DRM_ERROR("Failed to allocate a shader id.\n"); | ||
191 | goto out_no_id; | ||
192 | } | ||
193 | |||
194 | if (unlikely(res->id >= VMWGFX_NUM_GB_SHADER)) { | ||
195 | ret = -EBUSY; | ||
196 | goto out_no_fifo; | ||
197 | } | ||
198 | |||
199 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | ||
200 | if (unlikely(cmd == NULL)) { | ||
201 | DRM_ERROR("Failed reserving FIFO space for shader " | ||
202 | "creation.\n"); | ||
203 | ret = -ENOMEM; | ||
204 | goto out_no_fifo; | ||
205 | } | ||
206 | |||
207 | cmd->header.id = SVGA_3D_CMD_DEFINE_GB_SHADER; | ||
208 | cmd->header.size = sizeof(cmd->body); | ||
209 | cmd->body.shid = res->id; | ||
210 | cmd->body.type = shader->type; | ||
211 | cmd->body.sizeInBytes = shader->size; | ||
212 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | ||
213 | (void) vmw_3d_resource_inc(dev_priv, false); | ||
214 | |||
215 | return 0; | ||
216 | |||
217 | out_no_fifo: | ||
218 | vmw_resource_release_id(res); | ||
219 | out_no_id: | ||
220 | return ret; | ||
221 | } | ||
222 | |||
223 | static int vmw_gb_shader_bind(struct vmw_resource *res, | ||
224 | struct ttm_validate_buffer *val_buf) | ||
225 | { | ||
226 | struct vmw_private *dev_priv = res->dev_priv; | ||
227 | struct { | ||
228 | SVGA3dCmdHeader header; | ||
229 | SVGA3dCmdBindGBShader body; | ||
230 | } *cmd; | ||
231 | struct ttm_buffer_object *bo = val_buf->bo; | ||
232 | |||
233 | BUG_ON(bo->mem.mem_type != VMW_PL_MOB); | ||
234 | |||
235 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | ||
236 | if (unlikely(cmd == NULL)) { | ||
237 | DRM_ERROR("Failed reserving FIFO space for shader " | ||
238 | "binding.\n"); | ||
239 | return -ENOMEM; | ||
240 | } | ||
241 | |||
242 | cmd->header.id = SVGA_3D_CMD_BIND_GB_SHADER; | ||
243 | cmd->header.size = sizeof(cmd->body); | ||
244 | cmd->body.shid = res->id; | ||
245 | cmd->body.mobid = bo->mem.start; | ||
246 | cmd->body.offsetInBytes = 0; | ||
247 | res->backup_dirty = false; | ||
248 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int vmw_gb_shader_unbind(struct vmw_resource *res, | ||
254 | bool readback, | ||
255 | struct ttm_validate_buffer *val_buf) | ||
256 | { | ||
257 | struct vmw_private *dev_priv = res->dev_priv; | ||
258 | struct { | ||
259 | SVGA3dCmdHeader header; | ||
260 | SVGA3dCmdBindGBShader body; | ||
261 | } *cmd; | ||
262 | struct vmw_fence_obj *fence; | ||
263 | |||
264 | BUG_ON(res->backup->base.mem.mem_type != VMW_PL_MOB); | ||
265 | |||
266 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | ||
267 | if (unlikely(cmd == NULL)) { | ||
268 | DRM_ERROR("Failed reserving FIFO space for shader " | ||
269 | "unbinding.\n"); | ||
270 | return -ENOMEM; | ||
271 | } | ||
272 | |||
273 | cmd->header.id = SVGA_3D_CMD_BIND_GB_SHADER; | ||
274 | cmd->header.size = sizeof(cmd->body); | ||
275 | cmd->body.shid = res->id; | ||
276 | cmd->body.mobid = SVGA3D_INVALID_ID; | ||
277 | cmd->body.offsetInBytes = 0; | ||
278 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | ||
279 | |||
280 | /* | ||
281 | * Create a fence object and fence the backup buffer. | ||
282 | */ | ||
283 | |||
284 | (void) vmw_execbuf_fence_commands(NULL, dev_priv, | ||
285 | &fence, NULL); | ||
286 | |||
287 | vmw_fence_single_bo(val_buf->bo, fence); | ||
288 | |||
289 | if (likely(fence != NULL)) | ||
290 | vmw_fence_obj_unreference(&fence); | ||
291 | |||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static int vmw_gb_shader_destroy(struct vmw_resource *res) | ||
296 | { | ||
297 | struct vmw_private *dev_priv = res->dev_priv; | ||
298 | struct { | ||
299 | SVGA3dCmdHeader header; | ||
300 | SVGA3dCmdDestroyGBShader body; | ||
301 | } *cmd; | ||
302 | |||
303 | if (likely(res->id == -1)) | ||
304 | return 0; | ||
305 | |||
306 | mutex_lock(&dev_priv->binding_mutex); | ||
307 | vmw_context_binding_res_list_scrub(&res->binding_head); | ||
308 | |||
309 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | ||
310 | if (unlikely(cmd == NULL)) { | ||
311 | DRM_ERROR("Failed reserving FIFO space for shader " | ||
312 | "destruction.\n"); | ||
313 | mutex_unlock(&dev_priv->binding_mutex); | ||
314 | return -ENOMEM; | ||
315 | } | ||
316 | |||
317 | cmd->header.id = SVGA_3D_CMD_DESTROY_GB_SHADER; | ||
318 | cmd->header.size = sizeof(cmd->body); | ||
319 | cmd->body.shid = res->id; | ||
320 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | ||
321 | mutex_unlock(&dev_priv->binding_mutex); | ||
322 | vmw_resource_release_id(res); | ||
323 | vmw_3d_resource_dec(dev_priv, false); | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | /** | ||
329 | * User-space shader management: | ||
330 | */ | ||
331 | |||
332 | static struct vmw_resource * | ||
333 | vmw_user_shader_base_to_res(struct ttm_base_object *base) | ||
334 | { | ||
335 | return &(container_of(base, struct vmw_user_shader, base)-> | ||
336 | shader.res); | ||
337 | } | ||
338 | |||
339 | static void vmw_user_shader_free(struct vmw_resource *res) | ||
340 | { | ||
341 | struct vmw_user_shader *ushader = | ||
342 | container_of(res, struct vmw_user_shader, shader.res); | ||
343 | struct vmw_private *dev_priv = res->dev_priv; | ||
344 | |||
345 | ttm_base_object_kfree(ushader, base); | ||
346 | ttm_mem_global_free(vmw_mem_glob(dev_priv), | ||
347 | vmw_user_shader_size); | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * This function is called when user space has no more references on the | ||
352 | * base object. It releases the base-object's reference on the resource object. | ||
353 | */ | ||
354 | |||
355 | static void vmw_user_shader_base_release(struct ttm_base_object **p_base) | ||
356 | { | ||
357 | struct ttm_base_object *base = *p_base; | ||
358 | struct vmw_resource *res = vmw_user_shader_base_to_res(base); | ||
359 | |||
360 | *p_base = NULL; | ||
361 | vmw_resource_unreference(&res); | ||
362 | } | ||
363 | |||
364 | int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data, | ||
365 | struct drm_file *file_priv) | ||
366 | { | ||
367 | struct drm_vmw_shader_arg *arg = (struct drm_vmw_shader_arg *)data; | ||
368 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
369 | |||
370 | return ttm_ref_object_base_unref(tfile, arg->handle, | ||
371 | TTM_REF_USAGE); | ||
372 | } | ||
373 | |||
374 | static int vmw_shader_alloc(struct vmw_private *dev_priv, | ||
375 | struct vmw_dma_buffer *buffer, | ||
376 | size_t shader_size, | ||
377 | size_t offset, | ||
378 | SVGA3dShaderType shader_type, | ||
379 | struct ttm_object_file *tfile, | ||
380 | u32 *handle) | ||
381 | { | ||
382 | struct vmw_user_shader *ushader; | ||
383 | struct vmw_resource *res, *tmp; | ||
384 | int ret; | ||
385 | |||
386 | /* | ||
387 | * Approximate idr memory usage with 128 bytes. It will be limited | ||
388 | * by maximum number_of shaders anyway. | ||
389 | */ | ||
390 | if (unlikely(vmw_user_shader_size == 0)) | ||
391 | vmw_user_shader_size = | ||
392 | ttm_round_pot(sizeof(struct vmw_user_shader)) + 128; | ||
393 | |||
394 | ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), | ||
395 | vmw_user_shader_size, | ||
396 | false, true); | ||
397 | if (unlikely(ret != 0)) { | ||
398 | if (ret != -ERESTARTSYS) | ||
399 | DRM_ERROR("Out of graphics memory for shader " | ||
400 | "creation.\n"); | ||
401 | goto out; | ||
402 | } | ||
403 | |||
404 | ushader = kzalloc(sizeof(*ushader), GFP_KERNEL); | ||
405 | if (unlikely(ushader == NULL)) { | ||
406 | ttm_mem_global_free(vmw_mem_glob(dev_priv), | ||
407 | vmw_user_shader_size); | ||
408 | ret = -ENOMEM; | ||
409 | goto out; | ||
410 | } | ||
411 | |||
412 | res = &ushader->shader.res; | ||
413 | ushader->base.shareable = false; | ||
414 | ushader->base.tfile = NULL; | ||
415 | |||
416 | /* | ||
417 | * From here on, the destructor takes over resource freeing. | ||
418 | */ | ||
419 | |||
420 | ret = vmw_gb_shader_init(dev_priv, res, shader_size, | ||
421 | offset, shader_type, buffer, | ||
422 | vmw_user_shader_free); | ||
423 | if (unlikely(ret != 0)) | ||
424 | goto out; | ||
425 | |||
426 | tmp = vmw_resource_reference(res); | ||
427 | ret = ttm_base_object_init(tfile, &ushader->base, false, | ||
428 | VMW_RES_SHADER, | ||
429 | &vmw_user_shader_base_release, NULL); | ||
430 | |||
431 | if (unlikely(ret != 0)) { | ||
432 | vmw_resource_unreference(&tmp); | ||
433 | goto out_err; | ||
434 | } | ||
435 | |||
436 | if (handle) | ||
437 | *handle = ushader->base.hash.key; | ||
438 | out_err: | ||
439 | vmw_resource_unreference(&res); | ||
440 | out: | ||
441 | return ret; | ||
442 | } | ||
443 | |||
444 | |||
445 | int vmw_shader_define_ioctl(struct drm_device *dev, void *data, | ||
446 | struct drm_file *file_priv) | ||
447 | { | ||
448 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
449 | struct drm_vmw_shader_create_arg *arg = | ||
450 | (struct drm_vmw_shader_create_arg *)data; | ||
451 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
452 | struct vmw_master *vmaster = vmw_master(file_priv->master); | ||
453 | struct vmw_dma_buffer *buffer = NULL; | ||
454 | SVGA3dShaderType shader_type; | ||
455 | int ret; | ||
456 | |||
457 | if (arg->buffer_handle != SVGA3D_INVALID_ID) { | ||
458 | ret = vmw_user_dmabuf_lookup(tfile, arg->buffer_handle, | ||
459 | &buffer); | ||
460 | if (unlikely(ret != 0)) { | ||
461 | DRM_ERROR("Could not find buffer for shader " | ||
462 | "creation.\n"); | ||
463 | return ret; | ||
464 | } | ||
465 | |||
466 | if ((u64)buffer->base.num_pages * PAGE_SIZE < | ||
467 | (u64)arg->size + (u64)arg->offset) { | ||
468 | DRM_ERROR("Illegal buffer- or shader size.\n"); | ||
469 | ret = -EINVAL; | ||
470 | goto out_bad_arg; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | switch (arg->shader_type) { | ||
475 | case drm_vmw_shader_type_vs: | ||
476 | shader_type = SVGA3D_SHADERTYPE_VS; | ||
477 | break; | ||
478 | case drm_vmw_shader_type_ps: | ||
479 | shader_type = SVGA3D_SHADERTYPE_PS; | ||
480 | break; | ||
481 | case drm_vmw_shader_type_gs: | ||
482 | shader_type = SVGA3D_SHADERTYPE_GS; | ||
483 | break; | ||
484 | default: | ||
485 | DRM_ERROR("Illegal shader type.\n"); | ||
486 | ret = -EINVAL; | ||
487 | goto out_bad_arg; | ||
488 | } | ||
489 | |||
490 | ret = ttm_read_lock(&vmaster->lock, true); | ||
491 | if (unlikely(ret != 0)) | ||
492 | goto out_bad_arg; | ||
493 | |||
494 | ret = vmw_shader_alloc(dev_priv, buffer, arg->size, arg->offset, | ||
495 | shader_type, tfile, &arg->shader_handle); | ||
496 | |||
497 | ttm_read_unlock(&vmaster->lock); | ||
498 | out_bad_arg: | ||
499 | vmw_dmabuf_unreference(&buffer); | ||
500 | return ret; | ||
501 | } | ||
502 | |||
503 | /** | ||
504 | * vmw_compat_shader_lookup - Look up a compat shader | ||
505 | * | ||
506 | * @man: Pointer to the compat shader manager. | ||
507 | * @shader_type: The shader type, that combined with the user_key identifies | ||
508 | * the shader. | ||
509 | * @user_key: On entry, this should be a pointer to the user_key. | ||
510 | * On successful exit, it will contain the guest-backed shader's TTM handle. | ||
511 | * | ||
512 | * Returns 0 on success. Non-zero on failure, in which case the value pointed | ||
513 | * to by @user_key is unmodified. | ||
514 | */ | ||
515 | int vmw_compat_shader_lookup(struct vmw_compat_shader_manager *man, | ||
516 | SVGA3dShaderType shader_type, | ||
517 | u32 *user_key) | ||
518 | { | ||
519 | struct drm_hash_item *hash; | ||
520 | int ret; | ||
521 | unsigned long key = *user_key | (shader_type << 24); | ||
522 | |||
523 | ret = drm_ht_find_item(&man->shaders, key, &hash); | ||
524 | if (unlikely(ret != 0)) | ||
525 | return ret; | ||
526 | |||
527 | *user_key = drm_hash_entry(hash, struct vmw_compat_shader, | ||
528 | hash)->handle; | ||
529 | |||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | /** | ||
534 | * vmw_compat_shader_free - Free a compat shader. | ||
535 | * | ||
536 | * @man: Pointer to the compat shader manager. | ||
537 | * @entry: Pointer to a struct vmw_compat_shader. | ||
538 | * | ||
539 | * Frees a struct vmw_compat_shder entry and drops its reference to the | ||
540 | * guest backed shader. | ||
541 | */ | ||
542 | static void vmw_compat_shader_free(struct vmw_compat_shader_manager *man, | ||
543 | struct vmw_compat_shader *entry) | ||
544 | { | ||
545 | list_del(&entry->head); | ||
546 | WARN_ON(drm_ht_remove_item(&man->shaders, &entry->hash)); | ||
547 | WARN_ON(ttm_ref_object_base_unref(entry->tfile, entry->handle, | ||
548 | TTM_REF_USAGE)); | ||
549 | kfree(entry); | ||
550 | } | ||
551 | |||
552 | /** | ||
553 | * vmw_compat_shaders_commit - Commit a list of compat shader actions. | ||
554 | * | ||
555 | * @man: Pointer to the compat shader manager. | ||
556 | * @list: Caller's list of compat shader actions. | ||
557 | * | ||
558 | * This function commits a list of compat shader additions or removals. | ||
559 | * It is typically called when the execbuf ioctl call triggering these | ||
560 | * actions has commited the fifo contents to the device. | ||
561 | */ | ||
562 | void vmw_compat_shaders_commit(struct vmw_compat_shader_manager *man, | ||
563 | struct list_head *list) | ||
564 | { | ||
565 | struct vmw_compat_shader *entry, *next; | ||
566 | |||
567 | list_for_each_entry_safe(entry, next, list, head) { | ||
568 | list_del(&entry->head); | ||
569 | switch (entry->state) { | ||
570 | case VMW_COMPAT_ADD: | ||
571 | entry->state = VMW_COMPAT_COMMITED; | ||
572 | list_add_tail(&entry->head, &man->list); | ||
573 | break; | ||
574 | case VMW_COMPAT_DEL: | ||
575 | ttm_ref_object_base_unref(entry->tfile, entry->handle, | ||
576 | TTM_REF_USAGE); | ||
577 | kfree(entry); | ||
578 | break; | ||
579 | default: | ||
580 | BUG(); | ||
581 | break; | ||
582 | } | ||
583 | } | ||
584 | } | ||
585 | |||
586 | /** | ||
587 | * vmw_compat_shaders_revert - Revert a list of compat shader actions | ||
588 | * | ||
589 | * @man: Pointer to the compat shader manager. | ||
590 | * @list: Caller's list of compat shader actions. | ||
591 | * | ||
592 | * This function reverts a list of compat shader additions or removals. | ||
593 | * It is typically called when the execbuf ioctl call triggering these | ||
594 | * actions failed for some reason, and the command stream was never | ||
595 | * submitted. | ||
596 | */ | ||
597 | void vmw_compat_shaders_revert(struct vmw_compat_shader_manager *man, | ||
598 | struct list_head *list) | ||
599 | { | ||
600 | struct vmw_compat_shader *entry, *next; | ||
601 | int ret; | ||
602 | |||
603 | list_for_each_entry_safe(entry, next, list, head) { | ||
604 | switch (entry->state) { | ||
605 | case VMW_COMPAT_ADD: | ||
606 | vmw_compat_shader_free(man, entry); | ||
607 | break; | ||
608 | case VMW_COMPAT_DEL: | ||
609 | ret = drm_ht_insert_item(&man->shaders, &entry->hash); | ||
610 | list_del(&entry->head); | ||
611 | list_add_tail(&entry->head, &man->list); | ||
612 | entry->state = VMW_COMPAT_COMMITED; | ||
613 | break; | ||
614 | default: | ||
615 | BUG(); | ||
616 | break; | ||
617 | } | ||
618 | } | ||
619 | } | ||
620 | |||
621 | /** | ||
622 | * vmw_compat_shader_remove - Stage a compat shader for removal. | ||
623 | * | ||
624 | * @man: Pointer to the compat shader manager | ||
625 | * @user_key: The key that is used to identify the shader. The key is | ||
626 | * unique to the shader type. | ||
627 | * @shader_type: Shader type. | ||
628 | * @list: Caller's list of staged shader actions. | ||
629 | * | ||
630 | * This function stages a compat shader for removal and removes the key from | ||
631 | * the shader manager's hash table. If the shader was previously only staged | ||
632 | * for addition it is completely removed (But the execbuf code may keep a | ||
633 | * reference if it was bound to a context between addition and removal). If | ||
634 | * it was previously commited to the manager, it is staged for removal. | ||
635 | */ | ||
636 | int vmw_compat_shader_remove(struct vmw_compat_shader_manager *man, | ||
637 | u32 user_key, SVGA3dShaderType shader_type, | ||
638 | struct list_head *list) | ||
639 | { | ||
640 | struct vmw_compat_shader *entry; | ||
641 | struct drm_hash_item *hash; | ||
642 | int ret; | ||
643 | |||
644 | ret = drm_ht_find_item(&man->shaders, user_key | (shader_type << 24), | ||
645 | &hash); | ||
646 | if (likely(ret != 0)) | ||
647 | return -EINVAL; | ||
648 | |||
649 | entry = drm_hash_entry(hash, struct vmw_compat_shader, hash); | ||
650 | |||
651 | switch (entry->state) { | ||
652 | case VMW_COMPAT_ADD: | ||
653 | vmw_compat_shader_free(man, entry); | ||
654 | break; | ||
655 | case VMW_COMPAT_COMMITED: | ||
656 | (void) drm_ht_remove_item(&man->shaders, &entry->hash); | ||
657 | list_del(&entry->head); | ||
658 | entry->state = VMW_COMPAT_DEL; | ||
659 | list_add_tail(&entry->head, list); | ||
660 | break; | ||
661 | default: | ||
662 | BUG(); | ||
663 | break; | ||
664 | } | ||
665 | |||
666 | return 0; | ||
667 | } | ||
668 | |||
669 | /** | ||
670 | * vmw_compat_shader_add - Create a compat shader and add the | ||
671 | * key to the manager | ||
672 | * | ||
673 | * @man: Pointer to the compat shader manager | ||
674 | * @user_key: The key that is used to identify the shader. The key is | ||
675 | * unique to the shader type. | ||
676 | * @bytecode: Pointer to the bytecode of the shader. | ||
677 | * @shader_type: Shader type. | ||
678 | * @tfile: Pointer to a struct ttm_object_file that the guest-backed shader is | ||
679 | * to be created with. | ||
680 | * @list: Caller's list of staged shader actions. | ||
681 | * | ||
682 | * Note that only the key is added to the shader manager's hash table. | ||
683 | * The shader is not yet added to the shader manager's list of shaders. | ||
684 | */ | ||
685 | int vmw_compat_shader_add(struct vmw_compat_shader_manager *man, | ||
686 | u32 user_key, const void *bytecode, | ||
687 | SVGA3dShaderType shader_type, | ||
688 | size_t size, | ||
689 | struct ttm_object_file *tfile, | ||
690 | struct list_head *list) | ||
691 | { | ||
692 | struct vmw_dma_buffer *buf; | ||
693 | struct ttm_bo_kmap_obj map; | ||
694 | bool is_iomem; | ||
695 | struct vmw_compat_shader *compat; | ||
696 | u32 handle; | ||
697 | int ret; | ||
698 | |||
699 | if (user_key > ((1 << 24) - 1) || (unsigned) shader_type > 16) | ||
700 | return -EINVAL; | ||
701 | |||
702 | /* Allocate and pin a DMA buffer */ | ||
703 | buf = kzalloc(sizeof(*buf), GFP_KERNEL); | ||
704 | if (unlikely(buf == NULL)) | ||
705 | return -ENOMEM; | ||
706 | |||
707 | ret = vmw_dmabuf_init(man->dev_priv, buf, size, &vmw_sys_ne_placement, | ||
708 | true, vmw_dmabuf_bo_free); | ||
709 | if (unlikely(ret != 0)) | ||
710 | goto out; | ||
711 | |||
712 | ret = ttm_bo_reserve(&buf->base, false, true, false, NULL); | ||
713 | if (unlikely(ret != 0)) | ||
714 | goto no_reserve; | ||
715 | |||
716 | /* Map and copy shader bytecode. */ | ||
717 | ret = ttm_bo_kmap(&buf->base, 0, PAGE_ALIGN(size) >> PAGE_SHIFT, | ||
718 | &map); | ||
719 | if (unlikely(ret != 0)) { | ||
720 | ttm_bo_unreserve(&buf->base); | ||
721 | goto no_reserve; | ||
722 | } | ||
723 | |||
724 | memcpy(ttm_kmap_obj_virtual(&map, &is_iomem), bytecode, size); | ||
725 | WARN_ON(is_iomem); | ||
726 | |||
727 | ttm_bo_kunmap(&map); | ||
728 | ret = ttm_bo_validate(&buf->base, &vmw_sys_placement, false, true); | ||
729 | WARN_ON(ret != 0); | ||
730 | ttm_bo_unreserve(&buf->base); | ||
731 | |||
732 | /* Create a guest-backed shader container backed by the dma buffer */ | ||
733 | ret = vmw_shader_alloc(man->dev_priv, buf, size, 0, shader_type, | ||
734 | tfile, &handle); | ||
735 | vmw_dmabuf_unreference(&buf); | ||
736 | if (unlikely(ret != 0)) | ||
737 | goto no_reserve; | ||
738 | /* | ||
739 | * Create a compat shader structure and stage it for insertion | ||
740 | * in the manager | ||
741 | */ | ||
742 | compat = kzalloc(sizeof(*compat), GFP_KERNEL); | ||
743 | if (compat == NULL) | ||
744 | goto no_compat; | ||
745 | |||
746 | compat->hash.key = user_key | (shader_type << 24); | ||
747 | ret = drm_ht_insert_item(&man->shaders, &compat->hash); | ||
748 | if (unlikely(ret != 0)) | ||
749 | goto out_invalid_key; | ||
750 | |||
751 | compat->state = VMW_COMPAT_ADD; | ||
752 | compat->handle = handle; | ||
753 | compat->tfile = tfile; | ||
754 | list_add_tail(&compat->head, list); | ||
755 | |||
756 | return 0; | ||
757 | |||
758 | out_invalid_key: | ||
759 | kfree(compat); | ||
760 | no_compat: | ||
761 | ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE); | ||
762 | no_reserve: | ||
763 | out: | ||
764 | return ret; | ||
765 | } | ||
766 | |||
767 | /** | ||
768 | * vmw_compat_shader_man_create - Create a compat shader manager | ||
769 | * | ||
770 | * @dev_priv: Pointer to a device private structure. | ||
771 | * | ||
772 | * Typically done at file open time. If successful returns a pointer to a | ||
773 | * compat shader manager. Otherwise returns an error pointer. | ||
774 | */ | ||
775 | struct vmw_compat_shader_manager * | ||
776 | vmw_compat_shader_man_create(struct vmw_private *dev_priv) | ||
777 | { | ||
778 | struct vmw_compat_shader_manager *man; | ||
779 | int ret; | ||
780 | |||
781 | man = kzalloc(sizeof(*man), GFP_KERNEL); | ||
782 | if (man == NULL) | ||
783 | return ERR_PTR(-ENOMEM); | ||
784 | |||
785 | man->dev_priv = dev_priv; | ||
786 | INIT_LIST_HEAD(&man->list); | ||
787 | ret = drm_ht_create(&man->shaders, VMW_COMPAT_SHADER_HT_ORDER); | ||
788 | if (ret == 0) | ||
789 | return man; | ||
790 | |||
791 | kfree(man); | ||
792 | return ERR_PTR(ret); | ||
793 | } | ||
794 | |||
795 | /** | ||
796 | * vmw_compat_shader_man_destroy - Destroy a compat shader manager | ||
797 | * | ||
798 | * @man: Pointer to the shader manager to destroy. | ||
799 | * | ||
800 | * Typically done at file close time. | ||
801 | */ | ||
802 | void vmw_compat_shader_man_destroy(struct vmw_compat_shader_manager *man) | ||
803 | { | ||
804 | struct vmw_compat_shader *entry, *next; | ||
805 | |||
806 | mutex_lock(&man->dev_priv->cmdbuf_mutex); | ||
807 | list_for_each_entry_safe(entry, next, &man->list, head) | ||
808 | vmw_compat_shader_free(man, entry); | ||
809 | |||
810 | mutex_unlock(&man->dev_priv->cmdbuf_mutex); | ||
811 | kfree(man); | ||
812 | } | ||