]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vmware/vmci/vmci_resource.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / sys / dev / vmware / vmci / vmci_resource.c
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 }