1 1.13 andvar /* $NetBSD: subr_hash.c,v 1.13 2025/08/06 11:11:34 andvar Exp $ */ 2 1.1 pooka 3 1.1 pooka /* 4 1.1 pooka * Copyright (c) 1982, 1986, 1991, 1993 5 1.1 pooka * The Regents of the University of California. All rights reserved. 6 1.1 pooka * (c) UNIX System Laboratories, Inc. 7 1.1 pooka * All or some portions of this file are derived from material licensed 8 1.1 pooka * to the University of California by American Telephone and Telegraph 9 1.1 pooka * Co. or Unix System Laboratories, Inc. and are reproduced herein with 10 1.1 pooka * the permission of UNIX System Laboratories, Inc. 11 1.1 pooka * 12 1.1 pooka * Redistribution and use in source and binary forms, with or without 13 1.1 pooka * modification, are permitted provided that the following conditions 14 1.1 pooka * are met: 15 1.1 pooka * 1. Redistributions of source code must retain the above copyright 16 1.1 pooka * notice, this list of conditions and the following disclaimer. 17 1.1 pooka * 2. Redistributions in binary form must reproduce the above copyright 18 1.1 pooka * notice, this list of conditions and the following disclaimer in the 19 1.1 pooka * documentation and/or other materials provided with the distribution. 20 1.1 pooka * 3. Neither the name of the University nor the names of its contributors 21 1.1 pooka * may be used to endorse or promote products derived from this software 22 1.1 pooka * without specific prior written permission. 23 1.1 pooka * 24 1.1 pooka * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 1.1 pooka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 1.1 pooka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 1.1 pooka * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 1.1 pooka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 1.1 pooka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 1.1 pooka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 1.1 pooka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 1.1 pooka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 1.1 pooka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 1.1 pooka * SUCH DAMAGE. 35 1.1 pooka * 36 1.1 pooka * @(#)kern_subr.c 8.4 (Berkeley) 2/14/95 37 1.1 pooka */ 38 1.1 pooka 39 1.1 pooka #include <sys/cdefs.h> 40 1.13 andvar __KERNEL_RCSID(0, "$NetBSD: subr_hash.c,v 1.13 2025/08/06 11:11:34 andvar Exp $"); 41 1.1 pooka 42 1.1 pooka #include <sys/param.h> 43 1.6 rmind #include <sys/bitops.h> 44 1.3 ad #include <sys/kmem.h> 45 1.1 pooka #include <sys/systm.h> 46 1.7 ozaki #include <sys/pslist.h> 47 1.8 simonb #include <sys/rwlock.h> 48 1.8 simonb #include <sys/sysctl.h> 49 1.8 simonb 50 1.8 simonb static int hashstat_sysctl(SYSCTLFN_PROTO); 51 1.1 pooka 52 1.5 rmind static size_t 53 1.5 rmind hash_list_size(enum hashtype htype) 54 1.5 rmind { 55 1.5 rmind LIST_HEAD(, generic) *hashtbl_list; 56 1.5 rmind SLIST_HEAD(, generic) *hashtbl_slist; 57 1.5 rmind TAILQ_HEAD(, generic) *hashtbl_tailq; 58 1.7 ozaki struct pslist_head *hashtbl_pslist; 59 1.5 rmind size_t esize; 60 1.5 rmind 61 1.5 rmind switch (htype) { 62 1.5 rmind case HASH_LIST: 63 1.5 rmind esize = sizeof(*hashtbl_list); 64 1.5 rmind break; 65 1.7 ozaki case HASH_PSLIST: 66 1.7 ozaki esize = sizeof(*hashtbl_pslist); 67 1.7 ozaki break; 68 1.5 rmind case HASH_SLIST: 69 1.5 rmind esize = sizeof(*hashtbl_slist); 70 1.5 rmind break; 71 1.5 rmind case HASH_TAILQ: 72 1.5 rmind esize = sizeof(*hashtbl_tailq); 73 1.5 rmind break; 74 1.5 rmind default: 75 1.5 rmind panic("hashdone: invalid table type"); 76 1.5 rmind } 77 1.5 rmind return esize; 78 1.5 rmind } 79 1.5 rmind 80 1.1 pooka /* 81 1.1 pooka * General routine to allocate a hash table. 82 1.1 pooka * Allocate enough memory to hold at least `elements' list-head pointers. 83 1.1 pooka * Return a pointer to the allocated space and set *hashmask to a pattern 84 1.1 pooka * suitable for masking a value to use as an index into the returned array. 85 1.1 pooka */ 86 1.1 pooka void * 87 1.3 ad hashinit(u_int elements, enum hashtype htype, bool waitok, u_long *hashmask) 88 1.1 pooka { 89 1.1 pooka LIST_HEAD(, generic) *hashtbl_list; 90 1.2 rmind SLIST_HEAD(, generic) *hashtbl_slist; 91 1.1 pooka TAILQ_HEAD(, generic) *hashtbl_tailq; 92 1.7 ozaki struct pslist_head *hashtbl_pslist; 93 1.5 rmind u_long hashsize, i; 94 1.1 pooka size_t esize; 95 1.1 pooka void *p; 96 1.5 rmind 97 1.5 rmind KASSERT(elements > 0); 98 1.4 christos 99 1.4 christos #define MAXELEMENTS (1U << ((sizeof(elements) * NBBY) - 1)) 100 1.4 christos if (elements > MAXELEMENTS) 101 1.4 christos elements = MAXELEMENTS; 102 1.4 christos 103 1.6 rmind hashsize = 1UL << (ilog2(elements - 1) + 1); 104 1.6 rmind esize = hash_list_size(htype); 105 1.1 pooka 106 1.5 rmind p = kmem_alloc(hashsize * esize, waitok ? KM_SLEEP : KM_NOSLEEP); 107 1.3 ad if (p == NULL) 108 1.5 rmind return NULL; 109 1.1 pooka 110 1.1 pooka switch (htype) { 111 1.1 pooka case HASH_LIST: 112 1.1 pooka hashtbl_list = p; 113 1.1 pooka for (i = 0; i < hashsize; i++) 114 1.1 pooka LIST_INIT(&hashtbl_list[i]); 115 1.1 pooka break; 116 1.7 ozaki case HASH_PSLIST: 117 1.7 ozaki hashtbl_pslist = p; 118 1.7 ozaki for (i = 0; i < hashsize; i++) 119 1.7 ozaki PSLIST_INIT(&hashtbl_pslist[i]); 120 1.7 ozaki break; 121 1.2 rmind case HASH_SLIST: 122 1.2 rmind hashtbl_slist = p; 123 1.2 rmind for (i = 0; i < hashsize; i++) 124 1.2 rmind SLIST_INIT(&hashtbl_slist[i]); 125 1.2 rmind break; 126 1.1 pooka case HASH_TAILQ: 127 1.1 pooka hashtbl_tailq = p; 128 1.1 pooka for (i = 0; i < hashsize; i++) 129 1.1 pooka TAILQ_INIT(&hashtbl_tailq[i]); 130 1.1 pooka break; 131 1.1 pooka } 132 1.1 pooka *hashmask = hashsize - 1; 133 1.5 rmind return p; 134 1.1 pooka } 135 1.1 pooka 136 1.1 pooka /* 137 1.13 andvar * Free memory from hash table previously allocated via hashinit(). 138 1.1 pooka */ 139 1.1 pooka void 140 1.3 ad hashdone(void *hashtbl, enum hashtype htype, u_long hashmask) 141 1.1 pooka { 142 1.5 rmind const size_t esize = hash_list_size(htype); 143 1.3 ad kmem_free(hashtbl, esize * (hashmask + 1)); 144 1.1 pooka } 145 1.8 simonb 146 1.8 simonb /* 147 1.8 simonb * Support for hash statistics (vmstat -H / vmstat -h hashname). 148 1.8 simonb */ 149 1.8 simonb 150 1.8 simonb struct hashstat { 151 1.8 simonb const char *hs_name; 152 1.8 simonb hashstat_func_t hs_func; 153 1.8 simonb TAILQ_ENTRY(hashstat) hs_next; 154 1.8 simonb }; 155 1.8 simonb TAILQ_HEAD(, hashstat) hashstat_list = 156 1.8 simonb TAILQ_HEAD_INITIALIZER(hashstat_list); 157 1.8 simonb static krwlock_t hashstat_lock; 158 1.8 simonb 159 1.8 simonb void 160 1.8 simonb hashstat_register(const char *name, hashstat_func_t func) 161 1.8 simonb { 162 1.8 simonb struct hashstat *hs; 163 1.8 simonb 164 1.8 simonb hs = kmem_alloc(sizeof(*hs), KM_SLEEP); 165 1.8 simonb 166 1.8 simonb hs->hs_name = name; 167 1.8 simonb hs->hs_func = func; 168 1.8 simonb 169 1.8 simonb rw_enter(&hashstat_lock, RW_WRITER); 170 1.8 simonb TAILQ_INSERT_TAIL(&hashstat_list, hs, hs_next); 171 1.8 simonb rw_exit(&hashstat_lock); 172 1.8 simonb } 173 1.8 simonb 174 1.8 simonb /* 175 1.8 simonb * sysctl support for returning kernel hash statistics. 176 1.8 simonb * 177 1.8 simonb * We (ab)use CTL_DESCRIBE and CTL_QUERY: 178 1.8 simonb * When passed an OID of CTL_DESCRIBE, return a list and description 179 1.8 simonb * of the available hashes. 180 1.8 simonb * When passed an OID of CTL_QUERY, use the hash name passed in the 181 1.8 simonb * "new" hash input as the name of a single hash to return stats on. 182 1.8 simonb */ 183 1.8 simonb static int 184 1.8 simonb hashstat_sysctl(SYSCTLFN_ARGS) 185 1.8 simonb { 186 1.8 simonb struct hashstat_sysctl hs; 187 1.8 simonb struct hashstat *hash; 188 1.8 simonb char queryname[SYSCTL_NAMELEN]; 189 1.8 simonb size_t written; 190 1.8 simonb bool fill, query; 191 1.8 simonb int error; 192 1.8 simonb 193 1.8 simonb if (oldp == NULL) { 194 1.8 simonb *oldlenp = 0; 195 1.8 simonb TAILQ_FOREACH(hash, &hashstat_list, hs_next) 196 1.8 simonb *oldlenp += sizeof(hs); 197 1.8 simonb return 0; 198 1.8 simonb } 199 1.8 simonb 200 1.8 simonb error = 0; 201 1.8 simonb written = 0; 202 1.8 simonb 203 1.8 simonb if (namelen > 0 && name[0] == CTL_DESCRIBE) 204 1.8 simonb fill = false; 205 1.8 simonb else 206 1.8 simonb fill = true; 207 1.8 simonb 208 1.8 simonb if (namelen > 0 && name[0] == CTL_QUERY) { 209 1.8 simonb const struct hashstat_sysctl *h = newp; 210 1.10 christos size_t s; 211 1.8 simonb 212 1.8 simonb if (h == NULL) { 213 1.8 simonb /* Can't QUERY one hash without supplying the hash name. */ 214 1.8 simonb return EINVAL; 215 1.8 simonb } 216 1.8 simonb query = true; 217 1.10 christos error = sysctl_copyinstr(l, h->hash_name, queryname, 218 1.10 christos sizeof(queryname), &s); 219 1.10 christos if (error) 220 1.10 christos return error; 221 1.8 simonb } else { 222 1.8 simonb query = false; 223 1.8 simonb } 224 1.8 simonb 225 1.8 simonb sysctl_unlock(); 226 1.8 simonb rw_enter(&hashstat_lock, RW_READER); 227 1.8 simonb TAILQ_FOREACH(hash, &hashstat_list, hs_next) { 228 1.9 simonb if (query && (strcmp(hash->hs_name, queryname) != 0)) { 229 1.8 simonb continue; 230 1.8 simonb } 231 1.8 simonb 232 1.8 simonb memset(&hs, 0, sizeof(hs)); 233 1.8 simonb error = hash->hs_func(&hs, fill); 234 1.8 simonb if (error) 235 1.8 simonb break; 236 1.8 simonb 237 1.8 simonb error = sysctl_copyout(l, &hs, oldp, sizeof(hs)); 238 1.8 simonb if (error) 239 1.8 simonb break; 240 1.8 simonb written += sizeof(hs); 241 1.8 simonb oldp = (char *)oldp + sizeof(hs); 242 1.8 simonb } 243 1.8 simonb rw_exit(&hashstat_lock); 244 1.8 simonb sysctl_relock(); 245 1.8 simonb 246 1.12 simonb if (query && written == 0) /* query not found? */ 247 1.12 simonb error = ENOENT; 248 1.12 simonb 249 1.8 simonb *oldlenp = written; 250 1.8 simonb return error; 251 1.8 simonb } 252 1.8 simonb 253 1.8 simonb 254 1.8 simonb SYSCTL_SETUP(sysctl_hash_setup, "sysctl hash stats setup") 255 1.8 simonb { 256 1.8 simonb 257 1.8 simonb rw_init(&hashstat_lock); /* as good a place as any for this */ 258 1.8 simonb 259 1.8 simonb sysctl_createv(NULL, 0, NULL, NULL, 260 1.8 simonb CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 261 1.8 simonb CTLTYPE_STRUCT, 262 1.8 simonb "hashstat", SYSCTL_DESCR("kernel hash statistics"), 263 1.8 simonb hashstat_sysctl, 0, NULL, 0, 264 1.8 simonb CTL_KERN, CTL_CREATE, CTL_EOL); 265 1.8 simonb } 266