xref: /freebsd/sys/dev/vmware/vmci/vmci_resource.c (revision 02e9120893770924227138ba49df1edb3896112a)
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
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
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
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
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
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 *
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
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
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
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
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
392 vmci_resource_sync(void)
393 {
394 
395 	vmci_hashtable_sync(resource_table);
396 }
397