1 // SPDX-License-Identifier: GPL-2.0 OR MIT 2 /************************************************************************** 3 * 4 * Copyright © 2018-2023 VMware, Inc., Palo Alto, CA., USA 5 * All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the 9 * "Software"), to deal in the Software without restriction, including 10 * without limitation the rights to use, copy, modify, merge, publish, 11 * distribute, sub license, and/or sell copies of the Software, and to 12 * permit persons to whom the Software is furnished to do so, subject to 13 * the following conditions: 14 * 15 * The above copyright notice and this permission notice (including the 16 * next paragraph) shall be included in all copies or substantial portions 17 * of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 * USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 **************************************************************************/ 28 29 #include "vmwgfx_binding.h" 30 #include "vmwgfx_bo.h" 31 #include "vmwgfx_drv.h" 32 #include "vmwgfx_resource_priv.h" 33 34 #include <drm/ttm/ttm_placement.h> 35 36 /** 37 * struct vmw_dx_streamoutput - Streamoutput resource metadata. 38 * @res: Base resource struct. 39 * @ctx: Non-refcounted context to which @res belong. 40 * @cotable: Refcounted cotable holding this Streamoutput. 41 * @cotable_head: List head for cotable-so_res list. 42 * @id: User-space provided identifier. 43 * @size: User-space provided mob size. 44 * @committed: Whether streamoutput is actually created or pending creation. 45 */ 46 struct vmw_dx_streamoutput { 47 struct vmw_resource res; 48 struct vmw_resource *ctx; 49 struct vmw_resource *cotable; 50 struct list_head cotable_head; 51 u32 id; 52 u32 size; 53 bool committed; 54 }; 55 56 static int vmw_dx_streamoutput_create(struct vmw_resource *res); 57 static int vmw_dx_streamoutput_bind(struct vmw_resource *res, 58 struct ttm_validate_buffer *val_buf); 59 static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback, 60 struct ttm_validate_buffer *val_buf); 61 static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res, 62 enum vmw_cmdbuf_res_state state); 63 64 static const struct vmw_res_func vmw_dx_streamoutput_func = { 65 .res_type = vmw_res_streamoutput, 66 .needs_backup = true, 67 .may_evict = false, 68 .type_name = "DX streamoutput", 69 .backup_placement = &vmw_mob_placement, 70 .create = vmw_dx_streamoutput_create, 71 .destroy = NULL, /* Command buffer managed resource. */ 72 .bind = vmw_dx_streamoutput_bind, 73 .unbind = vmw_dx_streamoutput_unbind, 74 .commit_notify = vmw_dx_streamoutput_commit_notify, 75 }; 76 77 static inline struct vmw_dx_streamoutput * 78 vmw_res_to_dx_streamoutput(struct vmw_resource *res) 79 { 80 return container_of(res, struct vmw_dx_streamoutput, res); 81 } 82 83 /** 84 * vmw_dx_streamoutput_unscrub - Reattach the MOB to streamoutput. 85 * @res: The streamoutput resource. 86 * 87 * Return: 0 on success, negative error code on failure. 88 */ 89 static int vmw_dx_streamoutput_unscrub(struct vmw_resource *res) 90 { 91 struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); 92 struct vmw_private *dev_priv = res->dev_priv; 93 struct { 94 SVGA3dCmdHeader header; 95 SVGA3dCmdDXBindStreamOutput body; 96 } *cmd; 97 98 if (!list_empty(&so->cotable_head) || !so->committed ) 99 return 0; 100 101 cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id); 102 if (!cmd) 103 return -ENOMEM; 104 105 cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT; 106 cmd->header.size = sizeof(cmd->body); 107 cmd->body.soid = so->id; 108 cmd->body.mobid = res->backup->base.resource->start; 109 cmd->body.offsetInBytes = res->backup_offset; 110 cmd->body.sizeInBytes = so->size; 111 vmw_cmd_commit(dev_priv, sizeof(*cmd)); 112 113 vmw_cotable_add_resource(so->cotable, &so->cotable_head); 114 115 return 0; 116 } 117 118 static int vmw_dx_streamoutput_create(struct vmw_resource *res) 119 { 120 struct vmw_private *dev_priv = res->dev_priv; 121 struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); 122 int ret = 0; 123 124 WARN_ON_ONCE(!so->committed); 125 126 if (vmw_resource_mob_attached(res)) { 127 mutex_lock(&dev_priv->binding_mutex); 128 ret = vmw_dx_streamoutput_unscrub(res); 129 mutex_unlock(&dev_priv->binding_mutex); 130 } 131 132 res->id = so->id; 133 134 return ret; 135 } 136 137 static int vmw_dx_streamoutput_bind(struct vmw_resource *res, 138 struct ttm_validate_buffer *val_buf) 139 { 140 struct vmw_private *dev_priv = res->dev_priv; 141 struct ttm_buffer_object *bo = val_buf->bo; 142 int ret; 143 144 if (WARN_ON(bo->resource->mem_type != VMW_PL_MOB)) 145 return -EINVAL; 146 147 mutex_lock(&dev_priv->binding_mutex); 148 ret = vmw_dx_streamoutput_unscrub(res); 149 mutex_unlock(&dev_priv->binding_mutex); 150 151 return ret; 152 } 153 154 /** 155 * vmw_dx_streamoutput_scrub - Unbind the MOB from streamoutput. 156 * @res: The streamoutput resource. 157 * 158 * Return: 0 on success, negative error code on failure. 159 */ 160 static int vmw_dx_streamoutput_scrub(struct vmw_resource *res) 161 { 162 struct vmw_private *dev_priv = res->dev_priv; 163 struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); 164 struct { 165 SVGA3dCmdHeader header; 166 SVGA3dCmdDXBindStreamOutput body; 167 } *cmd; 168 169 if (list_empty(&so->cotable_head)) 170 return 0; 171 172 WARN_ON_ONCE(!so->committed); 173 174 cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id); 175 if (!cmd) 176 return -ENOMEM; 177 178 cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT; 179 cmd->header.size = sizeof(cmd->body); 180 cmd->body.soid = res->id; 181 cmd->body.mobid = SVGA3D_INVALID_ID; 182 cmd->body.offsetInBytes = 0; 183 cmd->body.sizeInBytes = so->size; 184 vmw_cmd_commit(dev_priv, sizeof(*cmd)); 185 186 res->id = -1; 187 list_del_init(&so->cotable_head); 188 189 return 0; 190 } 191 192 static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback, 193 struct ttm_validate_buffer *val_buf) 194 { 195 struct vmw_private *dev_priv = res->dev_priv; 196 struct vmw_fence_obj *fence; 197 int ret; 198 199 if (WARN_ON(res->backup->base.resource->mem_type != VMW_PL_MOB)) 200 return -EINVAL; 201 202 mutex_lock(&dev_priv->binding_mutex); 203 ret = vmw_dx_streamoutput_scrub(res); 204 mutex_unlock(&dev_priv->binding_mutex); 205 206 if (ret) 207 return ret; 208 209 (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); 210 vmw_bo_fence_single(val_buf->bo, fence); 211 212 if (fence != NULL) 213 vmw_fence_obj_unreference(&fence); 214 215 return 0; 216 } 217 218 static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res, 219 enum vmw_cmdbuf_res_state state) 220 { 221 struct vmw_private *dev_priv = res->dev_priv; 222 struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); 223 224 if (state == VMW_CMDBUF_RES_ADD) { 225 mutex_lock(&dev_priv->binding_mutex); 226 vmw_cotable_add_resource(so->cotable, &so->cotable_head); 227 so->committed = true; 228 res->id = so->id; 229 mutex_unlock(&dev_priv->binding_mutex); 230 } else { 231 mutex_lock(&dev_priv->binding_mutex); 232 list_del_init(&so->cotable_head); 233 so->committed = false; 234 res->id = -1; 235 mutex_unlock(&dev_priv->binding_mutex); 236 } 237 } 238 239 /** 240 * vmw_dx_streamoutput_lookup - Do a streamoutput resource lookup by user key. 241 * @man: Command buffer managed resource manager for current context. 242 * @user_key: User-space identifier for lookup. 243 * 244 * Return: Valid refcounted vmw_resource on success, error pointer on failure. 245 */ 246 struct vmw_resource * 247 vmw_dx_streamoutput_lookup(struct vmw_cmdbuf_res_manager *man, 248 u32 user_key) 249 { 250 return vmw_cmdbuf_res_lookup(man, vmw_cmdbuf_res_streamoutput, 251 user_key); 252 } 253 254 static void vmw_dx_streamoutput_res_free(struct vmw_resource *res) 255 { 256 struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); 257 258 vmw_resource_unreference(&so->cotable); 259 kfree(so); 260 } 261 262 static void vmw_dx_streamoutput_hw_destroy(struct vmw_resource *res) 263 { 264 /* Destroyed by user-space cmd buf or as part of context takedown. */ 265 res->id = -1; 266 } 267 268 /** 269 * vmw_dx_streamoutput_add - Add a streamoutput as a cmd buf managed resource. 270 * @man: Command buffer managed resource manager for current context. 271 * @ctx: Pointer to context resource. 272 * @user_key: The identifier for this streamoutput. 273 * @list: The list of staged command buffer managed resources. 274 * 275 * Return: 0 on success, negative error code on failure. 276 */ 277 int vmw_dx_streamoutput_add(struct vmw_cmdbuf_res_manager *man, 278 struct vmw_resource *ctx, u32 user_key, 279 struct list_head *list) 280 { 281 struct vmw_dx_streamoutput *so; 282 struct vmw_resource *res; 283 struct vmw_private *dev_priv = ctx->dev_priv; 284 int ret; 285 286 so = kmalloc(sizeof(*so), GFP_KERNEL); 287 if (!so) { 288 return -ENOMEM; 289 } 290 291 res = &so->res; 292 so->ctx = ctx; 293 so->cotable = vmw_resource_reference 294 (vmw_context_cotable(ctx, SVGA_COTABLE_STREAMOUTPUT)); 295 so->id = user_key; 296 so->committed = false; 297 INIT_LIST_HEAD(&so->cotable_head); 298 ret = vmw_resource_init(dev_priv, res, true, 299 vmw_dx_streamoutput_res_free, 300 &vmw_dx_streamoutput_func); 301 if (ret) 302 goto out_resource_init; 303 304 ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_streamoutput, user_key, 305 res, list); 306 if (ret) 307 goto out_resource_init; 308 309 res->id = so->id; 310 res->hw_destroy = vmw_dx_streamoutput_hw_destroy; 311 312 out_resource_init: 313 vmw_resource_unreference(&res); 314 315 return ret; 316 } 317 318 /** 319 * vmw_dx_streamoutput_set_size - Sets streamoutput mob size in res struct. 320 * @res: The streamoutput res for which need to set size. 321 * @size: The size provided by user-space to set. 322 */ 323 void vmw_dx_streamoutput_set_size(struct vmw_resource *res, u32 size) 324 { 325 struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); 326 327 so->size = size; 328 } 329 330 /** 331 * vmw_dx_streamoutput_remove - Stage streamoutput for removal. 332 * @man: Command buffer managed resource manager for current context. 333 * @user_key: The identifier for this streamoutput. 334 * @list: The list of staged command buffer managed resources. 335 * 336 * Return: 0 on success, negative error code on failure. 337 */ 338 int vmw_dx_streamoutput_remove(struct vmw_cmdbuf_res_manager *man, 339 u32 user_key, 340 struct list_head *list) 341 { 342 struct vmw_resource *r; 343 344 return vmw_cmdbuf_res_remove(man, vmw_cmdbuf_res_streamoutput, 345 (u32)user_key, list, &r); 346 } 347 348 /** 349 * vmw_dx_streamoutput_cotable_list_scrub - cotable unbind_func callback. 350 * @dev_priv: Device private. 351 * @list: The list of cotable resources. 352 * @readback: Whether the call was part of a readback unbind. 353 */ 354 void vmw_dx_streamoutput_cotable_list_scrub(struct vmw_private *dev_priv, 355 struct list_head *list, 356 bool readback) 357 { 358 struct vmw_dx_streamoutput *entry, *next; 359 360 lockdep_assert_held_once(&dev_priv->binding_mutex); 361 362 list_for_each_entry_safe(entry, next, list, cotable_head) { 363 WARN_ON(vmw_dx_streamoutput_scrub(&entry->res)); 364 if (!readback) 365 entry->committed =false; 366 } 367 } 368