Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

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