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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 * 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 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 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 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 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 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 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 */ 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 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 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 * 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 */ 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 * 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