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