18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * VMware VMCI Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/vmw_vmci_defs.h> 98c2ecf20Sopenharmony_ci#include <linux/hash.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/rculist.h> 128c2ecf20Sopenharmony_ci#include <linux/completion.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "vmci_resource.h" 158c2ecf20Sopenharmony_ci#include "vmci_driver.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define VMCI_RESOURCE_HASH_BITS 7 198c2ecf20Sopenharmony_ci#define VMCI_RESOURCE_HASH_BUCKETS (1 << VMCI_RESOURCE_HASH_BITS) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct vmci_hash_table { 228c2ecf20Sopenharmony_ci spinlock_t lock; 238c2ecf20Sopenharmony_ci struct hlist_head entries[VMCI_RESOURCE_HASH_BUCKETS]; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic struct vmci_hash_table vmci_resource_table = { 278c2ecf20Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(vmci_resource_table.lock), 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic unsigned int vmci_resource_hash(struct vmci_handle handle) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci return hash_32(handle.resource, VMCI_RESOURCE_HASH_BITS); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Gets a resource (if one exists) matching given handle from the hash table. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistatic struct vmci_resource *vmci_resource_lookup(struct vmci_handle handle, 398c2ecf20Sopenharmony_ci enum vmci_resource_type type) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct vmci_resource *r, *resource = NULL; 428c2ecf20Sopenharmony_ci unsigned int idx = vmci_resource_hash(handle); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci rcu_read_lock(); 458c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(r, 468c2ecf20Sopenharmony_ci &vmci_resource_table.entries[idx], node) { 478c2ecf20Sopenharmony_ci u32 cid = r->handle.context; 488c2ecf20Sopenharmony_ci u32 rid = r->handle.resource; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (r->type == type && 518c2ecf20Sopenharmony_ci rid == handle.resource && 528c2ecf20Sopenharmony_ci (cid == handle.context || cid == VMCI_INVALID_ID || 538c2ecf20Sopenharmony_ci handle.context == VMCI_INVALID_ID)) { 548c2ecf20Sopenharmony_ci resource = r; 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci rcu_read_unlock(); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return resource; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * Find an unused resource ID and return it. The first 658c2ecf20Sopenharmony_ci * VMCI_RESERVED_RESOURCE_ID_MAX are reserved so we start from 668c2ecf20Sopenharmony_ci * its value + 1. 678c2ecf20Sopenharmony_ci * Returns VMCI resource id on success, VMCI_INVALID_ID on failure. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_cistatic u32 vmci_resource_find_id(u32 context_id, 708c2ecf20Sopenharmony_ci enum vmci_resource_type resource_type) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci static u32 resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; 738c2ecf20Sopenharmony_ci u32 old_rid = resource_id; 748c2ecf20Sopenharmony_ci u32 current_rid; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * Generate a unique resource ID. Keep on trying until we wrap around 788c2ecf20Sopenharmony_ci * in the RID space. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci do { 818c2ecf20Sopenharmony_ci struct vmci_handle handle; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci current_rid = resource_id; 848c2ecf20Sopenharmony_ci resource_id++; 858c2ecf20Sopenharmony_ci if (unlikely(resource_id == VMCI_INVALID_ID)) { 868c2ecf20Sopenharmony_ci /* Skip the reserved rids. */ 878c2ecf20Sopenharmony_ci resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci handle = vmci_make_handle(context_id, current_rid); 918c2ecf20Sopenharmony_ci if (!vmci_resource_lookup(handle, resource_type)) 928c2ecf20Sopenharmony_ci return current_rid; 938c2ecf20Sopenharmony_ci } while (resource_id != old_rid); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return VMCI_INVALID_ID; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ciint vmci_resource_add(struct vmci_resource *resource, 1008c2ecf20Sopenharmony_ci enum vmci_resource_type resource_type, 1018c2ecf20Sopenharmony_ci struct vmci_handle handle) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci unsigned int idx; 1058c2ecf20Sopenharmony_ci int result; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci spin_lock(&vmci_resource_table.lock); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (handle.resource == VMCI_INVALID_ID) { 1108c2ecf20Sopenharmony_ci handle.resource = vmci_resource_find_id(handle.context, 1118c2ecf20Sopenharmony_ci resource_type); 1128c2ecf20Sopenharmony_ci if (handle.resource == VMCI_INVALID_ID) { 1138c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_HANDLE; 1148c2ecf20Sopenharmony_ci goto out; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci } else if (vmci_resource_lookup(handle, resource_type)) { 1178c2ecf20Sopenharmony_ci result = VMCI_ERROR_ALREADY_EXISTS; 1188c2ecf20Sopenharmony_ci goto out; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci resource->handle = handle; 1228c2ecf20Sopenharmony_ci resource->type = resource_type; 1238c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&resource->node); 1248c2ecf20Sopenharmony_ci kref_init(&resource->kref); 1258c2ecf20Sopenharmony_ci init_completion(&resource->done); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci idx = vmci_resource_hash(resource->handle); 1288c2ecf20Sopenharmony_ci hlist_add_head_rcu(&resource->node, &vmci_resource_table.entries[idx]); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci result = VMCI_SUCCESS; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ciout: 1338c2ecf20Sopenharmony_ci spin_unlock(&vmci_resource_table.lock); 1348c2ecf20Sopenharmony_ci return result; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_civoid vmci_resource_remove(struct vmci_resource *resource) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct vmci_handle handle = resource->handle; 1408c2ecf20Sopenharmony_ci unsigned int idx = vmci_resource_hash(handle); 1418c2ecf20Sopenharmony_ci struct vmci_resource *r; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Remove resource from hash table. */ 1448c2ecf20Sopenharmony_ci spin_lock(&vmci_resource_table.lock); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci hlist_for_each_entry(r, &vmci_resource_table.entries[idx], node) { 1478c2ecf20Sopenharmony_ci if (vmci_handle_is_equal(r->handle, resource->handle) && 1488c2ecf20Sopenharmony_ci resource->type == r->type) { 1498c2ecf20Sopenharmony_ci hlist_del_init_rcu(&r->node); 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci spin_unlock(&vmci_resource_table.lock); 1558c2ecf20Sopenharmony_ci synchronize_rcu(); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci vmci_resource_put(resource); 1588c2ecf20Sopenharmony_ci wait_for_completion(&resource->done); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistruct vmci_resource * 1628c2ecf20Sopenharmony_civmci_resource_by_handle(struct vmci_handle resource_handle, 1638c2ecf20Sopenharmony_ci enum vmci_resource_type resource_type) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct vmci_resource *r, *resource = NULL; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci rcu_read_lock(); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci r = vmci_resource_lookup(resource_handle, resource_type); 1708c2ecf20Sopenharmony_ci if (r && 1718c2ecf20Sopenharmony_ci (resource_type == r->type || 1728c2ecf20Sopenharmony_ci resource_type == VMCI_RESOURCE_TYPE_ANY)) { 1738c2ecf20Sopenharmony_ci resource = vmci_resource_get(r); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci rcu_read_unlock(); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return resource; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * Get a reference to given resource. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cistruct vmci_resource *vmci_resource_get(struct vmci_resource *resource) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci kref_get(&resource->kref); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return resource; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void vmci_release_resource(struct kref *kref) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct vmci_resource *resource = 1948c2ecf20Sopenharmony_ci container_of(kref, struct vmci_resource, kref); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Verify the resource has been unlinked from hash table */ 1978c2ecf20Sopenharmony_ci WARN_ON(!hlist_unhashed(&resource->node)); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Signal that container of this resource can now be destroyed */ 2008c2ecf20Sopenharmony_ci complete(&resource->done); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* 2048c2ecf20Sopenharmony_ci * Resource's release function will get called if last reference. 2058c2ecf20Sopenharmony_ci * If it is the last reference, then we are sure that nobody else 2068c2ecf20Sopenharmony_ci * can increment the count again (it's gone from the resource hash 2078c2ecf20Sopenharmony_ci * table), so there's no need for locking here. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ciint vmci_resource_put(struct vmci_resource *resource) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci /* 2128c2ecf20Sopenharmony_ci * We propagate the information back to caller in case it wants to know 2138c2ecf20Sopenharmony_ci * whether entry was freed. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci return kref_put(&resource->kref, vmci_release_resource) ? 2168c2ecf20Sopenharmony_ci VMCI_SUCCESS_ENTRY_DEAD : VMCI_SUCCESS; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistruct vmci_handle vmci_resource_handle(struct vmci_resource *resource) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci return resource->handle; 2228c2ecf20Sopenharmony_ci} 223