Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/vmware/vmci/vmci_resource.c
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:executable | null | * \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* Copyright (c) 2018 VMware, Inc. All Rights Reserved. | |||||
* | |||||
* SPDX-License-Identifier: (BSD-2-Clause AND GPL-2.0) | |||||
*/ | |||||
/* Implementation of the VMCI Resource Access Control API. */ | |||||
#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); | |||||
} |