2b8899b47a
and VMware legal: - Add a dual BSD-2 Clause/GPLv2 LICENSE file in the VMCI directory - Remove the use of "All Rights Reserved" - Per best practice, remove copyright/license info from Makefile Reviewed by: imp, emaste, jhb, Vishnu Dasa <vdasa@vmware.com> Approved by: VMware legal via Mark Peek <markpeek@vmware.com> Differential Revision: https://reviews.freebsd.org/D14979
399 lines
8.9 KiB
C
399 lines
8.9 KiB
C
/*-
|
|
* Copyright (c) 2018 VMware, Inc.
|
|
*
|
|
* SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
|
|
*/
|
|
|
|
/* Implementation of the VMCI Resource Access Control API. */
|
|
|
|
#include <sys/cdefs.h>
|
|
__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);
|
|
}
|