2 * Copyright (c) 2018 VMware, Inc.
4 * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
7 /* Implementation of the VMCI Hashtable. */
10 __FBSDID("$FreeBSD$");
13 #include "vmci_driver.h"
14 #include "vmci_hashtable.h"
15 #include "vmci_kernel_defs.h"
16 #include "vmci_utils.h"
18 #define LGPFX "vmci_hashtable: "
20 #define VMCI_HASHTABLE_HASH(_h, _sz) \
21 vmci_hash_id(VMCI_HANDLE_TO_RESOURCE_ID(_h), (_sz))
23 static int hashtable_unlink_entry(struct vmci_hashtable *table,
24 struct vmci_hash_entry *entry);
25 static bool vmci_hashtable_entry_exists_locked(struct vmci_hashtable *table,
26 struct vmci_handle handle);
29 *------------------------------------------------------------------------------
31 * vmci_hashtable_create --
33 * Creates a hashtable.
41 *------------------------------------------------------------------------------
44 struct vmci_hashtable *
45 vmci_hashtable_create(int size)
47 struct vmci_hashtable *table;
49 table = vmci_alloc_kernel_mem(sizeof(*table),
53 memset(table, 0, sizeof(*table));
55 table->entries = vmci_alloc_kernel_mem(sizeof(*table->entries) * size,
57 if (table->entries == NULL) {
58 vmci_free_kernel_mem(table, sizeof(*table));
61 memset(table->entries, 0, sizeof(*table->entries) * size);
63 if (vmci_init_lock(&table->lock, "VMCI Hashtable lock") <
65 vmci_free_kernel_mem(table->entries, sizeof(*table->entries) * size);
66 vmci_free_kernel_mem(table, sizeof(*table));
74 *------------------------------------------------------------------------------
76 * vmci_hashtable_destroy --
78 * This function should be called at module exit time. We rely on the
79 * module ref count to insure that no one is accessing any hash table
80 * entries at this point in time. Hence we should be able to just remove
81 * all entries from the hash table.
89 *------------------------------------------------------------------------------
93 vmci_hashtable_destroy(struct vmci_hashtable *table)
98 vmci_grab_lock_bh(&table->lock);
99 vmci_free_kernel_mem(table->entries, sizeof(*table->entries) *
101 table->entries = NULL;
102 vmci_release_lock_bh(&table->lock);
103 vmci_cleanup_lock(&table->lock);
104 vmci_free_kernel_mem(table, sizeof(*table));
108 *------------------------------------------------------------------------------
110 * vmci_hashtable_init_entry --
112 * Initializes a hash entry.
120 *------------------------------------------------------------------------------
123 vmci_hashtable_init_entry(struct vmci_hash_entry *entry,
124 struct vmci_handle handle)
128 entry->handle = handle;
129 entry->ref_count = 0;
133 *------------------------------------------------------------------------------
135 * vmci_hashtable_add_entry --
137 * Adds an entry to the hashtable.
145 *------------------------------------------------------------------------------
149 vmci_hashtable_add_entry(struct vmci_hashtable *table,
150 struct vmci_hash_entry *entry)
157 vmci_grab_lock_bh(&table->lock);
159 if (vmci_hashtable_entry_exists_locked(table, entry->handle)) {
160 VMCI_LOG_DEBUG(LGPFX"Entry (handle=0x%x:0x%x) already "
161 "exists.\n", entry->handle.context,
162 entry->handle.resource);
163 vmci_release_lock_bh(&table->lock);
164 return (VMCI_ERROR_DUPLICATE_ENTRY);
167 idx = VMCI_HASHTABLE_HASH(entry->handle, table->size);
168 ASSERT(idx < table->size);
170 /* New entry is added to top/front of hash bucket. */
172 entry->next = table->entries[idx];
173 table->entries[idx] = entry;
174 vmci_release_lock_bh(&table->lock);
176 return (VMCI_SUCCESS);
180 *------------------------------------------------------------------------------
182 * vmci_hashtable_remove_entry --
184 * Removes an entry from the hashtable.
192 *------------------------------------------------------------------------------
196 vmci_hashtable_remove_entry(struct vmci_hashtable *table,
197 struct vmci_hash_entry *entry)
204 vmci_grab_lock_bh(&table->lock);
206 /* First unlink the entry. */
207 result = hashtable_unlink_entry(table, entry);
208 if (result != VMCI_SUCCESS) {
209 /* We failed to find the entry. */
213 /* Decrement refcount and check if this is last reference. */
215 if (entry->ref_count == 0) {
216 result = VMCI_SUCCESS_ENTRY_DEAD;
221 vmci_release_lock_bh(&table->lock);
227 *------------------------------------------------------------------------------
229 * vmci_hashtable_get_entry_locked --
231 * Looks up an entry in the hash table, that is already locked.
234 * If the element is found, a pointer to the element is returned.
235 * Otherwise NULL is returned.
238 * The reference count of the returned element is increased.
240 *------------------------------------------------------------------------------
243 static struct vmci_hash_entry *
244 vmci_hashtable_get_entry_locked(struct vmci_hashtable *table,
245 struct vmci_handle handle)
247 struct vmci_hash_entry *cur = NULL;
250 ASSERT(!VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE));
253 idx = VMCI_HASHTABLE_HASH(handle, table->size);
255 cur = table->entries[idx];
260 if (VMCI_HANDLE_TO_RESOURCE_ID(cur->handle) ==
261 VMCI_HANDLE_TO_RESOURCE_ID(handle)) {
262 if ((VMCI_HANDLE_TO_CONTEXT_ID(cur->handle) ==
263 VMCI_HANDLE_TO_CONTEXT_ID(handle)) ||
264 (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(cur->handle))) {
276 *------------------------------------------------------------------------------
278 * vmci_hashtable_get_entry --
280 * Gets an entry from the hashtable.
288 *------------------------------------------------------------------------------
291 struct vmci_hash_entry *
292 vmci_hashtable_get_entry(struct vmci_hashtable *table,
293 struct vmci_handle handle)
295 struct vmci_hash_entry *entry;
297 if (VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE))
302 vmci_grab_lock_bh(&table->lock);
303 entry = vmci_hashtable_get_entry_locked(table, handle);
304 vmci_release_lock_bh(&table->lock);
310 *------------------------------------------------------------------------------
312 * vmci_hashtable_hold_entry --
314 * Hold the given entry. This will increment the entry's reference count.
315 * This is like a GetEntry() but without having to lookup the entry by
324 *------------------------------------------------------------------------------
328 vmci_hashtable_hold_entry(struct vmci_hashtable *table,
329 struct vmci_hash_entry *entry)
335 vmci_grab_lock_bh(&table->lock);
337 vmci_release_lock_bh(&table->lock);
341 *------------------------------------------------------------------------------
343 * vmci_hashtable_release_entry_locked --
345 * Releases an element previously obtained with
346 * vmci_hashtable_get_entry_locked.
349 * If the entry is removed from the hash table, VMCI_SUCCESS_ENTRY_DEAD
350 * is returned. Otherwise, VMCI_SUCCESS is returned.
353 * The reference count of the entry is decreased and the entry is removed
354 * from the hash table on 0.
356 *------------------------------------------------------------------------------
360 vmci_hashtable_release_entry_locked(struct vmci_hashtable *table,
361 struct vmci_hash_entry *entry)
363 int result = VMCI_SUCCESS;
369 /* Check if this is last reference and report if so. */
370 if (entry->ref_count == 0) {
373 * Remove entry from hash table if not already removed. This
374 * could have happened already because VMCIHashTable_RemoveEntry
375 * was called to unlink it. We ignore if it is not found.
376 * Datagram handles will often have RemoveEntry called, whereas
377 * SharedMemory regions rely on ReleaseEntry to unlink the entry
378 * , since the creator does not call RemoveEntry when it
382 hashtable_unlink_entry(table, entry);
383 result = VMCI_SUCCESS_ENTRY_DEAD;
390 *------------------------------------------------------------------------------
392 * vmci_hashtable_release_entry --
394 * Releases an entry from the hashtable.
402 *------------------------------------------------------------------------------
406 vmci_hashtable_release_entry(struct vmci_hashtable *table,
407 struct vmci_hash_entry *entry)
412 vmci_grab_lock_bh(&table->lock);
413 result = vmci_hashtable_release_entry_locked(table, entry);
414 vmci_release_lock_bh(&table->lock);
420 *------------------------------------------------------------------------------
422 * vmci_hashtable_entry_exists --
424 * Returns whether an entry exists in the hashtable
427 * true if handle already in hashtable. false otherwise.
432 *------------------------------------------------------------------------------
436 vmci_hashtable_entry_exists(struct vmci_hashtable *table,
437 struct vmci_handle handle)
443 vmci_grab_lock_bh(&table->lock);
444 exists = vmci_hashtable_entry_exists_locked(table, handle);
445 vmci_release_lock_bh(&table->lock);
451 *------------------------------------------------------------------------------
453 * vmci_hashtable_entry_exists_locked --
455 * Unlocked version of vmci_hashtable_entry_exists.
458 * true if handle already in hashtable. false otherwise.
463 *------------------------------------------------------------------------------
467 vmci_hashtable_entry_exists_locked(struct vmci_hashtable *table,
468 struct vmci_handle handle)
471 struct vmci_hash_entry *entry;
476 idx = VMCI_HASHTABLE_HASH(handle, table->size);
478 entry = table->entries[idx];
480 if (VMCI_HANDLE_TO_RESOURCE_ID(entry->handle) ==
481 VMCI_HANDLE_TO_RESOURCE_ID(handle))
482 if ((VMCI_HANDLE_TO_CONTEXT_ID(entry->handle) ==
483 VMCI_HANDLE_TO_CONTEXT_ID(handle)) ||
484 (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(handle)) ||
485 (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(entry->handle)))
494 *------------------------------------------------------------------------------
496 * hashtable_unlink_entry --
498 * Assumes caller holds table lock.
506 *------------------------------------------------------------------------------
510 hashtable_unlink_entry(struct vmci_hashtable *table,
511 struct vmci_hash_entry *entry)
514 struct vmci_hash_entry *prev, *cur;
517 idx = VMCI_HASHTABLE_HASH(entry->handle, table->size);
520 cur = table->entries[idx];
523 result = VMCI_ERROR_NOT_FOUND;
526 if (VMCI_HANDLE_EQUAL(cur->handle, entry->handle)) {
527 ASSERT(cur == entry);
529 /* Remove entry and break. */
531 prev->next = cur->next;
533 table->entries[idx] = cur->next;
535 result = VMCI_SUCCESS;
545 *------------------------------------------------------------------------------
547 * vmci_hashtable_sync --
549 * Use this as a synchronization point when setting globals, for example,
550 * during device shutdown.
558 *------------------------------------------------------------------------------
562 vmci_hashtable_sync(struct vmci_hashtable *table)
566 vmci_grab_lock_bh(&table->lock);
567 vmci_release_lock_bh(&table->lock);