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. */
19 static uint32_t resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
20 static vmci_lock resource_id_lock;
21
22 static void vmci_resource_do_remove(struct vmci_resource *resource);
23
24 static 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
45 int
46 vmci_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
81 void
82 vmci_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
110 vmci_id
111 vmci_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
164 int
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)
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
213 void
214 vmci_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
246 struct vmci_resource *
247 vmci_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
282 void
283 vmci_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
307 static void inline
308 vmci_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
333 int
334 vmci_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
368 struct vmci_handle
369 vmci_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
393 void
394 vmci_resource_sync(void)
395 {
396
397 vmci_hashtable_sync(resource_table);
398 }
Cache object: ebd490384a40881594df3cb2f265a188
|