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. */
17 static uint32_t resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
18 static vmci_lock resource_id_lock;
19
20 static void vmci_resource_do_remove(struct vmci_resource *resource);
21
22 static 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
43 int
vmci_resource_init(void)44 vmci_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
79 void
vmci_resource_exit(void)80 vmci_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
108 vmci_id
vmci_resource_get_id(vmci_id context_id)109 vmci_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
162 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)163 vmci_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
211 void
vmci_resource_remove(struct vmci_handle resource_handle,vmci_resource_type resource_type)212 vmci_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
244 struct vmci_resource *
vmci_resource_get(struct vmci_handle resource_handle,vmci_resource_type resource_type)245 vmci_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
280 void
vmci_resource_hold(struct vmci_resource * resource)281 vmci_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
305 static void inline
vmci_resource_do_remove(struct vmci_resource * resource)306 vmci_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
331 int
vmci_resource_release(struct vmci_resource * resource)332 vmci_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
366 struct vmci_handle
vmci_resource_handle(struct vmci_resource * resource)367 vmci_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
391 void
vmci_resource_sync(void)392 vmci_resource_sync(void)
393 {
394
395 vmci_hashtable_sync(resource_table);
396 }
397