Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT |
| 2 | /************************************************************************** |
| 3 | * |
| 4 | * Copyright © 2018-2019 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 <drm/ttm/ttm_placement.h> |
| 30 | |
| 31 | #include "vmwgfx_drv.h" |
| 32 | #include "vmwgfx_resource_priv.h" |
| 33 | #include "vmwgfx_binding.h" |
| 34 | |
| 35 | /** |
| 36 | * struct vmw_dx_streamoutput - Streamoutput resource metadata. |
| 37 | * @res: Base resource struct. |
| 38 | * @ctx: Non-refcounted context to which @res belong. |
| 39 | * @cotable: Refcounted cotable holding this Streamoutput. |
| 40 | * @cotable_head: List head for cotable-so_res list. |
| 41 | * @id: User-space provided identifier. |
| 42 | * @size: User-space provided mob size. |
| 43 | * @committed: Whether streamoutput is actually created or pending creation. |
| 44 | */ |
| 45 | struct vmw_dx_streamoutput { |
| 46 | struct vmw_resource res; |
| 47 | struct vmw_resource *ctx; |
| 48 | struct vmw_resource *cotable; |
| 49 | struct list_head cotable_head; |
| 50 | u32 id; |
| 51 | u32 size; |
| 52 | bool committed; |
| 53 | }; |
| 54 | |
| 55 | static int vmw_dx_streamoutput_create(struct vmw_resource *res); |
| 56 | static int vmw_dx_streamoutput_bind(struct vmw_resource *res, |
| 57 | struct ttm_validate_buffer *val_buf); |
| 58 | static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback, |
| 59 | struct ttm_validate_buffer *val_buf); |
| 60 | static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res, |
| 61 | enum vmw_cmdbuf_res_state state); |
| 62 | |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 63 | static const struct vmw_res_func vmw_dx_streamoutput_func = { |
| 64 | .res_type = vmw_res_streamoutput, |
| 65 | .needs_backup = true, |
| 66 | .may_evict = false, |
| 67 | .type_name = "DX streamoutput", |
| 68 | .backup_placement = &vmw_mob_placement, |
| 69 | .create = vmw_dx_streamoutput_create, |
| 70 | .destroy = NULL, /* Command buffer managed resource. */ |
| 71 | .bind = vmw_dx_streamoutput_bind, |
| 72 | .unbind = vmw_dx_streamoutput_unbind, |
| 73 | .commit_notify = vmw_dx_streamoutput_commit_notify, |
| 74 | }; |
| 75 | |
| 76 | static inline struct vmw_dx_streamoutput * |
| 77 | vmw_res_to_dx_streamoutput(struct vmw_resource *res) |
| 78 | { |
| 79 | return container_of(res, struct vmw_dx_streamoutput, res); |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * vmw_dx_streamoutput_unscrub - Reattach the MOB to streamoutput. |
| 84 | * @res: The streamoutput resource. |
| 85 | * |
| 86 | * Return: 0 on success, negative error code on failure. |
| 87 | */ |
| 88 | static int vmw_dx_streamoutput_unscrub(struct vmw_resource *res) |
| 89 | { |
| 90 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
| 91 | struct vmw_private *dev_priv = res->dev_priv; |
| 92 | struct { |
| 93 | SVGA3dCmdHeader header; |
| 94 | SVGA3dCmdDXBindStreamOutput body; |
| 95 | } *cmd; |
| 96 | |
| 97 | if (!list_empty(&so->cotable_head) || !so->committed ) |
| 98 | return 0; |
| 99 | |
Zack Rusin | 8426ed9 | 2020-11-18 12:54:19 -0500 | [diff] [blame] | 100 | cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id); |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 101 | if (!cmd) |
| 102 | return -ENOMEM; |
| 103 | |
| 104 | cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT; |
| 105 | cmd->header.size = sizeof(cmd->body); |
| 106 | cmd->body.soid = so->id; |
Christian König | d311675 | 2021-04-12 15:11:47 +0200 | [diff] [blame] | 107 | cmd->body.mobid = res->backup->base.resource->start; |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 108 | cmd->body.offsetInBytes = res->backup_offset; |
| 109 | cmd->body.sizeInBytes = so->size; |
Zack Rusin | 8426ed9 | 2020-11-18 12:54:19 -0500 | [diff] [blame] | 110 | vmw_cmd_commit(dev_priv, sizeof(*cmd)); |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 111 | |
| 112 | vmw_cotable_add_resource(so->cotable, &so->cotable_head); |
| 113 | |
| 114 | return 0; |
| 115 | } |
| 116 | |
| 117 | static int vmw_dx_streamoutput_create(struct vmw_resource *res) |
| 118 | { |
| 119 | struct vmw_private *dev_priv = res->dev_priv; |
| 120 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
| 121 | int ret = 0; |
| 122 | |
| 123 | WARN_ON_ONCE(!so->committed); |
| 124 | |
| 125 | if (vmw_resource_mob_attached(res)) { |
| 126 | mutex_lock(&dev_priv->binding_mutex); |
| 127 | ret = vmw_dx_streamoutput_unscrub(res); |
| 128 | mutex_unlock(&dev_priv->binding_mutex); |
| 129 | } |
| 130 | |
| 131 | res->id = so->id; |
| 132 | |
| 133 | return ret; |
| 134 | } |
| 135 | |
| 136 | static int vmw_dx_streamoutput_bind(struct vmw_resource *res, |
| 137 | struct ttm_validate_buffer *val_buf) |
| 138 | { |
| 139 | struct vmw_private *dev_priv = res->dev_priv; |
| 140 | struct ttm_buffer_object *bo = val_buf->bo; |
| 141 | int ret; |
| 142 | |
Christian König | d311675 | 2021-04-12 15:11:47 +0200 | [diff] [blame] | 143 | if (WARN_ON(bo->resource->mem_type != VMW_PL_MOB)) |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 144 | return -EINVAL; |
| 145 | |
| 146 | mutex_lock(&dev_priv->binding_mutex); |
| 147 | ret = vmw_dx_streamoutput_unscrub(res); |
| 148 | mutex_unlock(&dev_priv->binding_mutex); |
| 149 | |
| 150 | return ret; |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * vmw_dx_streamoutput_scrub - Unbind the MOB from streamoutput. |
| 155 | * @res: The streamoutput resource. |
| 156 | * |
| 157 | * Return: 0 on success, negative error code on failure. |
| 158 | */ |
| 159 | static int vmw_dx_streamoutput_scrub(struct vmw_resource *res) |
| 160 | { |
| 161 | struct vmw_private *dev_priv = res->dev_priv; |
| 162 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
| 163 | struct { |
| 164 | SVGA3dCmdHeader header; |
| 165 | SVGA3dCmdDXBindStreamOutput body; |
| 166 | } *cmd; |
| 167 | |
| 168 | if (list_empty(&so->cotable_head)) |
| 169 | return 0; |
| 170 | |
| 171 | WARN_ON_ONCE(!so->committed); |
| 172 | |
Zack Rusin | 8426ed9 | 2020-11-18 12:54:19 -0500 | [diff] [blame] | 173 | cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id); |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 174 | if (!cmd) |
| 175 | return -ENOMEM; |
| 176 | |
| 177 | cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT; |
| 178 | cmd->header.size = sizeof(cmd->body); |
| 179 | cmd->body.soid = res->id; |
| 180 | cmd->body.mobid = SVGA3D_INVALID_ID; |
| 181 | cmd->body.offsetInBytes = 0; |
| 182 | cmd->body.sizeInBytes = so->size; |
Zack Rusin | 8426ed9 | 2020-11-18 12:54:19 -0500 | [diff] [blame] | 183 | vmw_cmd_commit(dev_priv, sizeof(*cmd)); |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 184 | |
| 185 | res->id = -1; |
| 186 | list_del_init(&so->cotable_head); |
| 187 | |
| 188 | return 0; |
| 189 | } |
| 190 | |
| 191 | static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback, |
| 192 | struct ttm_validate_buffer *val_buf) |
| 193 | { |
| 194 | struct vmw_private *dev_priv = res->dev_priv; |
| 195 | struct vmw_fence_obj *fence; |
| 196 | int ret; |
| 197 | |
Christian König | d311675 | 2021-04-12 15:11:47 +0200 | [diff] [blame] | 198 | if (WARN_ON(res->backup->base.resource->mem_type != VMW_PL_MOB)) |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 199 | return -EINVAL; |
| 200 | |
| 201 | mutex_lock(&dev_priv->binding_mutex); |
| 202 | ret = vmw_dx_streamoutput_scrub(res); |
| 203 | mutex_unlock(&dev_priv->binding_mutex); |
| 204 | |
| 205 | if (ret) |
| 206 | return ret; |
| 207 | |
| 208 | (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); |
| 209 | vmw_bo_fence_single(val_buf->bo, fence); |
| 210 | |
| 211 | if (fence != NULL) |
| 212 | vmw_fence_obj_unreference(&fence); |
| 213 | |
| 214 | return 0; |
| 215 | } |
| 216 | |
| 217 | static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res, |
| 218 | enum vmw_cmdbuf_res_state state) |
| 219 | { |
| 220 | struct vmw_private *dev_priv = res->dev_priv; |
| 221 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
| 222 | |
| 223 | if (state == VMW_CMDBUF_RES_ADD) { |
| 224 | mutex_lock(&dev_priv->binding_mutex); |
| 225 | vmw_cotable_add_resource(so->cotable, &so->cotable_head); |
| 226 | so->committed = true; |
| 227 | res->id = so->id; |
| 228 | mutex_unlock(&dev_priv->binding_mutex); |
| 229 | } else { |
| 230 | mutex_lock(&dev_priv->binding_mutex); |
| 231 | list_del_init(&so->cotable_head); |
| 232 | so->committed = false; |
| 233 | res->id = -1; |
| 234 | mutex_unlock(&dev_priv->binding_mutex); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | /** |
| 239 | * vmw_dx_streamoutput_lookup - Do a streamoutput resource lookup by user key. |
| 240 | * @man: Command buffer managed resource manager for current context. |
| 241 | * @user_key: User-space identifier for lookup. |
| 242 | * |
| 243 | * Return: Valid refcounted vmw_resource on success, error pointer on failure. |
| 244 | */ |
| 245 | struct vmw_resource * |
| 246 | vmw_dx_streamoutput_lookup(struct vmw_cmdbuf_res_manager *man, |
| 247 | u32 user_key) |
| 248 | { |
| 249 | return vmw_cmdbuf_res_lookup(man, vmw_cmdbuf_res_streamoutput, |
| 250 | user_key); |
| 251 | } |
| 252 | |
| 253 | static void vmw_dx_streamoutput_res_free(struct vmw_resource *res) |
| 254 | { |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 255 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
| 256 | |
| 257 | vmw_resource_unreference(&so->cotable); |
| 258 | kfree(so); |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 259 | } |
| 260 | |
| 261 | static void vmw_dx_streamoutput_hw_destroy(struct vmw_resource *res) |
| 262 | { |
| 263 | /* Destroyed by user-space cmd buf or as part of context takedown. */ |
| 264 | res->id = -1; |
| 265 | } |
| 266 | |
| 267 | /** |
| 268 | * vmw_dx_streamoutput_add - Add a streamoutput as a cmd buf managed resource. |
| 269 | * @man: Command buffer managed resource manager for current context. |
| 270 | * @ctx: Pointer to context resource. |
| 271 | * @user_key: The identifier for this streamoutput. |
| 272 | * @list: The list of staged command buffer managed resources. |
| 273 | * |
| 274 | * Return: 0 on success, negative error code on failure. |
| 275 | */ |
| 276 | int vmw_dx_streamoutput_add(struct vmw_cmdbuf_res_manager *man, |
| 277 | struct vmw_resource *ctx, u32 user_key, |
| 278 | struct list_head *list) |
| 279 | { |
| 280 | struct vmw_dx_streamoutput *so; |
| 281 | struct vmw_resource *res; |
| 282 | struct vmw_private *dev_priv = ctx->dev_priv; |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 283 | int ret; |
| 284 | |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 285 | so = kmalloc(sizeof(*so), GFP_KERNEL); |
| 286 | if (!so) { |
Deepak Rawat | e8bead9 | 2018-12-13 14:04:31 -0800 | [diff] [blame] | 287 | return -ENOMEM; |
| 288 | } |
| 289 | |
| 290 | res = &so->res; |
| 291 | so->ctx = ctx; |
| 292 | so->cotable = vmw_resource_reference |
| 293 | (vmw_context_cotable(ctx, SVGA_COTABLE_STREAMOUTPUT)); |
| 294 | so->id = user_key; |
| 295 | so->committed = false; |
| 296 | INIT_LIST_HEAD(&so->cotable_head); |
| 297 | ret = vmw_resource_init(dev_priv, res, true, |
| 298 | vmw_dx_streamoutput_res_free, |
| 299 | &vmw_dx_streamoutput_func); |
| 300 | if (ret) |
| 301 | goto out_resource_init; |
| 302 | |
| 303 | ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_streamoutput, user_key, |
| 304 | res, list); |
| 305 | if (ret) |
| 306 | goto out_resource_init; |
| 307 | |
| 308 | res->id = so->id; |
| 309 | res->hw_destroy = vmw_dx_streamoutput_hw_destroy; |
| 310 | |
| 311 | out_resource_init: |
| 312 | vmw_resource_unreference(&res); |
| 313 | |
| 314 | return ret; |
| 315 | } |
| 316 | |
| 317 | /** |
| 318 | * vmw_dx_streamoutput_set_size - Sets streamoutput mob size in res struct. |
| 319 | * @res: The streamoutput res for which need to set size. |
| 320 | * @size: The size provided by user-space to set. |
| 321 | */ |
| 322 | void vmw_dx_streamoutput_set_size(struct vmw_resource *res, u32 size) |
| 323 | { |
| 324 | struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); |
| 325 | |
| 326 | so->size = size; |
| 327 | } |
| 328 | |
| 329 | /** |
| 330 | * vmw_dx_streamoutput_remove - Stage streamoutput for removal. |
| 331 | * @man: Command buffer managed resource manager for current context. |
| 332 | * @user_key: The identifier for this streamoutput. |
| 333 | * @list: The list of staged command buffer managed resources. |
| 334 | * |
| 335 | * Return: 0 on success, negative error code on failure. |
| 336 | */ |
| 337 | int vmw_dx_streamoutput_remove(struct vmw_cmdbuf_res_manager *man, |
| 338 | u32 user_key, |
| 339 | struct list_head *list) |
| 340 | { |
| 341 | struct vmw_resource *r; |
| 342 | |
| 343 | return vmw_cmdbuf_res_remove(man, vmw_cmdbuf_res_streamoutput, |
| 344 | (u32)user_key, list, &r); |
| 345 | } |
| 346 | |
| 347 | /** |
| 348 | * vmw_dx_streamoutput_cotable_list_scrub - cotable unbind_func callback. |
| 349 | * @dev_priv: Device private. |
| 350 | * @list: The list of cotable resources. |
| 351 | * @readback: Whether the call was part of a readback unbind. |
| 352 | */ |
| 353 | void vmw_dx_streamoutput_cotable_list_scrub(struct vmw_private *dev_priv, |
| 354 | struct list_head *list, |
| 355 | bool readback) |
| 356 | { |
| 357 | struct vmw_dx_streamoutput *entry, *next; |
| 358 | |
| 359 | lockdep_assert_held_once(&dev_priv->binding_mutex); |
| 360 | |
| 361 | list_for_each_entry_safe(entry, next, list, cotable_head) { |
| 362 | WARN_ON(vmw_dx_streamoutput_scrub(&entry->res)); |
| 363 | if (!readback) |
| 364 | entry->committed =false; |
| 365 | } |
| 366 | } |