]> git.proxmox.com Git - mirror_zfs.git/commitdiff
module: icp: rip out modhash. Replace the one user with AVL
authorнаб <nabijaczleweli@nabijaczleweli.xyz>
Sat, 25 Dec 2021 00:07:58 +0000 (01:07 +0100)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Wed, 16 Feb 2022 00:24:59 +0000 (16:24 -0800)
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
Closes #12901

lib/libicp/Makefile.am
module/icp/Makefile.in
module/icp/core/kcf_mech_tabs.c
module/icp/illumos-crypto.c
module/icp/include/sys/crypto/impl.h
module/icp/include/sys/modhash.h [deleted file]
module/icp/include/sys/modhash_impl.h [deleted file]
module/icp/os/modhash.c [deleted file]

index 6ebcd4907161832fa58f8d0daf44ade864b7b257..382253f6fa48f08168cc16e7191af2ca87bf112d 100644 (file)
@@ -51,7 +51,6 @@ KERNEL_C = \
        io/aes.c \
        io/sha2_mod.c \
        io/skein_mod.c \
-       os/modhash.c \
        core/kcf_sched.c \
        core/kcf_prov_lib.c \
        core/kcf_callprov.c \
index eb807d0e519aa43d21d9958c479731fd704c2226..72c9ab12adb7ab2606dfab2f21f4d0908fc8fc5c 100644 (file)
@@ -26,7 +26,6 @@ $(MODULE)-objs += spi/kcf_spi.o
 $(MODULE)-objs += io/aes.o
 $(MODULE)-objs += io/sha2_mod.o
 $(MODULE)-objs += io/skein_mod.o
-$(MODULE)-objs += os/modhash.o
 $(MODULE)-objs += algs/modes/cbc.o
 $(MODULE)-objs += algs/modes/ccm.o
 $(MODULE)-objs += algs/modes/ctr.o
index 1cb19896a99aa6424fdcba33bde924f01476fb02..aba3b43e85d64e9d548666d55567b291c2e74577 100644 (file)
@@ -27,7 +27,6 @@
 #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 */
 
@@ -55,9 +54,6 @@
 /*
  * 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)
@@ -72,9 +68,6 @@
  * 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 */
@@ -111,42 +104,22 @@ const kcf_mech_entry_tab_t kcf_mech_tabs_tab[KCF_LAST_OPSCLASS + 1] = {
        {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);
 }
 
 /*
@@ -161,14 +134,9 @@ kcf_init_mech_tabs(void)
        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;
@@ -176,9 +144,7 @@ kcf_init_mech_tabs(void)
                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]);
                        }
                }
        }
@@ -213,10 +179,6 @@ kcf_init_mech_tabs(void)
 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);
 
@@ -226,41 +188,28 @@ kcf_create_mech_entry(kcf_ops_class_t class, const char *mechname)
         * 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);
 }
 
 /*
@@ -299,7 +248,7 @@ kcf_add_mech_provider(short mech_indx,
         * 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;
@@ -325,7 +274,7 @@ kcf_add_mech_provider(short mech_indx,
                        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);
        }
 
@@ -398,7 +347,7 @@ kcf_remove_mech_provider(const char *mech_name, kcf_provider_desc_t *prov_desc)
        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
@@ -484,9 +433,7 @@ kcf_get_mech_entry(crypto_mech_type_t mech_type, kcf_mech_entry_t **mep)
  * 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.
@@ -504,7 +451,15 @@ kcf_get_mech_entry(crypto_mech_type_t mech_type, kcf_mech_entry_t **mep)
 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)
index 5b2820220f2cbc79448c54a754d6d92f3e6f407e..f68f6bc765a25cae3d81614ef33c0cfb423ced33 100644 (file)
@@ -36,7 +36,6 @@
 #include <sys/crypto/api.h>
 #include <sys/crypto/impl.h>
 #include <sys/crypto/sched_impl.h>
-#include <sys/modhash_impl.h>
 #include <sys/crypto/icp.h>
 
 /*
@@ -114,16 +113,12 @@ icp_fini(void)
        kcf_sched_destroy();
        kcf_prov_tab_destroy();
        kcf_destroy_mech_tabs();
-       mod_hash_fini();
 }
 
 /* roughly equivalent to kcf.c: _init() */
 int __init
 icp_init(void)
 {
-       /* initialize the mod hash module */
-       mod_hash_init();
-
        /* initialize the mechanisms tables supported out-of-the-box */
        kcf_init_mech_tabs();
 
index 78fe7eb64ebb48c359c8c3fbba4120f74d0c7cce..172661adc46dd92f119b41405fbbb8607a735db1 100644 (file)
@@ -34,6 +34,7 @@
 #include <sys/crypto/common.h>
 #include <sys/crypto/api.h>
 #include <sys/crypto/spi.h>
+#include <sys/avl.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -245,6 +246,7 @@ typedef     struct kcf_mech_entry {
        crypto_mech_name_t      me_name;        /* mechanism name */
        crypto_mech_type_t      me_mechid;      /* Internal id for mechanism */
        kcf_prov_mech_desc_t    *me_sw_prov;    /* provider */
+       avl_node_t      me_node;
 } kcf_mech_entry_t;
 
 /*
@@ -300,7 +302,7 @@ extern const kcf_mech_entry_tab_t kcf_mech_tabs_tab[];
 
 #define        KCF_MECH2CLASS(mech_type) ((kcf_ops_class_t)((mech_type) >> 32))
 
-#define        KCF_MECH2INDEX(mech_type) ((int)(mech_type))
+#define        KCF_MECH2INDEX(mech_type) ((int)((mech_type) & 0xFFFFFFFF))
 
 #define        KCF_TO_PROV_MECH_INDX(pd, mech_type)                    \
        ((pd)->pd_mech_indx[KCF_MECH2CLASS(mech_type)]          \
diff --git a/module/icp/include/sys/modhash.h b/module/icp/include/sys/modhash.h
deleted file mode 100644 (file)
index 06b52ff..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _SYS_MODHASH_H
-#define        _SYS_MODHASH_H
-
-/*
- * Generic hash implementation for the kernel.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <sys/zfs_context.h>
-
-/*
- * Opaque data types for storing keys and values
- */
-typedef void *mod_hash_val_t;
-typedef void *mod_hash_key_t;
-
-/*
- * Opaque data type for reservation
- */
-typedef void *mod_hash_hndl_t;
-
-/*
- * Opaque type for hash itself.
- */
-struct mod_hash;
-typedef struct mod_hash mod_hash_t;
-
-/*
- * String hash table
- */
-mod_hash_t *mod_hash_create_strhash_nodtr(char *, size_t,
-       void (*)(mod_hash_val_t));
-mod_hash_t *mod_hash_create_strhash(char *, size_t, void (*)(mod_hash_val_t));
-void mod_hash_destroy_strhash(mod_hash_t *);
-int mod_hash_strkey_cmp(mod_hash_key_t, mod_hash_key_t);
-void mod_hash_strkey_dtor(mod_hash_key_t);
-void mod_hash_strval_dtor(mod_hash_val_t);
-uint_t mod_hash_bystr(void *, mod_hash_key_t);
-
-/*
- * Pointer hash table
- */
-mod_hash_t *mod_hash_create_ptrhash(char *, size_t, void (*)(mod_hash_val_t),
-       size_t);
-void mod_hash_destroy_ptrhash(mod_hash_t *);
-int mod_hash_ptrkey_cmp(mod_hash_key_t, mod_hash_key_t);
-uint_t mod_hash_byptr(void *, mod_hash_key_t);
-
-/*
- * ID hash table
- */
-mod_hash_t *mod_hash_create_idhash(char *, size_t, void (*)(mod_hash_val_t));
-void mod_hash_destroy_idhash(mod_hash_t *);
-int mod_hash_idkey_cmp(mod_hash_key_t, mod_hash_key_t);
-uint_t mod_hash_byid(void *, mod_hash_key_t);
-uint_t mod_hash_iddata_gen(size_t);
-
-/*
- * Hash management functions
- */
-mod_hash_t *mod_hash_create_extended(char *, size_t, void (*)(mod_hash_key_t),
-       void (*)(mod_hash_val_t), uint_t (*)(void *, mod_hash_key_t), void *,
-       int (*)(mod_hash_key_t, mod_hash_key_t), int);
-
-void mod_hash_destroy_hash(mod_hash_t *);
-void mod_hash_clear(mod_hash_t *);
-
-/*
- * Null key and value destructors
- */
-void mod_hash_null_keydtor(mod_hash_key_t);
-void mod_hash_null_valdtor(mod_hash_val_t);
-
-/*
- * Basic hash operations
- */
-
-/*
- * Error codes for insert, remove, find, destroy.
- */
-#define        MH_ERR_NOMEM -1
-#define        MH_ERR_DUPLICATE -2
-#define        MH_ERR_NOTFOUND -3
-
-/*
- * Return codes for hash walkers
- */
-#define        MH_WALK_CONTINUE 0
-#define        MH_WALK_TERMINATE 1
-
-/*
- * Basic hash operations
- */
-int mod_hash_insert(mod_hash_t *, mod_hash_key_t, mod_hash_val_t);
-int mod_hash_replace(mod_hash_t *, mod_hash_key_t, mod_hash_val_t);
-int mod_hash_remove(mod_hash_t *, mod_hash_key_t, mod_hash_val_t *);
-int mod_hash_destroy(mod_hash_t *, mod_hash_key_t);
-int mod_hash_find(mod_hash_t *, mod_hash_key_t, mod_hash_val_t *);
-int mod_hash_find_cb(mod_hash_t *, mod_hash_key_t, mod_hash_val_t *,
-       void (*)(mod_hash_key_t, mod_hash_val_t));
-int mod_hash_find_cb_rval(mod_hash_t *, mod_hash_key_t, mod_hash_val_t *,
-       int (*)(mod_hash_key_t, mod_hash_val_t), int *);
-void mod_hash_walk(mod_hash_t *,
-       uint_t (*)(mod_hash_key_t, mod_hash_val_t *, void *), void *);
-
-/*
- * Reserving hash operations
- */
-int mod_hash_reserve(mod_hash_t *, mod_hash_hndl_t *);
-int mod_hash_reserve_nosleep(mod_hash_t *, mod_hash_hndl_t *);
-void mod_hash_cancel(mod_hash_t *, mod_hash_hndl_t *);
-int mod_hash_insert_reserve(mod_hash_t *, mod_hash_key_t, mod_hash_val_t,
-       mod_hash_hndl_t);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_MODHASH_H */
diff --git a/module/icp/include/sys/modhash_impl.h b/module/icp/include/sys/modhash_impl.h
deleted file mode 100644 (file)
index 3130773..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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 2006 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _SYS_MODHASH_IMPL_H
-#define        _SYS_MODHASH_IMPL_H
-
-/*
- * Internal details for the kernel's generic hash implementation.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <sys/zfs_context.h>
-#include <sys/modhash.h>
-
-struct mod_hash_entry {
-       mod_hash_key_t mhe_key;                 /* stored hash key      */
-       mod_hash_val_t mhe_val;                 /* stored hash value    */
-       struct mod_hash_entry *mhe_next;        /* next item in chain   */
-};
-
-struct mod_hash_stat {
-       ulong_t mhs_hit;        /* tried a 'find' and it succeeded */
-       ulong_t mhs_miss;       /* tried a 'find' but it failed */
-       ulong_t mhs_coll;       /* occur when insert fails because of dup's */
-       ulong_t mhs_nelems;     /* total number of stored key/value pairs */
-       ulong_t mhs_nomem;      /* number of times kmem_alloc failed */
-};
-
-struct mod_hash {
-       krwlock_t       mh_contents;    /* lock protecting contents */
-       char            *mh_name;       /* hash name */
-       int             mh_sleep;       /* kmem_alloc flag */
-       size_t          mh_nchains;     /* # of elements in mh_entries */
-
-       /* key and val destructor */
-       void    (*mh_kdtor)(mod_hash_key_t);
-       void    (*mh_vdtor)(mod_hash_val_t);
-
-       /* key comparator */
-       int     (*mh_keycmp)(mod_hash_key_t, mod_hash_key_t);
-
-       /* hash algorithm, and algorithm-private data */
-       uint_t  (*mh_hashalg)(void *, mod_hash_key_t);
-       void    *mh_hashalg_data;
-
-       struct mod_hash *mh_next;       /* next hash in list */
-
-       struct mod_hash_stat mh_stat;
-
-       struct mod_hash_entry *mh_entries[1];
-};
-
-/*
- * MH_SIZE()
- *     Compute the size of a mod_hash_t, in bytes, given the number of
- *     elements it contains.
- */
-#define        MH_SIZE(n) \
-       (sizeof (mod_hash_t) + ((n) - 1) * (sizeof (struct mod_hash_entry *)))
-
-/*
- * Module initialization; called once.
- */
-void mod_hash_fini(void);
-void mod_hash_init(void);
-
-/*
- * Internal routines.  Use directly with care.
- */
-uint_t i_mod_hash(mod_hash_t *, mod_hash_key_t);
-int i_mod_hash_insert_nosync(mod_hash_t *, mod_hash_key_t, mod_hash_val_t,
-    mod_hash_hndl_t);
-int i_mod_hash_remove_nosync(mod_hash_t *, mod_hash_key_t, mod_hash_val_t *);
-int i_mod_hash_find_nosync(mod_hash_t *, mod_hash_key_t, mod_hash_val_t *);
-void i_mod_hash_walk_nosync(mod_hash_t *, uint_t (*)(mod_hash_key_t,
-    mod_hash_val_t *, void *), void *);
-void i_mod_hash_clear_nosync(mod_hash_t *hash);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_MODHASH_IMPL_H */
diff --git a/module/icp/os/modhash.c b/module/icp/os/modhash.c
deleted file mode 100644 (file)
index 8bd0697..0000000
+++ /dev/null
@@ -1,927 +0,0 @@
-/*
- * 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);
-}