1/*-
2 * Copyright (c) 2018 VMware, Inc.
3 *
4 * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
5 */
6
7/* Implementation of the VMCI Resource Access Control API. */
8
9#include <sys/cdefs.h>
10__FBSDID("$FreeBSD$");
11
12#include "vmci_driver.h"
13#include "vmci_kernel_defs.h"
14#include "vmci_resource.h"
15
16#define LGPFX	"vmci_resource: "
17
18/* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */
19static uint32_t resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
20static vmci_lock resource_id_lock;
21
22static void	vmci_resource_do_remove(struct vmci_resource *resource);
23
24static struct vmci_hashtable *resource_table = NULL;
25
26/* Public Resource Access Control API. */
27
28/*
29 *------------------------------------------------------------------------------
30 *
31 * vmci_resource_init --
32 *
33 *     Initializes the VMCI Resource Access Control API. Creates a hashtable to
34 *     hold all resources, and registers vectors and callbacks for hypercalls.
35 *
36 * Results:
37 *     None.
38 *
39 * Side effects:
40 *     None.
41 *
42 *------------------------------------------------------------------------------
43 */
44
45int
46vmci_resource_init(void)
47{
48	int err;
49
50	err = vmci_init_lock(&resource_id_lock, "VMCI RID lock");
51	if (err < VMCI_SUCCESS)
52		return (err);
53
54	resource_table = vmci_hashtable_create(128);
55	if (resource_table == NULL) {
56		VMCI_LOG_WARNING((LGPFX"Failed creating a resource hash table "
57		    "for VMCI.\n"));
58		vmci_cleanup_lock(&resource_id_lock);
59		return (VMCI_ERROR_NO_MEM);
60	}
61
62	return (VMCI_SUCCESS);
63}
64
65/*
66 *------------------------------------------------------------------------------
67 *
68 * vmci_resource_exit --
69 *
70 *      Cleans up resources.
71 *
72 * Results:
73 *      None.
74 *
75 * Side effects:
76 *      None.
77 *
78 *------------------------------------------------------------------------------
79 */
80
81void
82vmci_resource_exit(void)
83{
84
85	/* Cleanup resources.*/
86	vmci_cleanup_lock(&resource_id_lock);
87
88	if (resource_table)
89		vmci_hashtable_destroy(resource_table);
90}
91
92/*
93 *------------------------------------------------------------------------------
94 *
95 *  vmci_resource_get_id --
96 *
97 *      Return resource ID. The first VMCI_RESERVED_RESOURCE_ID_MAX are reserved
98 *      so we start from its value + 1.
99 *
100 *  Result:
101 *      VMCI resource id on success, VMCI_INVALID_ID on failure.
102 *
103 *  Side effects:
104 *      None.
105 *
106 *
107 *------------------------------------------------------------------------------
108 */
109
110vmci_id
111vmci_resource_get_id(vmci_id context_id)
112{
113	vmci_id current_rid;
114	vmci_id old_rid;
115	bool found_rid;
116
117	old_rid = resource_id;
118	found_rid = false;
119
120	/*
121	 * Generate a unique resource ID. Keep on trying until we wrap around
122	 * in the RID space.
123	 */
124	ASSERT(old_rid > VMCI_RESERVED_RESOURCE_ID_MAX);
125
126	do {
127		struct vmci_handle handle;
128
129		vmci_grab_lock(&resource_id_lock);
130		current_rid = resource_id;
131		handle = VMCI_MAKE_HANDLE(context_id, current_rid);
132		resource_id++;
133		if (UNLIKELY(resource_id == VMCI_INVALID_ID)) {
134			/* Skip the reserved rids. */
135			resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
136		}
137		vmci_release_lock(&resource_id_lock);
138		found_rid = !vmci_hashtable_entry_exists(resource_table,
139		    handle);
140	} while (!found_rid && resource_id != old_rid);
141
142	if (UNLIKELY(!found_rid))
143		return (VMCI_INVALID_ID);
144	else
145		return (current_rid);
146}
147
148/*
149 *------------------------------------------------------------------------------
150 *
151 * vmci_resource_add --
152 *
153 *     Add resource to hashtable.
154 *
155 * Results:
156 *     VMCI_SUCCESS if successful, error code if not.
157 *
158 * Side effects:
159 *     None.
160 *
161 *------------------------------------------------------------------------------
162 */
163
164int
165vmci_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)
168{
169	int result;
170
171	ASSERT(resource);
172
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);
178	}
179
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;
184
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);
191		return (result);
192	}
193
194	return (result);
195}
196
197/*
198 *------------------------------------------------------------------------------
199 *
200 * vmci_resource_remove --
201 *
202 *     Remove resource from hashtable.
203 *
204 * Results:
205 *     None.
206 *
207 * Side effects:
208 *     None.
209 *
210 *------------------------------------------------------------------------------
211 */
212
213void
214vmci_resource_remove(struct vmci_handle resource_handle,
215    vmci_resource_type resource_type)
216{
217	struct vmci_resource *resource;
218
219	resource = vmci_resource_get(resource_handle, resource_type);
220	if (resource == NULL)
221		return;
222
223	/* Remove resource from hashtable. */
224	vmci_hashtable_remove_entry(resource_table, &resource->hash_entry);
225
226	vmci_resource_release(resource);
227	/* resource could be freed by now. */
228}
229
230/*
231 *------------------------------------------------------------------------------
232 *
233 * vmci_resource_get --
234 *
235 *     Get resource from hashtable.
236 *
237 * Results:
238 *     Resource if successful. Otherwise NULL.
239 *
240 * Side effects:
241 *     None.
242 *
243 *------------------------------------------------------------------------------
244 */
245
246struct vmci_resource *
247vmci_resource_get(struct vmci_handle resource_handle,
248    vmci_resource_type resource_type)
249{
250	struct vmci_hash_entry *entry;
251	struct vmci_resource *resource;
252
253	entry = vmci_hashtable_get_entry(resource_table, resource_handle);
254	if (entry == NULL)
255		return (NULL);
256	resource = RESOURCE_CONTAINER(entry, struct vmci_resource, hash_entry);
257	if (resource_type == VMCI_RESOURCE_TYPE_ANY ||
258		resource->type == resource_type) {
259		return (resource);
260	}
261	vmci_hashtable_release_entry(resource_table, entry);
262	return (NULL);
263}
264
265/*
266 *------------------------------------------------------------------------------
267 *
268 * vmci_resource_hold --
269 *
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.
272 *
273 * Results:
274 *     None.
275 *
276 * Side effects:
277 *     None.
278 *
279 *------------------------------------------------------------------------------
280 */
281
282void
283vmci_resource_hold(struct vmci_resource *resource)
284{
285
286	ASSERT(resource);
287	vmci_hashtable_hold_entry(resource_table, &resource->hash_entry);
288}
289
290/*
291 *------------------------------------------------------------------------------
292 *
293 * vmci_resource_do_remove --
294 *
295 *     Deallocates data structures associated with the given resource and
296 *     invoke any call back registered for the resource.
297 *
298 * Results:
299 *     None.
300 *
301 * Side effects:
302 *     May deallocate memory and invoke a callback for the removed resource.
303 *
304 *------------------------------------------------------------------------------
305 */
306
307static void inline
308vmci_resource_do_remove(struct vmci_resource *resource)
309{
310
311	ASSERT(resource);
312
313	if (resource->container_free_cb) {
314		resource->container_free_cb(resource->container_object);
315		/* Resource has been freed don't dereference it. */
316	}
317}
318
319/*
320 *------------------------------------------------------------------------------
321 *
322 * vmci_resource_release --
323 *
324 * Results:
325 *     None.
326 *
327 * Side effects:
328 *     Resource's containerFreeCB will get called if last reference.
329 *
330 *------------------------------------------------------------------------------
331 */
332
333int
334vmci_resource_release(struct vmci_resource *resource)
335{
336	int result;
337
338	ASSERT(resource);
339
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);
344
345	/*
346	 * We propagate the information back to caller in case it wants to know
347	 * whether entry was freed.
348	 */
349	return (result);
350}
351
352/*
353 *------------------------------------------------------------------------------
354 *
355 * vmci_resource_handle --
356 *
357 *     Get the handle for the given resource.
358 *
359 * Results:
360 *     The resource's associated handle.
361 *
362 * Side effects:
363 *     None.
364 *
365 *------------------------------------------------------------------------------
366 */
367
368struct vmci_handle
369vmci_resource_handle(struct vmci_resource *resource)
370{
371
372	ASSERT(resource);
373	return (resource->hash_entry.handle);
374}
375
376/*
377 *------------------------------------------------------------------------------
378 *
379 * vmci_resource_sync --
380 *
381 *     Use this as a synchronization point when setting globals, for example,
382 *     during device shutdown.
383 *
384 * Results:
385 *     None.
386 *
387 * Side effects:
388 *     None.
389 *
390 *------------------------------------------------------------------------------
391 */
392
393void
394vmci_resource_sync(void)
395{
396
397	vmci_hashtable_sync(resource_table);
398}
399