1 /*- 2 * Copyright (c) 2018 VMware, Inc. 3 * 4 * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0) 5 */ 6 7 /* Implementation of the VMCI Resource Access Control API. */ 8 9 #include <sys/cdefs.h> 10 #include "vmci_driver.h" 11 #include "vmci_kernel_defs.h" 12 #include "vmci_resource.h" 13 14 #define LGPFX "vmci_resource: " 15 16 /* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */ 17 static uint32_t resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; 18 static vmci_lock resource_id_lock; 19 20 static void vmci_resource_do_remove(struct vmci_resource *resource); 21 22 static struct vmci_hashtable *resource_table = NULL; 23 24 /* Public Resource Access Control API. */ 25 26 /* 27 *------------------------------------------------------------------------------ 28 * 29 * vmci_resource_init -- 30 * 31 * Initializes the VMCI Resource Access Control API. Creates a hashtable to 32 * hold all resources, and registers vectors and callbacks for hypercalls. 33 * 34 * Results: 35 * None. 36 * 37 * Side effects: 38 * None. 39 * 40 *------------------------------------------------------------------------------ 41 */ 42 43 int 44 vmci_resource_init(void) 45 { 46 int err; 47 48 err = vmci_init_lock(&resource_id_lock, "VMCI RID lock"); 49 if (err < VMCI_SUCCESS) 50 return (err); 51 52 resource_table = vmci_hashtable_create(128); 53 if (resource_table == NULL) { 54 VMCI_LOG_WARNING((LGPFX"Failed creating a resource hash table " 55 "for VMCI.\n")); 56 vmci_cleanup_lock(&resource_id_lock); 57 return (VMCI_ERROR_NO_MEM); 58 } 59 60 return (VMCI_SUCCESS); 61 } 62 63 /* 64 *------------------------------------------------------------------------------ 65 * 66 * vmci_resource_exit -- 67 * 68 * Cleans up resources. 69 * 70 * Results: 71 * None. 72 * 73 * Side effects: 74 * None. 75 * 76 *------------------------------------------------------------------------------ 77 */ 78 79 void 80 vmci_resource_exit(void) 81 { 82 83 /* Cleanup resources.*/ 84 vmci_cleanup_lock(&resource_id_lock); 85 86 if (resource_table) 87 vmci_hashtable_destroy(resource_table); 88 } 89 90 /* 91 *------------------------------------------------------------------------------ 92 * 93 * vmci_resource_get_id -- 94 * 95 * Return resource ID. The first VMCI_RESERVED_RESOURCE_ID_MAX are reserved 96 * so we start from its value + 1. 97 * 98 * Result: 99 * VMCI resource id on success, VMCI_INVALID_ID on failure. 100 * 101 * Side effects: 102 * None. 103 * 104 * 105 *------------------------------------------------------------------------------ 106 */ 107 108 vmci_id 109 vmci_resource_get_id(vmci_id context_id) 110 { 111 vmci_id current_rid; 112 vmci_id old_rid; 113 bool found_rid; 114 115 old_rid = resource_id; 116 found_rid = false; 117 118 /* 119 * Generate a unique resource ID. Keep on trying until we wrap around 120 * in the RID space. 121 */ 122 ASSERT(old_rid > VMCI_RESERVED_RESOURCE_ID_MAX); 123 124 do { 125 struct vmci_handle handle; 126 127 vmci_grab_lock(&resource_id_lock); 128 current_rid = resource_id; 129 handle = VMCI_MAKE_HANDLE(context_id, current_rid); 130 resource_id++; 131 if (UNLIKELY(resource_id == VMCI_INVALID_ID)) { 132 /* Skip the reserved rids. */ 133 resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; 134 } 135 vmci_release_lock(&resource_id_lock); 136 found_rid = !vmci_hashtable_entry_exists(resource_table, 137 handle); 138 } while (!found_rid && resource_id != old_rid); 139 140 if (UNLIKELY(!found_rid)) 141 return (VMCI_INVALID_ID); 142 else 143 return (current_rid); 144 } 145 146 /* 147 *------------------------------------------------------------------------------ 148 * 149 * vmci_resource_add -- 150 * 151 * Add resource to hashtable. 152 * 153 * Results: 154 * VMCI_SUCCESS if successful, error code if not. 155 * 156 * Side effects: 157 * None. 158 * 159 *------------------------------------------------------------------------------ 160 */ 161 162 int 163 vmci_resource_add(struct vmci_resource *resource, 164 vmci_resource_type resource_type, struct vmci_handle resource_handle, 165 vmci_resource_free_cb container_free_cb, void *container_object) 166 { 167 int result; 168 169 ASSERT(resource); 170 171 if (VMCI_HANDLE_EQUAL(resource_handle, VMCI_INVALID_HANDLE)) { 172 VMCI_LOG_DEBUG(LGPFX"Invalid argument resource " 173 "(handle=0x%x:0x%x).\n", resource_handle.context, 174 resource_handle.resource); 175 return (VMCI_ERROR_INVALID_ARGS); 176 } 177 178 vmci_hashtable_init_entry(&resource->hash_entry, resource_handle); 179 resource->type = resource_type; 180 resource->container_free_cb = container_free_cb; 181 resource->container_object = container_object; 182 183 /* Add resource to hashtable. */ 184 result = vmci_hashtable_add_entry(resource_table, 185 &resource->hash_entry); 186 if (result != VMCI_SUCCESS) { 187 VMCI_LOG_DEBUG(LGPFX"Failed to add entry to hash table " 188 "(result=%d).\n", result); 189 return (result); 190 } 191 192 return (result); 193 } 194 195 /* 196 *------------------------------------------------------------------------------ 197 * 198 * vmci_resource_remove -- 199 * 200 * Remove resource from hashtable. 201 * 202 * Results: 203 * None. 204 * 205 * Side effects: 206 * None. 207 * 208 *------------------------------------------------------------------------------ 209 */ 210 211 void 212 vmci_resource_remove(struct vmci_handle resource_handle, 213 vmci_resource_type resource_type) 214 { 215 struct vmci_resource *resource; 216 217 resource = vmci_resource_get(resource_handle, resource_type); 218 if (resource == NULL) 219 return; 220 221 /* Remove resource from hashtable. */ 222 vmci_hashtable_remove_entry(resource_table, &resource->hash_entry); 223 224 vmci_resource_release(resource); 225 /* resource could be freed by now. */ 226 } 227 228 /* 229 *------------------------------------------------------------------------------ 230 * 231 * vmci_resource_get -- 232 * 233 * Get resource from hashtable. 234 * 235 * Results: 236 * Resource if successful. Otherwise NULL. 237 * 238 * Side effects: 239 * None. 240 * 241 *------------------------------------------------------------------------------ 242 */ 243 244 struct vmci_resource * 245 vmci_resource_get(struct vmci_handle resource_handle, 246 vmci_resource_type resource_type) 247 { 248 struct vmci_hash_entry *entry; 249 struct vmci_resource *resource; 250 251 entry = vmci_hashtable_get_entry(resource_table, resource_handle); 252 if (entry == NULL) 253 return (NULL); 254 resource = RESOURCE_CONTAINER(entry, struct vmci_resource, hash_entry); 255 if (resource_type == VMCI_RESOURCE_TYPE_ANY || 256 resource->type == resource_type) { 257 return (resource); 258 } 259 vmci_hashtable_release_entry(resource_table, entry); 260 return (NULL); 261 } 262 263 /* 264 *------------------------------------------------------------------------------ 265 * 266 * vmci_resource_hold -- 267 * 268 * Hold the given resource. This will hold the hashtable entry. This is like 269 * doing a Get() but without having to lookup the resource by handle. 270 * 271 * Results: 272 * None. 273 * 274 * Side effects: 275 * None. 276 * 277 *------------------------------------------------------------------------------ 278 */ 279 280 void 281 vmci_resource_hold(struct vmci_resource *resource) 282 { 283 284 ASSERT(resource); 285 vmci_hashtable_hold_entry(resource_table, &resource->hash_entry); 286 } 287 288 /* 289 *------------------------------------------------------------------------------ 290 * 291 * vmci_resource_do_remove -- 292 * 293 * Deallocates data structures associated with the given resource and 294 * invoke any call back registered for the resource. 295 * 296 * Results: 297 * None. 298 * 299 * Side effects: 300 * May deallocate memory and invoke a callback for the removed resource. 301 * 302 *------------------------------------------------------------------------------ 303 */ 304 305 static void inline 306 vmci_resource_do_remove(struct vmci_resource *resource) 307 { 308 309 ASSERT(resource); 310 311 if (resource->container_free_cb) { 312 resource->container_free_cb(resource->container_object); 313 /* Resource has been freed don't dereference it. */ 314 } 315 } 316 317 /* 318 *------------------------------------------------------------------------------ 319 * 320 * vmci_resource_release -- 321 * 322 * Results: 323 * None. 324 * 325 * Side effects: 326 * Resource's containerFreeCB will get called if last reference. 327 * 328 *------------------------------------------------------------------------------ 329 */ 330 331 int 332 vmci_resource_release(struct vmci_resource *resource) 333 { 334 int result; 335 336 ASSERT(resource); 337 338 result = vmci_hashtable_release_entry(resource_table, 339 &resource->hash_entry); 340 if (result == VMCI_SUCCESS_ENTRY_DEAD) 341 vmci_resource_do_remove(resource); 342 343 /* 344 * We propagate the information back to caller in case it wants to know 345 * whether entry was freed. 346 */ 347 return (result); 348 } 349 350 /* 351 *------------------------------------------------------------------------------ 352 * 353 * vmci_resource_handle -- 354 * 355 * Get the handle for the given resource. 356 * 357 * Results: 358 * The resource's associated handle. 359 * 360 * Side effects: 361 * None. 362 * 363 *------------------------------------------------------------------------------ 364 */ 365 366 struct vmci_handle 367 vmci_resource_handle(struct vmci_resource *resource) 368 { 369 370 ASSERT(resource); 371 return (resource->hash_entry.handle); 372 } 373 374 /* 375 *------------------------------------------------------------------------------ 376 * 377 * vmci_resource_sync -- 378 * 379 * Use this as a synchronization point when setting globals, for example, 380 * during device shutdown. 381 * 382 * Results: 383 * None. 384 * 385 * Side effects: 386 * None. 387 * 388 *------------------------------------------------------------------------------ 389 */ 390 391 void 392 vmci_resource_sync(void) 393 { 394 395 vmci_hashtable_sync(resource_table); 396 } 397