freebsd-skq/sys/dev/vmware/vmci/vmci_resource.c
mp 2b8899b47a Update VMCI license based on comments from core, the FreeBSD Foundation,
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
2018-04-08 01:32:56 +00:00

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);
}