]> Pileus Git - ~andy/linux/blob - drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
nfsd: fix lost nfserrno() call in nfsd_setattr()
[~andy/linux] / drivers / gpu / drm / vmwgfx / vmwgfx_shader.c
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 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
783         man->dev_priv = dev_priv;
784         INIT_LIST_HEAD(&man->list);
785         ret = drm_ht_create(&man->shaders, VMW_COMPAT_SHADER_HT_ORDER);
786         if (ret == 0)
787                 return man;
788
789         kfree(man);
790         return ERR_PTR(ret);
791 }
792
793 /**
794  * vmw_compat_shader_man_destroy - Destroy a compat shader manager
795  *
796  * @man: Pointer to the shader manager to destroy.
797  *
798  * Typically done at file close time.
799  */
800 void vmw_compat_shader_man_destroy(struct vmw_compat_shader_manager *man)
801 {
802         struct vmw_compat_shader *entry, *next;
803
804         mutex_lock(&man->dev_priv->cmdbuf_mutex);
805         list_for_each_entry_safe(entry, next, &man->list, head)
806                 vmw_compat_shader_free(man, entry);
807
808         mutex_unlock(&man->dev_priv->cmdbuf_mutex);
809         kfree(man);
810 }