diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_resource.c')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 1187 |
1 files changed, 1187 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c new file mode 100644 index 000000000000..f8fbbc67a406 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | |||
@@ -0,0 +1,1187 @@ | |||
1 | /************************************************************************** | ||
2 | * | ||
3 | * Copyright © 2009 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_drm.h" | ||
30 | #include "ttm/ttm_object.h" | ||
31 | #include "ttm/ttm_placement.h" | ||
32 | #include "drmP.h" | ||
33 | |||
34 | #define VMW_RES_CONTEXT ttm_driver_type0 | ||
35 | #define VMW_RES_SURFACE ttm_driver_type1 | ||
36 | #define VMW_RES_STREAM ttm_driver_type2 | ||
37 | |||
38 | struct vmw_user_context { | ||
39 | struct ttm_base_object base; | ||
40 | struct vmw_resource res; | ||
41 | }; | ||
42 | |||
43 | struct vmw_user_surface { | ||
44 | struct ttm_base_object base; | ||
45 | struct vmw_surface srf; | ||
46 | }; | ||
47 | |||
48 | struct vmw_user_dma_buffer { | ||
49 | struct ttm_base_object base; | ||
50 | struct vmw_dma_buffer dma; | ||
51 | }; | ||
52 | |||
53 | struct vmw_bo_user_rep { | ||
54 | uint32_t handle; | ||
55 | uint64_t map_handle; | ||
56 | }; | ||
57 | |||
58 | struct vmw_stream { | ||
59 | struct vmw_resource res; | ||
60 | uint32_t stream_id; | ||
61 | }; | ||
62 | |||
63 | struct vmw_user_stream { | ||
64 | struct ttm_base_object base; | ||
65 | struct vmw_stream stream; | ||
66 | }; | ||
67 | |||
68 | static inline struct vmw_dma_buffer * | ||
69 | vmw_dma_buffer(struct ttm_buffer_object *bo) | ||
70 | { | ||
71 | return container_of(bo, struct vmw_dma_buffer, base); | ||
72 | } | ||
73 | |||
74 | static inline struct vmw_user_dma_buffer * | ||
75 | vmw_user_dma_buffer(struct ttm_buffer_object *bo) | ||
76 | { | ||
77 | struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); | ||
78 | return container_of(vmw_bo, struct vmw_user_dma_buffer, dma); | ||
79 | } | ||
80 | |||
81 | struct vmw_resource *vmw_resource_reference(struct vmw_resource *res) | ||
82 | { | ||
83 | kref_get(&res->kref); | ||
84 | return res; | ||
85 | } | ||
86 | |||
87 | static void vmw_resource_release(struct kref *kref) | ||
88 | { | ||
89 | struct vmw_resource *res = | ||
90 | container_of(kref, struct vmw_resource, kref); | ||
91 | struct vmw_private *dev_priv = res->dev_priv; | ||
92 | |||
93 | idr_remove(res->idr, res->id); | ||
94 | write_unlock(&dev_priv->resource_lock); | ||
95 | |||
96 | if (likely(res->hw_destroy != NULL)) | ||
97 | res->hw_destroy(res); | ||
98 | |||
99 | if (res->res_free != NULL) | ||
100 | res->res_free(res); | ||
101 | else | ||
102 | kfree(res); | ||
103 | |||
104 | write_lock(&dev_priv->resource_lock); | ||
105 | } | ||
106 | |||
107 | void vmw_resource_unreference(struct vmw_resource **p_res) | ||
108 | { | ||
109 | struct vmw_resource *res = *p_res; | ||
110 | struct vmw_private *dev_priv = res->dev_priv; | ||
111 | |||
112 | *p_res = NULL; | ||
113 | write_lock(&dev_priv->resource_lock); | ||
114 | kref_put(&res->kref, vmw_resource_release); | ||
115 | write_unlock(&dev_priv->resource_lock); | ||
116 | } | ||
117 | |||
118 | static int vmw_resource_init(struct vmw_private *dev_priv, | ||
119 | struct vmw_resource *res, | ||
120 | struct idr *idr, | ||
121 | enum ttm_object_type obj_type, | ||
122 | void (*res_free) (struct vmw_resource *res)) | ||
123 | { | ||
124 | int ret; | ||
125 | |||
126 | kref_init(&res->kref); | ||
127 | res->hw_destroy = NULL; | ||
128 | res->res_free = res_free; | ||
129 | res->res_type = obj_type; | ||
130 | res->idr = idr; | ||
131 | res->avail = false; | ||
132 | res->dev_priv = dev_priv; | ||
133 | |||
134 | do { | ||
135 | if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0)) | ||
136 | return -ENOMEM; | ||
137 | |||
138 | write_lock(&dev_priv->resource_lock); | ||
139 | ret = idr_get_new_above(idr, res, 1, &res->id); | ||
140 | write_unlock(&dev_priv->resource_lock); | ||
141 | |||
142 | } while (ret == -EAGAIN); | ||
143 | |||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * vmw_resource_activate | ||
149 | * | ||
150 | * @res: Pointer to the newly created resource | ||
151 | * @hw_destroy: Destroy function. NULL if none. | ||
152 | * | ||
153 | * Activate a resource after the hardware has been made aware of it. | ||
154 | * Set tye destroy function to @destroy. Typically this frees the | ||
155 | * resource and destroys the hardware resources associated with it. | ||
156 | * Activate basically means that the function vmw_resource_lookup will | ||
157 | * find it. | ||
158 | */ | ||
159 | |||
160 | static void vmw_resource_activate(struct vmw_resource *res, | ||
161 | void (*hw_destroy) (struct vmw_resource *)) | ||
162 | { | ||
163 | struct vmw_private *dev_priv = res->dev_priv; | ||
164 | |||
165 | write_lock(&dev_priv->resource_lock); | ||
166 | res->avail = true; | ||
167 | res->hw_destroy = hw_destroy; | ||
168 | write_unlock(&dev_priv->resource_lock); | ||
169 | } | ||
170 | |||
171 | struct vmw_resource *vmw_resource_lookup(struct vmw_private *dev_priv, | ||
172 | struct idr *idr, int id) | ||
173 | { | ||
174 | struct vmw_resource *res; | ||
175 | |||
176 | read_lock(&dev_priv->resource_lock); | ||
177 | res = idr_find(idr, id); | ||
178 | if (res && res->avail) | ||
179 | kref_get(&res->kref); | ||
180 | else | ||
181 | res = NULL; | ||
182 | read_unlock(&dev_priv->resource_lock); | ||
183 | |||
184 | if (unlikely(res == NULL)) | ||
185 | return NULL; | ||
186 | |||
187 | return res; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * Context management: | ||
192 | */ | ||
193 | |||
194 | static void vmw_hw_context_destroy(struct vmw_resource *res) | ||
195 | { | ||
196 | |||
197 | struct vmw_private *dev_priv = res->dev_priv; | ||
198 | struct { | ||
199 | SVGA3dCmdHeader header; | ||
200 | SVGA3dCmdDestroyContext body; | ||
201 | } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | ||
202 | |||
203 | if (unlikely(cmd == NULL)) { | ||
204 | DRM_ERROR("Failed reserving FIFO space for surface " | ||
205 | "destruction.\n"); | ||
206 | return; | ||
207 | } | ||
208 | |||
209 | cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY); | ||
210 | cmd->header.size = cpu_to_le32(sizeof(cmd->body)); | ||
211 | cmd->body.cid = cpu_to_le32(res->id); | ||
212 | |||
213 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | ||
214 | } | ||
215 | |||
216 | static int vmw_context_init(struct vmw_private *dev_priv, | ||
217 | struct vmw_resource *res, | ||
218 | void (*res_free) (struct vmw_resource *res)) | ||
219 | { | ||
220 | int ret; | ||
221 | |||
222 | struct { | ||
223 | SVGA3dCmdHeader header; | ||
224 | SVGA3dCmdDefineContext body; | ||
225 | } *cmd; | ||
226 | |||
227 | ret = vmw_resource_init(dev_priv, res, &dev_priv->context_idr, | ||
228 | VMW_RES_CONTEXT, res_free); | ||
229 | |||
230 | if (unlikely(ret != 0)) { | ||
231 | if (res_free == NULL) | ||
232 | kfree(res); | ||
233 | else | ||
234 | res_free(res); | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | ||
239 | if (unlikely(cmd == NULL)) { | ||
240 | DRM_ERROR("Fifo reserve failed.\n"); | ||
241 | vmw_resource_unreference(&res); | ||
242 | return -ENOMEM; | ||
243 | } | ||
244 | |||
245 | cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE); | ||
246 | cmd->header.size = cpu_to_le32(sizeof(cmd->body)); | ||
247 | cmd->body.cid = cpu_to_le32(res->id); | ||
248 | |||
249 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | ||
250 | vmw_resource_activate(res, vmw_hw_context_destroy); | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv) | ||
255 | { | ||
256 | struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL); | ||
257 | int ret; | ||
258 | |||
259 | if (unlikely(res == NULL)) | ||
260 | return NULL; | ||
261 | |||
262 | ret = vmw_context_init(dev_priv, res, NULL); | ||
263 | return (ret == 0) ? res : NULL; | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * User-space context management: | ||
268 | */ | ||
269 | |||
270 | static void vmw_user_context_free(struct vmw_resource *res) | ||
271 | { | ||
272 | struct vmw_user_context *ctx = | ||
273 | container_of(res, struct vmw_user_context, res); | ||
274 | |||
275 | kfree(ctx); | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * This function is called when user space has no more references on the | ||
280 | * base object. It releases the base-object's reference on the resource object. | ||
281 | */ | ||
282 | |||
283 | static void vmw_user_context_base_release(struct ttm_base_object **p_base) | ||
284 | { | ||
285 | struct ttm_base_object *base = *p_base; | ||
286 | struct vmw_user_context *ctx = | ||
287 | container_of(base, struct vmw_user_context, base); | ||
288 | struct vmw_resource *res = &ctx->res; | ||
289 | |||
290 | *p_base = NULL; | ||
291 | vmw_resource_unreference(&res); | ||
292 | } | ||
293 | |||
294 | int vmw_context_destroy_ioctl(struct drm_device *dev, void *data, | ||
295 | struct drm_file *file_priv) | ||
296 | { | ||
297 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
298 | struct vmw_resource *res; | ||
299 | struct vmw_user_context *ctx; | ||
300 | struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; | ||
301 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
302 | int ret = 0; | ||
303 | |||
304 | res = vmw_resource_lookup(dev_priv, &dev_priv->context_idr, arg->cid); | ||
305 | if (unlikely(res == NULL)) | ||
306 | return -EINVAL; | ||
307 | |||
308 | if (res->res_free != &vmw_user_context_free) { | ||
309 | ret = -EINVAL; | ||
310 | goto out; | ||
311 | } | ||
312 | |||
313 | ctx = container_of(res, struct vmw_user_context, res); | ||
314 | if (ctx->base.tfile != tfile && !ctx->base.shareable) { | ||
315 | ret = -EPERM; | ||
316 | goto out; | ||
317 | } | ||
318 | |||
319 | ttm_ref_object_base_unref(tfile, ctx->base.hash.key, TTM_REF_USAGE); | ||
320 | out: | ||
321 | vmw_resource_unreference(&res); | ||
322 | return ret; | ||
323 | } | ||
324 | |||
325 | int vmw_context_define_ioctl(struct drm_device *dev, void *data, | ||
326 | struct drm_file *file_priv) | ||
327 | { | ||
328 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
329 | struct vmw_user_context *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); | ||
330 | struct vmw_resource *res; | ||
331 | struct vmw_resource *tmp; | ||
332 | struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; | ||
333 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
334 | int ret; | ||
335 | |||
336 | if (unlikely(ctx == NULL)) | ||
337 | return -ENOMEM; | ||
338 | |||
339 | res = &ctx->res; | ||
340 | ctx->base.shareable = false; | ||
341 | ctx->base.tfile = NULL; | ||
342 | |||
343 | ret = vmw_context_init(dev_priv, res, vmw_user_context_free); | ||
344 | if (unlikely(ret != 0)) | ||
345 | return ret; | ||
346 | |||
347 | tmp = vmw_resource_reference(&ctx->res); | ||
348 | ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, | ||
349 | &vmw_user_context_base_release, NULL); | ||
350 | |||
351 | if (unlikely(ret != 0)) { | ||
352 | vmw_resource_unreference(&tmp); | ||
353 | goto out_err; | ||
354 | } | ||
355 | |||
356 | arg->cid = res->id; | ||
357 | out_err: | ||
358 | vmw_resource_unreference(&res); | ||
359 | return ret; | ||
360 | |||
361 | } | ||
362 | |||
363 | int vmw_context_check(struct vmw_private *dev_priv, | ||
364 | struct ttm_object_file *tfile, | ||
365 | int id) | ||
366 | { | ||
367 | struct vmw_resource *res; | ||
368 | int ret = 0; | ||
369 | |||
370 | read_lock(&dev_priv->resource_lock); | ||
371 | res = idr_find(&dev_priv->context_idr, id); | ||
372 | if (res && res->avail) { | ||
373 | struct vmw_user_context *ctx = | ||
374 | container_of(res, struct vmw_user_context, res); | ||
375 | if (ctx->base.tfile != tfile && !ctx->base.shareable) | ||
376 | ret = -EPERM; | ||
377 | } else | ||
378 | ret = -EINVAL; | ||
379 | read_unlock(&dev_priv->resource_lock); | ||
380 | |||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | |||
385 | /** | ||
386 | * Surface management. | ||
387 | */ | ||
388 | |||
389 | static void vmw_hw_surface_destroy(struct vmw_resource *res) | ||
390 | { | ||
391 | |||
392 | struct vmw_private *dev_priv = res->dev_priv; | ||
393 | struct { | ||
394 | SVGA3dCmdHeader header; | ||
395 | SVGA3dCmdDestroySurface body; | ||
396 | } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | ||
397 | |||
398 | if (unlikely(cmd == NULL)) { | ||
399 | DRM_ERROR("Failed reserving FIFO space for surface " | ||
400 | "destruction.\n"); | ||
401 | return; | ||
402 | } | ||
403 | |||
404 | cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DESTROY); | ||
405 | cmd->header.size = cpu_to_le32(sizeof(cmd->body)); | ||
406 | cmd->body.sid = cpu_to_le32(res->id); | ||
407 | |||
408 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | ||
409 | } | ||
410 | |||
411 | void vmw_surface_res_free(struct vmw_resource *res) | ||
412 | { | ||
413 | struct vmw_surface *srf = container_of(res, struct vmw_surface, res); | ||
414 | |||
415 | kfree(srf->sizes); | ||
416 | kfree(srf->snooper.image); | ||
417 | kfree(srf); | ||
418 | } | ||
419 | |||
420 | int vmw_surface_init(struct vmw_private *dev_priv, | ||
421 | struct vmw_surface *srf, | ||
422 | void (*res_free) (struct vmw_resource *res)) | ||
423 | { | ||
424 | int ret; | ||
425 | struct { | ||
426 | SVGA3dCmdHeader header; | ||
427 | SVGA3dCmdDefineSurface body; | ||
428 | } *cmd; | ||
429 | SVGA3dSize *cmd_size; | ||
430 | struct vmw_resource *res = &srf->res; | ||
431 | struct drm_vmw_size *src_size; | ||
432 | size_t submit_size; | ||
433 | uint32_t cmd_len; | ||
434 | int i; | ||
435 | |||
436 | BUG_ON(res_free == NULL); | ||
437 | ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr, | ||
438 | VMW_RES_SURFACE, res_free); | ||
439 | |||
440 | if (unlikely(ret != 0)) { | ||
441 | res_free(res); | ||
442 | return ret; | ||
443 | } | ||
444 | |||
445 | submit_size = sizeof(*cmd) + srf->num_sizes * sizeof(SVGA3dSize); | ||
446 | cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize); | ||
447 | |||
448 | cmd = vmw_fifo_reserve(dev_priv, submit_size); | ||
449 | if (unlikely(cmd == NULL)) { | ||
450 | DRM_ERROR("Fifo reserve failed for create surface.\n"); | ||
451 | vmw_resource_unreference(&res); | ||
452 | return -ENOMEM; | ||
453 | } | ||
454 | |||
455 | cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DEFINE); | ||
456 | cmd->header.size = cpu_to_le32(cmd_len); | ||
457 | cmd->body.sid = cpu_to_le32(res->id); | ||
458 | cmd->body.surfaceFlags = cpu_to_le32(srf->flags); | ||
459 | cmd->body.format = cpu_to_le32(srf->format); | ||
460 | for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { | ||
461 | cmd->body.face[i].numMipLevels = | ||
462 | cpu_to_le32(srf->mip_levels[i]); | ||
463 | } | ||
464 | |||
465 | cmd += 1; | ||
466 | cmd_size = (SVGA3dSize *) cmd; | ||
467 | src_size = srf->sizes; | ||
468 | |||
469 | for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) { | ||
470 | cmd_size->width = cpu_to_le32(src_size->width); | ||
471 | cmd_size->height = cpu_to_le32(src_size->height); | ||
472 | cmd_size->depth = cpu_to_le32(src_size->depth); | ||
473 | } | ||
474 | |||
475 | vmw_fifo_commit(dev_priv, submit_size); | ||
476 | vmw_resource_activate(res, vmw_hw_surface_destroy); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static void vmw_user_surface_free(struct vmw_resource *res) | ||
481 | { | ||
482 | struct vmw_surface *srf = container_of(res, struct vmw_surface, res); | ||
483 | struct vmw_user_surface *user_srf = | ||
484 | container_of(srf, struct vmw_user_surface, srf); | ||
485 | |||
486 | kfree(srf->sizes); | ||
487 | kfree(srf->snooper.image); | ||
488 | kfree(user_srf); | ||
489 | } | ||
490 | |||
491 | int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv, | ||
492 | struct ttm_object_file *tfile, | ||
493 | uint32_t handle, struct vmw_surface **out) | ||
494 | { | ||
495 | struct vmw_resource *res; | ||
496 | struct vmw_surface *srf; | ||
497 | struct vmw_user_surface *user_srf; | ||
498 | struct ttm_base_object *base; | ||
499 | int ret = -EINVAL; | ||
500 | |||
501 | base = ttm_base_object_lookup(tfile, handle); | ||
502 | if (unlikely(base == NULL)) | ||
503 | return -EINVAL; | ||
504 | |||
505 | if (unlikely(base->object_type != VMW_RES_SURFACE)) | ||
506 | goto out_bad_resource; | ||
507 | |||
508 | user_srf = container_of(base, struct vmw_user_surface, base); | ||
509 | srf = &user_srf->srf; | ||
510 | res = &srf->res; | ||
511 | |||
512 | read_lock(&dev_priv->resource_lock); | ||
513 | |||
514 | if (!res->avail || res->res_free != &vmw_user_surface_free) { | ||
515 | read_unlock(&dev_priv->resource_lock); | ||
516 | goto out_bad_resource; | ||
517 | } | ||
518 | |||
519 | kref_get(&res->kref); | ||
520 | read_unlock(&dev_priv->resource_lock); | ||
521 | |||
522 | *out = srf; | ||
523 | ret = 0; | ||
524 | |||
525 | out_bad_resource: | ||
526 | ttm_base_object_unref(&base); | ||
527 | |||
528 | return ret; | ||
529 | } | ||
530 | |||
531 | static void vmw_user_surface_base_release(struct ttm_base_object **p_base) | ||
532 | { | ||
533 | struct ttm_base_object *base = *p_base; | ||
534 | struct vmw_user_surface *user_srf = | ||
535 | container_of(base, struct vmw_user_surface, base); | ||
536 | struct vmw_resource *res = &user_srf->srf.res; | ||
537 | |||
538 | *p_base = NULL; | ||
539 | vmw_resource_unreference(&res); | ||
540 | } | ||
541 | |||
542 | int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, | ||
543 | struct drm_file *file_priv) | ||
544 | { | ||
545 | struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data; | ||
546 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
547 | |||
548 | return ttm_ref_object_base_unref(tfile, arg->sid, TTM_REF_USAGE); | ||
549 | } | ||
550 | |||
551 | int vmw_surface_define_ioctl(struct drm_device *dev, void *data, | ||
552 | struct drm_file *file_priv) | ||
553 | { | ||
554 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
555 | struct vmw_user_surface *user_srf = | ||
556 | kmalloc(sizeof(*user_srf), GFP_KERNEL); | ||
557 | struct vmw_surface *srf; | ||
558 | struct vmw_resource *res; | ||
559 | struct vmw_resource *tmp; | ||
560 | union drm_vmw_surface_create_arg *arg = | ||
561 | (union drm_vmw_surface_create_arg *)data; | ||
562 | struct drm_vmw_surface_create_req *req = &arg->req; | ||
563 | struct drm_vmw_surface_arg *rep = &arg->rep; | ||
564 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
565 | struct drm_vmw_size __user *user_sizes; | ||
566 | int ret; | ||
567 | int i; | ||
568 | |||
569 | if (unlikely(user_srf == NULL)) | ||
570 | return -ENOMEM; | ||
571 | |||
572 | srf = &user_srf->srf; | ||
573 | res = &srf->res; | ||
574 | |||
575 | srf->flags = req->flags; | ||
576 | srf->format = req->format; | ||
577 | srf->scanout = req->scanout; | ||
578 | memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); | ||
579 | srf->num_sizes = 0; | ||
580 | for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) | ||
581 | srf->num_sizes += srf->mip_levels[i]; | ||
582 | |||
583 | if (srf->num_sizes > DRM_VMW_MAX_SURFACE_FACES * | ||
584 | DRM_VMW_MAX_MIP_LEVELS) { | ||
585 | ret = -EINVAL; | ||
586 | goto out_err0; | ||
587 | } | ||
588 | |||
589 | srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL); | ||
590 | if (unlikely(srf->sizes == NULL)) { | ||
591 | ret = -ENOMEM; | ||
592 | goto out_err0; | ||
593 | } | ||
594 | |||
595 | user_sizes = (struct drm_vmw_size __user *)(unsigned long) | ||
596 | req->size_addr; | ||
597 | |||
598 | ret = copy_from_user(srf->sizes, user_sizes, | ||
599 | srf->num_sizes * sizeof(*srf->sizes)); | ||
600 | if (unlikely(ret != 0)) | ||
601 | goto out_err1; | ||
602 | |||
603 | if (srf->scanout && | ||
604 | srf->num_sizes == 1 && | ||
605 | srf->sizes[0].width == 64 && | ||
606 | srf->sizes[0].height == 64 && | ||
607 | srf->format == SVGA3D_A8R8G8B8) { | ||
608 | |||
609 | srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL); | ||
610 | /* clear the image */ | ||
611 | if (srf->snooper.image) { | ||
612 | memset(srf->snooper.image, 0x00, 64 * 64 * 4); | ||
613 | } else { | ||
614 | DRM_ERROR("Failed to allocate cursor_image\n"); | ||
615 | ret = -ENOMEM; | ||
616 | goto out_err1; | ||
617 | } | ||
618 | } else { | ||
619 | srf->snooper.image = NULL; | ||
620 | } | ||
621 | srf->snooper.crtc = NULL; | ||
622 | |||
623 | user_srf->base.shareable = false; | ||
624 | user_srf->base.tfile = NULL; | ||
625 | |||
626 | /** | ||
627 | * From this point, the generic resource management functions | ||
628 | * destroy the object on failure. | ||
629 | */ | ||
630 | |||
631 | ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free); | ||
632 | if (unlikely(ret != 0)) | ||
633 | return ret; | ||
634 | |||
635 | tmp = vmw_resource_reference(&srf->res); | ||
636 | ret = ttm_base_object_init(tfile, &user_srf->base, | ||
637 | req->shareable, VMW_RES_SURFACE, | ||
638 | &vmw_user_surface_base_release, NULL); | ||
639 | |||
640 | if (unlikely(ret != 0)) { | ||
641 | vmw_resource_unreference(&tmp); | ||
642 | vmw_resource_unreference(&res); | ||
643 | return ret; | ||
644 | } | ||
645 | |||
646 | rep->sid = user_srf->base.hash.key; | ||
647 | if (rep->sid == SVGA3D_INVALID_ID) | ||
648 | DRM_ERROR("Created bad Surface ID.\n"); | ||
649 | |||
650 | vmw_resource_unreference(&res); | ||
651 | return 0; | ||
652 | out_err1: | ||
653 | kfree(srf->sizes); | ||
654 | out_err0: | ||
655 | kfree(user_srf); | ||
656 | return ret; | ||
657 | } | ||
658 | |||
659 | int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, | ||
660 | struct drm_file *file_priv) | ||
661 | { | ||
662 | union drm_vmw_surface_reference_arg *arg = | ||
663 | (union drm_vmw_surface_reference_arg *)data; | ||
664 | struct drm_vmw_surface_arg *req = &arg->req; | ||
665 | struct drm_vmw_surface_create_req *rep = &arg->rep; | ||
666 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
667 | struct vmw_surface *srf; | ||
668 | struct vmw_user_surface *user_srf; | ||
669 | struct drm_vmw_size __user *user_sizes; | ||
670 | struct ttm_base_object *base; | ||
671 | int ret = -EINVAL; | ||
672 | |||
673 | base = ttm_base_object_lookup(tfile, req->sid); | ||
674 | if (unlikely(base == NULL)) { | ||
675 | DRM_ERROR("Could not find surface to reference.\n"); | ||
676 | return -EINVAL; | ||
677 | } | ||
678 | |||
679 | if (unlikely(base->object_type != VMW_RES_SURFACE)) | ||
680 | goto out_bad_resource; | ||
681 | |||
682 | user_srf = container_of(base, struct vmw_user_surface, base); | ||
683 | srf = &user_srf->srf; | ||
684 | |||
685 | ret = ttm_ref_object_add(tfile, &user_srf->base, TTM_REF_USAGE, NULL); | ||
686 | if (unlikely(ret != 0)) { | ||
687 | DRM_ERROR("Could not add a reference to a surface.\n"); | ||
688 | goto out_no_reference; | ||
689 | } | ||
690 | |||
691 | rep->flags = srf->flags; | ||
692 | rep->format = srf->format; | ||
693 | memcpy(rep->mip_levels, srf->mip_levels, sizeof(srf->mip_levels)); | ||
694 | user_sizes = (struct drm_vmw_size __user *)(unsigned long) | ||
695 | rep->size_addr; | ||
696 | |||
697 | if (user_sizes) | ||
698 | ret = copy_to_user(user_sizes, srf->sizes, | ||
699 | srf->num_sizes * sizeof(*srf->sizes)); | ||
700 | if (unlikely(ret != 0)) | ||
701 | DRM_ERROR("copy_to_user failed %p %u\n", | ||
702 | user_sizes, srf->num_sizes); | ||
703 | out_bad_resource: | ||
704 | out_no_reference: | ||
705 | ttm_base_object_unref(&base); | ||
706 | |||
707 | return ret; | ||
708 | } | ||
709 | |||
710 | int vmw_surface_check(struct vmw_private *dev_priv, | ||
711 | struct ttm_object_file *tfile, | ||
712 | uint32_t handle, int *id) | ||
713 | { | ||
714 | struct ttm_base_object *base; | ||
715 | struct vmw_user_surface *user_srf; | ||
716 | |||
717 | int ret = -EPERM; | ||
718 | |||
719 | base = ttm_base_object_lookup(tfile, handle); | ||
720 | if (unlikely(base == NULL)) | ||
721 | return -EINVAL; | ||
722 | |||
723 | if (unlikely(base->object_type != VMW_RES_SURFACE)) | ||
724 | goto out_bad_surface; | ||
725 | |||
726 | user_srf = container_of(base, struct vmw_user_surface, base); | ||
727 | *id = user_srf->srf.res.id; | ||
728 | ret = 0; | ||
729 | |||
730 | out_bad_surface: | ||
731 | /** | ||
732 | * FIXME: May deadlock here when called from the | ||
733 | * command parsing code. | ||
734 | */ | ||
735 | |||
736 | ttm_base_object_unref(&base); | ||
737 | return ret; | ||
738 | } | ||
739 | |||
740 | /** | ||
741 | * Buffer management. | ||
742 | */ | ||
743 | |||
744 | static size_t vmw_dmabuf_acc_size(struct ttm_bo_global *glob, | ||
745 | unsigned long num_pages) | ||
746 | { | ||
747 | static size_t bo_user_size = ~0; | ||
748 | |||
749 | size_t page_array_size = | ||
750 | (num_pages * sizeof(void *) + PAGE_SIZE - 1) & PAGE_MASK; | ||
751 | |||
752 | if (unlikely(bo_user_size == ~0)) { | ||
753 | bo_user_size = glob->ttm_bo_extra_size + | ||
754 | ttm_round_pot(sizeof(struct vmw_dma_buffer)); | ||
755 | } | ||
756 | |||
757 | return bo_user_size + page_array_size; | ||
758 | } | ||
759 | |||
760 | void vmw_dmabuf_gmr_unbind(struct ttm_buffer_object *bo) | ||
761 | { | ||
762 | struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); | ||
763 | struct ttm_bo_global *glob = bo->glob; | ||
764 | struct vmw_private *dev_priv = | ||
765 | container_of(bo->bdev, struct vmw_private, bdev); | ||
766 | |||
767 | if (vmw_bo->gmr_bound) { | ||
768 | vmw_gmr_unbind(dev_priv, vmw_bo->gmr_id); | ||
769 | spin_lock(&glob->lru_lock); | ||
770 | ida_remove(&dev_priv->gmr_ida, vmw_bo->gmr_id); | ||
771 | spin_unlock(&glob->lru_lock); | ||
772 | vmw_bo->gmr_bound = false; | ||
773 | } | ||
774 | } | ||
775 | |||
776 | void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo) | ||
777 | { | ||
778 | struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); | ||
779 | struct ttm_bo_global *glob = bo->glob; | ||
780 | |||
781 | vmw_dmabuf_gmr_unbind(bo); | ||
782 | ttm_mem_global_free(glob->mem_glob, bo->acc_size); | ||
783 | kfree(vmw_bo); | ||
784 | } | ||
785 | |||
786 | int vmw_dmabuf_init(struct vmw_private *dev_priv, | ||
787 | struct vmw_dma_buffer *vmw_bo, | ||
788 | size_t size, struct ttm_placement *placement, | ||
789 | bool interruptible, | ||
790 | void (*bo_free) (struct ttm_buffer_object *bo)) | ||
791 | { | ||
792 | struct ttm_bo_device *bdev = &dev_priv->bdev; | ||
793 | struct ttm_mem_global *mem_glob = bdev->glob->mem_glob; | ||
794 | size_t acc_size; | ||
795 | int ret; | ||
796 | |||
797 | BUG_ON(!bo_free); | ||
798 | |||
799 | acc_size = | ||
800 | vmw_dmabuf_acc_size(bdev->glob, | ||
801 | (size + PAGE_SIZE - 1) >> PAGE_SHIFT); | ||
802 | |||
803 | ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false); | ||
804 | if (unlikely(ret != 0)) { | ||
805 | /* we must free the bo here as | ||
806 | * ttm_buffer_object_init does so as well */ | ||
807 | bo_free(&vmw_bo->base); | ||
808 | return ret; | ||
809 | } | ||
810 | |||
811 | memset(vmw_bo, 0, sizeof(*vmw_bo)); | ||
812 | |||
813 | INIT_LIST_HEAD(&vmw_bo->gmr_lru); | ||
814 | INIT_LIST_HEAD(&vmw_bo->validate_list); | ||
815 | vmw_bo->gmr_id = 0; | ||
816 | vmw_bo->gmr_bound = false; | ||
817 | |||
818 | ret = ttm_bo_init(bdev, &vmw_bo->base, size, | ||
819 | ttm_bo_type_device, placement, | ||
820 | 0, 0, interruptible, | ||
821 | NULL, acc_size, bo_free); | ||
822 | return ret; | ||
823 | } | ||
824 | |||
825 | static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo) | ||
826 | { | ||
827 | struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo); | ||
828 | struct ttm_bo_global *glob = bo->glob; | ||
829 | |||
830 | vmw_dmabuf_gmr_unbind(bo); | ||
831 | ttm_mem_global_free(glob->mem_glob, bo->acc_size); | ||
832 | kfree(vmw_user_bo); | ||
833 | } | ||
834 | |||
835 | static void vmw_user_dmabuf_release(struct ttm_base_object **p_base) | ||
836 | { | ||
837 | struct vmw_user_dma_buffer *vmw_user_bo; | ||
838 | struct ttm_base_object *base = *p_base; | ||
839 | struct ttm_buffer_object *bo; | ||
840 | |||
841 | *p_base = NULL; | ||
842 | |||
843 | if (unlikely(base == NULL)) | ||
844 | return; | ||
845 | |||
846 | vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base); | ||
847 | bo = &vmw_user_bo->dma.base; | ||
848 | ttm_bo_unref(&bo); | ||
849 | } | ||
850 | |||
851 | int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data, | ||
852 | struct drm_file *file_priv) | ||
853 | { | ||
854 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
855 | union drm_vmw_alloc_dmabuf_arg *arg = | ||
856 | (union drm_vmw_alloc_dmabuf_arg *)data; | ||
857 | struct drm_vmw_alloc_dmabuf_req *req = &arg->req; | ||
858 | struct drm_vmw_dmabuf_rep *rep = &arg->rep; | ||
859 | struct vmw_user_dma_buffer *vmw_user_bo; | ||
860 | struct ttm_buffer_object *tmp; | ||
861 | struct vmw_master *vmaster = vmw_master(file_priv->master); | ||
862 | int ret; | ||
863 | |||
864 | vmw_user_bo = kzalloc(sizeof(*vmw_user_bo), GFP_KERNEL); | ||
865 | if (unlikely(vmw_user_bo == NULL)) | ||
866 | return -ENOMEM; | ||
867 | |||
868 | ret = ttm_read_lock(&vmaster->lock, true); | ||
869 | if (unlikely(ret != 0)) { | ||
870 | kfree(vmw_user_bo); | ||
871 | return ret; | ||
872 | } | ||
873 | |||
874 | ret = vmw_dmabuf_init(dev_priv, &vmw_user_bo->dma, req->size, | ||
875 | &vmw_vram_sys_placement, true, | ||
876 | &vmw_user_dmabuf_destroy); | ||
877 | if (unlikely(ret != 0)) | ||
878 | return ret; | ||
879 | |||
880 | tmp = ttm_bo_reference(&vmw_user_bo->dma.base); | ||
881 | ret = ttm_base_object_init(vmw_fpriv(file_priv)->tfile, | ||
882 | &vmw_user_bo->base, | ||
883 | false, | ||
884 | ttm_buffer_type, | ||
885 | &vmw_user_dmabuf_release, NULL); | ||
886 | if (unlikely(ret != 0)) { | ||
887 | ttm_bo_unref(&tmp); | ||
888 | } else { | ||
889 | rep->handle = vmw_user_bo->base.hash.key; | ||
890 | rep->map_handle = vmw_user_bo->dma.base.addr_space_offset; | ||
891 | rep->cur_gmr_id = vmw_user_bo->base.hash.key; | ||
892 | rep->cur_gmr_offset = 0; | ||
893 | } | ||
894 | ttm_bo_unref(&tmp); | ||
895 | |||
896 | ttm_read_unlock(&vmaster->lock); | ||
897 | |||
898 | return 0; | ||
899 | } | ||
900 | |||
901 | int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data, | ||
902 | struct drm_file *file_priv) | ||
903 | { | ||
904 | struct drm_vmw_unref_dmabuf_arg *arg = | ||
905 | (struct drm_vmw_unref_dmabuf_arg *)data; | ||
906 | |||
907 | return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, | ||
908 | arg->handle, | ||
909 | TTM_REF_USAGE); | ||
910 | } | ||
911 | |||
912 | uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo, | ||
913 | uint32_t cur_validate_node) | ||
914 | { | ||
915 | struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); | ||
916 | |||
917 | if (likely(vmw_bo->on_validate_list)) | ||
918 | return vmw_bo->cur_validate_node; | ||
919 | |||
920 | vmw_bo->cur_validate_node = cur_validate_node; | ||
921 | vmw_bo->on_validate_list = true; | ||
922 | |||
923 | return cur_validate_node; | ||
924 | } | ||
925 | |||
926 | void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo) | ||
927 | { | ||
928 | struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); | ||
929 | |||
930 | vmw_bo->on_validate_list = false; | ||
931 | } | ||
932 | |||
933 | uint32_t vmw_dmabuf_gmr(struct ttm_buffer_object *bo) | ||
934 | { | ||
935 | struct vmw_dma_buffer *vmw_bo; | ||
936 | |||
937 | if (bo->mem.mem_type == TTM_PL_VRAM) | ||
938 | return SVGA_GMR_FRAMEBUFFER; | ||
939 | |||
940 | vmw_bo = vmw_dma_buffer(bo); | ||
941 | |||
942 | return (vmw_bo->gmr_bound) ? vmw_bo->gmr_id : SVGA_GMR_NULL; | ||
943 | } | ||
944 | |||
945 | void vmw_dmabuf_set_gmr(struct ttm_buffer_object *bo, uint32_t id) | ||
946 | { | ||
947 | struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); | ||
948 | vmw_bo->gmr_bound = true; | ||
949 | vmw_bo->gmr_id = id; | ||
950 | } | ||
951 | |||
952 | int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, | ||
953 | uint32_t handle, struct vmw_dma_buffer **out) | ||
954 | { | ||
955 | struct vmw_user_dma_buffer *vmw_user_bo; | ||
956 | struct ttm_base_object *base; | ||
957 | |||
958 | base = ttm_base_object_lookup(tfile, handle); | ||
959 | if (unlikely(base == NULL)) { | ||
960 | printk(KERN_ERR "Invalid buffer object handle 0x%08lx.\n", | ||
961 | (unsigned long)handle); | ||
962 | return -ESRCH; | ||
963 | } | ||
964 | |||
965 | if (unlikely(base->object_type != ttm_buffer_type)) { | ||
966 | ttm_base_object_unref(&base); | ||
967 | printk(KERN_ERR "Invalid buffer object handle 0x%08lx.\n", | ||
968 | (unsigned long)handle); | ||
969 | return -EINVAL; | ||
970 | } | ||
971 | |||
972 | vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base); | ||
973 | (void)ttm_bo_reference(&vmw_user_bo->dma.base); | ||
974 | ttm_base_object_unref(&base); | ||
975 | *out = &vmw_user_bo->dma; | ||
976 | |||
977 | return 0; | ||
978 | } | ||
979 | |||
980 | /** | ||
981 | * TODO: Implement a gmr id eviction mechanism. Currently we just fail | ||
982 | * when we're out of ids, causing GMR space to be allocated | ||
983 | * out of VRAM. | ||
984 | */ | ||
985 | |||
986 | int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id) | ||
987 | { | ||
988 | struct ttm_bo_global *glob = dev_priv->bdev.glob; | ||
989 | int id; | ||
990 | int ret; | ||
991 | |||
992 | do { | ||
993 | if (unlikely(ida_pre_get(&dev_priv->gmr_ida, GFP_KERNEL) == 0)) | ||
994 | return -ENOMEM; | ||
995 | |||
996 | spin_lock(&glob->lru_lock); | ||
997 | ret = ida_get_new(&dev_priv->gmr_ida, &id); | ||
998 | spin_unlock(&glob->lru_lock); | ||
999 | } while (ret == -EAGAIN); | ||
1000 | |||
1001 | if (unlikely(ret != 0)) | ||
1002 | return ret; | ||
1003 | |||
1004 | if (unlikely(id >= dev_priv->max_gmr_ids)) { | ||
1005 | spin_lock(&glob->lru_lock); | ||
1006 | ida_remove(&dev_priv->gmr_ida, id); | ||
1007 | spin_unlock(&glob->lru_lock); | ||
1008 | return -EBUSY; | ||
1009 | } | ||
1010 | |||
1011 | *p_id = (uint32_t) id; | ||
1012 | return 0; | ||
1013 | } | ||
1014 | |||
1015 | /* | ||
1016 | * Stream managment | ||
1017 | */ | ||
1018 | |||
1019 | static void vmw_stream_destroy(struct vmw_resource *res) | ||
1020 | { | ||
1021 | struct vmw_private *dev_priv = res->dev_priv; | ||
1022 | struct vmw_stream *stream; | ||
1023 | int ret; | ||
1024 | |||
1025 | DRM_INFO("%s: unref\n", __func__); | ||
1026 | stream = container_of(res, struct vmw_stream, res); | ||
1027 | |||
1028 | ret = vmw_overlay_unref(dev_priv, stream->stream_id); | ||
1029 | WARN_ON(ret != 0); | ||
1030 | } | ||
1031 | |||
1032 | static int vmw_stream_init(struct vmw_private *dev_priv, | ||
1033 | struct vmw_stream *stream, | ||
1034 | void (*res_free) (struct vmw_resource *res)) | ||
1035 | { | ||
1036 | struct vmw_resource *res = &stream->res; | ||
1037 | int ret; | ||
1038 | |||
1039 | ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr, | ||
1040 | VMW_RES_STREAM, res_free); | ||
1041 | |||
1042 | if (unlikely(ret != 0)) { | ||
1043 | if (res_free == NULL) | ||
1044 | kfree(stream); | ||
1045 | else | ||
1046 | res_free(&stream->res); | ||
1047 | return ret; | ||
1048 | } | ||
1049 | |||
1050 | ret = vmw_overlay_claim(dev_priv, &stream->stream_id); | ||
1051 | if (ret) { | ||
1052 | vmw_resource_unreference(&res); | ||
1053 | return ret; | ||
1054 | } | ||
1055 | |||
1056 | DRM_INFO("%s: claimed\n", __func__); | ||
1057 | |||
1058 | vmw_resource_activate(&stream->res, vmw_stream_destroy); | ||
1059 | return 0; | ||
1060 | } | ||
1061 | |||
1062 | /** | ||
1063 | * User-space context management: | ||
1064 | */ | ||
1065 | |||
1066 | static void vmw_user_stream_free(struct vmw_resource *res) | ||
1067 | { | ||
1068 | struct vmw_user_stream *stream = | ||
1069 | container_of(res, struct vmw_user_stream, stream.res); | ||
1070 | |||
1071 | kfree(stream); | ||
1072 | } | ||
1073 | |||
1074 | /** | ||
1075 | * This function is called when user space has no more references on the | ||
1076 | * base object. It releases the base-object's reference on the resource object. | ||
1077 | */ | ||
1078 | |||
1079 | static void vmw_user_stream_base_release(struct ttm_base_object **p_base) | ||
1080 | { | ||
1081 | struct ttm_base_object *base = *p_base; | ||
1082 | struct vmw_user_stream *stream = | ||
1083 | container_of(base, struct vmw_user_stream, base); | ||
1084 | struct vmw_resource *res = &stream->stream.res; | ||
1085 | |||
1086 | *p_base = NULL; | ||
1087 | vmw_resource_unreference(&res); | ||
1088 | } | ||
1089 | |||
1090 | int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, | ||
1091 | struct drm_file *file_priv) | ||
1092 | { | ||
1093 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
1094 | struct vmw_resource *res; | ||
1095 | struct vmw_user_stream *stream; | ||
1096 | struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; | ||
1097 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
1098 | int ret = 0; | ||
1099 | |||
1100 | res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, arg->stream_id); | ||
1101 | if (unlikely(res == NULL)) | ||
1102 | return -EINVAL; | ||
1103 | |||
1104 | if (res->res_free != &vmw_user_stream_free) { | ||
1105 | ret = -EINVAL; | ||
1106 | goto out; | ||
1107 | } | ||
1108 | |||
1109 | stream = container_of(res, struct vmw_user_stream, stream.res); | ||
1110 | if (stream->base.tfile != tfile) { | ||
1111 | ret = -EINVAL; | ||
1112 | goto out; | ||
1113 | } | ||
1114 | |||
1115 | ttm_ref_object_base_unref(tfile, stream->base.hash.key, TTM_REF_USAGE); | ||
1116 | out: | ||
1117 | vmw_resource_unreference(&res); | ||
1118 | return ret; | ||
1119 | } | ||
1120 | |||
1121 | int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, | ||
1122 | struct drm_file *file_priv) | ||
1123 | { | ||
1124 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
1125 | struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL); | ||
1126 | struct vmw_resource *res; | ||
1127 | struct vmw_resource *tmp; | ||
1128 | struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; | ||
1129 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
1130 | int ret; | ||
1131 | |||
1132 | if (unlikely(stream == NULL)) | ||
1133 | return -ENOMEM; | ||
1134 | |||
1135 | res = &stream->stream.res; | ||
1136 | stream->base.shareable = false; | ||
1137 | stream->base.tfile = NULL; | ||
1138 | |||
1139 | ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free); | ||
1140 | if (unlikely(ret != 0)) | ||
1141 | return ret; | ||
1142 | |||
1143 | tmp = vmw_resource_reference(res); | ||
1144 | ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM, | ||
1145 | &vmw_user_stream_base_release, NULL); | ||
1146 | |||
1147 | if (unlikely(ret != 0)) { | ||
1148 | vmw_resource_unreference(&tmp); | ||
1149 | goto out_err; | ||
1150 | } | ||
1151 | |||
1152 | arg->stream_id = res->id; | ||
1153 | out_err: | ||
1154 | vmw_resource_unreference(&res); | ||
1155 | return ret; | ||
1156 | } | ||
1157 | |||
1158 | int vmw_user_stream_lookup(struct vmw_private *dev_priv, | ||
1159 | struct ttm_object_file *tfile, | ||
1160 | uint32_t *inout_id, struct vmw_resource **out) | ||
1161 | { | ||
1162 | struct vmw_user_stream *stream; | ||
1163 | struct vmw_resource *res; | ||
1164 | int ret; | ||
1165 | |||
1166 | res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, *inout_id); | ||
1167 | if (unlikely(res == NULL)) | ||
1168 | return -EINVAL; | ||
1169 | |||
1170 | if (res->res_free != &vmw_user_stream_free) { | ||
1171 | ret = -EINVAL; | ||
1172 | goto err_ref; | ||
1173 | } | ||
1174 | |||
1175 | stream = container_of(res, struct vmw_user_stream, stream.res); | ||
1176 | if (stream->base.tfile != tfile) { | ||
1177 | ret = -EPERM; | ||
1178 | goto err_ref; | ||
1179 | } | ||
1180 | |||
1181 | *inout_id = stream->stream.stream_id; | ||
1182 | *out = res; | ||
1183 | return 0; | ||
1184 | err_ref: | ||
1185 | vmw_resource_unreference(&res); | ||
1186 | return ret; | ||
1187 | } | ||