xref: /freebsd/sys/dev/vmware/vmci/vmci_resource.c (revision 0957b409)
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 }
399