1/* $NetBSD: vmwgfx_cmdbuf_res.c,v 1.3 2021/12/18 23:45:45 riastradh Exp $ */ 2 3// SPDX-License-Identifier: GPL-2.0 OR MIT 4/************************************************************************** 5 * 6 * Copyright 2014-2015 VMware, Inc., Palo Alto, CA., USA 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sub license, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice (including the 17 * next paragraph) shall be included in all copies or substantial portions 18 * of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 23 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 24 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 25 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 26 * USE OR OTHER DEALINGS IN THE SOFTWARE. 27 * 28 **************************************************************************/ 29 30#include <sys/cdefs.h> 31__KERNEL_RCSID(0, "$NetBSD: vmwgfx_cmdbuf_res.c,v 1.3 2021/12/18 23:45:45 riastradh Exp $"); 32 33#include "vmwgfx_drv.h" 34#include "vmwgfx_resource_priv.h" 35 36#define VMW_CMDBUF_RES_MAN_HT_ORDER 12 37 38/** 39 * struct vmw_cmdbuf_res - Command buffer managed resource entry. 40 * 41 * @res: Refcounted pointer to a struct vmw_resource. 42 * @hash: Hash entry for the manager hash table. 43 * @head: List head used either by the staging list or the manager list 44 * of commited resources. 45 * @state: Staging state of this resource entry. 46 * @man: Pointer to a resource manager for this entry. 47 */ 48struct vmw_cmdbuf_res { 49 struct vmw_resource *res; 50 struct drm_hash_item hash; 51 struct list_head head; 52 enum vmw_cmdbuf_res_state state; 53 struct vmw_cmdbuf_res_manager *man; 54}; 55 56/** 57 * struct vmw_cmdbuf_res_manager - Command buffer resource manager. 58 * 59 * @resources: Hash table containing staged and commited command buffer 60 * resources 61 * @list: List of commited command buffer resources. 62 * @dev_priv: Pointer to a device private structure. 63 * 64 * @resources and @list are protected by the cmdbuf mutex for now. 65 */ 66struct vmw_cmdbuf_res_manager { 67 struct drm_open_hash resources; 68 struct list_head list; 69 struct vmw_private *dev_priv; 70}; 71 72 73/** 74 * vmw_cmdbuf_res_lookup - Look up a command buffer resource 75 * 76 * @man: Pointer to the command buffer resource manager 77 * @resource_type: The resource type, that combined with the user key 78 * identifies the resource. 79 * @user_key: The user key. 80 * 81 * Returns a valid refcounted struct vmw_resource pointer on success, 82 * an error pointer on failure. 83 */ 84struct vmw_resource * 85vmw_cmdbuf_res_lookup(struct vmw_cmdbuf_res_manager *man, 86 enum vmw_cmdbuf_res_type res_type, 87 u32 user_key) 88{ 89 struct drm_hash_item *hash; 90 int ret; 91 unsigned long key = user_key | (res_type << 24); 92 93 ret = drm_ht_find_item(&man->resources, key, &hash); 94 if (unlikely(ret != 0)) 95 return ERR_PTR(ret); 96 97 return drm_hash_entry(hash, struct vmw_cmdbuf_res, hash)->res; 98} 99 100/** 101 * vmw_cmdbuf_res_free - Free a command buffer resource. 102 * 103 * @man: Pointer to the command buffer resource manager 104 * @entry: Pointer to a struct vmw_cmdbuf_res. 105 * 106 * Frees a struct vmw_cmdbuf_res entry and drops its reference to the 107 * struct vmw_resource. 108 */ 109static void vmw_cmdbuf_res_free(struct vmw_cmdbuf_res_manager *man, 110 struct vmw_cmdbuf_res *entry) 111{ 112 list_del(&entry->head); 113 WARN_ON(drm_ht_remove_item(&man->resources, &entry->hash)); 114 vmw_resource_unreference(&entry->res); 115 kfree(entry); 116} 117 118/** 119 * vmw_cmdbuf_res_commit - Commit a list of command buffer resource actions 120 * 121 * @list: Caller's list of command buffer resource actions. 122 * 123 * This function commits a list of command buffer resource 124 * additions or removals. 125 * It is typically called when the execbuf ioctl call triggering these 126 * actions has commited the fifo contents to the device. 127 */ 128void vmw_cmdbuf_res_commit(struct list_head *list) 129{ 130 struct vmw_cmdbuf_res *entry, *next; 131 132 list_for_each_entry_safe(entry, next, list, head) { 133 list_del(&entry->head); 134 if (entry->res->func->commit_notify) 135 entry->res->func->commit_notify(entry->res, 136 entry->state); 137 switch (entry->state) { 138 case VMW_CMDBUF_RES_ADD: 139 entry->state = VMW_CMDBUF_RES_COMMITTED; 140 list_add_tail(&entry->head, &entry->man->list); 141 break; 142 case VMW_CMDBUF_RES_DEL: 143 vmw_resource_unreference(&entry->res); 144 kfree(entry); 145 break; 146 default: 147 BUG(); 148 break; 149 } 150 } 151} 152 153/** 154 * vmw_cmdbuf_res_revert - Revert a list of command buffer resource actions 155 * 156 * @man: Pointer to the command buffer resource manager 157 * @list: Caller's list of command buffer resource action 158 * 159 * This function reverts a list of command buffer resource 160 * additions or removals. 161 * It is typically called when the execbuf ioctl call triggering these 162 * actions failed for some reason, and the command stream was never 163 * submitted. 164 */ 165void vmw_cmdbuf_res_revert(struct list_head *list) 166{ 167 struct vmw_cmdbuf_res *entry, *next; 168 int ret; 169 170 list_for_each_entry_safe(entry, next, list, head) { 171 switch (entry->state) { 172 case VMW_CMDBUF_RES_ADD: 173 vmw_cmdbuf_res_free(entry->man, entry); 174 break; 175 case VMW_CMDBUF_RES_DEL: 176 ret = drm_ht_insert_item(&entry->man->resources, 177 &entry->hash); 178 list_del(&entry->head); 179 list_add_tail(&entry->head, &entry->man->list); 180 entry->state = VMW_CMDBUF_RES_COMMITTED; 181 break; 182 default: 183 BUG(); 184 break; 185 } 186 } 187} 188 189/** 190 * vmw_cmdbuf_res_add - Stage a command buffer managed resource for addition. 191 * 192 * @man: Pointer to the command buffer resource manager. 193 * @res_type: The resource type. 194 * @user_key: The user-space id of the resource. 195 * @res: Valid (refcount != 0) pointer to a struct vmw_resource. 196 * @list: The staging list. 197 * 198 * This function allocates a struct vmw_cmdbuf_res entry and adds the 199 * resource to the hash table of the manager identified by @man. The 200 * entry is then put on the staging list identified by @list. 201 */ 202int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man, 203 enum vmw_cmdbuf_res_type res_type, 204 u32 user_key, 205 struct vmw_resource *res, 206 struct list_head *list) 207{ 208 struct vmw_cmdbuf_res *cres; 209 int ret; 210 211 cres = kzalloc(sizeof(*cres), GFP_KERNEL); 212 if (unlikely(!cres)) 213 return -ENOMEM; 214 215 cres->hash.key = user_key | (res_type << 24); 216 ret = drm_ht_insert_item(&man->resources, &cres->hash); 217 if (unlikely(ret != 0)) { 218 kfree(cres); 219 goto out_invalid_key; 220 } 221 222 cres->state = VMW_CMDBUF_RES_ADD; 223 cres->res = vmw_resource_reference(res); 224 cres->man = man; 225 list_add_tail(&cres->head, list); 226 227out_invalid_key: 228 return ret; 229} 230 231/** 232 * vmw_cmdbuf_res_remove - Stage a command buffer managed resource for removal. 233 * 234 * @man: Pointer to the command buffer resource manager. 235 * @res_type: The resource type. 236 * @user_key: The user-space id of the resource. 237 * @list: The staging list. 238 * @res_p: If the resource is in an already committed state, points to the 239 * struct vmw_resource on successful return. The pointer will be 240 * non ref-counted. 241 * 242 * This function looks up the struct vmw_cmdbuf_res entry from the manager 243 * hash table and, if it exists, removes it. Depending on its current staging 244 * state it then either removes the entry from the staging list or adds it 245 * to it with a staging state of removal. 246 */ 247int vmw_cmdbuf_res_remove(struct vmw_cmdbuf_res_manager *man, 248 enum vmw_cmdbuf_res_type res_type, 249 u32 user_key, 250 struct list_head *list, 251 struct vmw_resource **res_p) 252{ 253 struct vmw_cmdbuf_res *entry; 254 struct drm_hash_item *hash; 255 int ret; 256 257 ret = drm_ht_find_item(&man->resources, user_key | (res_type << 24), 258 &hash); 259 if (likely(ret != 0)) 260 return -EINVAL; 261 262 entry = drm_hash_entry(hash, struct vmw_cmdbuf_res, hash); 263 264 switch (entry->state) { 265 case VMW_CMDBUF_RES_ADD: 266 vmw_cmdbuf_res_free(man, entry); 267 *res_p = NULL; 268 break; 269 case VMW_CMDBUF_RES_COMMITTED: 270 (void) drm_ht_remove_item(&man->resources, &entry->hash); 271 list_del(&entry->head); 272 entry->state = VMW_CMDBUF_RES_DEL; 273 list_add_tail(&entry->head, list); 274 *res_p = entry->res; 275 break; 276 default: 277 BUG(); 278 break; 279 } 280 281 return 0; 282} 283 284/** 285 * vmw_cmdbuf_res_man_create - Allocate a command buffer managed resource 286 * manager. 287 * 288 * @dev_priv: Pointer to a struct vmw_private 289 * 290 * Allocates and initializes a command buffer managed resource manager. Returns 291 * an error pointer on failure. 292 */ 293struct vmw_cmdbuf_res_manager * 294vmw_cmdbuf_res_man_create(struct vmw_private *dev_priv) 295{ 296 struct vmw_cmdbuf_res_manager *man; 297 int ret; 298 299 man = kzalloc(sizeof(*man), GFP_KERNEL); 300 if (!man) 301 return ERR_PTR(-ENOMEM); 302 303 man->dev_priv = dev_priv; 304 INIT_LIST_HEAD(&man->list); 305 ret = drm_ht_create(&man->resources, VMW_CMDBUF_RES_MAN_HT_ORDER); 306 if (ret == 0) 307 return man; 308 309 kfree(man); 310 return ERR_PTR(ret); 311} 312 313/** 314 * vmw_cmdbuf_res_man_destroy - Destroy a command buffer managed resource 315 * manager. 316 * 317 * @man: Pointer to the manager to destroy. 318 * 319 * This function destroys a command buffer managed resource manager and 320 * unreferences / frees all command buffer managed resources and -entries 321 * associated with it. 322 */ 323void vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man) 324{ 325 struct vmw_cmdbuf_res *entry, *next; 326 327 list_for_each_entry_safe(entry, next, &man->list, head) 328 vmw_cmdbuf_res_free(man, entry); 329 330 drm_ht_remove(&man->resources); 331 kfree(man); 332} 333 334/** 335 * 336 * vmw_cmdbuf_res_man_size - Return the size of a command buffer managed 337 * resource manager 338 * 339 * Returns the approximate allocation size of a command buffer managed 340 * resource manager. 341 */ 342size_t vmw_cmdbuf_res_man_size(void) 343{ 344 static size_t res_man_size; 345 346 if (unlikely(res_man_size == 0)) 347 res_man_size = 348 ttm_round_pot(sizeof(struct vmw_cmdbuf_res_manager)) + 349 ttm_round_pot(sizeof(struct hlist_head) << 350 VMW_CMDBUF_RES_MAN_HT_ORDER); 351 352 return res_man_size; 353} 354