#include <sys/crypto/common.h>
#include <sys/crypto/api.h>
#include <sys/crypto/impl.h>
-#include <sys/modhash.h>
/* Cryptographic mechanisms tables and their access functions */
/*
* Locking conventions:
* --------------------
- * A global mutex, kcf_mech_tabs_lock, serializes writes to the
- * mechanism table via kcf_create_mech_entry().
- *
* A mutex is associated with every entry of the tables.
* The mutex is acquired whenever the entry is accessed for
* 1) retrieving the mech_id (comparing the mech name)
* long enough to justify the cost of using rwlocks, so the per-mechanism
* entry mutex won't be very *hot*.
*
- * When both kcf_mech_tabs_lock and a mech_entry mutex need to be held,
- * kcf_mech_tabs_lock must always be acquired first.
- *
*/
/* Mechanisms tables */
{KCF_MAXMAC, kcf_mac_mechs_tab},
};
-static kmutex_t kcf_mech_tabs_lock;
+static avl_tree_t kcf_mech_hash;
-static const int kcf_mech_hash_size = 256;
-static mod_hash_t *kcf_mech_hash; /* mech name to id hash */
-
-static crypto_mech_type_t
-kcf_mech_hash_find(const char *mechname)
+static int
+kcf_mech_hash_compar(const void *lhs, const void *rhs)
{
- mod_hash_val_t hv;
- crypto_mech_type_t mt;
-
- mt = CRYPTO_MECH_INVALID;
- if (mod_hash_find(kcf_mech_hash, (mod_hash_key_t)mechname, &hv) == 0) {
- mt = *(crypto_mech_type_t *)hv;
- ASSERT(mt != CRYPTO_MECH_INVALID);
- }
-
- return (mt);
+ const kcf_mech_entry_t *l = lhs, *r = rhs;
+ int cmp = strncmp(l->me_name, r->me_name, CRYPTO_MAX_MECH_NAME);
+ return ((0 < cmp) - (cmp < 0));
}
void
kcf_destroy_mech_tabs(void)
{
- int i, max;
- kcf_ops_class_t class;
- kcf_mech_entry_t *me_tab;
-
- if (kcf_mech_hash)
- mod_hash_destroy_hash(kcf_mech_hash);
-
- mutex_destroy(&kcf_mech_tabs_lock);
-
- for (class = KCF_FIRST_OPSCLASS; class <= KCF_LAST_OPSCLASS; class++) {
- max = kcf_mech_tabs_tab[class].met_size;
- me_tab = kcf_mech_tabs_tab[class].met_tab;
- }
+ for (void *cookie = NULL; avl_destroy_nodes(&kcf_mech_hash, &cookie); )
+ ;
+ avl_destroy(&kcf_mech_hash);
}
/*
kcf_ops_class_t class;
kcf_mech_entry_t *me_tab;
- /* Initializes the mutex locks. */
-
- mutex_init(&kcf_mech_tabs_lock, NULL, MUTEX_DEFAULT, NULL);
-
/* Then the pre-defined mechanism entries */
-
- kcf_mech_hash = mod_hash_create_strhash_nodtr("kcf mech2id hash",
- kcf_mech_hash_size, mod_hash_null_valdtor);
+ avl_create(&kcf_mech_hash, kcf_mech_hash_compar,
+ sizeof (kcf_mech_entry_t), offsetof(kcf_mech_entry_t, me_node));
for (class = KCF_FIRST_OPSCLASS; class <= KCF_LAST_OPSCLASS; class++) {
int max = kcf_mech_tabs_tab[class].met_size;
for (int i = 0; i < max; i++) {
if (me_tab[i].me_name[0] != 0) {
me_tab[i].me_mechid = KCF_MECHID(class, i);
- (void) mod_hash_insert(kcf_mech_hash,
- (mod_hash_key_t)me_tab[i].me_name,
- (mod_hash_val_t)&(me_tab[i].me_mechid));
+ avl_add(&kcf_mech_hash, &me_tab[i]);
}
}
}
static int
kcf_create_mech_entry(kcf_ops_class_t class, const char *mechname)
{
- crypto_mech_type_t mt;
- kcf_mech_entry_t *me_tab;
- int i = 0, size;
-
if ((class < KCF_FIRST_OPSCLASS) || (class > KCF_LAST_OPSCLASS))
return (KCF_INVALID_MECH_CLASS);
* First check if the mechanism is already in one of the tables.
* The mech_entry could be in another class.
*/
- mutex_enter(&kcf_mech_tabs_lock);
- mt = kcf_mech_hash_find(mechname);
- if (mt != CRYPTO_MECH_INVALID) {
- /* Nothing to do, regardless the suggested class. */
- mutex_exit(&kcf_mech_tabs_lock);
+ avl_index_t where = 0;
+ kcf_mech_entry_t tmptab;
+ strlcpy(tmptab.me_name, mechname, CRYPTO_MAX_MECH_NAME);
+ if (avl_find(&kcf_mech_hash, &tmptab, &where) != NULL)
return (KCF_SUCCESS);
- }
/* Now take the next unused mech entry in the class's tab */
- me_tab = kcf_mech_tabs_tab[class].met_tab;
- size = kcf_mech_tabs_tab[class].met_size;
+ kcf_mech_entry_t *me_tab = kcf_mech_tabs_tab[class].met_tab;
+ int size = kcf_mech_tabs_tab[class].met_size;
- while (i < size) {
+ for (int i = 0; i < size; ++i)
if (me_tab[i].me_name[0] == 0) {
/* Found an empty spot */
- (void) strlcpy(me_tab[i].me_name, mechname,
+ strlcpy(me_tab[i].me_name, mechname,
CRYPTO_MAX_MECH_NAME);
- me_tab[i].me_name[CRYPTO_MAX_MECH_NAME-1] = '\0';
me_tab[i].me_mechid = KCF_MECHID(class, i);
/* Add the new mechanism to the hash table */
- (void) mod_hash_insert(kcf_mech_hash,
- (mod_hash_key_t)me_tab[i].me_name,
- (mod_hash_val_t)&(me_tab[i].me_mechid));
- break;
+ avl_insert(&kcf_mech_hash, &me_tab[i], where);
+ return (KCF_SUCCESS);
}
- i++;
- }
- mutex_exit(&kcf_mech_tabs_lock);
-
- if (i == size) {
- return (KCF_MECH_TAB_FULL);
- }
-
- return (KCF_SUCCESS);
+ return (KCF_MECH_TAB_FULL);
}
/*
* Find the class corresponding to the function group flag of
* the mechanism.
*/
- kcf_mech_type = kcf_mech_hash_find(mech_info->cm_mech_name);
+ kcf_mech_type = crypto_mech2id(mech_info->cm_mech_name);
if (kcf_mech_type == CRYPTO_MECH_INVALID) {
crypto_func_group_t fg = mech_info->cm_func_group_mask;
kcf_ops_class_t class;
return (error);
}
/* get the KCF mech type that was assigned to the mechanism */
- kcf_mech_type = kcf_mech_hash_find(mech_info->cm_mech_name);
+ kcf_mech_type = crypto_mech2id(mech_info->cm_mech_name);
ASSERT(kcf_mech_type != CRYPTO_MECH_INVALID);
}
kcf_mech_entry_t *mech_entry;
/* get the KCF mech type that was assigned to the mechanism */
- if ((mech_type = kcf_mech_hash_find(mech_name)) ==
+ if ((mech_type = crypto_mech2id(mech_name)) ==
CRYPTO_MECH_INVALID) {
/*
* Provider was not allowed for this mech due to policy or
* Description:
* Walks the mechanisms tables, looking for an entry that matches the
* mechname. Once it find it, it builds the 64-bit mech_type and returns
- * it. If there are no providers for the mechanism,
- * but there is an unloaded provider, this routine will attempt
- * to load it.
+ * it.
*
* Context:
* Process and interruption.
crypto_mech_type_t
crypto_mech2id(const char *mechname)
{
- return (kcf_mech_hash_find(mechname));
+ kcf_mech_entry_t tmptab, *found;
+ strlcpy(tmptab.me_name, mechname, CRYPTO_MAX_MECH_NAME);
+
+ if ((found = avl_find(&kcf_mech_hash, &tmptab, NULL))) {
+ ASSERT(found->me_mechid != CRYPTO_MECH_INVALID);
+ return (found->me_mechid);
+ }
+
+ return (CRYPTO_MECH_INVALID);
}
#if defined(_KERNEL)
+++ /dev/null
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * mod_hash: flexible hash table implementation.
- *
- * This is a reasonably fast, reasonably flexible hash table implementation
- * which features pluggable hash algorithms to support storing arbitrary keys
- * and values. It is designed to handle small (< 100,000 items) amounts of
- * data. The hash uses chaining to resolve collisions, and does not feature a
- * mechanism to grow the hash. Care must be taken to pick nchains to be large
- * enough for the application at hand, or lots of time will be wasted searching
- * hash chains.
- *
- * The client of the hash is required to supply a number of items to support
- * the various hash functions:
- *
- * - Destructor functions for the key and value being hashed.
- * A destructor is responsible for freeing an object when the hash
- * table is no longer storing it. Since keys and values can be of
- * arbitrary type, separate destructors for keys & values are used.
- * These may be mod_hash_null_keydtor and mod_hash_null_valdtor if no
- * destructor is needed for either a key or value.
- *
- * - A hashing algorithm which returns a uint_t representing a hash index
- * The number returned need _not_ be between 0 and nchains. The mod_hash
- * code will take care of doing that. The second argument (after the
- * key) to the hashing function is a void * that represents
- * hash_alg_data-- this is provided so that the hashing algorithm can
- * maintain some state across calls, or keep algorithm-specific
- * constants associated with the hash table.
- *
- * A pointer-hashing and a string-hashing algorithm are supplied in
- * this file.
- *
- * - A key comparator (a la qsort).
- * This is used when searching the hash chain. The key comparator
- * determines if two keys match. It should follow the return value
- * semantics of strcmp.
- *
- * string and pointer comparators are supplied in this file.
- *
- * mod_hash_create_strhash() and mod_hash_create_ptrhash() provide good
- * examples of how to create a customized hash table.
- *
- * Basic hash operations:
- *
- * mod_hash_create_strhash(name, nchains, dtor),
- * create a hash using strings as keys.
- * NOTE: This create a hash which automatically cleans up the string
- * values it is given for keys.
- *
- * mod_hash_create_ptrhash(name, nchains, dtor, key_elem_size):
- * create a hash using pointers as keys.
- *
- * mod_hash_create_extended(name, nchains, kdtor, vdtor,
- * hash_alg, hash_alg_data,
- * keycmp, sleep)
- * create a customized hash table.
- *
- * mod_hash_destroy_hash(hash):
- * destroy the given hash table, calling the key and value destructors
- * on each key-value pair stored in the hash.
- *
- * mod_hash_insert(hash, key, val):
- * place a key, value pair into the given hash.
- * duplicate keys are rejected.
- *
- * mod_hash_insert_reserve(hash, key, val, handle):
- * place a key, value pair into the given hash, using handle to indicate
- * the reserved storage for the pair. (no memory allocation is needed
- * during a mod_hash_insert_reserve.) duplicate keys are rejected.
- *
- * mod_hash_reserve(hash, *handle):
- * reserve storage for a key-value pair using the memory allocation
- * policy of 'hash', returning the storage handle in 'handle'.
- *
- * mod_hash_reserve_nosleep(hash, *handle): reserve storage for a key-value
- * pair ignoring the memory allocation policy of 'hash' and always without
- * sleep, returning the storage handle in 'handle'.
- *
- * mod_hash_remove(hash, key, *val):
- * remove a key-value pair with key 'key' from 'hash', destroying the
- * stored key, and returning the value in val.
- *
- * mod_hash_replace(hash, key, val)
- * atomically remove an existing key-value pair from a hash, and replace
- * the key and value with the ones supplied. The removed key and value
- * (if any) are destroyed.
- *
- * mod_hash_destroy(hash, key):
- * remove a key-value pair with key 'key' from 'hash', destroying both
- * stored key and stored value.
- *
- * mod_hash_find(hash, key, val):
- * find a value in the hash table corresponding to the given key.
- *
- * mod_hash_find_cb(hash, key, val, found_callback)
- * find a value in the hash table corresponding to the given key.
- * If a value is found, call specified callback passing key and val to it.
- * The callback is called with the hash lock held.
- * It is intended to be used in situations where the act of locating the
- * data must also modify it - such as in reference counting schemes.
- *
- * mod_hash_walk(hash, callback(key, elem, arg), arg)
- * walks all the elements in the hashtable and invokes the callback
- * function with the key/value pair for each element. the hashtable
- * is locked for readers so the callback function should not attempt
- * to do any updates to the hashable. the callback function should
- * return MH_WALK_CONTINUE to continue walking the hashtable or
- * MH_WALK_TERMINATE to abort the walk of the hashtable.
- *
- * mod_hash_clear(hash):
- * clears the given hash table of entries, calling the key and value
- * destructors for every element in the hash.
- */
-
-#include <sys/zfs_context.h>
-#include <sys/bitmap.h>
-#include <sys/modhash_impl.h>
-#include <sys/sysmacros.h>
-
-/*
- * MH_KEY_DESTROY()
- * Invoke the key destructor.
- */
-#define MH_KEY_DESTROY(hash, key) ((hash->mh_kdtor)(key))
-
-/*
- * MH_VAL_DESTROY()
- * Invoke the value destructor.
- */
-#define MH_VAL_DESTROY(hash, val) ((hash->mh_vdtor)(val))
-
-/*
- * MH_KEYCMP()
- * Call the key comparator for the given hash keys.
- */
-#define MH_KEYCMP(hash, key1, key2) ((hash->mh_keycmp)(key1, key2))
-
-/*
- * Cache for struct mod_hash_entry
- */
-kmem_cache_t *mh_e_cache = NULL;
-mod_hash_t *mh_head = NULL;
-kmutex_t mh_head_lock;
-
-/*
- * mod_hash_null_keydtor()
- * mod_hash_null_valdtor()
- * no-op key and value destructors.
- */
-void
-mod_hash_null_keydtor(mod_hash_key_t key)
-{
- (void) key;
-}
-
-void
-mod_hash_null_valdtor(mod_hash_val_t val)
-{
- (void) val;
-}
-
-/*
- * mod_hash_bystr()
- * mod_hash_strkey_cmp()
- * mod_hash_strkey_dtor()
- * mod_hash_strval_dtor()
- * Hash and key comparison routines for hashes with string keys.
- *
- * mod_hash_create_strhash()
- * Create a hash using strings as keys
- *
- * The string hashing algorithm is from the "Dragon Book" --
- * "Compilers: Principles, Tools & Techniques", by Aho, Sethi, Ullman
- */
-
-uint_t
-mod_hash_bystr(void *hash_data, mod_hash_key_t key)
-{
- (void) hash_data;
- uint_t hash = 0;
- uint_t g;
- char *p, *k = (char *)key;
-
- ASSERT(k);
- for (p = k; *p != '\0'; p++) {
- hash = (hash << 4) + *p;
- if ((g = (hash & 0xf0000000)) != 0) {
- hash ^= (g >> 24);
- hash ^= g;
- }
- }
- return (hash);
-}
-
-int
-mod_hash_strkey_cmp(mod_hash_key_t key1, mod_hash_key_t key2)
-{
- return (strcmp((char *)key1, (char *)key2));
-}
-
-void
-mod_hash_strkey_dtor(mod_hash_key_t key)
-{
- char *c = (char *)key;
- kmem_free(c, strlen(c) + 1);
-}
-
-void
-mod_hash_strval_dtor(mod_hash_val_t val)
-{
- char *c = (char *)val;
- kmem_free(c, strlen(c) + 1);
-}
-
-mod_hash_t *
-mod_hash_create_strhash_nodtr(char *name, size_t nchains,
- void (*val_dtor)(mod_hash_val_t))
-{
- return mod_hash_create_extended(name, nchains, mod_hash_null_keydtor,
- val_dtor, mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
-}
-
-mod_hash_t *
-mod_hash_create_strhash(char *name, size_t nchains,
- void (*val_dtor)(mod_hash_val_t))
-{
- return mod_hash_create_extended(name, nchains, mod_hash_strkey_dtor,
- val_dtor, mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
-}
-
-void
-mod_hash_destroy_strhash(mod_hash_t *strhash)
-{
- ASSERT(strhash);
- mod_hash_destroy_hash(strhash);
-}
-
-
-/*
- * mod_hash_byptr()
- * mod_hash_ptrkey_cmp()
- * Hash and key comparison routines for hashes with pointer keys.
- *
- * mod_hash_create_ptrhash()
- * mod_hash_destroy_ptrhash()
- * Create a hash that uses pointers as keys. This hash algorithm
- * picks an appropriate set of middle bits in the address to hash on
- * based on the size of the hash table and a hint about the size of
- * the items pointed at.
- */
-uint_t
-mod_hash_byptr(void *hash_data, mod_hash_key_t key)
-{
- uintptr_t k = (uintptr_t)key;
- k >>= (int)(uintptr_t)hash_data;
-
- return ((uint_t)k);
-}
-
-int
-mod_hash_ptrkey_cmp(mod_hash_key_t key1, mod_hash_key_t key2)
-{
- uintptr_t k1 = (uintptr_t)key1;
- uintptr_t k2 = (uintptr_t)key2;
- if (k1 > k2)
- return (-1);
- else if (k1 < k2)
- return (1);
- else
- return (0);
-}
-
-mod_hash_t *
-mod_hash_create_ptrhash(char *name, size_t nchains,
- void (*val_dtor)(mod_hash_val_t), size_t key_elem_size)
-{
- size_t rshift;
-
- /*
- * We want to hash on the bits in the middle of the address word
- * Bits far to the right in the word have little significance, and
- * are likely to all look the same (for example, an array of
- * 256-byte structures will have the bottom 8 bits of address
- * words the same). So we want to right-shift each address to
- * ignore the bottom bits.
- *
- * The high bits, which are also unused, will get taken out when
- * mod_hash takes hashkey % nchains.
- */
- rshift = highbit64(key_elem_size);
-
- return mod_hash_create_extended(name, nchains, mod_hash_null_keydtor,
- val_dtor, mod_hash_byptr, (void *)rshift, mod_hash_ptrkey_cmp,
- KM_SLEEP);
-}
-
-void
-mod_hash_destroy_ptrhash(mod_hash_t *hash)
-{
- ASSERT(hash);
- mod_hash_destroy_hash(hash);
-}
-
-/*
- * mod_hash_byid()
- * mod_hash_idkey_cmp()
- * Hash and key comparison routines for hashes with 32-bit unsigned keys.
- *
- * mod_hash_create_idhash()
- * mod_hash_destroy_idhash()
- * mod_hash_iddata_gen()
- * Create a hash that uses numeric keys.
- *
- * The hash algorithm is documented in "Introduction to Algorithms"
- * (Cormen, Leiserson, Rivest); when the hash table is created, it
- * attempts to find the next largest prime above the number of hash
- * slots. The hash index is then this number times the key modulo
- * the hash size, or (key * prime) % nchains.
- */
-uint_t
-mod_hash_byid(void *hash_data, mod_hash_key_t key)
-{
- uint_t kval = (uint_t)(uintptr_t)hash_data;
- return ((uint_t)(uintptr_t)key * (uint_t)kval);
-}
-
-int
-mod_hash_idkey_cmp(mod_hash_key_t key1, mod_hash_key_t key2)
-{
- return ((uint_t)(uintptr_t)key1 - (uint_t)(uintptr_t)key2);
-}
-
-/*
- * Generate the next largest prime number greater than nchains; this value
- * is intended to be later passed in to mod_hash_create_extended() as the
- * hash_data.
- */
-uint_t
-mod_hash_iddata_gen(size_t nchains)
-{
- uint_t kval, i, prime;
-
- /*
- * Pick the first (odd) prime greater than nchains. Make sure kval is
- * odd (so start with nchains +1 or +2 as appropriate).
- */
- kval = (nchains % 2 == 0) ? nchains + 1 : nchains + 2;
-
- for (;;) {
- prime = 1;
- for (i = 3; i * i <= kval; i += 2) {
- if (kval % i == 0)
- prime = 0;
- }
- if (prime == 1)
- break;
- kval += 2;
- }
- return (kval);
-}
-
-mod_hash_t *
-mod_hash_create_idhash(char *name, size_t nchains,
- void (*val_dtor)(mod_hash_val_t))
-{
- uint_t kval = mod_hash_iddata_gen(nchains);
-
- return (mod_hash_create_extended(name, nchains, mod_hash_null_keydtor,
- val_dtor, mod_hash_byid, (void *)(uintptr_t)kval,
- mod_hash_idkey_cmp, KM_SLEEP));
-}
-
-void
-mod_hash_destroy_idhash(mod_hash_t *hash)
-{
- ASSERT(hash);
- mod_hash_destroy_hash(hash);
-}
-
-void
-mod_hash_fini(void)
-{
- mutex_destroy(&mh_head_lock);
-
- if (mh_e_cache) {
- kmem_cache_destroy(mh_e_cache);
- mh_e_cache = NULL;
- }
-}
-
-/*
- * mod_hash_init()
- * sets up globals, etc for mod_hash_*
- */
-void
-mod_hash_init(void)
-{
- ASSERT(mh_e_cache == NULL);
- mh_e_cache = kmem_cache_create("mod_hash_entries",
- sizeof (struct mod_hash_entry), 0, NULL, NULL, NULL, NULL,
- NULL, 0);
-
- mutex_init(&mh_head_lock, NULL, MUTEX_DEFAULT, NULL);
-}
-
-/*
- * mod_hash_create_extended()
- * The full-blown hash creation function.
- *
- * notes:
- * nchains - how many hash slots to create. More hash slots will
- * result in shorter hash chains, but will consume
- * slightly more memory up front.
- * sleep - should be KM_SLEEP or KM_NOSLEEP, to indicate whether
- * to sleep for memory, or fail in low-memory conditions.
- *
- * Fails only if KM_NOSLEEP was specified, and no memory was available.
- */
-mod_hash_t *
-mod_hash_create_extended(
- char *hname, /* descriptive name for hash */
- size_t nchains, /* number of hash slots */
- void (*kdtor)(mod_hash_key_t), /* key destructor */
- void (*vdtor)(mod_hash_val_t), /* value destructor */
- uint_t (*hash_alg)(void *, mod_hash_key_t), /* hash algorithm */
- void *hash_alg_data, /* pass-thru arg for hash_alg */
- int (*keycmp)(mod_hash_key_t, mod_hash_key_t), /* key comparator */
- int sleep) /* whether to sleep for mem */
-{
- mod_hash_t *mod_hash;
- size_t size;
- ASSERT(hname && keycmp && hash_alg && vdtor && kdtor);
-
- if ((mod_hash = kmem_zalloc(MH_SIZE(nchains), sleep)) == NULL)
- return (NULL);
-
- size = strlen(hname) + 1;
- mod_hash->mh_name = kmem_alloc(size, sleep);
- if (mod_hash->mh_name == NULL) {
- kmem_free(mod_hash, MH_SIZE(nchains));
- return (NULL);
- }
- (void) strlcpy(mod_hash->mh_name, hname, size);
-
- rw_init(&mod_hash->mh_contents, NULL, RW_DEFAULT, NULL);
- mod_hash->mh_sleep = sleep;
- mod_hash->mh_nchains = nchains;
- mod_hash->mh_kdtor = kdtor;
- mod_hash->mh_vdtor = vdtor;
- mod_hash->mh_hashalg = hash_alg;
- mod_hash->mh_hashalg_data = hash_alg_data;
- mod_hash->mh_keycmp = keycmp;
-
- /*
- * Link the hash up on the list of hashes
- */
- mutex_enter(&mh_head_lock);
- mod_hash->mh_next = mh_head;
- mh_head = mod_hash;
- mutex_exit(&mh_head_lock);
-
- return (mod_hash);
-}
-
-/*
- * mod_hash_destroy_hash()
- * destroy a hash table, destroying all of its stored keys and values
- * as well.
- */
-void
-mod_hash_destroy_hash(mod_hash_t *hash)
-{
- mod_hash_t *mhp, *mhpp;
-
- mutex_enter(&mh_head_lock);
- /*
- * Remove the hash from the hash list
- */
- if (hash == mh_head) { /* removing 1st list elem */
- mh_head = mh_head->mh_next;
- } else {
- /*
- * mhpp can start out NULL since we know the 1st elem isn't the
- * droid we're looking for.
- */
- mhpp = NULL;
- for (mhp = mh_head; mhp != NULL; mhp = mhp->mh_next) {
- if (mhp == hash) {
- mhpp->mh_next = mhp->mh_next;
- break;
- }
- mhpp = mhp;
- }
- }
- mutex_exit(&mh_head_lock);
-
- /*
- * Clean out keys and values.
- */
- mod_hash_clear(hash);
-
- rw_destroy(&hash->mh_contents);
- kmem_free(hash->mh_name, strlen(hash->mh_name) + 1);
- kmem_free(hash, MH_SIZE(hash->mh_nchains));
-}
-
-/*
- * i_mod_hash()
- * Call the hashing algorithm for this hash table, with the given key.
- */
-uint_t
-i_mod_hash(mod_hash_t *hash, mod_hash_key_t key)
-{
- uint_t h;
- /*
- * Prevent div by 0 problems;
- * Also a nice shortcut when using a hash as a list
- */
- if (hash->mh_nchains == 1)
- return (0);
-
- h = (hash->mh_hashalg)(hash->mh_hashalg_data, key);
- return (h % (hash->mh_nchains - 1));
-}
-
-/*
- * i_mod_hash_insert_nosync()
- * mod_hash_insert()
- * mod_hash_insert_reserve()
- * insert 'val' into the hash table, using 'key' as its key. If 'key' is
- * already a key in the hash, an error will be returned, and the key-val
- * pair will not be inserted. i_mod_hash_insert_nosync() supports a simple
- * handle abstraction, allowing hash entry allocation to be separated from
- * the hash insertion. this abstraction allows simple use of the mod_hash
- * structure in situations where mod_hash_insert() with a KM_SLEEP
- * allocation policy would otherwise be unsafe.
- */
-int
-i_mod_hash_insert_nosync(mod_hash_t *hash, mod_hash_key_t key,
- mod_hash_val_t val, mod_hash_hndl_t handle)
-{
- uint_t hashidx;
- struct mod_hash_entry *entry;
-
- ASSERT(hash);
-
- /*
- * If we've not been given reserved storage, allocate storage directly,
- * using the hash's allocation policy.
- */
- if (handle == (mod_hash_hndl_t)0) {
- entry = kmem_cache_alloc(mh_e_cache, hash->mh_sleep);
- if (entry == NULL) {
- hash->mh_stat.mhs_nomem++;
- return (MH_ERR_NOMEM);
- }
- } else {
- entry = (struct mod_hash_entry *)handle;
- }
-
- hashidx = i_mod_hash(hash, key);
- entry->mhe_key = key;
- entry->mhe_val = val;
- entry->mhe_next = hash->mh_entries[hashidx];
-
- hash->mh_entries[hashidx] = entry;
- hash->mh_stat.mhs_nelems++;
-
- return (0);
-}
-
-int
-mod_hash_insert(mod_hash_t *hash, mod_hash_key_t key, mod_hash_val_t val)
-{
- int res;
- mod_hash_val_t v;
-
- rw_enter(&hash->mh_contents, RW_WRITER);
-
- /*
- * Disallow duplicate keys in the hash
- */
- if (i_mod_hash_find_nosync(hash, key, &v) == 0) {
- rw_exit(&hash->mh_contents);
- hash->mh_stat.mhs_coll++;
- return (MH_ERR_DUPLICATE);
- }
-
- res = i_mod_hash_insert_nosync(hash, key, val, (mod_hash_hndl_t)0);
- rw_exit(&hash->mh_contents);
-
- return (res);
-}
-
-int
-mod_hash_insert_reserve(mod_hash_t *hash, mod_hash_key_t key,
- mod_hash_val_t val, mod_hash_hndl_t handle)
-{
- int res;
- mod_hash_val_t v;
-
- rw_enter(&hash->mh_contents, RW_WRITER);
-
- /*
- * Disallow duplicate keys in the hash
- */
- if (i_mod_hash_find_nosync(hash, key, &v) == 0) {
- rw_exit(&hash->mh_contents);
- hash->mh_stat.mhs_coll++;
- return (MH_ERR_DUPLICATE);
- }
- res = i_mod_hash_insert_nosync(hash, key, val, handle);
- rw_exit(&hash->mh_contents);
-
- return (res);
-}
-
-/*
- * mod_hash_reserve()
- * mod_hash_reserve_nosleep()
- * mod_hash_cancel()
- * Make or cancel a mod_hash_entry_t reservation. Reservations are used in
- * mod_hash_insert_reserve() above.
- */
-int
-mod_hash_reserve(mod_hash_t *hash, mod_hash_hndl_t *handlep)
-{
- *handlep = kmem_cache_alloc(mh_e_cache, hash->mh_sleep);
- if (*handlep == NULL) {
- hash->mh_stat.mhs_nomem++;
- return (MH_ERR_NOMEM);
- }
-
- return (0);
-}
-
-int
-mod_hash_reserve_nosleep(mod_hash_t *hash, mod_hash_hndl_t *handlep)
-{
- *handlep = kmem_cache_alloc(mh_e_cache, KM_NOSLEEP);
- if (*handlep == NULL) {
- hash->mh_stat.mhs_nomem++;
- return (MH_ERR_NOMEM);
- }
-
- return (0);
-
-}
-
-void
-mod_hash_cancel(mod_hash_t *hash, mod_hash_hndl_t *handlep)
-{
- (void) hash;
- kmem_cache_free(mh_e_cache, *handlep);
- *handlep = (mod_hash_hndl_t)0;
-}
-
-/*
- * i_mod_hash_remove_nosync()
- * mod_hash_remove()
- * Remove an element from the hash table.
- */
-int
-i_mod_hash_remove_nosync(mod_hash_t *hash, mod_hash_key_t key,
- mod_hash_val_t *val)
-{
- int hashidx;
- struct mod_hash_entry *e, *ep;
-
- hashidx = i_mod_hash(hash, key);
- ep = NULL; /* e's parent */
-
- for (e = hash->mh_entries[hashidx]; e != NULL; e = e->mhe_next) {
- if (MH_KEYCMP(hash, e->mhe_key, key) == 0)
- break;
- ep = e;
- }
-
- if (e == NULL) { /* not found */
- return (MH_ERR_NOTFOUND);
- }
-
- if (ep == NULL) /* special case 1st element in bucket */
- hash->mh_entries[hashidx] = e->mhe_next;
- else
- ep->mhe_next = e->mhe_next;
-
- /*
- * Clean up resources used by the node's key.
- */
- MH_KEY_DESTROY(hash, e->mhe_key);
-
- *val = e->mhe_val;
- kmem_cache_free(mh_e_cache, e);
- hash->mh_stat.mhs_nelems--;
-
- return (0);
-}
-
-int
-mod_hash_remove(mod_hash_t *hash, mod_hash_key_t key, mod_hash_val_t *val)
-{
- int res;
-
- rw_enter(&hash->mh_contents, RW_WRITER);
- res = i_mod_hash_remove_nosync(hash, key, val);
- rw_exit(&hash->mh_contents);
-
- return (res);
-}
-
-/*
- * mod_hash_replace()
- * atomically remove an existing key-value pair from a hash, and replace
- * the key and value with the ones supplied. The removed key and value
- * (if any) are destroyed.
- */
-int
-mod_hash_replace(mod_hash_t *hash, mod_hash_key_t key, mod_hash_val_t val)
-{
- int res;
- mod_hash_val_t v;
-
- rw_enter(&hash->mh_contents, RW_WRITER);
-
- if (i_mod_hash_remove_nosync(hash, key, &v) == 0) {
- /*
- * mod_hash_remove() takes care of freeing up the key resources.
- */
- MH_VAL_DESTROY(hash, v);
- }
- res = i_mod_hash_insert_nosync(hash, key, val, (mod_hash_hndl_t)0);
-
- rw_exit(&hash->mh_contents);
-
- return (res);
-}
-
-/*
- * mod_hash_destroy()
- * Remove an element from the hash table matching 'key', and destroy it.
- */
-int
-mod_hash_destroy(mod_hash_t *hash, mod_hash_key_t key)
-{
- mod_hash_val_t val;
- int rv;
-
- rw_enter(&hash->mh_contents, RW_WRITER);
-
- if ((rv = i_mod_hash_remove_nosync(hash, key, &val)) == 0) {
- /*
- * mod_hash_remove() takes care of freeing up the key resources.
- */
- MH_VAL_DESTROY(hash, val);
- }
-
- rw_exit(&hash->mh_contents);
- return (rv);
-}
-
-/*
- * i_mod_hash_find_nosync()
- * mod_hash_find()
- * Find a value in the hash table corresponding to the given key.
- */
-int
-i_mod_hash_find_nosync(mod_hash_t *hash, mod_hash_key_t key,
- mod_hash_val_t *val)
-{
- uint_t hashidx;
- struct mod_hash_entry *e;
-
- hashidx = i_mod_hash(hash, key);
-
- for (e = hash->mh_entries[hashidx]; e != NULL; e = e->mhe_next) {
- if (MH_KEYCMP(hash, e->mhe_key, key) == 0) {
- *val = e->mhe_val;
- hash->mh_stat.mhs_hit++;
- return (0);
- }
- }
- hash->mh_stat.mhs_miss++;
- return (MH_ERR_NOTFOUND);
-}
-
-int
-mod_hash_find(mod_hash_t *hash, mod_hash_key_t key, mod_hash_val_t *val)
-{
- int res;
-
- rw_enter(&hash->mh_contents, RW_READER);
- res = i_mod_hash_find_nosync(hash, key, val);
- rw_exit(&hash->mh_contents);
-
- return (res);
-}
-
-int
-mod_hash_find_cb(mod_hash_t *hash, mod_hash_key_t key, mod_hash_val_t *val,
- void (*find_cb)(mod_hash_key_t, mod_hash_val_t))
-{
- int res;
-
- rw_enter(&hash->mh_contents, RW_READER);
- res = i_mod_hash_find_nosync(hash, key, val);
- if (res == 0) {
- find_cb(key, *val);
- }
- rw_exit(&hash->mh_contents);
-
- return (res);
-}
-
-int
-mod_hash_find_cb_rval(mod_hash_t *hash, mod_hash_key_t key, mod_hash_val_t *val,
- int (*find_cb)(mod_hash_key_t, mod_hash_val_t), int *cb_rval)
-{
- int res;
-
- rw_enter(&hash->mh_contents, RW_READER);
- res = i_mod_hash_find_nosync(hash, key, val);
- if (res == 0) {
- *cb_rval = find_cb(key, *val);
- }
- rw_exit(&hash->mh_contents);
-
- return (res);
-}
-
-void
-i_mod_hash_walk_nosync(mod_hash_t *hash,
- uint_t (*callback)(mod_hash_key_t, mod_hash_val_t *, void *), void *arg)
-{
- struct mod_hash_entry *e;
- uint_t hashidx;
- int res = MH_WALK_CONTINUE;
-
- for (hashidx = 0;
- (hashidx < (hash->mh_nchains - 1)) && (res == MH_WALK_CONTINUE);
- hashidx++) {
- e = hash->mh_entries[hashidx];
- while ((e != NULL) && (res == MH_WALK_CONTINUE)) {
- res = callback(e->mhe_key, e->mhe_val, arg);
- e = e->mhe_next;
- }
- }
-}
-
-/*
- * mod_hash_walk()
- * Walks all the elements in the hashtable and invokes the callback
- * function with the key/value pair for each element. The hashtable
- * is locked for readers so the callback function should not attempt
- * to do any updates to the hashable. The callback function should
- * return MH_WALK_CONTINUE to continue walking the hashtable or
- * MH_WALK_TERMINATE to abort the walk of the hashtable.
- */
-void
-mod_hash_walk(mod_hash_t *hash,
- uint_t (*callback)(mod_hash_key_t, mod_hash_val_t *, void *), void *arg)
-{
- rw_enter(&hash->mh_contents, RW_READER);
- i_mod_hash_walk_nosync(hash, callback, arg);
- rw_exit(&hash->mh_contents);
-}
-
-
-/*
- * i_mod_hash_clear_nosync()
- * mod_hash_clear()
- * Clears the given hash table by calling the destructor of every hash
- * element and freeing up all mod_hash_entry's.
- */
-void
-i_mod_hash_clear_nosync(mod_hash_t *hash)
-{
- int i;
- struct mod_hash_entry *e, *old_e;
-
- for (i = 0; i < hash->mh_nchains; i++) {
- e = hash->mh_entries[i];
- while (e != NULL) {
- MH_KEY_DESTROY(hash, e->mhe_key);
- MH_VAL_DESTROY(hash, e->mhe_val);
- old_e = e;
- e = e->mhe_next;
- kmem_cache_free(mh_e_cache, old_e);
- }
- hash->mh_entries[i] = NULL;
- }
- hash->mh_stat.mhs_nelems = 0;
-}
-
-void
-mod_hash_clear(mod_hash_t *hash)
-{
- ASSERT(hash);
- rw_enter(&hash->mh_contents, RW_WRITER);
- i_mod_hash_clear_nosync(hash);
- rw_exit(&hash->mh_contents);
-}