1 // SPDX-License-Identifier: GPL-2.0 OR MIT 2 /************************************************************************** 3 * 4 * Copyright 2014-2015 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 * Treat context OTables as resources to make use of the resource 29 * backing MOB eviction mechanism, that is used to read back the COTable 30 * whenever the backing MOB is evicted. 31 */ 32 33 #include <drm/ttm/ttm_placement.h> 34 35 #include "vmwgfx_drv.h" 36 #include "vmwgfx_resource_priv.h" 37 #include "vmwgfx_so.h" 38 39 /** 40 * struct vmw_cotable - Context Object Table resource 41 * 42 * @res: struct vmw_resource we are deriving from. 43 * @ctx: non-refcounted pointer to the owning context. 44 * @size_read_back: Size of data read back during eviction. 45 * @seen_entries: Seen entries in command stream for this cotable. 46 * @type: The cotable type. 47 * @scrubbed: Whether the cotable has been scrubbed. 48 * @resource_list: List of resources in the cotable. 49 */ 50 struct vmw_cotable { 51 struct vmw_resource res; 52 struct vmw_resource *ctx; 53 size_t size_read_back; 54 int seen_entries; 55 u32 type; 56 bool scrubbed; 57 struct list_head resource_list; 58 }; 59 60 /** 61 * struct vmw_cotable_info - Static info about cotable types 62 * 63 * @min_initial_entries: Min number of initial intries at cotable allocation 64 * for this cotable type. 65 * @size: Size of each entry. 66 * @unbind_func: Unbind call-back function. 67 */ 68 struct vmw_cotable_info { 69 u32 min_initial_entries; 70 u32 size; 71 void (*unbind_func)(struct vmw_private *, struct list_head *, 72 bool); 73 }; 74 75 static const struct vmw_cotable_info co_info[] = { 76 {1, sizeof(SVGACOTableDXRTViewEntry), &vmw_view_cotable_list_destroy}, 77 {1, sizeof(SVGACOTableDXDSViewEntry), &vmw_view_cotable_list_destroy}, 78 {1, sizeof(SVGACOTableDXSRViewEntry), &vmw_view_cotable_list_destroy}, 79 {1, sizeof(SVGACOTableDXElementLayoutEntry), NULL}, 80 {1, sizeof(SVGACOTableDXBlendStateEntry), NULL}, 81 {1, sizeof(SVGACOTableDXDepthStencilEntry), NULL}, 82 {1, sizeof(SVGACOTableDXRasterizerStateEntry), NULL}, 83 {1, sizeof(SVGACOTableDXSamplerEntry), NULL}, 84 {1, sizeof(SVGACOTableDXStreamOutputEntry), &vmw_dx_streamoutput_cotable_list_scrub}, 85 {1, sizeof(SVGACOTableDXQueryEntry), NULL}, 86 {1, sizeof(SVGACOTableDXShaderEntry), &vmw_dx_shader_cotable_list_scrub}, 87 {1, sizeof(SVGACOTableDXUAViewEntry), &vmw_view_cotable_list_destroy} 88 }; 89 90 /* 91 * Cotables with bindings that we remove must be scrubbed first, 92 * otherwise, the device will swap in an invalid context when we remove 93 * bindings before scrubbing a cotable... 94 */ 95 const SVGACOTableType vmw_cotable_scrub_order[] = { 96 SVGA_COTABLE_RTVIEW, 97 SVGA_COTABLE_DSVIEW, 98 SVGA_COTABLE_SRVIEW, 99 SVGA_COTABLE_DXSHADER, 100 SVGA_COTABLE_ELEMENTLAYOUT, 101 SVGA_COTABLE_BLENDSTATE, 102 SVGA_COTABLE_DEPTHSTENCIL, 103 SVGA_COTABLE_RASTERIZERSTATE, 104 SVGA_COTABLE_SAMPLER, 105 SVGA_COTABLE_STREAMOUTPUT, 106 SVGA_COTABLE_DXQUERY, 107 SVGA_COTABLE_UAVIEW, 108 }; 109 110 static int vmw_cotable_bind(struct vmw_resource *res, 111 struct ttm_validate_buffer *val_buf); 112 static int vmw_cotable_unbind(struct vmw_resource *res, 113 bool readback, 114 struct ttm_validate_buffer *val_buf); 115 static int vmw_cotable_create(struct vmw_resource *res); 116 static int vmw_cotable_destroy(struct vmw_resource *res); 117 118 static const struct vmw_res_func vmw_cotable_func = { 119 .res_type = vmw_res_cotable, 120 .needs_backup = true, 121 .may_evict = true, 122 .prio = 3, 123 .dirty_prio = 3, 124 .type_name = "context guest backed object tables", 125 .backup_placement = &vmw_mob_placement, 126 .create = vmw_cotable_create, 127 .destroy = vmw_cotable_destroy, 128 .bind = vmw_cotable_bind, 129 .unbind = vmw_cotable_unbind, 130 }; 131 132 /** 133 * vmw_cotable - Convert a struct vmw_resource pointer to a struct 134 * vmw_cotable pointer 135 * 136 * @res: Pointer to the resource. 137 */ 138 static struct vmw_cotable *vmw_cotable(struct vmw_resource *res) 139 { 140 return container_of(res, struct vmw_cotable, res); 141 } 142 143 /** 144 * vmw_cotable_destroy - Cotable resource destroy callback 145 * 146 * @res: Pointer to the cotable resource. 147 * 148 * There is no device cotable destroy command, so this function only 149 * makes sure that the resource id is set to invalid. 150 */ 151 static int vmw_cotable_destroy(struct vmw_resource *res) 152 { 153 res->id = -1; 154 return 0; 155 } 156 157 /** 158 * vmw_cotable_unscrub - Undo a cotable unscrub operation 159 * 160 * @res: Pointer to the cotable resource 161 * 162 * This function issues commands to (re)bind the cotable to 163 * its backing mob, which needs to be validated and reserved at this point. 164 * This is identical to bind() except the function interface looks different. 165 */ 166 static int vmw_cotable_unscrub(struct vmw_resource *res) 167 { 168 struct vmw_cotable *vcotbl = vmw_cotable(res); 169 struct vmw_private *dev_priv = res->dev_priv; 170 struct ttm_buffer_object *bo = &res->backup->base; 171 struct { 172 SVGA3dCmdHeader header; 173 SVGA3dCmdDXSetCOTable body; 174 } *cmd; 175 176 WARN_ON_ONCE(bo->resource->mem_type != VMW_PL_MOB); 177 dma_resv_assert_held(bo->base.resv); 178 179 cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); 180 if (!cmd) 181 return -ENOMEM; 182 183 WARN_ON(vcotbl->ctx->id == SVGA3D_INVALID_ID); 184 WARN_ON(bo->resource->mem_type != VMW_PL_MOB); 185 cmd->header.id = SVGA_3D_CMD_DX_SET_COTABLE; 186 cmd->header.size = sizeof(cmd->body); 187 cmd->body.cid = vcotbl->ctx->id; 188 cmd->body.type = vcotbl->type; 189 cmd->body.mobid = bo->resource->start; 190 cmd->body.validSizeInBytes = vcotbl->size_read_back; 191 192 vmw_cmd_commit_flush(dev_priv, sizeof(*cmd)); 193 vcotbl->scrubbed = false; 194 195 return 0; 196 } 197 198 /** 199 * vmw_cotable_bind - Undo a cotable unscrub operation 200 * 201 * @res: Pointer to the cotable resource 202 * @val_buf: Pointer to a struct ttm_validate_buffer prepared by the caller 203 * for convenience / fencing. 204 * 205 * This function issues commands to (re)bind the cotable to 206 * its backing mob, which needs to be validated and reserved at this point. 207 */ 208 static int vmw_cotable_bind(struct vmw_resource *res, 209 struct ttm_validate_buffer *val_buf) 210 { 211 /* 212 * The create() callback may have changed @res->backup without 213 * the caller noticing, and with val_buf->bo still pointing to 214 * the old backup buffer. Although hackish, and not used currently, 215 * take the opportunity to correct the value here so that it's not 216 * misused in the future. 217 */ 218 val_buf->bo = &res->backup->base; 219 220 return vmw_cotable_unscrub(res); 221 } 222 223 /** 224 * vmw_cotable_scrub - Scrub the cotable from the device. 225 * 226 * @res: Pointer to the cotable resource. 227 * @readback: Whether initiate a readback of the cotable data to the backup 228 * buffer. 229 * 230 * In some situations (context swapouts) it might be desirable to make the 231 * device forget about the cotable without performing a full unbind. A full 232 * unbind requires reserved backup buffers and it might not be possible to 233 * reserve them due to locking order violation issues. The vmw_cotable_scrub 234 * function implements a partial unbind() without that requirement but with the 235 * following restrictions. 236 * 1) Before the cotable is again used by the GPU, vmw_cotable_unscrub() must 237 * be called. 238 * 2) Before the cotable backing buffer is used by the CPU, or during the 239 * resource destruction, vmw_cotable_unbind() must be called. 240 */ 241 int vmw_cotable_scrub(struct vmw_resource *res, bool readback) 242 { 243 struct vmw_cotable *vcotbl = vmw_cotable(res); 244 struct vmw_private *dev_priv = res->dev_priv; 245 size_t submit_size; 246 247 struct { 248 SVGA3dCmdHeader header; 249 SVGA3dCmdDXReadbackCOTable body; 250 } *cmd0; 251 struct { 252 SVGA3dCmdHeader header; 253 SVGA3dCmdDXSetCOTable body; 254 } *cmd1; 255 256 if (vcotbl->scrubbed) 257 return 0; 258 259 if (co_info[vcotbl->type].unbind_func) 260 co_info[vcotbl->type].unbind_func(dev_priv, 261 &vcotbl->resource_list, 262 readback); 263 submit_size = sizeof(*cmd1); 264 if (readback) 265 submit_size += sizeof(*cmd0); 266 267 cmd1 = VMW_CMD_RESERVE(dev_priv, submit_size); 268 if (!cmd1) 269 return -ENOMEM; 270 271 vcotbl->size_read_back = 0; 272 if (readback) { 273 cmd0 = (void *) cmd1; 274 cmd0->header.id = SVGA_3D_CMD_DX_READBACK_COTABLE; 275 cmd0->header.size = sizeof(cmd0->body); 276 cmd0->body.cid = vcotbl->ctx->id; 277 cmd0->body.type = vcotbl->type; 278 cmd1 = (void *) &cmd0[1]; 279 vcotbl->size_read_back = res->backup_size; 280 } 281 cmd1->header.id = SVGA_3D_CMD_DX_SET_COTABLE; 282 cmd1->header.size = sizeof(cmd1->body); 283 cmd1->body.cid = vcotbl->ctx->id; 284 cmd1->body.type = vcotbl->type; 285 cmd1->body.mobid = SVGA3D_INVALID_ID; 286 cmd1->body.validSizeInBytes = 0; 287 vmw_cmd_commit_flush(dev_priv, submit_size); 288 vcotbl->scrubbed = true; 289 290 /* Trigger a create() on next validate. */ 291 res->id = -1; 292 293 return 0; 294 } 295 296 /** 297 * vmw_cotable_unbind - Cotable resource unbind callback 298 * 299 * @res: Pointer to the cotable resource. 300 * @readback: Whether to read back cotable data to the backup buffer. 301 * @val_buf: Pointer to a struct ttm_validate_buffer prepared by the caller 302 * for convenience / fencing. 303 * 304 * Unbinds the cotable from the device and fences the backup buffer. 305 */ 306 static int vmw_cotable_unbind(struct vmw_resource *res, 307 bool readback, 308 struct ttm_validate_buffer *val_buf) 309 { 310 struct vmw_cotable *vcotbl = vmw_cotable(res); 311 struct vmw_private *dev_priv = res->dev_priv; 312 struct ttm_buffer_object *bo = val_buf->bo; 313 struct vmw_fence_obj *fence; 314 315 if (!vmw_resource_mob_attached(res)) 316 return 0; 317 318 WARN_ON_ONCE(bo->resource->mem_type != VMW_PL_MOB); 319 dma_resv_assert_held(bo->base.resv); 320 321 mutex_lock(&dev_priv->binding_mutex); 322 if (!vcotbl->scrubbed) 323 vmw_dx_context_scrub_cotables(vcotbl->ctx, readback); 324 mutex_unlock(&dev_priv->binding_mutex); 325 (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); 326 vmw_bo_fence_single(bo, fence); 327 if (likely(fence != NULL)) 328 vmw_fence_obj_unreference(&fence); 329 330 return 0; 331 } 332 333 /** 334 * vmw_cotable_readback - Read back a cotable without unbinding. 335 * 336 * @res: The cotable resource. 337 * 338 * Reads back a cotable to its backing mob without scrubbing the MOB from 339 * the cotable. The MOB is fenced for subsequent CPU access. 340 */ 341 static int vmw_cotable_readback(struct vmw_resource *res) 342 { 343 struct vmw_cotable *vcotbl = vmw_cotable(res); 344 struct vmw_private *dev_priv = res->dev_priv; 345 346 struct { 347 SVGA3dCmdHeader header; 348 SVGA3dCmdDXReadbackCOTable body; 349 } *cmd; 350 struct vmw_fence_obj *fence; 351 352 if (!vcotbl->scrubbed) { 353 cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); 354 if (!cmd) 355 return -ENOMEM; 356 357 cmd->header.id = SVGA_3D_CMD_DX_READBACK_COTABLE; 358 cmd->header.size = sizeof(cmd->body); 359 cmd->body.cid = vcotbl->ctx->id; 360 cmd->body.type = vcotbl->type; 361 vcotbl->size_read_back = res->backup_size; 362 vmw_cmd_commit(dev_priv, sizeof(*cmd)); 363 } 364 365 (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); 366 vmw_bo_fence_single(&res->backup->base, fence); 367 vmw_fence_obj_unreference(&fence); 368 369 return 0; 370 } 371 372 /** 373 * vmw_cotable_resize - Resize a cotable. 374 * 375 * @res: The cotable resource. 376 * @new_size: The new size. 377 * 378 * Resizes a cotable and binds the new backup buffer. 379 * On failure the cotable is left intact. 380 * Important! This function may not fail once the MOB switch has been 381 * committed to hardware. That would put the device context in an 382 * invalid state which we can't currently recover from. 383 */ 384 static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size) 385 { 386 struct ttm_operation_ctx ctx = { false, false }; 387 struct vmw_private *dev_priv = res->dev_priv; 388 struct vmw_cotable *vcotbl = vmw_cotable(res); 389 struct vmw_buffer_object *buf, *old_buf = res->backup; 390 struct ttm_buffer_object *bo, *old_bo = &res->backup->base; 391 size_t old_size = res->backup_size; 392 size_t old_size_read_back = vcotbl->size_read_back; 393 size_t cur_size_read_back; 394 struct ttm_bo_kmap_obj old_map, new_map; 395 int ret; 396 size_t i; 397 398 ret = vmw_cotable_readback(res); 399 if (ret) 400 return ret; 401 402 cur_size_read_back = vcotbl->size_read_back; 403 vcotbl->size_read_back = old_size_read_back; 404 405 /* 406 * While device is processing, Allocate and reserve a buffer object 407 * for the new COTable. Initially pin the buffer object to make sure 408 * we can use tryreserve without failure. 409 */ 410 ret = vmw_bo_create(dev_priv, new_size, &vmw_mob_placement, 411 true, true, vmw_bo_bo_free, &buf); 412 if (ret) { 413 DRM_ERROR("Failed initializing new cotable MOB.\n"); 414 return ret; 415 } 416 417 bo = &buf->base; 418 WARN_ON_ONCE(ttm_bo_reserve(bo, false, true, NULL)); 419 420 ret = ttm_bo_wait(old_bo, false, false); 421 if (unlikely(ret != 0)) { 422 DRM_ERROR("Failed waiting for cotable unbind.\n"); 423 goto out_wait; 424 } 425 426 /* 427 * Do a page by page copy of COTables. This eliminates slow vmap()s. 428 * This should really be a TTM utility. 429 */ 430 for (i = 0; i < old_bo->resource->num_pages; ++i) { 431 bool dummy; 432 433 ret = ttm_bo_kmap(old_bo, i, 1, &old_map); 434 if (unlikely(ret != 0)) { 435 DRM_ERROR("Failed mapping old COTable on resize.\n"); 436 goto out_wait; 437 } 438 ret = ttm_bo_kmap(bo, i, 1, &new_map); 439 if (unlikely(ret != 0)) { 440 DRM_ERROR("Failed mapping new COTable on resize.\n"); 441 goto out_map_new; 442 } 443 memcpy(ttm_kmap_obj_virtual(&new_map, &dummy), 444 ttm_kmap_obj_virtual(&old_map, &dummy), 445 PAGE_SIZE); 446 ttm_bo_kunmap(&new_map); 447 ttm_bo_kunmap(&old_map); 448 } 449 450 /* Unpin new buffer, and switch backup buffers. */ 451 ret = ttm_bo_validate(bo, &vmw_mob_placement, &ctx); 452 if (unlikely(ret != 0)) { 453 DRM_ERROR("Failed validating new COTable backup buffer.\n"); 454 goto out_wait; 455 } 456 457 vmw_resource_mob_detach(res); 458 res->backup = buf; 459 res->backup_size = new_size; 460 vcotbl->size_read_back = cur_size_read_back; 461 462 /* 463 * Now tell the device to switch. If this fails, then we need to 464 * revert the full resize. 465 */ 466 ret = vmw_cotable_unscrub(res); 467 if (ret) { 468 DRM_ERROR("Failed switching COTable backup buffer.\n"); 469 res->backup = old_buf; 470 res->backup_size = old_size; 471 vcotbl->size_read_back = old_size_read_back; 472 vmw_resource_mob_attach(res); 473 goto out_wait; 474 } 475 476 vmw_resource_mob_attach(res); 477 /* Let go of the old mob. */ 478 vmw_bo_unreference(&old_buf); 479 res->id = vcotbl->type; 480 481 /* Release the pin acquired in vmw_bo_init */ 482 ttm_bo_unpin(bo); 483 484 return 0; 485 486 out_map_new: 487 ttm_bo_kunmap(&old_map); 488 out_wait: 489 ttm_bo_unpin(bo); 490 ttm_bo_unreserve(bo); 491 vmw_bo_unreference(&buf); 492 493 return ret; 494 } 495 496 /** 497 * vmw_cotable_create - Cotable resource create callback 498 * 499 * @res: Pointer to a cotable resource. 500 * 501 * There is no separate create command for cotables, so this callback, which 502 * is called before bind() in the validation sequence is instead used for two 503 * things. 504 * 1) Unscrub the cotable if it is scrubbed and still attached to a backup 505 * buffer. 506 * 2) Resize the cotable if needed. 507 */ 508 static int vmw_cotable_create(struct vmw_resource *res) 509 { 510 struct vmw_cotable *vcotbl = vmw_cotable(res); 511 size_t new_size = res->backup_size; 512 size_t needed_size; 513 int ret; 514 515 /* Check whether we need to resize the cotable */ 516 needed_size = (vcotbl->seen_entries + 1) * co_info[vcotbl->type].size; 517 while (needed_size > new_size) 518 new_size *= 2; 519 520 if (likely(new_size <= res->backup_size)) { 521 if (vcotbl->scrubbed && vmw_resource_mob_attached(res)) { 522 ret = vmw_cotable_unscrub(res); 523 if (ret) 524 return ret; 525 } 526 res->id = vcotbl->type; 527 return 0; 528 } 529 530 return vmw_cotable_resize(res, new_size); 531 } 532 533 /** 534 * vmw_hw_cotable_destroy - Cotable hw_destroy callback 535 * 536 * @res: Pointer to a cotable resource. 537 * 538 * The final (part of resource destruction) destroy callback. 539 */ 540 static void vmw_hw_cotable_destroy(struct vmw_resource *res) 541 { 542 (void) vmw_cotable_destroy(res); 543 } 544 545 /** 546 * vmw_cotable_free - Cotable resource destructor 547 * 548 * @res: Pointer to a cotable resource. 549 */ 550 static void vmw_cotable_free(struct vmw_resource *res) 551 { 552 kfree(res); 553 } 554 555 /** 556 * vmw_cotable_alloc - Create a cotable resource 557 * 558 * @dev_priv: Pointer to a device private struct. 559 * @ctx: Pointer to the context resource. 560 * The cotable resource will not add a refcount. 561 * @type: The cotable type. 562 */ 563 struct vmw_resource *vmw_cotable_alloc(struct vmw_private *dev_priv, 564 struct vmw_resource *ctx, 565 u32 type) 566 { 567 struct vmw_cotable *vcotbl; 568 int ret; 569 u32 num_entries; 570 571 vcotbl = kzalloc(sizeof(*vcotbl), GFP_KERNEL); 572 if (unlikely(!vcotbl)) { 573 ret = -ENOMEM; 574 goto out_no_alloc; 575 } 576 577 ret = vmw_resource_init(dev_priv, &vcotbl->res, true, 578 vmw_cotable_free, &vmw_cotable_func); 579 if (unlikely(ret != 0)) 580 goto out_no_init; 581 582 INIT_LIST_HEAD(&vcotbl->resource_list); 583 vcotbl->res.id = type; 584 vcotbl->res.backup_size = PAGE_SIZE; 585 num_entries = PAGE_SIZE / co_info[type].size; 586 if (num_entries < co_info[type].min_initial_entries) { 587 vcotbl->res.backup_size = co_info[type].min_initial_entries * 588 co_info[type].size; 589 vcotbl->res.backup_size = PFN_ALIGN(vcotbl->res.backup_size); 590 } 591 592 vcotbl->scrubbed = true; 593 vcotbl->seen_entries = -1; 594 vcotbl->type = type; 595 vcotbl->ctx = ctx; 596 597 vcotbl->res.hw_destroy = vmw_hw_cotable_destroy; 598 599 return &vcotbl->res; 600 601 out_no_init: 602 kfree(vcotbl); 603 out_no_alloc: 604 return ERR_PTR(ret); 605 } 606 607 /** 608 * vmw_cotable_notify - Notify the cotable about an item creation 609 * 610 * @res: Pointer to a cotable resource. 611 * @id: Item id. 612 */ 613 int vmw_cotable_notify(struct vmw_resource *res, int id) 614 { 615 struct vmw_cotable *vcotbl = vmw_cotable(res); 616 617 if (id < 0 || id >= SVGA_COTABLE_MAX_IDS) { 618 DRM_ERROR("Illegal COTable id. Type is %u. Id is %d\n", 619 (unsigned) vcotbl->type, id); 620 return -EINVAL; 621 } 622 623 if (vcotbl->seen_entries < id) { 624 /* Trigger a call to create() on next validate */ 625 res->id = -1; 626 vcotbl->seen_entries = id; 627 } 628 629 return 0; 630 } 631 632 /** 633 * vmw_cotable_add_resource - add a view to the cotable's list of active views. 634 * 635 * @res: pointer struct vmw_resource representing the cotable. 636 * @head: pointer to the struct list_head member of the resource, dedicated 637 * to the cotable active resource list. 638 */ 639 void vmw_cotable_add_resource(struct vmw_resource *res, struct list_head *head) 640 { 641 struct vmw_cotable *vcotbl = 642 container_of(res, struct vmw_cotable, res); 643 644 list_add_tail(head, &vcotbl->resource_list); 645 } 646