1bc63dedbSGeorge Zhang /* 2bc63dedbSGeorge Zhang * VMware VMCI Driver 3bc63dedbSGeorge Zhang * 4bc63dedbSGeorge Zhang * Copyright (C) 2012 VMware, Inc. All rights reserved. 5bc63dedbSGeorge Zhang * 6bc63dedbSGeorge Zhang * This program is free software; you can redistribute it and/or modify it 7bc63dedbSGeorge Zhang * under the terms of the GNU General Public License as published by the 8bc63dedbSGeorge Zhang * Free Software Foundation version 2 and no later version. 9bc63dedbSGeorge Zhang * 10bc63dedbSGeorge Zhang * This program is distributed in the hope that it will be useful, but 11bc63dedbSGeorge Zhang * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12bc63dedbSGeorge Zhang * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13bc63dedbSGeorge Zhang * for more details. 14bc63dedbSGeorge Zhang */ 15bc63dedbSGeorge Zhang 16bc63dedbSGeorge Zhang #include <linux/vmw_vmci_defs.h> 17bc63dedbSGeorge Zhang #include <linux/hash.h> 18bc63dedbSGeorge Zhang #include <linux/types.h> 19bc63dedbSGeorge Zhang #include <linux/rculist.h> 20bc63dedbSGeorge Zhang 21bc63dedbSGeorge Zhang #include "vmci_resource.h" 22bc63dedbSGeorge Zhang #include "vmci_driver.h" 23bc63dedbSGeorge Zhang 24bc63dedbSGeorge Zhang 25bc63dedbSGeorge Zhang #define VMCI_RESOURCE_HASH_BITS 7 26bc63dedbSGeorge Zhang #define VMCI_RESOURCE_HASH_BUCKETS (1 << VMCI_RESOURCE_HASH_BITS) 27bc63dedbSGeorge Zhang 28bc63dedbSGeorge Zhang struct vmci_hash_table { 29bc63dedbSGeorge Zhang spinlock_t lock; 30bc63dedbSGeorge Zhang struct hlist_head entries[VMCI_RESOURCE_HASH_BUCKETS]; 31bc63dedbSGeorge Zhang }; 32bc63dedbSGeorge Zhang 33bc63dedbSGeorge Zhang static struct vmci_hash_table vmci_resource_table = { 34bc63dedbSGeorge Zhang .lock = __SPIN_LOCK_UNLOCKED(vmci_resource_table.lock), 35bc63dedbSGeorge Zhang }; 36bc63dedbSGeorge Zhang 37bc63dedbSGeorge Zhang static unsigned int vmci_resource_hash(struct vmci_handle handle) 38bc63dedbSGeorge Zhang { 39bc63dedbSGeorge Zhang return hash_32(handle.resource, VMCI_RESOURCE_HASH_BITS); 40bc63dedbSGeorge Zhang } 41bc63dedbSGeorge Zhang 42bc63dedbSGeorge Zhang /* 43bc63dedbSGeorge Zhang * Gets a resource (if one exists) matching given handle from the hash table. 44bc63dedbSGeorge Zhang */ 45bc63dedbSGeorge Zhang static struct vmci_resource *vmci_resource_lookup(struct vmci_handle handle, 46bc63dedbSGeorge Zhang enum vmci_resource_type type) 47bc63dedbSGeorge Zhang { 48bc63dedbSGeorge Zhang struct vmci_resource *r, *resource = NULL; 49bc63dedbSGeorge Zhang unsigned int idx = vmci_resource_hash(handle); 50bc63dedbSGeorge Zhang 51bc63dedbSGeorge Zhang rcu_read_lock(); 52b67bfe0dSSasha Levin hlist_for_each_entry_rcu(r, 53bc63dedbSGeorge Zhang &vmci_resource_table.entries[idx], node) { 54bc63dedbSGeorge Zhang u32 cid = r->handle.context; 55bc63dedbSGeorge Zhang u32 rid = r->handle.resource; 56bc63dedbSGeorge Zhang 57bc63dedbSGeorge Zhang if (r->type == type && 58bc63dedbSGeorge Zhang rid == handle.resource && 59bc63dedbSGeorge Zhang (cid == handle.context || cid == VMCI_INVALID_ID)) { 60bc63dedbSGeorge Zhang resource = r; 61bc63dedbSGeorge Zhang break; 62bc63dedbSGeorge Zhang } 63bc63dedbSGeorge Zhang } 64bc63dedbSGeorge Zhang rcu_read_unlock(); 65bc63dedbSGeorge Zhang 66bc63dedbSGeorge Zhang return resource; 67bc63dedbSGeorge Zhang } 68bc63dedbSGeorge Zhang 69bc63dedbSGeorge Zhang /* 70bc63dedbSGeorge Zhang * Find an unused resource ID and return it. The first 71bc63dedbSGeorge Zhang * VMCI_RESERVED_RESOURCE_ID_MAX are reserved so we start from 72bc63dedbSGeorge Zhang * its value + 1. 73bc63dedbSGeorge Zhang * Returns VMCI resource id on success, VMCI_INVALID_ID on failure. 74bc63dedbSGeorge Zhang */ 75bc63dedbSGeorge Zhang static u32 vmci_resource_find_id(u32 context_id, 76bc63dedbSGeorge Zhang enum vmci_resource_type resource_type) 77bc63dedbSGeorge Zhang { 78bc63dedbSGeorge Zhang static u32 resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; 79bc63dedbSGeorge Zhang u32 old_rid = resource_id; 80bc63dedbSGeorge Zhang u32 current_rid; 81bc63dedbSGeorge Zhang 82bc63dedbSGeorge Zhang /* 83bc63dedbSGeorge Zhang * Generate a unique resource ID. Keep on trying until we wrap around 84bc63dedbSGeorge Zhang * in the RID space. 85bc63dedbSGeorge Zhang */ 86bc63dedbSGeorge Zhang do { 87bc63dedbSGeorge Zhang struct vmci_handle handle; 88bc63dedbSGeorge Zhang 89bc63dedbSGeorge Zhang current_rid = resource_id; 90bc63dedbSGeorge Zhang resource_id++; 91bc63dedbSGeorge Zhang if (unlikely(resource_id == VMCI_INVALID_ID)) { 92bc63dedbSGeorge Zhang /* Skip the reserved rids. */ 93bc63dedbSGeorge Zhang resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; 94bc63dedbSGeorge Zhang } 95bc63dedbSGeorge Zhang 96bc63dedbSGeorge Zhang handle = vmci_make_handle(context_id, current_rid); 97bc63dedbSGeorge Zhang if (!vmci_resource_lookup(handle, resource_type)) 98bc63dedbSGeorge Zhang return current_rid; 99bc63dedbSGeorge Zhang } while (resource_id != old_rid); 100bc63dedbSGeorge Zhang 101bc63dedbSGeorge Zhang return VMCI_INVALID_ID; 102bc63dedbSGeorge Zhang } 103bc63dedbSGeorge Zhang 104bc63dedbSGeorge Zhang 105bc63dedbSGeorge Zhang int vmci_resource_add(struct vmci_resource *resource, 106bc63dedbSGeorge Zhang enum vmci_resource_type resource_type, 107bc63dedbSGeorge Zhang struct vmci_handle handle) 108bc63dedbSGeorge Zhang 109bc63dedbSGeorge Zhang { 110bc63dedbSGeorge Zhang unsigned int idx; 111bc63dedbSGeorge Zhang int result; 112bc63dedbSGeorge Zhang 113bc63dedbSGeorge Zhang spin_lock(&vmci_resource_table.lock); 114bc63dedbSGeorge Zhang 115bc63dedbSGeorge Zhang if (handle.resource == VMCI_INVALID_ID) { 116bc63dedbSGeorge Zhang handle.resource = vmci_resource_find_id(handle.context, 117bc63dedbSGeorge Zhang resource_type); 118bc63dedbSGeorge Zhang if (handle.resource == VMCI_INVALID_ID) { 119bc63dedbSGeorge Zhang result = VMCI_ERROR_NO_HANDLE; 120bc63dedbSGeorge Zhang goto out; 121bc63dedbSGeorge Zhang } 122bc63dedbSGeorge Zhang } else if (vmci_resource_lookup(handle, resource_type)) { 123bc63dedbSGeorge Zhang result = VMCI_ERROR_ALREADY_EXISTS; 124bc63dedbSGeorge Zhang goto out; 125bc63dedbSGeorge Zhang } 126bc63dedbSGeorge Zhang 127bc63dedbSGeorge Zhang resource->handle = handle; 128bc63dedbSGeorge Zhang resource->type = resource_type; 129bc63dedbSGeorge Zhang INIT_HLIST_NODE(&resource->node); 130bc63dedbSGeorge Zhang kref_init(&resource->kref); 131bc63dedbSGeorge Zhang init_completion(&resource->done); 132bc63dedbSGeorge Zhang 133bc63dedbSGeorge Zhang idx = vmci_resource_hash(resource->handle); 134bc63dedbSGeorge Zhang hlist_add_head_rcu(&resource->node, &vmci_resource_table.entries[idx]); 135bc63dedbSGeorge Zhang 136bc63dedbSGeorge Zhang result = VMCI_SUCCESS; 137bc63dedbSGeorge Zhang 138bc63dedbSGeorge Zhang out: 139bc63dedbSGeorge Zhang spin_unlock(&vmci_resource_table.lock); 140bc63dedbSGeorge Zhang return result; 141bc63dedbSGeorge Zhang } 142bc63dedbSGeorge Zhang 143bc63dedbSGeorge Zhang void vmci_resource_remove(struct vmci_resource *resource) 144bc63dedbSGeorge Zhang { 145bc63dedbSGeorge Zhang struct vmci_handle handle = resource->handle; 146bc63dedbSGeorge Zhang unsigned int idx = vmci_resource_hash(handle); 147bc63dedbSGeorge Zhang struct vmci_resource *r; 148bc63dedbSGeorge Zhang 149bc63dedbSGeorge Zhang /* Remove resource from hash table. */ 150bc63dedbSGeorge Zhang spin_lock(&vmci_resource_table.lock); 151bc63dedbSGeorge Zhang 152b67bfe0dSSasha Levin hlist_for_each_entry(r, &vmci_resource_table.entries[idx], node) { 153bc63dedbSGeorge Zhang if (vmci_handle_is_equal(r->handle, resource->handle)) { 154bc63dedbSGeorge Zhang hlist_del_init_rcu(&r->node); 155bc63dedbSGeorge Zhang break; 156bc63dedbSGeorge Zhang } 157bc63dedbSGeorge Zhang } 158bc63dedbSGeorge Zhang 159bc63dedbSGeorge Zhang spin_unlock(&vmci_resource_table.lock); 160bc63dedbSGeorge Zhang synchronize_rcu(); 161bc63dedbSGeorge Zhang 162bc63dedbSGeorge Zhang vmci_resource_put(resource); 163bc63dedbSGeorge Zhang wait_for_completion(&resource->done); 164bc63dedbSGeorge Zhang } 165bc63dedbSGeorge Zhang 166bc63dedbSGeorge Zhang struct vmci_resource * 167bc63dedbSGeorge Zhang vmci_resource_by_handle(struct vmci_handle resource_handle, 168bc63dedbSGeorge Zhang enum vmci_resource_type resource_type) 169bc63dedbSGeorge Zhang { 170bc63dedbSGeorge Zhang struct vmci_resource *r, *resource = NULL; 171bc63dedbSGeorge Zhang 172bc63dedbSGeorge Zhang rcu_read_lock(); 173bc63dedbSGeorge Zhang 174bc63dedbSGeorge Zhang r = vmci_resource_lookup(resource_handle, resource_type); 175bc63dedbSGeorge Zhang if (r && 176bc63dedbSGeorge Zhang (resource_type == r->type || 177bc63dedbSGeorge Zhang resource_type == VMCI_RESOURCE_TYPE_ANY)) { 178bc63dedbSGeorge Zhang resource = vmci_resource_get(r); 179bc63dedbSGeorge Zhang } 180bc63dedbSGeorge Zhang 181bc63dedbSGeorge Zhang rcu_read_unlock(); 182bc63dedbSGeorge Zhang 183bc63dedbSGeorge Zhang return resource; 184bc63dedbSGeorge Zhang } 185bc63dedbSGeorge Zhang 186bc63dedbSGeorge Zhang /* 187bc63dedbSGeorge Zhang * Get a reference to given resource. 188bc63dedbSGeorge Zhang */ 189bc63dedbSGeorge Zhang struct vmci_resource *vmci_resource_get(struct vmci_resource *resource) 190bc63dedbSGeorge Zhang { 191bc63dedbSGeorge Zhang kref_get(&resource->kref); 192bc63dedbSGeorge Zhang 193bc63dedbSGeorge Zhang return resource; 194bc63dedbSGeorge Zhang } 195bc63dedbSGeorge Zhang 196bc63dedbSGeorge Zhang static void vmci_release_resource(struct kref *kref) 197bc63dedbSGeorge Zhang { 198bc63dedbSGeorge Zhang struct vmci_resource *resource = 199bc63dedbSGeorge Zhang container_of(kref, struct vmci_resource, kref); 200bc63dedbSGeorge Zhang 201bc63dedbSGeorge Zhang /* Verify the resource has been unlinked from hash table */ 202bc63dedbSGeorge Zhang WARN_ON(!hlist_unhashed(&resource->node)); 203bc63dedbSGeorge Zhang 204bc63dedbSGeorge Zhang /* Signal that container of this resource can now be destroyed */ 205bc63dedbSGeorge Zhang complete(&resource->done); 206bc63dedbSGeorge Zhang } 207bc63dedbSGeorge Zhang 208bc63dedbSGeorge Zhang /* 209bc63dedbSGeorge Zhang * Resource's release function will get called if last reference. 210bc63dedbSGeorge Zhang * If it is the last reference, then we are sure that nobody else 211bc63dedbSGeorge Zhang * can increment the count again (it's gone from the resource hash 212bc63dedbSGeorge Zhang * table), so there's no need for locking here. 213bc63dedbSGeorge Zhang */ 214bc63dedbSGeorge Zhang int vmci_resource_put(struct vmci_resource *resource) 215bc63dedbSGeorge Zhang { 216bc63dedbSGeorge Zhang /* 217bc63dedbSGeorge Zhang * We propagate the information back to caller in case it wants to know 218bc63dedbSGeorge Zhang * whether entry was freed. 219bc63dedbSGeorge Zhang */ 220bc63dedbSGeorge Zhang return kref_put(&resource->kref, vmci_release_resource) ? 221bc63dedbSGeorge Zhang VMCI_SUCCESS_ENTRY_DEAD : VMCI_SUCCESS; 222bc63dedbSGeorge Zhang } 223bc63dedbSGeorge Zhang 224bc63dedbSGeorge Zhang struct vmci_handle vmci_resource_handle(struct vmci_resource *resource) 225bc63dedbSGeorge Zhang { 226bc63dedbSGeorge Zhang return resource->handle; 227bc63dedbSGeorge Zhang } 228