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