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