• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2 /**************************************************************************
3  *
4  * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA
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 <drm/ttm/ttm_placement.h>
29 
30 #include "vmwgfx_binding.h"
31 #include "vmwgfx_bo.h"
32 #include "vmwgfx_drv.h"
33 #include "vmwgfx_resource_priv.h"
34 
35 struct vmw_user_context {
36 	struct ttm_base_object base;
37 	struct vmw_resource res;
38 	struct vmw_ctx_binding_state *cbs;
39 	struct vmw_cmdbuf_res_manager *man;
40 	struct vmw_resource *cotables[SVGA_COTABLE_MAX];
41 	spinlock_t cotable_lock;
42 	struct vmw_bo *dx_query_mob;
43 };
44 
45 static void vmw_user_context_free(struct vmw_resource *res);
46 static struct vmw_resource *
47 vmw_user_context_base_to_res(struct ttm_base_object *base);
48 
49 static int vmw_gb_context_create(struct vmw_resource *res);
50 static int vmw_gb_context_bind(struct vmw_resource *res,
51 			       struct ttm_validate_buffer *val_buf);
52 static int vmw_gb_context_unbind(struct vmw_resource *res,
53 				 bool readback,
54 				 struct ttm_validate_buffer *val_buf);
55 static int vmw_gb_context_destroy(struct vmw_resource *res);
56 static int vmw_dx_context_create(struct vmw_resource *res);
57 static int vmw_dx_context_bind(struct vmw_resource *res,
58 			       struct ttm_validate_buffer *val_buf);
59 static int vmw_dx_context_unbind(struct vmw_resource *res,
60 				 bool readback,
61 				 struct ttm_validate_buffer *val_buf);
62 static int vmw_dx_context_destroy(struct vmw_resource *res);
63 
64 static const struct vmw_user_resource_conv user_context_conv = {
65 	.object_type = VMW_RES_CONTEXT,
66 	.base_obj_to_res = vmw_user_context_base_to_res,
67 	.res_free = vmw_user_context_free
68 };
69 
70 const struct vmw_user_resource_conv *user_context_converter =
71 	&user_context_conv;
72 
73 
74 static const struct vmw_res_func vmw_legacy_context_func = {
75 	.res_type = vmw_res_context,
76 	.needs_guest_memory = false,
77 	.may_evict = false,
78 	.type_name = "legacy contexts",
79 	.domain = VMW_BO_DOMAIN_SYS,
80 	.busy_domain = VMW_BO_DOMAIN_SYS,
81 	.create = NULL,
82 	.destroy = NULL,
83 	.bind = NULL,
84 	.unbind = NULL
85 };
86 
87 static const struct vmw_res_func vmw_gb_context_func = {
88 	.res_type = vmw_res_context,
89 	.needs_guest_memory = true,
90 	.may_evict = true,
91 	.prio = 3,
92 	.dirty_prio = 3,
93 	.type_name = "guest backed contexts",
94 	.domain = VMW_BO_DOMAIN_MOB,
95 	.busy_domain = VMW_BO_DOMAIN_MOB,
96 	.create = vmw_gb_context_create,
97 	.destroy = vmw_gb_context_destroy,
98 	.bind = vmw_gb_context_bind,
99 	.unbind = vmw_gb_context_unbind
100 };
101 
102 static const struct vmw_res_func vmw_dx_context_func = {
103 	.res_type = vmw_res_dx_context,
104 	.needs_guest_memory = true,
105 	.may_evict = true,
106 	.prio = 3,
107 	.dirty_prio = 3,
108 	.type_name = "dx contexts",
109 	.domain = VMW_BO_DOMAIN_MOB,
110 	.busy_domain = VMW_BO_DOMAIN_MOB,
111 	.create = vmw_dx_context_create,
112 	.destroy = vmw_dx_context_destroy,
113 	.bind = vmw_dx_context_bind,
114 	.unbind = vmw_dx_context_unbind
115 };
116 
117 /*
118  * Context management:
119  */
120 
vmw_context_cotables_unref(struct vmw_private * dev_priv,struct vmw_user_context * uctx)121 static void vmw_context_cotables_unref(struct vmw_private *dev_priv,
122 				       struct vmw_user_context *uctx)
123 {
124 	struct vmw_resource *res;
125 	int i;
126 	u32 cotable_max = has_sm5_context(dev_priv) ?
127 		SVGA_COTABLE_MAX : SVGA_COTABLE_DX10_MAX;
128 
129 	for (i = 0; i < cotable_max; ++i) {
130 		spin_lock(&uctx->cotable_lock);
131 		res = uctx->cotables[i];
132 		uctx->cotables[i] = NULL;
133 		spin_unlock(&uctx->cotable_lock);
134 
135 		if (res)
136 			vmw_resource_unreference(&res);
137 	}
138 }
139 
vmw_hw_context_destroy(struct vmw_resource * res)140 static void vmw_hw_context_destroy(struct vmw_resource *res)
141 {
142 	struct vmw_user_context *uctx =
143 		container_of(res, struct vmw_user_context, res);
144 	struct vmw_private *dev_priv = res->dev_priv;
145 	struct {
146 		SVGA3dCmdHeader header;
147 		SVGA3dCmdDestroyContext body;
148 	} *cmd;
149 
150 
151 	if (res->func->destroy == vmw_gb_context_destroy ||
152 	    res->func->destroy == vmw_dx_context_destroy) {
153 		mutex_lock(&dev_priv->cmdbuf_mutex);
154 		vmw_cmdbuf_res_man_destroy(uctx->man);
155 		mutex_lock(&dev_priv->binding_mutex);
156 		vmw_binding_state_kill(uctx->cbs);
157 		(void) res->func->destroy(res);
158 		mutex_unlock(&dev_priv->binding_mutex);
159 		if (dev_priv->pinned_bo != NULL &&
160 		    !dev_priv->query_cid_valid)
161 			__vmw_execbuf_release_pinned_bo(dev_priv, NULL);
162 		mutex_unlock(&dev_priv->cmdbuf_mutex);
163 		vmw_context_cotables_unref(dev_priv, uctx);
164 		return;
165 	}
166 
167 	vmw_execbuf_release_pinned_bo(dev_priv);
168 	cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
169 	if (unlikely(cmd == NULL))
170 		return;
171 
172 	cmd->header.id = SVGA_3D_CMD_CONTEXT_DESTROY;
173 	cmd->header.size = sizeof(cmd->body);
174 	cmd->body.cid = res->id;
175 
176 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
177 	vmw_fifo_resource_dec(dev_priv);
178 }
179 
vmw_gb_context_init(struct vmw_private * dev_priv,bool dx,struct vmw_resource * res,void (* res_free)(struct vmw_resource * res))180 static int vmw_gb_context_init(struct vmw_private *dev_priv,
181 			       bool dx,
182 			       struct vmw_resource *res,
183 			       void (*res_free)(struct vmw_resource *res))
184 {
185 	int ret, i;
186 	struct vmw_user_context *uctx =
187 		container_of(res, struct vmw_user_context, res);
188 
189 	res->guest_memory_size = (dx ? sizeof(SVGADXContextMobFormat) :
190 				 sizeof(SVGAGBContextData));
191 	ret = vmw_resource_init(dev_priv, res, true,
192 				res_free,
193 				dx ? &vmw_dx_context_func :
194 				&vmw_gb_context_func);
195 	if (unlikely(ret != 0))
196 		goto out_err;
197 
198 	if (dev_priv->has_mob) {
199 		uctx->man = vmw_cmdbuf_res_man_create(dev_priv);
200 		if (IS_ERR(uctx->man)) {
201 			ret = PTR_ERR(uctx->man);
202 			uctx->man = NULL;
203 			goto out_err;
204 		}
205 	}
206 
207 	uctx->cbs = vmw_binding_state_alloc(dev_priv);
208 	if (IS_ERR(uctx->cbs)) {
209 		ret = PTR_ERR(uctx->cbs);
210 		goto out_err;
211 	}
212 
213 	spin_lock_init(&uctx->cotable_lock);
214 
215 	if (dx) {
216 		u32 cotable_max = has_sm5_context(dev_priv) ?
217 			SVGA_COTABLE_MAX : SVGA_COTABLE_DX10_MAX;
218 		for (i = 0; i < cotable_max; ++i) {
219 			uctx->cotables[i] = vmw_cotable_alloc(dev_priv,
220 							      &uctx->res, i);
221 			if (IS_ERR(uctx->cotables[i])) {
222 				ret = PTR_ERR(uctx->cotables[i]);
223 				goto out_cotables;
224 			}
225 		}
226 	}
227 
228 	res->hw_destroy = vmw_hw_context_destroy;
229 	return 0;
230 
231 out_cotables:
232 	vmw_context_cotables_unref(dev_priv, uctx);
233 out_err:
234 	if (res_free)
235 		res_free(res);
236 	else
237 		kfree(res);
238 	return ret;
239 }
240 
vmw_context_init(struct vmw_private * dev_priv,struct vmw_resource * res,void (* res_free)(struct vmw_resource * res),bool dx)241 static int vmw_context_init(struct vmw_private *dev_priv,
242 			    struct vmw_resource *res,
243 			    void (*res_free)(struct vmw_resource *res),
244 			    bool dx)
245 {
246 	int ret;
247 
248 	struct {
249 		SVGA3dCmdHeader header;
250 		SVGA3dCmdDefineContext body;
251 	} *cmd;
252 
253 	if (dev_priv->has_mob)
254 		return vmw_gb_context_init(dev_priv, dx, res, res_free);
255 
256 	ret = vmw_resource_init(dev_priv, res, false,
257 				res_free, &vmw_legacy_context_func);
258 
259 	if (unlikely(ret != 0)) {
260 		DRM_ERROR("Failed to allocate a resource id.\n");
261 		goto out_early;
262 	}
263 
264 	if (unlikely(res->id >= SVGA3D_HB_MAX_CONTEXT_IDS)) {
265 		DRM_ERROR("Out of hw context ids.\n");
266 		vmw_resource_unreference(&res);
267 		return -ENOMEM;
268 	}
269 
270 	cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
271 	if (unlikely(cmd == NULL)) {
272 		vmw_resource_unreference(&res);
273 		return -ENOMEM;
274 	}
275 
276 	cmd->header.id = SVGA_3D_CMD_CONTEXT_DEFINE;
277 	cmd->header.size = sizeof(cmd->body);
278 	cmd->body.cid = res->id;
279 
280 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
281 	vmw_fifo_resource_inc(dev_priv);
282 	res->hw_destroy = vmw_hw_context_destroy;
283 	return 0;
284 
285 out_early:
286 	if (res_free == NULL)
287 		kfree(res);
288 	else
289 		res_free(res);
290 	return ret;
291 }
292 
293 
294 /*
295  * GB context.
296  */
297 
vmw_gb_context_create(struct vmw_resource * res)298 static int vmw_gb_context_create(struct vmw_resource *res)
299 {
300 	struct vmw_private *dev_priv = res->dev_priv;
301 	int ret;
302 	struct {
303 		SVGA3dCmdHeader header;
304 		SVGA3dCmdDefineGBContext body;
305 	} *cmd;
306 
307 	if (likely(res->id != -1))
308 		return 0;
309 
310 	ret = vmw_resource_alloc_id(res);
311 	if (unlikely(ret != 0)) {
312 		DRM_ERROR("Failed to allocate a context id.\n");
313 		goto out_no_id;
314 	}
315 
316 	if (unlikely(res->id >= VMWGFX_NUM_GB_CONTEXT)) {
317 		ret = -EBUSY;
318 		goto out_no_fifo;
319 	}
320 
321 	cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
322 	if (unlikely(cmd == NULL)) {
323 		ret = -ENOMEM;
324 		goto out_no_fifo;
325 	}
326 
327 	cmd->header.id = SVGA_3D_CMD_DEFINE_GB_CONTEXT;
328 	cmd->header.size = sizeof(cmd->body);
329 	cmd->body.cid = res->id;
330 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
331 	vmw_fifo_resource_inc(dev_priv);
332 
333 	return 0;
334 
335 out_no_fifo:
336 	vmw_resource_release_id(res);
337 out_no_id:
338 	return ret;
339 }
340 
vmw_gb_context_bind(struct vmw_resource * res,struct ttm_validate_buffer * val_buf)341 static int vmw_gb_context_bind(struct vmw_resource *res,
342 			       struct ttm_validate_buffer *val_buf)
343 {
344 	struct vmw_private *dev_priv = res->dev_priv;
345 	struct {
346 		SVGA3dCmdHeader header;
347 		SVGA3dCmdBindGBContext body;
348 	} *cmd;
349 	struct ttm_buffer_object *bo = val_buf->bo;
350 
351 	BUG_ON(bo->resource->mem_type != VMW_PL_MOB);
352 
353 	cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
354 	if (unlikely(cmd == NULL))
355 		return -ENOMEM;
356 
357 	cmd->header.id = SVGA_3D_CMD_BIND_GB_CONTEXT;
358 	cmd->header.size = sizeof(cmd->body);
359 	cmd->body.cid = res->id;
360 	cmd->body.mobid = bo->resource->start;
361 	cmd->body.validContents = res->guest_memory_dirty;
362 	res->guest_memory_dirty = false;
363 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
364 
365 	return 0;
366 }
367 
vmw_gb_context_unbind(struct vmw_resource * res,bool readback,struct ttm_validate_buffer * val_buf)368 static int vmw_gb_context_unbind(struct vmw_resource *res,
369 				 bool readback,
370 				 struct ttm_validate_buffer *val_buf)
371 {
372 	struct vmw_private *dev_priv = res->dev_priv;
373 	struct ttm_buffer_object *bo = val_buf->bo;
374 	struct vmw_fence_obj *fence;
375 	struct vmw_user_context *uctx =
376 		container_of(res, struct vmw_user_context, res);
377 
378 	struct {
379 		SVGA3dCmdHeader header;
380 		SVGA3dCmdReadbackGBContext body;
381 	} *cmd1;
382 	struct {
383 		SVGA3dCmdHeader header;
384 		SVGA3dCmdBindGBContext body;
385 	} *cmd2;
386 	uint32_t submit_size;
387 	uint8_t *cmd;
388 
389 
390 	BUG_ON(bo->resource->mem_type != VMW_PL_MOB);
391 
392 	mutex_lock(&dev_priv->binding_mutex);
393 	vmw_binding_state_scrub(uctx->cbs);
394 
395 	submit_size = sizeof(*cmd2) + (readback ? sizeof(*cmd1) : 0);
396 
397 	cmd = VMW_CMD_RESERVE(dev_priv, submit_size);
398 	if (unlikely(cmd == NULL)) {
399 		mutex_unlock(&dev_priv->binding_mutex);
400 		return -ENOMEM;
401 	}
402 
403 	cmd2 = (void *) cmd;
404 	if (readback) {
405 		cmd1 = (void *) cmd;
406 		cmd1->header.id = SVGA_3D_CMD_READBACK_GB_CONTEXT;
407 		cmd1->header.size = sizeof(cmd1->body);
408 		cmd1->body.cid = res->id;
409 		cmd2 = (void *) (&cmd1[1]);
410 	}
411 	cmd2->header.id = SVGA_3D_CMD_BIND_GB_CONTEXT;
412 	cmd2->header.size = sizeof(cmd2->body);
413 	cmd2->body.cid = res->id;
414 	cmd2->body.mobid = SVGA3D_INVALID_ID;
415 
416 	vmw_cmd_commit(dev_priv, submit_size);
417 	mutex_unlock(&dev_priv->binding_mutex);
418 
419 	/*
420 	 * Create a fence object and fence the backup buffer.
421 	 */
422 
423 	(void) vmw_execbuf_fence_commands(NULL, dev_priv,
424 					  &fence, NULL);
425 
426 	vmw_bo_fence_single(bo, fence);
427 
428 	if (likely(fence != NULL))
429 		vmw_fence_obj_unreference(&fence);
430 
431 	return 0;
432 }
433 
vmw_gb_context_destroy(struct vmw_resource * res)434 static int vmw_gb_context_destroy(struct vmw_resource *res)
435 {
436 	struct vmw_private *dev_priv = res->dev_priv;
437 	struct {
438 		SVGA3dCmdHeader header;
439 		SVGA3dCmdDestroyGBContext body;
440 	} *cmd;
441 
442 	if (likely(res->id == -1))
443 		return 0;
444 
445 	cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
446 	if (unlikely(cmd == NULL))
447 		return -ENOMEM;
448 
449 	cmd->header.id = SVGA_3D_CMD_DESTROY_GB_CONTEXT;
450 	cmd->header.size = sizeof(cmd->body);
451 	cmd->body.cid = res->id;
452 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
453 	if (dev_priv->query_cid == res->id)
454 		dev_priv->query_cid_valid = false;
455 	vmw_resource_release_id(res);
456 	vmw_fifo_resource_dec(dev_priv);
457 
458 	return 0;
459 }
460 
461 /*
462  * DX context.
463  */
464 
vmw_dx_context_create(struct vmw_resource * res)465 static int vmw_dx_context_create(struct vmw_resource *res)
466 {
467 	struct vmw_private *dev_priv = res->dev_priv;
468 	int ret;
469 	struct {
470 		SVGA3dCmdHeader header;
471 		SVGA3dCmdDXDefineContext body;
472 	} *cmd;
473 
474 	if (likely(res->id != -1))
475 		return 0;
476 
477 	ret = vmw_resource_alloc_id(res);
478 	if (unlikely(ret != 0)) {
479 		DRM_ERROR("Failed to allocate a context id.\n");
480 		goto out_no_id;
481 	}
482 
483 	if (unlikely(res->id >= VMWGFX_NUM_DXCONTEXT)) {
484 		ret = -EBUSY;
485 		goto out_no_fifo;
486 	}
487 
488 	cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
489 	if (unlikely(cmd == NULL)) {
490 		ret = -ENOMEM;
491 		goto out_no_fifo;
492 	}
493 
494 	cmd->header.id = SVGA_3D_CMD_DX_DEFINE_CONTEXT;
495 	cmd->header.size = sizeof(cmd->body);
496 	cmd->body.cid = res->id;
497 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
498 	vmw_fifo_resource_inc(dev_priv);
499 
500 	return 0;
501 
502 out_no_fifo:
503 	vmw_resource_release_id(res);
504 out_no_id:
505 	return ret;
506 }
507 
vmw_dx_context_bind(struct vmw_resource * res,struct ttm_validate_buffer * val_buf)508 static int vmw_dx_context_bind(struct vmw_resource *res,
509 			       struct ttm_validate_buffer *val_buf)
510 {
511 	struct vmw_private *dev_priv = res->dev_priv;
512 	struct {
513 		SVGA3dCmdHeader header;
514 		SVGA3dCmdDXBindContext body;
515 	} *cmd;
516 	struct ttm_buffer_object *bo = val_buf->bo;
517 
518 	BUG_ON(bo->resource->mem_type != VMW_PL_MOB);
519 
520 	cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
521 	if (unlikely(cmd == NULL))
522 		return -ENOMEM;
523 
524 	cmd->header.id = SVGA_3D_CMD_DX_BIND_CONTEXT;
525 	cmd->header.size = sizeof(cmd->body);
526 	cmd->body.cid = res->id;
527 	cmd->body.mobid = bo->resource->start;
528 	cmd->body.validContents = res->guest_memory_dirty;
529 	res->guest_memory_dirty = false;
530 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
531 
532 
533 	return 0;
534 }
535 
536 /**
537  * vmw_dx_context_scrub_cotables - Scrub all bindings and
538  * cotables from a context
539  *
540  * @ctx: Pointer to the context resource
541  * @readback: Whether to save the otable contents on scrubbing.
542  *
543  * COtables must be unbound before their context, but unbinding requires
544  * the backup buffer being reserved, whereas scrubbing does not.
545  * This function scrubs all cotables of a context, potentially reading back
546  * the contents into their backup buffers. However, scrubbing cotables
547  * also makes the device context invalid, so scrub all bindings first so
548  * that doesn't have to be done later with an invalid context.
549  */
vmw_dx_context_scrub_cotables(struct vmw_resource * ctx,bool readback)550 void vmw_dx_context_scrub_cotables(struct vmw_resource *ctx,
551 				   bool readback)
552 {
553 	struct vmw_user_context *uctx =
554 		container_of(ctx, struct vmw_user_context, res);
555 	u32 cotable_max = has_sm5_context(ctx->dev_priv) ?
556 		SVGA_COTABLE_MAX : SVGA_COTABLE_DX10_MAX;
557 	int i;
558 
559 	vmw_binding_state_scrub(uctx->cbs);
560 	for (i = 0; i < cotable_max; ++i) {
561 		struct vmw_resource *res;
562 
563 		/* Avoid racing with ongoing cotable destruction. */
564 		spin_lock(&uctx->cotable_lock);
565 		res = uctx->cotables[vmw_cotable_scrub_order[i]];
566 		if (res)
567 			res = vmw_resource_reference_unless_doomed(res);
568 		spin_unlock(&uctx->cotable_lock);
569 		if (!res)
570 			continue;
571 
572 		WARN_ON(vmw_cotable_scrub(res, readback));
573 		vmw_resource_unreference(&res);
574 	}
575 }
576 
vmw_dx_context_unbind(struct vmw_resource * res,bool readback,struct ttm_validate_buffer * val_buf)577 static int vmw_dx_context_unbind(struct vmw_resource *res,
578 				 bool readback,
579 				 struct ttm_validate_buffer *val_buf)
580 {
581 	struct vmw_private *dev_priv = res->dev_priv;
582 	struct ttm_buffer_object *bo = val_buf->bo;
583 	struct vmw_fence_obj *fence;
584 	struct vmw_user_context *uctx =
585 		container_of(res, struct vmw_user_context, res);
586 
587 	struct {
588 		SVGA3dCmdHeader header;
589 		SVGA3dCmdDXReadbackContext body;
590 	} *cmd1;
591 	struct {
592 		SVGA3dCmdHeader header;
593 		SVGA3dCmdDXBindContext body;
594 	} *cmd2;
595 	uint32_t submit_size;
596 	uint8_t *cmd;
597 
598 
599 	BUG_ON(bo->resource->mem_type != VMW_PL_MOB);
600 
601 	mutex_lock(&dev_priv->binding_mutex);
602 	vmw_dx_context_scrub_cotables(res, readback);
603 
604 	if (uctx->dx_query_mob && uctx->dx_query_mob->dx_query_ctx &&
605 	    readback) {
606 		WARN_ON(uctx->dx_query_mob->dx_query_ctx != res);
607 		if (vmw_query_readback_all(uctx->dx_query_mob))
608 			DRM_ERROR("Failed to read back query states\n");
609 	}
610 
611 	submit_size = sizeof(*cmd2) + (readback ? sizeof(*cmd1) : 0);
612 
613 	cmd = VMW_CMD_RESERVE(dev_priv, submit_size);
614 	if (unlikely(cmd == NULL)) {
615 		mutex_unlock(&dev_priv->binding_mutex);
616 		return -ENOMEM;
617 	}
618 
619 	cmd2 = (void *) cmd;
620 	if (readback) {
621 		cmd1 = (void *) cmd;
622 		cmd1->header.id = SVGA_3D_CMD_DX_READBACK_CONTEXT;
623 		cmd1->header.size = sizeof(cmd1->body);
624 		cmd1->body.cid = res->id;
625 		cmd2 = (void *) (&cmd1[1]);
626 	}
627 	cmd2->header.id = SVGA_3D_CMD_DX_BIND_CONTEXT;
628 	cmd2->header.size = sizeof(cmd2->body);
629 	cmd2->body.cid = res->id;
630 	cmd2->body.mobid = SVGA3D_INVALID_ID;
631 
632 	vmw_cmd_commit(dev_priv, submit_size);
633 	mutex_unlock(&dev_priv->binding_mutex);
634 
635 	/*
636 	 * Create a fence object and fence the backup buffer.
637 	 */
638 
639 	(void) vmw_execbuf_fence_commands(NULL, dev_priv,
640 					  &fence, NULL);
641 
642 	vmw_bo_fence_single(bo, fence);
643 
644 	if (likely(fence != NULL))
645 		vmw_fence_obj_unreference(&fence);
646 
647 	return 0;
648 }
649 
vmw_dx_context_destroy(struct vmw_resource * res)650 static int vmw_dx_context_destroy(struct vmw_resource *res)
651 {
652 	struct vmw_private *dev_priv = res->dev_priv;
653 	struct {
654 		SVGA3dCmdHeader header;
655 		SVGA3dCmdDXDestroyContext body;
656 	} *cmd;
657 
658 	if (likely(res->id == -1))
659 		return 0;
660 
661 	cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
662 	if (unlikely(cmd == NULL))
663 		return -ENOMEM;
664 
665 	cmd->header.id = SVGA_3D_CMD_DX_DESTROY_CONTEXT;
666 	cmd->header.size = sizeof(cmd->body);
667 	cmd->body.cid = res->id;
668 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
669 	if (dev_priv->query_cid == res->id)
670 		dev_priv->query_cid_valid = false;
671 	vmw_resource_release_id(res);
672 	vmw_fifo_resource_dec(dev_priv);
673 
674 	return 0;
675 }
676 
677 /*
678  * User-space context management:
679  */
680 
681 static struct vmw_resource *
vmw_user_context_base_to_res(struct ttm_base_object * base)682 vmw_user_context_base_to_res(struct ttm_base_object *base)
683 {
684 	return &(container_of(base, struct vmw_user_context, base)->res);
685 }
686 
vmw_user_context_free(struct vmw_resource * res)687 static void vmw_user_context_free(struct vmw_resource *res)
688 {
689 	struct vmw_user_context *ctx =
690 	    container_of(res, struct vmw_user_context, res);
691 
692 	if (ctx->cbs)
693 		vmw_binding_state_free(ctx->cbs);
694 
695 	(void) vmw_context_bind_dx_query(res, NULL);
696 
697 	ttm_base_object_kfree(ctx, base);
698 }
699 
700 /*
701  * This function is called when user space has no more references on the
702  * base object. It releases the base-object's reference on the resource object.
703  */
704 
vmw_user_context_base_release(struct ttm_base_object ** p_base)705 static void vmw_user_context_base_release(struct ttm_base_object **p_base)
706 {
707 	struct ttm_base_object *base = *p_base;
708 	struct vmw_user_context *ctx =
709 	    container_of(base, struct vmw_user_context, base);
710 	struct vmw_resource *res = &ctx->res;
711 
712 	*p_base = NULL;
713 	vmw_resource_unreference(&res);
714 }
715 
vmw_context_destroy_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)716 int vmw_context_destroy_ioctl(struct drm_device *dev, void *data,
717 			      struct drm_file *file_priv)
718 {
719 	struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
720 	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
721 
722 	return ttm_ref_object_base_unref(tfile, arg->cid);
723 }
724 
vmw_context_define(struct drm_device * dev,void * data,struct drm_file * file_priv,bool dx)725 static int vmw_context_define(struct drm_device *dev, void *data,
726 			      struct drm_file *file_priv, bool dx)
727 {
728 	struct vmw_private *dev_priv = vmw_priv(dev);
729 	struct vmw_user_context *ctx;
730 	struct vmw_resource *res;
731 	struct vmw_resource *tmp;
732 	struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
733 	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
734 	int ret;
735 
736 	if (!has_sm4_context(dev_priv) && dx) {
737 		VMW_DEBUG_USER("DX contexts not supported by device.\n");
738 		return -EINVAL;
739 	}
740 
741 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
742 	if (unlikely(!ctx)) {
743 		ret = -ENOMEM;
744 		goto out_ret;
745 	}
746 
747 	res = &ctx->res;
748 	ctx->base.shareable = false;
749 	ctx->base.tfile = NULL;
750 
751 	/*
752 	 * From here on, the destructor takes over resource freeing.
753 	 */
754 
755 	ret = vmw_context_init(dev_priv, res, vmw_user_context_free, dx);
756 	if (unlikely(ret != 0))
757 		goto out_ret;
758 
759 	tmp = vmw_resource_reference(&ctx->res);
760 	ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT,
761 				   &vmw_user_context_base_release);
762 
763 	if (unlikely(ret != 0)) {
764 		vmw_resource_unreference(&tmp);
765 		goto out_err;
766 	}
767 
768 	arg->cid = ctx->base.handle;
769 out_err:
770 	vmw_resource_unreference(&res);
771 out_ret:
772 	return ret;
773 }
774 
vmw_context_define_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)775 int vmw_context_define_ioctl(struct drm_device *dev, void *data,
776 			     struct drm_file *file_priv)
777 {
778 	return vmw_context_define(dev, data, file_priv, false);
779 }
780 
vmw_extended_context_define_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)781 int vmw_extended_context_define_ioctl(struct drm_device *dev, void *data,
782 				      struct drm_file *file_priv)
783 {
784 	union drm_vmw_extended_context_arg *arg = (typeof(arg)) data;
785 	struct drm_vmw_context_arg *rep = &arg->rep;
786 
787 	switch (arg->req) {
788 	case drm_vmw_context_legacy:
789 		return vmw_context_define(dev, rep, file_priv, false);
790 	case drm_vmw_context_dx:
791 		return vmw_context_define(dev, rep, file_priv, true);
792 	default:
793 		break;
794 	}
795 	return -EINVAL;
796 }
797 
798 /**
799  * vmw_context_binding_list - Return a list of context bindings
800  *
801  * @ctx: The context resource
802  *
803  * Returns the current list of bindings of the given context. Note that
804  * this list becomes stale as soon as the dev_priv::binding_mutex is unlocked.
805  */
vmw_context_binding_list(struct vmw_resource * ctx)806 struct list_head *vmw_context_binding_list(struct vmw_resource *ctx)
807 {
808 	struct vmw_user_context *uctx =
809 		container_of(ctx, struct vmw_user_context, res);
810 
811 	return vmw_binding_state_list(uctx->cbs);
812 }
813 
vmw_context_res_man(struct vmw_resource * ctx)814 struct vmw_cmdbuf_res_manager *vmw_context_res_man(struct vmw_resource *ctx)
815 {
816 	return container_of(ctx, struct vmw_user_context, res)->man;
817 }
818 
vmw_context_cotable(struct vmw_resource * ctx,SVGACOTableType cotable_type)819 struct vmw_resource *vmw_context_cotable(struct vmw_resource *ctx,
820 					 SVGACOTableType cotable_type)
821 {
822 	u32 cotable_max = has_sm5_context(ctx->dev_priv) ?
823 		SVGA_COTABLE_MAX : SVGA_COTABLE_DX10_MAX;
824 
825 	if (cotable_type >= cotable_max)
826 		return ERR_PTR(-EINVAL);
827 
828 	return container_of(ctx, struct vmw_user_context, res)->
829 		cotables[cotable_type];
830 }
831 
832 /**
833  * vmw_context_binding_state -
834  * Return a pointer to a context binding state structure
835  *
836  * @ctx: The context resource
837  *
838  * Returns the current state of bindings of the given context. Note that
839  * this state becomes stale as soon as the dev_priv::binding_mutex is unlocked.
840  */
841 struct vmw_ctx_binding_state *
vmw_context_binding_state(struct vmw_resource * ctx)842 vmw_context_binding_state(struct vmw_resource *ctx)
843 {
844 	return container_of(ctx, struct vmw_user_context, res)->cbs;
845 }
846 
847 /**
848  * vmw_context_bind_dx_query -
849  * Sets query MOB for the context.  If @mob is NULL, then this function will
850  * remove the association between the MOB and the context.  This function
851  * assumes the binding_mutex is held.
852  *
853  * @ctx_res: The context resource
854  * @mob: a reference to the query MOB
855  *
856  * Returns -EINVAL if a MOB has already been set and does not match the one
857  * specified in the parameter.  0 otherwise.
858  */
vmw_context_bind_dx_query(struct vmw_resource * ctx_res,struct vmw_bo * mob)859 int vmw_context_bind_dx_query(struct vmw_resource *ctx_res,
860 			      struct vmw_bo *mob)
861 {
862 	struct vmw_user_context *uctx =
863 		container_of(ctx_res, struct vmw_user_context, res);
864 
865 	if (mob == NULL) {
866 		if (uctx->dx_query_mob) {
867 			uctx->dx_query_mob->dx_query_ctx = NULL;
868 			vmw_bo_unreference(&uctx->dx_query_mob);
869 			uctx->dx_query_mob = NULL;
870 		}
871 
872 		return 0;
873 	}
874 
875 	/* Can only have one MOB per context for queries */
876 	if (uctx->dx_query_mob && uctx->dx_query_mob != mob)
877 		return -EINVAL;
878 
879 	mob->dx_query_ctx  = ctx_res;
880 
881 	if (!uctx->dx_query_mob)
882 		uctx->dx_query_mob = vmw_bo_reference(mob);
883 
884 	return 0;
885 }
886 
887 /**
888  * vmw_context_get_dx_query_mob - Returns non-counted reference to DX query mob
889  *
890  * @ctx_res: The context resource
891  */
892 struct vmw_bo *
vmw_context_get_dx_query_mob(struct vmw_resource * ctx_res)893 vmw_context_get_dx_query_mob(struct vmw_resource *ctx_res)
894 {
895 	struct vmw_user_context *uctx =
896 		container_of(ctx_res, struct vmw_user_context, res);
897 
898 	return uctx->dx_query_mob;
899 }
900