/*- * Copyright (c) 2018 VMware, Inc. * * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0) */ /* Implementation of the VMCI Resource Access Control API. */ #include __FBSDID("$FreeBSD$"); #include "vmci_driver.h" #include "vmci_kernel_defs.h" #include "vmci_resource.h" #define LGPFX "vmci_resource: " /* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */ static uint32_t resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; static vmci_lock resource_id_lock; static void vmci_resource_do_remove(struct vmci_resource *resource); static struct vmci_hashtable *resource_table = NULL; /* Public Resource Access Control API. */ /* *------------------------------------------------------------------------------ * * vmci_resource_init -- * * Initializes the VMCI Resource Access Control API. Creates a hashtable to * hold all resources, and registers vectors and callbacks for hypercalls. * * Results: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ int vmci_resource_init(void) { int err; err = vmci_init_lock(&resource_id_lock, "VMCI RID lock"); if (err < VMCI_SUCCESS) return (err); resource_table = vmci_hashtable_create(128); if (resource_table == NULL) { VMCI_LOG_WARNING((LGPFX"Failed creating a resource hash table " "for VMCI.\n")); vmci_cleanup_lock(&resource_id_lock); return (VMCI_ERROR_NO_MEM); } return (VMCI_SUCCESS); } /* *------------------------------------------------------------------------------ * * vmci_resource_exit -- * * Cleans up resources. * * Results: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ void vmci_resource_exit(void) { /* Cleanup resources.*/ vmci_cleanup_lock(&resource_id_lock); if (resource_table) vmci_hashtable_destroy(resource_table); } /* *------------------------------------------------------------------------------ * * vmci_resource_get_id -- * * Return resource ID. The first VMCI_RESERVED_RESOURCE_ID_MAX are reserved * so we start from its value + 1. * * Result: * VMCI resource id on success, VMCI_INVALID_ID on failure. * * Side effects: * None. * * *------------------------------------------------------------------------------ */ vmci_id vmci_resource_get_id(vmci_id context_id) { vmci_id current_rid; vmci_id old_rid; bool found_rid; old_rid = resource_id; found_rid = false; /* * Generate a unique resource ID. Keep on trying until we wrap around * in the RID space. */ ASSERT(old_rid > VMCI_RESERVED_RESOURCE_ID_MAX); do { struct vmci_handle handle; vmci_grab_lock(&resource_id_lock); current_rid = resource_id; handle = VMCI_MAKE_HANDLE(context_id, current_rid); resource_id++; if (UNLIKELY(resource_id == VMCI_INVALID_ID)) { /* Skip the reserved rids. */ resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; } vmci_release_lock(&resource_id_lock); found_rid = !vmci_hashtable_entry_exists(resource_table, handle); } while (!found_rid && resource_id != old_rid); if (UNLIKELY(!found_rid)) return (VMCI_INVALID_ID); else return (current_rid); } /* *------------------------------------------------------------------------------ * * vmci_resource_add -- * * Add resource to hashtable. * * Results: * VMCI_SUCCESS if successful, error code if not. * * Side effects: * None. * *------------------------------------------------------------------------------ */ int vmci_resource_add(struct vmci_resource *resource, vmci_resource_type resource_type, struct vmci_handle resource_handle, vmci_resource_free_cb container_free_cb, void *container_object) { int result; ASSERT(resource); if (VMCI_HANDLE_EQUAL(resource_handle, VMCI_INVALID_HANDLE)) { VMCI_LOG_DEBUG(LGPFX"Invalid argument resource " "(handle=0x%x:0x%x).\n", resource_handle.context, resource_handle.resource); return (VMCI_ERROR_INVALID_ARGS); } vmci_hashtable_init_entry(&resource->hash_entry, resource_handle); resource->type = resource_type; resource->container_free_cb = container_free_cb; resource->container_object = container_object; /* Add resource to hashtable. */ result = vmci_hashtable_add_entry(resource_table, &resource->hash_entry); if (result != VMCI_SUCCESS) { VMCI_LOG_DEBUG(LGPFX"Failed to add entry to hash table " "(result=%d).\n", result); return (result); } return (result); } /* *------------------------------------------------------------------------------ * * vmci_resource_remove -- * * Remove resource from hashtable. * * Results: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ void vmci_resource_remove(struct vmci_handle resource_handle, vmci_resource_type resource_type) { struct vmci_resource *resource; resource = vmci_resource_get(resource_handle, resource_type); if (resource == NULL) return; /* Remove resource from hashtable. */ vmci_hashtable_remove_entry(resource_table, &resource->hash_entry); vmci_resource_release(resource); /* resource could be freed by now. */ } /* *------------------------------------------------------------------------------ * * vmci_resource_get -- * * Get resource from hashtable. * * Results: * Resource if successful. Otherwise NULL. * * Side effects: * None. * *------------------------------------------------------------------------------ */ struct vmci_resource * vmci_resource_get(struct vmci_handle resource_handle, vmci_resource_type resource_type) { struct vmci_hash_entry *entry; struct vmci_resource *resource; entry = vmci_hashtable_get_entry(resource_table, resource_handle); if (entry == NULL) return (NULL); resource = RESOURCE_CONTAINER(entry, struct vmci_resource, hash_entry); if (resource_type == VMCI_RESOURCE_TYPE_ANY || resource->type == resource_type) { return (resource); } vmci_hashtable_release_entry(resource_table, entry); return (NULL); } /* *------------------------------------------------------------------------------ * * vmci_resource_hold -- * * Hold the given resource. This will hold the hashtable entry. This is like * doing a Get() but without having to lookup the resource by handle. * * Results: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ void vmci_resource_hold(struct vmci_resource *resource) { ASSERT(resource); vmci_hashtable_hold_entry(resource_table, &resource->hash_entry); } /* *------------------------------------------------------------------------------ * * vmci_resource_do_remove -- * * Deallocates data structures associated with the given resource and * invoke any call back registered for the resource. * * Results: * None. * * Side effects: * May deallocate memory and invoke a callback for the removed resource. * *------------------------------------------------------------------------------ */ static void inline vmci_resource_do_remove(struct vmci_resource *resource) { ASSERT(resource); if (resource->container_free_cb) { resource->container_free_cb(resource->container_object); /* Resource has been freed don't dereference it. */ } } /* *------------------------------------------------------------------------------ * * vmci_resource_release -- * * Results: * None. * * Side effects: * Resource's containerFreeCB will get called if last reference. * *------------------------------------------------------------------------------ */ int vmci_resource_release(struct vmci_resource *resource) { int result; ASSERT(resource); result = vmci_hashtable_release_entry(resource_table, &resource->hash_entry); if (result == VMCI_SUCCESS_ENTRY_DEAD) vmci_resource_do_remove(resource); /* * We propagate the information back to caller in case it wants to know * whether entry was freed. */ return (result); } /* *------------------------------------------------------------------------------ * * vmci_resource_handle -- * * Get the handle for the given resource. * * Results: * The resource's associated handle. * * Side effects: * None. * *------------------------------------------------------------------------------ */ struct vmci_handle vmci_resource_handle(struct vmci_resource *resource) { ASSERT(resource); return (resource->hash_entry.handle); } /* *------------------------------------------------------------------------------ * * vmci_resource_sync -- * * Use this as a synchronization point when setting globals, for example, * during device shutdown. * * Results: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ void vmci_resource_sync(void) { vmci_hashtable_sync(resource_table); }