163a93856SMark Peek /*-
2*3eeb7511SMark Peek * Copyright (c) 2018 VMware, Inc.
363a93856SMark Peek *
48c302b2eSMark Peek * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
563a93856SMark Peek */
663a93856SMark Peek
763a93856SMark Peek /* Implementation of the VMCI Resource Access Control API. */
863a93856SMark Peek
963a93856SMark Peek #include <sys/cdefs.h>
1063a93856SMark Peek #include "vmci_driver.h"
1163a93856SMark Peek #include "vmci_kernel_defs.h"
1263a93856SMark Peek #include "vmci_resource.h"
1363a93856SMark Peek
1463a93856SMark Peek #define LGPFX "vmci_resource: "
1563a93856SMark Peek
1663a93856SMark Peek /* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */
1763a93856SMark Peek static uint32_t resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
1863a93856SMark Peek static vmci_lock resource_id_lock;
1963a93856SMark Peek
2063a93856SMark Peek static void vmci_resource_do_remove(struct vmci_resource *resource);
2163a93856SMark Peek
2263a93856SMark Peek static struct vmci_hashtable *resource_table = NULL;
2363a93856SMark Peek
2463a93856SMark Peek /* Public Resource Access Control API. */
2563a93856SMark Peek
2663a93856SMark Peek /*
2763a93856SMark Peek *------------------------------------------------------------------------------
2863a93856SMark Peek *
2963a93856SMark Peek * vmci_resource_init --
3063a93856SMark Peek *
3163a93856SMark Peek * Initializes the VMCI Resource Access Control API. Creates a hashtable to
3263a93856SMark Peek * hold all resources, and registers vectors and callbacks for hypercalls.
3363a93856SMark Peek *
3463a93856SMark Peek * Results:
3563a93856SMark Peek * None.
3663a93856SMark Peek *
3763a93856SMark Peek * Side effects:
3863a93856SMark Peek * None.
3963a93856SMark Peek *
4063a93856SMark Peek *------------------------------------------------------------------------------
4163a93856SMark Peek */
4263a93856SMark Peek
4363a93856SMark Peek int
vmci_resource_init(void)4463a93856SMark Peek vmci_resource_init(void)
4563a93856SMark Peek {
4663a93856SMark Peek int err;
4763a93856SMark Peek
4863a93856SMark Peek err = vmci_init_lock(&resource_id_lock, "VMCI RID lock");
4963a93856SMark Peek if (err < VMCI_SUCCESS)
5063a93856SMark Peek return (err);
5163a93856SMark Peek
5263a93856SMark Peek resource_table = vmci_hashtable_create(128);
5363a93856SMark Peek if (resource_table == NULL) {
5463a93856SMark Peek VMCI_LOG_WARNING((LGPFX"Failed creating a resource hash table "
5563a93856SMark Peek "for VMCI.\n"));
5663a93856SMark Peek vmci_cleanup_lock(&resource_id_lock);
5763a93856SMark Peek return (VMCI_ERROR_NO_MEM);
5863a93856SMark Peek }
5963a93856SMark Peek
6063a93856SMark Peek return (VMCI_SUCCESS);
6163a93856SMark Peek }
6263a93856SMark Peek
6363a93856SMark Peek /*
6463a93856SMark Peek *------------------------------------------------------------------------------
6563a93856SMark Peek *
6663a93856SMark Peek * vmci_resource_exit --
6763a93856SMark Peek *
6863a93856SMark Peek * Cleans up resources.
6963a93856SMark Peek *
7063a93856SMark Peek * Results:
7163a93856SMark Peek * None.
7263a93856SMark Peek *
7363a93856SMark Peek * Side effects:
7463a93856SMark Peek * None.
7563a93856SMark Peek *
7663a93856SMark Peek *------------------------------------------------------------------------------
7763a93856SMark Peek */
7863a93856SMark Peek
7963a93856SMark Peek void
vmci_resource_exit(void)8063a93856SMark Peek vmci_resource_exit(void)
8163a93856SMark Peek {
8263a93856SMark Peek
8363a93856SMark Peek /* Cleanup resources.*/
8463a93856SMark Peek vmci_cleanup_lock(&resource_id_lock);
8563a93856SMark Peek
8663a93856SMark Peek if (resource_table)
8763a93856SMark Peek vmci_hashtable_destroy(resource_table);
8863a93856SMark Peek }
8963a93856SMark Peek
9063a93856SMark Peek /*
9163a93856SMark Peek *------------------------------------------------------------------------------
9263a93856SMark Peek *
9363a93856SMark Peek * vmci_resource_get_id --
9463a93856SMark Peek *
9563a93856SMark Peek * Return resource ID. The first VMCI_RESERVED_RESOURCE_ID_MAX are reserved
9663a93856SMark Peek * so we start from its value + 1.
9763a93856SMark Peek *
9863a93856SMark Peek * Result:
9963a93856SMark Peek * VMCI resource id on success, VMCI_INVALID_ID on failure.
10063a93856SMark Peek *
10163a93856SMark Peek * Side effects:
10263a93856SMark Peek * None.
10363a93856SMark Peek *
10463a93856SMark Peek *
10563a93856SMark Peek *------------------------------------------------------------------------------
10663a93856SMark Peek */
10763a93856SMark Peek
10863a93856SMark Peek vmci_id
vmci_resource_get_id(vmci_id context_id)10963a93856SMark Peek vmci_resource_get_id(vmci_id context_id)
11063a93856SMark Peek {
11163a93856SMark Peek vmci_id current_rid;
11263a93856SMark Peek vmci_id old_rid;
11363a93856SMark Peek bool found_rid;
11463a93856SMark Peek
11563a93856SMark Peek old_rid = resource_id;
11663a93856SMark Peek found_rid = false;
11763a93856SMark Peek
11863a93856SMark Peek /*
11963a93856SMark Peek * Generate a unique resource ID. Keep on trying until we wrap around
12063a93856SMark Peek * in the RID space.
12163a93856SMark Peek */
12263a93856SMark Peek ASSERT(old_rid > VMCI_RESERVED_RESOURCE_ID_MAX);
12363a93856SMark Peek
12463a93856SMark Peek do {
12563a93856SMark Peek struct vmci_handle handle;
12663a93856SMark Peek
12763a93856SMark Peek vmci_grab_lock(&resource_id_lock);
12863a93856SMark Peek current_rid = resource_id;
12963a93856SMark Peek handle = VMCI_MAKE_HANDLE(context_id, current_rid);
13063a93856SMark Peek resource_id++;
13163a93856SMark Peek if (UNLIKELY(resource_id == VMCI_INVALID_ID)) {
13263a93856SMark Peek /* Skip the reserved rids. */
13363a93856SMark Peek resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
13463a93856SMark Peek }
13563a93856SMark Peek vmci_release_lock(&resource_id_lock);
13663a93856SMark Peek found_rid = !vmci_hashtable_entry_exists(resource_table,
13763a93856SMark Peek handle);
13863a93856SMark Peek } while (!found_rid && resource_id != old_rid);
13963a93856SMark Peek
14063a93856SMark Peek if (UNLIKELY(!found_rid))
14163a93856SMark Peek return (VMCI_INVALID_ID);
14263a93856SMark Peek else
14363a93856SMark Peek return (current_rid);
14463a93856SMark Peek }
14563a93856SMark Peek
14663a93856SMark Peek /*
14763a93856SMark Peek *------------------------------------------------------------------------------
14863a93856SMark Peek *
14963a93856SMark Peek * vmci_resource_add --
15063a93856SMark Peek *
15163a93856SMark Peek * Add resource to hashtable.
15263a93856SMark Peek *
15363a93856SMark Peek * Results:
15463a93856SMark Peek * VMCI_SUCCESS if successful, error code if not.
15563a93856SMark Peek *
15663a93856SMark Peek * Side effects:
15763a93856SMark Peek * None.
15863a93856SMark Peek *
15963a93856SMark Peek *------------------------------------------------------------------------------
16063a93856SMark Peek */
16163a93856SMark Peek
16263a93856SMark Peek 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)16363a93856SMark Peek vmci_resource_add(struct vmci_resource *resource,
16463a93856SMark Peek vmci_resource_type resource_type, struct vmci_handle resource_handle,
16563a93856SMark Peek vmci_resource_free_cb container_free_cb, void *container_object)
16663a93856SMark Peek {
16763a93856SMark Peek int result;
16863a93856SMark Peek
16963a93856SMark Peek ASSERT(resource);
17063a93856SMark Peek
17163a93856SMark Peek if (VMCI_HANDLE_EQUAL(resource_handle, VMCI_INVALID_HANDLE)) {
17263a93856SMark Peek VMCI_LOG_DEBUG(LGPFX"Invalid argument resource "
17363a93856SMark Peek "(handle=0x%x:0x%x).\n", resource_handle.context,
17463a93856SMark Peek resource_handle.resource);
17563a93856SMark Peek return (VMCI_ERROR_INVALID_ARGS);
17663a93856SMark Peek }
17763a93856SMark Peek
17863a93856SMark Peek vmci_hashtable_init_entry(&resource->hash_entry, resource_handle);
17963a93856SMark Peek resource->type = resource_type;
18063a93856SMark Peek resource->container_free_cb = container_free_cb;
18163a93856SMark Peek resource->container_object = container_object;
18263a93856SMark Peek
18363a93856SMark Peek /* Add resource to hashtable. */
18463a93856SMark Peek result = vmci_hashtable_add_entry(resource_table,
18563a93856SMark Peek &resource->hash_entry);
18663a93856SMark Peek if (result != VMCI_SUCCESS) {
18763a93856SMark Peek VMCI_LOG_DEBUG(LGPFX"Failed to add entry to hash table "
18863a93856SMark Peek "(result=%d).\n", result);
18963a93856SMark Peek return (result);
19063a93856SMark Peek }
19163a93856SMark Peek
19263a93856SMark Peek return (result);
19363a93856SMark Peek }
19463a93856SMark Peek
19563a93856SMark Peek /*
19663a93856SMark Peek *------------------------------------------------------------------------------
19763a93856SMark Peek *
19863a93856SMark Peek * vmci_resource_remove --
19963a93856SMark Peek *
20063a93856SMark Peek * Remove resource from hashtable.
20163a93856SMark Peek *
20263a93856SMark Peek * Results:
20363a93856SMark Peek * None.
20463a93856SMark Peek *
20563a93856SMark Peek * Side effects:
20663a93856SMark Peek * None.
20763a93856SMark Peek *
20863a93856SMark Peek *------------------------------------------------------------------------------
20963a93856SMark Peek */
21063a93856SMark Peek
21163a93856SMark Peek void
vmci_resource_remove(struct vmci_handle resource_handle,vmci_resource_type resource_type)21263a93856SMark Peek vmci_resource_remove(struct vmci_handle resource_handle,
21363a93856SMark Peek vmci_resource_type resource_type)
21463a93856SMark Peek {
21563a93856SMark Peek struct vmci_resource *resource;
21663a93856SMark Peek
21763a93856SMark Peek resource = vmci_resource_get(resource_handle, resource_type);
21863a93856SMark Peek if (resource == NULL)
21963a93856SMark Peek return;
22063a93856SMark Peek
22163a93856SMark Peek /* Remove resource from hashtable. */
22263a93856SMark Peek vmci_hashtable_remove_entry(resource_table, &resource->hash_entry);
22363a93856SMark Peek
22463a93856SMark Peek vmci_resource_release(resource);
22563a93856SMark Peek /* resource could be freed by now. */
22663a93856SMark Peek }
22763a93856SMark Peek
22863a93856SMark Peek /*
22963a93856SMark Peek *------------------------------------------------------------------------------
23063a93856SMark Peek *
23163a93856SMark Peek * vmci_resource_get --
23263a93856SMark Peek *
23363a93856SMark Peek * Get resource from hashtable.
23463a93856SMark Peek *
23563a93856SMark Peek * Results:
23663a93856SMark Peek * Resource if successful. Otherwise NULL.
23763a93856SMark Peek *
23863a93856SMark Peek * Side effects:
23963a93856SMark Peek * None.
24063a93856SMark Peek *
24163a93856SMark Peek *------------------------------------------------------------------------------
24263a93856SMark Peek */
24363a93856SMark Peek
24463a93856SMark Peek struct vmci_resource *
vmci_resource_get(struct vmci_handle resource_handle,vmci_resource_type resource_type)24563a93856SMark Peek vmci_resource_get(struct vmci_handle resource_handle,
24663a93856SMark Peek vmci_resource_type resource_type)
24763a93856SMark Peek {
24863a93856SMark Peek struct vmci_hash_entry *entry;
24963a93856SMark Peek struct vmci_resource *resource;
25063a93856SMark Peek
25163a93856SMark Peek entry = vmci_hashtable_get_entry(resource_table, resource_handle);
25263a93856SMark Peek if (entry == NULL)
25363a93856SMark Peek return (NULL);
25463a93856SMark Peek resource = RESOURCE_CONTAINER(entry, struct vmci_resource, hash_entry);
25563a93856SMark Peek if (resource_type == VMCI_RESOURCE_TYPE_ANY ||
25663a93856SMark Peek resource->type == resource_type) {
25763a93856SMark Peek return (resource);
25863a93856SMark Peek }
25963a93856SMark Peek vmci_hashtable_release_entry(resource_table, entry);
26063a93856SMark Peek return (NULL);
26163a93856SMark Peek }
26263a93856SMark Peek
26363a93856SMark Peek /*
26463a93856SMark Peek *------------------------------------------------------------------------------
26563a93856SMark Peek *
26663a93856SMark Peek * vmci_resource_hold --
26763a93856SMark Peek *
26863a93856SMark Peek * Hold the given resource. This will hold the hashtable entry. This is like
26963a93856SMark Peek * doing a Get() but without having to lookup the resource by handle.
27063a93856SMark Peek *
27163a93856SMark Peek * Results:
27263a93856SMark Peek * None.
27363a93856SMark Peek *
27463a93856SMark Peek * Side effects:
27563a93856SMark Peek * None.
27663a93856SMark Peek *
27763a93856SMark Peek *------------------------------------------------------------------------------
27863a93856SMark Peek */
27963a93856SMark Peek
28063a93856SMark Peek void
vmci_resource_hold(struct vmci_resource * resource)28163a93856SMark Peek vmci_resource_hold(struct vmci_resource *resource)
28263a93856SMark Peek {
28363a93856SMark Peek
28463a93856SMark Peek ASSERT(resource);
28563a93856SMark Peek vmci_hashtable_hold_entry(resource_table, &resource->hash_entry);
28663a93856SMark Peek }
28763a93856SMark Peek
28863a93856SMark Peek /*
28963a93856SMark Peek *------------------------------------------------------------------------------
29063a93856SMark Peek *
29163a93856SMark Peek * vmci_resource_do_remove --
29263a93856SMark Peek *
29363a93856SMark Peek * Deallocates data structures associated with the given resource and
29463a93856SMark Peek * invoke any call back registered for the resource.
29563a93856SMark Peek *
29663a93856SMark Peek * Results:
29763a93856SMark Peek * None.
29863a93856SMark Peek *
29963a93856SMark Peek * Side effects:
30063a93856SMark Peek * May deallocate memory and invoke a callback for the removed resource.
30163a93856SMark Peek *
30263a93856SMark Peek *------------------------------------------------------------------------------
30363a93856SMark Peek */
30463a93856SMark Peek
30563a93856SMark Peek static void inline
vmci_resource_do_remove(struct vmci_resource * resource)30663a93856SMark Peek vmci_resource_do_remove(struct vmci_resource *resource)
30763a93856SMark Peek {
30863a93856SMark Peek
30963a93856SMark Peek ASSERT(resource);
31063a93856SMark Peek
31163a93856SMark Peek if (resource->container_free_cb) {
31263a93856SMark Peek resource->container_free_cb(resource->container_object);
31363a93856SMark Peek /* Resource has been freed don't dereference it. */
31463a93856SMark Peek }
31563a93856SMark Peek }
31663a93856SMark Peek
31763a93856SMark Peek /*
31863a93856SMark Peek *------------------------------------------------------------------------------
31963a93856SMark Peek *
32063a93856SMark Peek * vmci_resource_release --
32163a93856SMark Peek *
32263a93856SMark Peek * Results:
32363a93856SMark Peek * None.
32463a93856SMark Peek *
32563a93856SMark Peek * Side effects:
32663a93856SMark Peek * Resource's containerFreeCB will get called if last reference.
32763a93856SMark Peek *
32863a93856SMark Peek *------------------------------------------------------------------------------
32963a93856SMark Peek */
33063a93856SMark Peek
33163a93856SMark Peek int
vmci_resource_release(struct vmci_resource * resource)33263a93856SMark Peek vmci_resource_release(struct vmci_resource *resource)
33363a93856SMark Peek {
33463a93856SMark Peek int result;
33563a93856SMark Peek
33663a93856SMark Peek ASSERT(resource);
33763a93856SMark Peek
33863a93856SMark Peek result = vmci_hashtable_release_entry(resource_table,
33963a93856SMark Peek &resource->hash_entry);
34063a93856SMark Peek if (result == VMCI_SUCCESS_ENTRY_DEAD)
34163a93856SMark Peek vmci_resource_do_remove(resource);
34263a93856SMark Peek
34363a93856SMark Peek /*
34463a93856SMark Peek * We propagate the information back to caller in case it wants to know
34563a93856SMark Peek * whether entry was freed.
34663a93856SMark Peek */
34763a93856SMark Peek return (result);
34863a93856SMark Peek }
34963a93856SMark Peek
35063a93856SMark Peek /*
35163a93856SMark Peek *------------------------------------------------------------------------------
35263a93856SMark Peek *
35363a93856SMark Peek * vmci_resource_handle --
35463a93856SMark Peek *
35563a93856SMark Peek * Get the handle for the given resource.
35663a93856SMark Peek *
35763a93856SMark Peek * Results:
35863a93856SMark Peek * The resource's associated handle.
35963a93856SMark Peek *
36063a93856SMark Peek * Side effects:
36163a93856SMark Peek * None.
36263a93856SMark Peek *
36363a93856SMark Peek *------------------------------------------------------------------------------
36463a93856SMark Peek */
36563a93856SMark Peek
36663a93856SMark Peek struct vmci_handle
vmci_resource_handle(struct vmci_resource * resource)36763a93856SMark Peek vmci_resource_handle(struct vmci_resource *resource)
36863a93856SMark Peek {
36963a93856SMark Peek
37063a93856SMark Peek ASSERT(resource);
37163a93856SMark Peek return (resource->hash_entry.handle);
37263a93856SMark Peek }
37363a93856SMark Peek
37463a93856SMark Peek /*
37563a93856SMark Peek *------------------------------------------------------------------------------
37663a93856SMark Peek *
37763a93856SMark Peek * vmci_resource_sync --
37863a93856SMark Peek *
37963a93856SMark Peek * Use this as a synchronization point when setting globals, for example,
38063a93856SMark Peek * during device shutdown.
38163a93856SMark Peek *
38263a93856SMark Peek * Results:
38363a93856SMark Peek * None.
38463a93856SMark Peek *
38563a93856SMark Peek * Side effects:
38663a93856SMark Peek * None.
38763a93856SMark Peek *
38863a93856SMark Peek *------------------------------------------------------------------------------
38963a93856SMark Peek */
39063a93856SMark Peek
39163a93856SMark Peek void
vmci_resource_sync(void)39263a93856SMark Peek vmci_resource_sync(void)
39363a93856SMark Peek {
39463a93856SMark Peek
39563a93856SMark Peek vmci_hashtable_sync(resource_table);
39663a93856SMark Peek }
397