2 * Copyright (c) 2018 VMware, Inc.
4 * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
7 /* Implementation of the VMCI Resource Access Control API. */
10 __FBSDID("$FreeBSD$");
12 #include "vmci_driver.h"
13 #include "vmci_kernel_defs.h"
14 #include "vmci_resource.h"
16 #define LGPFX "vmci_resource: "
18 /* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */
19 static uint32_t resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
20 static vmci_lock resource_id_lock;
22 static void vmci_resource_do_remove(struct vmci_resource *resource);
24 static struct vmci_hashtable *resource_table = NULL;
26 /* Public Resource Access Control API. */
29 *------------------------------------------------------------------------------
31 * vmci_resource_init --
33 * Initializes the VMCI Resource Access Control API. Creates a hashtable to
34 * hold all resources, and registers vectors and callbacks for hypercalls.
42 *------------------------------------------------------------------------------
46 vmci_resource_init(void)
50 err = vmci_init_lock(&resource_id_lock, "VMCI RID lock");
51 if (err < VMCI_SUCCESS)
54 resource_table = vmci_hashtable_create(128);
55 if (resource_table == NULL) {
56 VMCI_LOG_WARNING((LGPFX"Failed creating a resource hash table "
58 vmci_cleanup_lock(&resource_id_lock);
59 return (VMCI_ERROR_NO_MEM);
62 return (VMCI_SUCCESS);
66 *------------------------------------------------------------------------------
68 * vmci_resource_exit --
70 * Cleans up resources.
78 *------------------------------------------------------------------------------
82 vmci_resource_exit(void)
85 /* Cleanup resources.*/
86 vmci_cleanup_lock(&resource_id_lock);
89 vmci_hashtable_destroy(resource_table);
93 *------------------------------------------------------------------------------
95 * vmci_resource_get_id --
97 * Return resource ID. The first VMCI_RESERVED_RESOURCE_ID_MAX are reserved
98 * so we start from its value + 1.
101 * VMCI resource id on success, VMCI_INVALID_ID on failure.
107 *------------------------------------------------------------------------------
111 vmci_resource_get_id(vmci_id context_id)
117 old_rid = resource_id;
121 * Generate a unique resource ID. Keep on trying until we wrap around
124 ASSERT(old_rid > VMCI_RESERVED_RESOURCE_ID_MAX);
127 struct vmci_handle handle;
129 vmci_grab_lock(&resource_id_lock);
130 current_rid = resource_id;
131 handle = VMCI_MAKE_HANDLE(context_id, current_rid);
133 if (UNLIKELY(resource_id == VMCI_INVALID_ID)) {
134 /* Skip the reserved rids. */
135 resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
137 vmci_release_lock(&resource_id_lock);
138 found_rid = !vmci_hashtable_entry_exists(resource_table,
140 } while (!found_rid && resource_id != old_rid);
142 if (UNLIKELY(!found_rid))
143 return (VMCI_INVALID_ID);
145 return (current_rid);
149 *------------------------------------------------------------------------------
151 * vmci_resource_add --
153 * Add resource to hashtable.
156 * VMCI_SUCCESS if successful, error code if not.
161 *------------------------------------------------------------------------------
165 vmci_resource_add(struct vmci_resource *resource,
166 vmci_resource_type resource_type, struct vmci_handle resource_handle,
167 vmci_resource_free_cb container_free_cb, void *container_object)
173 if (VMCI_HANDLE_EQUAL(resource_handle, VMCI_INVALID_HANDLE)) {
174 VMCI_LOG_DEBUG(LGPFX"Invalid argument resource "
175 "(handle=0x%x:0x%x).\n", resource_handle.context,
176 resource_handle.resource);
177 return (VMCI_ERROR_INVALID_ARGS);
180 vmci_hashtable_init_entry(&resource->hash_entry, resource_handle);
181 resource->type = resource_type;
182 resource->container_free_cb = container_free_cb;
183 resource->container_object = container_object;
185 /* Add resource to hashtable. */
186 result = vmci_hashtable_add_entry(resource_table,
187 &resource->hash_entry);
188 if (result != VMCI_SUCCESS) {
189 VMCI_LOG_DEBUG(LGPFX"Failed to add entry to hash table "
190 "(result=%d).\n", result);
198 *------------------------------------------------------------------------------
200 * vmci_resource_remove --
202 * Remove resource from hashtable.
210 *------------------------------------------------------------------------------
214 vmci_resource_remove(struct vmci_handle resource_handle,
215 vmci_resource_type resource_type)
217 struct vmci_resource *resource;
219 resource = vmci_resource_get(resource_handle, resource_type);
220 if (resource == NULL)
223 /* Remove resource from hashtable. */
224 vmci_hashtable_remove_entry(resource_table, &resource->hash_entry);
226 vmci_resource_release(resource);
227 /* resource could be freed by now. */
231 *------------------------------------------------------------------------------
233 * vmci_resource_get --
235 * Get resource from hashtable.
238 * Resource if successful. Otherwise NULL.
243 *------------------------------------------------------------------------------
246 struct vmci_resource *
247 vmci_resource_get(struct vmci_handle resource_handle,
248 vmci_resource_type resource_type)
250 struct vmci_hash_entry *entry;
251 struct vmci_resource *resource;
253 entry = vmci_hashtable_get_entry(resource_table, resource_handle);
256 resource = RESOURCE_CONTAINER(entry, struct vmci_resource, hash_entry);
257 if (resource_type == VMCI_RESOURCE_TYPE_ANY ||
258 resource->type == resource_type) {
261 vmci_hashtable_release_entry(resource_table, entry);
266 *------------------------------------------------------------------------------
268 * vmci_resource_hold --
270 * Hold the given resource. This will hold the hashtable entry. This is like
271 * doing a Get() but without having to lookup the resource by handle.
279 *------------------------------------------------------------------------------
283 vmci_resource_hold(struct vmci_resource *resource)
287 vmci_hashtable_hold_entry(resource_table, &resource->hash_entry);
291 *------------------------------------------------------------------------------
293 * vmci_resource_do_remove --
295 * Deallocates data structures associated with the given resource and
296 * invoke any call back registered for the resource.
302 * May deallocate memory and invoke a callback for the removed resource.
304 *------------------------------------------------------------------------------
308 vmci_resource_do_remove(struct vmci_resource *resource)
313 if (resource->container_free_cb) {
314 resource->container_free_cb(resource->container_object);
315 /* Resource has been freed don't dereference it. */
320 *------------------------------------------------------------------------------
322 * vmci_resource_release --
328 * Resource's containerFreeCB will get called if last reference.
330 *------------------------------------------------------------------------------
334 vmci_resource_release(struct vmci_resource *resource)
340 result = vmci_hashtable_release_entry(resource_table,
341 &resource->hash_entry);
342 if (result == VMCI_SUCCESS_ENTRY_DEAD)
343 vmci_resource_do_remove(resource);
346 * We propagate the information back to caller in case it wants to know
347 * whether entry was freed.
353 *------------------------------------------------------------------------------
355 * vmci_resource_handle --
357 * Get the handle for the given resource.
360 * The resource's associated handle.
365 *------------------------------------------------------------------------------
369 vmci_resource_handle(struct vmci_resource *resource)
373 return (resource->hash_entry.handle);
377 *------------------------------------------------------------------------------
379 * vmci_resource_sync --
381 * Use this as a synchronization point when setting globals, for example,
382 * during device shutdown.
390 *------------------------------------------------------------------------------
394 vmci_resource_sync(void)
397 vmci_hashtable_sync(resource_table);