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