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