1 /************************************************************************** 2 * 3 * Copyright © 2014 VMware, Inc., Palo Alto, CA., USA 4 * All Rights Reserved. 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 "vmwgfx_drv.h" 29 30 #define VMW_CMDBUF_RES_MAN_HT_ORDER 12 31 32 enum vmw_cmdbuf_res_state { 33 VMW_CMDBUF_RES_COMMITED, 34 VMW_CMDBUF_RES_ADD, 35 VMW_CMDBUF_RES_DEL 36 }; 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 */ 48 struct 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 */ 66 struct 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 */ 84 struct vmw_resource * 85 vmw_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 vmw_resource_reference 98 (drm_hash_entry(hash, struct vmw_cmdbuf_res, hash)->res); 99 } 100 101 /** 102 * vmw_cmdbuf_res_free - Free a command buffer resource. 103 * 104 * @man: Pointer to the command buffer resource manager 105 * @entry: Pointer to a struct vmw_cmdbuf_res. 106 * 107 * Frees a struct vmw_cmdbuf_res entry and drops its reference to the 108 * struct vmw_resource. 109 */ 110 static void vmw_cmdbuf_res_free(struct vmw_cmdbuf_res_manager *man, 111 struct vmw_cmdbuf_res *entry) 112 { 113 list_del(&entry->head); 114 WARN_ON(drm_ht_remove_item(&man->resources, &entry->hash)); 115 vmw_resource_unreference(&entry->res); 116 kfree(entry); 117 } 118 119 /** 120 * vmw_cmdbuf_res_commit - Commit a list of command buffer resource actions 121 * 122 * @list: Caller's list of command buffer resource actions. 123 * 124 * This function commits a list of command buffer resource 125 * additions or removals. 126 * It is typically called when the execbuf ioctl call triggering these 127 * actions has commited the fifo contents to the device. 128 */ 129 void vmw_cmdbuf_res_commit(struct list_head *list) 130 { 131 struct vmw_cmdbuf_res *entry, *next; 132 133 list_for_each_entry_safe(entry, next, list, head) { 134 list_del(&entry->head); 135 switch (entry->state) { 136 case VMW_CMDBUF_RES_ADD: 137 entry->state = VMW_CMDBUF_RES_COMMITED; 138 list_add_tail(&entry->head, &entry->man->list); 139 break; 140 case VMW_CMDBUF_RES_DEL: 141 vmw_resource_unreference(&entry->res); 142 kfree(entry); 143 break; 144 default: 145 BUG(); 146 break; 147 } 148 } 149 } 150 151 /** 152 * vmw_cmdbuf_res_revert - Revert a list of command buffer resource actions 153 * 154 * @man: Pointer to the command buffer resource manager 155 * @list: Caller's list of command buffer resource action 156 * 157 * This function reverts a list of command buffer resource 158 * additions or removals. 159 * It is typically called when the execbuf ioctl call triggering these 160 * actions failed for some reason, and the command stream was never 161 * submitted. 162 */ 163 void vmw_cmdbuf_res_revert(struct list_head *list) 164 { 165 struct vmw_cmdbuf_res *entry, *next; 166 int ret; 167 168 list_for_each_entry_safe(entry, next, list, head) { 169 switch (entry->state) { 170 case VMW_CMDBUF_RES_ADD: 171 vmw_cmdbuf_res_free(entry->man, entry); 172 break; 173 case VMW_CMDBUF_RES_DEL: 174 ret = drm_ht_insert_item(&entry->man->resources, 175 &entry->hash); 176 list_del(&entry->head); 177 list_add_tail(&entry->head, &entry->man->list); 178 entry->state = VMW_CMDBUF_RES_COMMITED; 179 break; 180 default: 181 BUG(); 182 break; 183 } 184 } 185 } 186 187 /** 188 * vmw_cmdbuf_res_add - Stage a command buffer managed resource for addition. 189 * 190 * @man: Pointer to the command buffer resource manager. 191 * @res_type: The resource type. 192 * @user_key: The user-space id of the resource. 193 * @res: Valid (refcount != 0) pointer to a struct vmw_resource. 194 * @list: The staging list. 195 * 196 * This function allocates a struct vmw_cmdbuf_res entry and adds the 197 * resource to the hash table of the manager identified by @man. The 198 * entry is then put on the staging list identified by @list. 199 */ 200 int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man, 201 enum vmw_cmdbuf_res_type res_type, 202 u32 user_key, 203 struct vmw_resource *res, 204 struct list_head *list) 205 { 206 struct vmw_cmdbuf_res *cres; 207 int ret; 208 209 cres = kzalloc(sizeof(*cres), GFP_KERNEL); 210 if (unlikely(cres == NULL)) 211 return -ENOMEM; 212 213 cres->hash.key = user_key | (res_type << 24); 214 ret = drm_ht_insert_item(&man->resources, &cres->hash); 215 if (unlikely(ret != 0)) 216 goto out_invalid_key; 217 218 cres->state = VMW_CMDBUF_RES_ADD; 219 cres->res = vmw_resource_reference(res); 220 cres->man = man; 221 list_add_tail(&cres->head, list); 222 223 out_invalid_key: 224 return ret; 225 } 226 227 /** 228 * vmw_cmdbuf_res_remove - Stage a command buffer managed resource for removal. 229 * 230 * @man: Pointer to the command buffer resource manager. 231 * @res_type: The resource type. 232 * @user_key: The user-space id of the resource. 233 * @list: The staging list. 234 * 235 * This function looks up the struct vmw_cmdbuf_res entry from the manager 236 * hash table and, if it exists, removes it. Depending on its current staging 237 * state it then either removes the entry from the staging list or adds it 238 * to it with a staging state of removal. 239 */ 240 int vmw_cmdbuf_res_remove(struct vmw_cmdbuf_res_manager *man, 241 enum vmw_cmdbuf_res_type res_type, 242 u32 user_key, 243 struct list_head *list) 244 { 245 struct vmw_cmdbuf_res *entry; 246 struct drm_hash_item *hash; 247 int ret; 248 249 ret = drm_ht_find_item(&man->resources, user_key | (res_type << 24), 250 &hash); 251 if (likely(ret != 0)) 252 return -EINVAL; 253 254 entry = drm_hash_entry(hash, struct vmw_cmdbuf_res, hash); 255 256 switch (entry->state) { 257 case VMW_CMDBUF_RES_ADD: 258 vmw_cmdbuf_res_free(man, entry); 259 break; 260 case VMW_CMDBUF_RES_COMMITED: 261 (void) drm_ht_remove_item(&man->resources, &entry->hash); 262 list_del(&entry->head); 263 entry->state = VMW_CMDBUF_RES_DEL; 264 list_add_tail(&entry->head, list); 265 break; 266 default: 267 BUG(); 268 break; 269 } 270 271 return 0; 272 } 273 274 /** 275 * vmw_cmdbuf_res_man_create - Allocate a command buffer managed resource 276 * manager. 277 * 278 * @dev_priv: Pointer to a struct vmw_private 279 * 280 * Allocates and initializes a command buffer managed resource manager. Returns 281 * an error pointer on failure. 282 */ 283 struct vmw_cmdbuf_res_manager * 284 vmw_cmdbuf_res_man_create(struct vmw_private *dev_priv) 285 { 286 struct vmw_cmdbuf_res_manager *man; 287 int ret; 288 289 man = kzalloc(sizeof(*man), GFP_KERNEL); 290 if (man == NULL) 291 return ERR_PTR(-ENOMEM); 292 293 man->dev_priv = dev_priv; 294 INIT_LIST_HEAD(&man->list); 295 ret = drm_ht_create(&man->resources, VMW_CMDBUF_RES_MAN_HT_ORDER); 296 if (ret == 0) 297 return man; 298 299 kfree(man); 300 return ERR_PTR(ret); 301 } 302 303 /** 304 * vmw_cmdbuf_res_man_destroy - Destroy a command buffer managed resource 305 * manager. 306 * 307 * @man: Pointer to the manager to destroy. 308 * 309 * This function destroys a command buffer managed resource manager and 310 * unreferences / frees all command buffer managed resources and -entries 311 * associated with it. 312 */ 313 void vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man) 314 { 315 struct vmw_cmdbuf_res *entry, *next; 316 317 list_for_each_entry_safe(entry, next, &man->list, head) 318 vmw_cmdbuf_res_free(man, entry); 319 320 kfree(man); 321 } 322 323 /** 324 * 325 * vmw_cmdbuf_res_man_size - Return the size of a command buffer managed 326 * resource manager 327 * 328 * Returns the approximate allocation size of a command buffer managed 329 * resource manager. 330 */ 331 size_t vmw_cmdbuf_res_man_size(void) 332 { 333 static size_t res_man_size; 334 335 if (unlikely(res_man_size == 0)) 336 res_man_size = 337 ttm_round_pot(sizeof(struct vmw_cmdbuf_res_manager)) + 338 ttm_round_pot(sizeof(struct hlist_head) << 339 VMW_CMDBUF_RES_MAN_HT_ORDER); 340 341 return res_man_size; 342 } 343