162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 262306a36Sopenharmony_ci/************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2014-2022 VMware, Inc., Palo Alto, CA., USA 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 762306a36Sopenharmony_ci * copy of this software and associated documentation files (the 862306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 962306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 1062306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1162306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1262306a36Sopenharmony_ci * the following conditions: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1562306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 1662306a36Sopenharmony_ci * of the Software. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1962306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2062306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2162306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2262306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2362306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2462306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci **************************************************************************/ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "vmwgfx_drv.h" 2962306a36Sopenharmony_ci#include "vmwgfx_resource_priv.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <linux/hashtable.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define VMW_CMDBUF_RES_MAN_HT_ORDER 12 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/** 3662306a36Sopenharmony_ci * struct vmw_cmdbuf_res - Command buffer managed resource entry. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * @res: Refcounted pointer to a struct vmw_resource. 3962306a36Sopenharmony_ci * @hash: Hash entry for the manager hash table. 4062306a36Sopenharmony_ci * @head: List head used either by the staging list or the manager list 4162306a36Sopenharmony_ci * of committed resources. 4262306a36Sopenharmony_ci * @state: Staging state of this resource entry. 4362306a36Sopenharmony_ci * @man: Pointer to a resource manager for this entry. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistruct vmw_cmdbuf_res { 4662306a36Sopenharmony_ci struct vmw_resource *res; 4762306a36Sopenharmony_ci struct vmwgfx_hash_item hash; 4862306a36Sopenharmony_ci struct list_head head; 4962306a36Sopenharmony_ci enum vmw_cmdbuf_res_state state; 5062306a36Sopenharmony_ci struct vmw_cmdbuf_res_manager *man; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/** 5462306a36Sopenharmony_ci * struct vmw_cmdbuf_res_manager - Command buffer resource manager. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * @resources: Hash table containing staged and committed command buffer 5762306a36Sopenharmony_ci * resources 5862306a36Sopenharmony_ci * @list: List of committed command buffer resources. 5962306a36Sopenharmony_ci * @dev_priv: Pointer to a device private structure. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * @resources and @list are protected by the cmdbuf mutex for now. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistruct vmw_cmdbuf_res_manager { 6462306a36Sopenharmony_ci DECLARE_HASHTABLE(resources, VMW_CMDBUF_RES_MAN_HT_ORDER); 6562306a36Sopenharmony_ci struct list_head list; 6662306a36Sopenharmony_ci struct vmw_private *dev_priv; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/** 7162306a36Sopenharmony_ci * vmw_cmdbuf_res_lookup - Look up a command buffer resource 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * @man: Pointer to the command buffer resource manager 7462306a36Sopenharmony_ci * @res_type: The resource type, that combined with the user key 7562306a36Sopenharmony_ci * identifies the resource. 7662306a36Sopenharmony_ci * @user_key: The user key. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * Returns a valid refcounted struct vmw_resource pointer on success, 7962306a36Sopenharmony_ci * an error pointer on failure. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistruct vmw_resource * 8262306a36Sopenharmony_civmw_cmdbuf_res_lookup(struct vmw_cmdbuf_res_manager *man, 8362306a36Sopenharmony_ci enum vmw_cmdbuf_res_type res_type, 8462306a36Sopenharmony_ci u32 user_key) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct vmwgfx_hash_item *hash; 8762306a36Sopenharmony_ci unsigned long key = user_key | (res_type << 24); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci hash_for_each_possible_rcu(man->resources, hash, head, key) { 9062306a36Sopenharmony_ci if (hash->key == key) 9162306a36Sopenharmony_ci return hlist_entry(hash, struct vmw_cmdbuf_res, hash)->res; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * vmw_cmdbuf_res_free - Free a command buffer resource. 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * @man: Pointer to the command buffer resource manager 10062306a36Sopenharmony_ci * @entry: Pointer to a struct vmw_cmdbuf_res. 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * Frees a struct vmw_cmdbuf_res entry and drops its reference to the 10362306a36Sopenharmony_ci * struct vmw_resource. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic void vmw_cmdbuf_res_free(struct vmw_cmdbuf_res_manager *man, 10662306a36Sopenharmony_ci struct vmw_cmdbuf_res *entry) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci list_del(&entry->head); 10962306a36Sopenharmony_ci hash_del_rcu(&entry->hash.head); 11062306a36Sopenharmony_ci vmw_resource_unreference(&entry->res); 11162306a36Sopenharmony_ci kfree(entry); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/** 11562306a36Sopenharmony_ci * vmw_cmdbuf_res_commit - Commit a list of command buffer resource actions 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * @list: Caller's list of command buffer resource actions. 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * This function commits a list of command buffer resource 12062306a36Sopenharmony_ci * additions or removals. 12162306a36Sopenharmony_ci * It is typically called when the execbuf ioctl call triggering these 12262306a36Sopenharmony_ci * actions has committed the fifo contents to the device. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_civoid vmw_cmdbuf_res_commit(struct list_head *list) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct vmw_cmdbuf_res *entry, *next; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci list_for_each_entry_safe(entry, next, list, head) { 12962306a36Sopenharmony_ci list_del(&entry->head); 13062306a36Sopenharmony_ci if (entry->res->func->commit_notify) 13162306a36Sopenharmony_ci entry->res->func->commit_notify(entry->res, 13262306a36Sopenharmony_ci entry->state); 13362306a36Sopenharmony_ci switch (entry->state) { 13462306a36Sopenharmony_ci case VMW_CMDBUF_RES_ADD: 13562306a36Sopenharmony_ci entry->state = VMW_CMDBUF_RES_COMMITTED; 13662306a36Sopenharmony_ci list_add_tail(&entry->head, &entry->man->list); 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci case VMW_CMDBUF_RES_DEL: 13962306a36Sopenharmony_ci vmw_resource_unreference(&entry->res); 14062306a36Sopenharmony_ci kfree(entry); 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci default: 14362306a36Sopenharmony_ci BUG(); 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/** 15062306a36Sopenharmony_ci * vmw_cmdbuf_res_revert - Revert a list of command buffer resource actions 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * @list: Caller's list of command buffer resource action 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * This function reverts a list of command buffer resource 15562306a36Sopenharmony_ci * additions or removals. 15662306a36Sopenharmony_ci * It is typically called when the execbuf ioctl call triggering these 15762306a36Sopenharmony_ci * actions failed for some reason, and the command stream was never 15862306a36Sopenharmony_ci * submitted. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_civoid vmw_cmdbuf_res_revert(struct list_head *list) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct vmw_cmdbuf_res *entry, *next; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci list_for_each_entry_safe(entry, next, list, head) { 16562306a36Sopenharmony_ci switch (entry->state) { 16662306a36Sopenharmony_ci case VMW_CMDBUF_RES_ADD: 16762306a36Sopenharmony_ci vmw_cmdbuf_res_free(entry->man, entry); 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case VMW_CMDBUF_RES_DEL: 17062306a36Sopenharmony_ci hash_add_rcu(entry->man->resources, &entry->hash.head, 17162306a36Sopenharmony_ci entry->hash.key); 17262306a36Sopenharmony_ci list_move_tail(&entry->head, &entry->man->list); 17362306a36Sopenharmony_ci entry->state = VMW_CMDBUF_RES_COMMITTED; 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci default: 17662306a36Sopenharmony_ci BUG(); 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/** 18362306a36Sopenharmony_ci * vmw_cmdbuf_res_add - Stage a command buffer managed resource for addition. 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * @man: Pointer to the command buffer resource manager. 18662306a36Sopenharmony_ci * @res_type: The resource type. 18762306a36Sopenharmony_ci * @user_key: The user-space id of the resource. 18862306a36Sopenharmony_ci * @res: Valid (refcount != 0) pointer to a struct vmw_resource. 18962306a36Sopenharmony_ci * @list: The staging list. 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * This function allocates a struct vmw_cmdbuf_res entry and adds the 19262306a36Sopenharmony_ci * resource to the hash table of the manager identified by @man. The 19362306a36Sopenharmony_ci * entry is then put on the staging list identified by @list. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ciint vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man, 19662306a36Sopenharmony_ci enum vmw_cmdbuf_res_type res_type, 19762306a36Sopenharmony_ci u32 user_key, 19862306a36Sopenharmony_ci struct vmw_resource *res, 19962306a36Sopenharmony_ci struct list_head *list) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct vmw_cmdbuf_res *cres; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci cres = kzalloc(sizeof(*cres), GFP_KERNEL); 20462306a36Sopenharmony_ci if (unlikely(!cres)) 20562306a36Sopenharmony_ci return -ENOMEM; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci cres->hash.key = user_key | (res_type << 24); 20862306a36Sopenharmony_ci hash_add_rcu(man->resources, &cres->hash.head, cres->hash.key); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci cres->state = VMW_CMDBUF_RES_ADD; 21162306a36Sopenharmony_ci cres->res = vmw_resource_reference(res); 21262306a36Sopenharmony_ci cres->man = man; 21362306a36Sopenharmony_ci list_add_tail(&cres->head, list); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/** 21962306a36Sopenharmony_ci * vmw_cmdbuf_res_remove - Stage a command buffer managed resource for removal. 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * @man: Pointer to the command buffer resource manager. 22262306a36Sopenharmony_ci * @res_type: The resource type. 22362306a36Sopenharmony_ci * @user_key: The user-space id of the resource. 22462306a36Sopenharmony_ci * @list: The staging list. 22562306a36Sopenharmony_ci * @res_p: If the resource is in an already committed state, points to the 22662306a36Sopenharmony_ci * struct vmw_resource on successful return. The pointer will be 22762306a36Sopenharmony_ci * non ref-counted. 22862306a36Sopenharmony_ci * 22962306a36Sopenharmony_ci * This function looks up the struct vmw_cmdbuf_res entry from the manager 23062306a36Sopenharmony_ci * hash table and, if it exists, removes it. Depending on its current staging 23162306a36Sopenharmony_ci * state it then either removes the entry from the staging list or adds it 23262306a36Sopenharmony_ci * to it with a staging state of removal. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ciint vmw_cmdbuf_res_remove(struct vmw_cmdbuf_res_manager *man, 23562306a36Sopenharmony_ci enum vmw_cmdbuf_res_type res_type, 23662306a36Sopenharmony_ci u32 user_key, 23762306a36Sopenharmony_ci struct list_head *list, 23862306a36Sopenharmony_ci struct vmw_resource **res_p) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct vmw_cmdbuf_res *entry = NULL; 24162306a36Sopenharmony_ci struct vmwgfx_hash_item *hash; 24262306a36Sopenharmony_ci unsigned long key = user_key | (res_type << 24); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci hash_for_each_possible_rcu(man->resources, hash, head, key) { 24562306a36Sopenharmony_ci if (hash->key == key) { 24662306a36Sopenharmony_ci entry = hlist_entry(hash, struct vmw_cmdbuf_res, hash); 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci if (unlikely(!entry)) 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci switch (entry->state) { 25462306a36Sopenharmony_ci case VMW_CMDBUF_RES_ADD: 25562306a36Sopenharmony_ci vmw_cmdbuf_res_free(man, entry); 25662306a36Sopenharmony_ci *res_p = NULL; 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci case VMW_CMDBUF_RES_COMMITTED: 25962306a36Sopenharmony_ci hash_del_rcu(&entry->hash.head); 26062306a36Sopenharmony_ci list_del(&entry->head); 26162306a36Sopenharmony_ci entry->state = VMW_CMDBUF_RES_DEL; 26262306a36Sopenharmony_ci list_add_tail(&entry->head, list); 26362306a36Sopenharmony_ci *res_p = entry->res; 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci default: 26662306a36Sopenharmony_ci BUG(); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/** 27462306a36Sopenharmony_ci * vmw_cmdbuf_res_man_create - Allocate a command buffer managed resource 27562306a36Sopenharmony_ci * manager. 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * @dev_priv: Pointer to a struct vmw_private 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * Allocates and initializes a command buffer managed resource manager. Returns 28062306a36Sopenharmony_ci * an error pointer on failure. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_cistruct vmw_cmdbuf_res_manager * 28362306a36Sopenharmony_civmw_cmdbuf_res_man_create(struct vmw_private *dev_priv) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct vmw_cmdbuf_res_manager *man; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci man = kzalloc(sizeof(*man), GFP_KERNEL); 28862306a36Sopenharmony_ci if (!man) 28962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci man->dev_priv = dev_priv; 29262306a36Sopenharmony_ci INIT_LIST_HEAD(&man->list); 29362306a36Sopenharmony_ci hash_init(man->resources); 29462306a36Sopenharmony_ci return man; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/** 29862306a36Sopenharmony_ci * vmw_cmdbuf_res_man_destroy - Destroy a command buffer managed resource 29962306a36Sopenharmony_ci * manager. 30062306a36Sopenharmony_ci * 30162306a36Sopenharmony_ci * @man: Pointer to the manager to destroy. 30262306a36Sopenharmony_ci * 30362306a36Sopenharmony_ci * This function destroys a command buffer managed resource manager and 30462306a36Sopenharmony_ci * unreferences / frees all command buffer managed resources and -entries 30562306a36Sopenharmony_ci * associated with it. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_civoid vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct vmw_cmdbuf_res *entry, *next; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci list_for_each_entry_safe(entry, next, &man->list, head) 31262306a36Sopenharmony_ci vmw_cmdbuf_res_free(man, entry); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci kfree(man); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 317