1*abf68d1cSGleb Smirnoff.\" 2*abf68d1cSGleb Smirnoff.\" Copyright (c) 2026 Gleb Smirnoff <glebius@FreeBSD.org> 3*abf68d1cSGleb Smirnoff.\" All rights reserved. 4*abf68d1cSGleb Smirnoff.\" 5*abf68d1cSGleb Smirnoff.\" Redistribution and use in source and binary forms, with or without 6*abf68d1cSGleb Smirnoff.\" modification, are permitted provided that the following conditions 7*abf68d1cSGleb Smirnoff.\" are met: 8*abf68d1cSGleb Smirnoff.\" 1. Redistributions of source code must retain the above copyright 9*abf68d1cSGleb Smirnoff.\" notice, this list of conditions and the following disclaimer. 10*abf68d1cSGleb Smirnoff.\" 2. Redistributions in binary form must reproduce the above copyright 11*abf68d1cSGleb Smirnoff.\" notice, this list of conditions and the following disclaimer in the 12*abf68d1cSGleb Smirnoff.\" documentation and/or other materials provided with the distribution. 13*abf68d1cSGleb Smirnoff.\" 14*abf68d1cSGleb Smirnoff.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*abf68d1cSGleb Smirnoff.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*abf68d1cSGleb Smirnoff.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*abf68d1cSGleb Smirnoff.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*abf68d1cSGleb Smirnoff.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*abf68d1cSGleb Smirnoff.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*abf68d1cSGleb Smirnoff.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*abf68d1cSGleb Smirnoff.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*abf68d1cSGleb Smirnoff.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*abf68d1cSGleb Smirnoff.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*abf68d1cSGleb Smirnoff.\" SUCH DAMAGE. 25*abf68d1cSGleb Smirnoff.\" 26*abf68d1cSGleb Smirnoff.Dd April 12, 2026 27*abf68d1cSGleb Smirnoff.Dt HASHALLOC 9 28*abf68d1cSGleb Smirnoff.Os 29*abf68d1cSGleb Smirnoff.Sh NAME 30*abf68d1cSGleb Smirnoff.Nm hashalloc , 31*abf68d1cSGleb Smirnoff.Nm hashfree 32*abf68d1cSGleb Smirnoff.Nd allocate and free kernel hash tables 33*abf68d1cSGleb Smirnoff.Sh SYNOPSIS 34*abf68d1cSGleb Smirnoff.In sys/malloc.h 35*abf68d1cSGleb Smirnoff.In sys/hash.h 36*abf68d1cSGleb Smirnoff.Ft void * 37*abf68d1cSGleb Smirnoff.Fn hashalloc "struct hashalloc_args *args" 38*abf68d1cSGleb Smirnoff.Ft void 39*abf68d1cSGleb Smirnoff.Fn hashfree "void *table" "struct hashalloc_args *args" 40*abf68d1cSGleb Smirnoff.Sh DESCRIPTION 41*abf68d1cSGleb SmirnoffThe 42*abf68d1cSGleb Smirnoff.Fn hashalloc 43*abf68d1cSGleb Smirnoffand 44*abf68d1cSGleb Smirnoff.Fn hashfree 45*abf68d1cSGleb Smirnofffunctions provide a flexible kernel programming interface (KPI) for allocating 46*abf68d1cSGleb Smirnoffand freeing hash tables with configurable bucket headers. 47*abf68d1cSGleb Smirnoff.Pp 48*abf68d1cSGleb Smirnoff.Pp 49*abf68d1cSGleb Smirnoff.Fn hashalloc 50*abf68d1cSGleb Smirnoffallocates memory for a hash table according to the parameters 51*abf68d1cSGleb Smirnoffspecified in the 52*abf68d1cSGleb Smirnoff.Fa args 53*abf68d1cSGleb Smirnoffstructure. 54*abf68d1cSGleb SmirnoffIt computes an appropriate number of buckets (adjusting 55*abf68d1cSGleb Smirnoff.Fa args->size 56*abf68d1cSGleb Smirnoffas needed based on the requested 57*abf68d1cSGleb Smirnoff.Fa type ) , 58*abf68d1cSGleb Smirnoffallocates memory using 59*abf68d1cSGleb Smirnoff.Xr malloc 9 , 60*abf68d1cSGleb Smirnoffinitializes each bucket's queue header (e.g., 61*abf68d1cSGleb Smirnoff.Vt LIST_HEAD , 62*abf68d1cSGleb Smirnoff.Vt TAILQ_HEAD , 63*abf68d1cSGleb Smirnoffetc.), and, if requested, initializes per-bucket locks. 64*abf68d1cSGleb SmirnoffThe returned memory allocation can be used as an array of header structures 65*abf68d1cSGleb Smirnoffthat start with initialized list header of the requested type followed by 66*abf68d1cSGleb Smirnoffinitialized lock of requested type. 67*abf68d1cSGleb Smirnoff.Pp 68*abf68d1cSGleb Smirnoff.Fn hashfree 69*abf68d1cSGleb Smirnofffrees a hash table previously allocated by 70*abf68d1cSGleb Smirnoff.Fn hashalloc . 71*abf68d1cSGleb Smirnoff.Pp 72*abf68d1cSGleb SmirnoffBoth functions require the caller to pass the same (or equivalent) 73*abf68d1cSGleb Smirnoff.Fa struct hashalloc_args 74*abf68d1cSGleb Smirnoffthat specifies the desired configuration of the hash table and has the 75*abf68d1cSGleb Smirnofffollowing members: 76*abf68d1cSGleb Smirnoff.Bd -literal -offset indent 77*abf68d1cSGleb Smirnoffstruct hashalloc_args { 78*abf68d1cSGleb Smirnoff /* Required arguments */ 79*abf68d1cSGleb Smirnoff size_t size; /* in: desired buckets, out: allocated */ 80*abf68d1cSGleb Smirnoff int mflags; /* malloc(9) flags */ 81*abf68d1cSGleb Smirnoff struct malloc_type *mtype; /* malloc(9) type */ 82*abf68d1cSGleb Smirnoff /* Optional arguments */ 83*abf68d1cSGleb Smirnoff size_t hdrsize; /* bucket header size; 0 = auto */ 84*abf68d1cSGleb Smirnoff enum { 85*abf68d1cSGleb Smirnoff HASH_TYPE_POWER2, 86*abf68d1cSGleb Smirnoff HASH_TYPE_PRIME, 87*abf68d1cSGleb Smirnoff } type; /* default HASH_TYPE_POWER2 */ 88*abf68d1cSGleb Smirnoff enum { 89*abf68d1cSGleb Smirnoff HASH_HEAD_LIST, 90*abf68d1cSGleb Smirnoff HASH_HEAD_CK_LIST, 91*abf68d1cSGleb Smirnoff HASH_HEAD_SLIST, 92*abf68d1cSGleb Smirnoff HASH_HEAD_CK_SLIST, 93*abf68d1cSGleb Smirnoff HASH_HEAD_STAILQ, 94*abf68d1cSGleb Smirnoff HASH_HEAD_CK_STAILQ, 95*abf68d1cSGleb Smirnoff HASH_HEAD_TAILQ, 96*abf68d1cSGleb Smirnoff } head; /* default HASH_HEAD_LIST */ 97*abf68d1cSGleb Smirnoff enum { 98*abf68d1cSGleb Smirnoff HASH_LOCK_NONE, 99*abf68d1cSGleb Smirnoff HASH_LOCK_MTX, 100*abf68d1cSGleb Smirnoff HASH_LOCK_RWLOCK, 101*abf68d1cSGleb Smirnoff HASH_LOCK_SX, 102*abf68d1cSGleb Smirnoff HASH_LOCK_RMLOCK, 103*abf68d1cSGleb Smirnoff HASH_LOCK_RMSLOCK, 104*abf68d1cSGleb Smirnoff } lock; /* default HASH_LOCK_NONE */ 105*abf68d1cSGleb Smirnoff int lopts; /* lock init options */ 106*abf68d1cSGleb Smirnoff const char *lname; /* lock name */ 107*abf68d1cSGleb Smirnoff int (*ctor)(void *); /* bucket constructor */ 108*abf68d1cSGleb Smirnoff void (*dtor)(void *); /* bucket destructor */ 109*abf68d1cSGleb Smirnoff /* Returned arguments */ 110*abf68d1cSGleb Smirnoff int error; /* error code in case of failure */ 111*abf68d1cSGleb Smirnoff}; 112*abf68d1cSGleb Smirnoff.Ed 113*abf68d1cSGleb Smirnoff.Pp 114*abf68d1cSGleb SmirnoffArgument members 115*abf68d1cSGleb Smirnoff.Fa size , 116*abf68d1cSGleb Smirnoff.Fa mflags 117*abf68d1cSGleb Smirnoffand 118*abf68d1cSGleb Smirnoff.Fa mtype 119*abf68d1cSGleb Smirnoffare required for the 120*abf68d1cSGleb Smirnoff.Fn hashalloc . 121*abf68d1cSGleb SmirnoffThe argument 122*abf68d1cSGleb Smirnoff.Fa size , 123*abf68d1cSGleb Smirnoffas filled by earlier call to 124*abf68d1cSGleb Smirnoff.Fn hashalloc , 125*abf68d1cSGleb Smirnoffand 126*abf68d1cSGleb Smirnoff.Fa mtype 127*abf68d1cSGleb Smirnoffare required for the 128*abf68d1cSGleb Smirnoff.Fn hashfree . 129*abf68d1cSGleb SmirnoffThe rest of arguments are optional and have reasonable defaults. 130*abf68d1cSGleb SmirnoffA hash table that was allocated with a non-default allocation arguments shall 131*abf68d1cSGleb Smirnoffpass the same arguments to 132*abf68d1cSGleb Smirnoff.Fn hashfree . 133*abf68d1cSGleb SmirnoffThe structure shall be initialized with sparse C99 initializer as it may 134*abf68d1cSGleb Smirnoffcontain opaque extension members. 135*abf68d1cSGleb SmirnoffThe structure may be allocated on the stack of a caller. 136*abf68d1cSGleb Smirnoff.Bl -tag -width ".Fa hdrsize" 137*abf68d1cSGleb Smirnoff.It Fa size 138*abf68d1cSGleb SmirnoffDesired number of buckets for 139*abf68d1cSGleb Smirnoff.Fn hashalloc . 140*abf68d1cSGleb SmirnoffUpon a successful return 141*abf68d1cSGleb Smirnoff.Fn hashalloc 142*abf68d1cSGleb Smirnoffsets this member to the actual number allocated 143*abf68d1cSGleb Smirnoff(may be rounded up to power-of-2 or nearest prime). 144*abf68d1cSGleb SmirnoffThe value returned by 145*abf68d1cSGleb Smirnoff.Fn hashalloc 146*abf68d1cSGleb Smirnoffshall be later supplied to the 147*abf68d1cSGleb Smirnoff.Fn hashfree . 148*abf68d1cSGleb Smirnoff.It Fa mflags , Fa mtype 149*abf68d1cSGleb SmirnoffPassed directly to 150*abf68d1cSGleb Smirnoff.Xr malloc 9 . 151*abf68d1cSGleb Smirnoff.It Fa hdrsize 152*abf68d1cSGleb SmirnoffOptional member that allows the caller to set a different (increased) size 153*abf68d1cSGleb Smirnoffof a bucket header. 154*abf68d1cSGleb Smirnoff.It Fa type 155*abf68d1cSGleb SmirnoffBucket count policy: 156*abf68d1cSGleb Smirnoff.Bl -tag -width ".Dv HASH_TYPE_POWER2" 157*abf68d1cSGleb Smirnoff.It Dv HASH_TYPE_POWER2 158*abf68d1cSGleb SmirnoffRounded up to largest power of two less than or equal to argument 159*abf68d1cSGleb Smirnoff.Fa size . 160*abf68d1cSGleb Smirnoff.It Dv HASH_TYPE_PRIME 161*abf68d1cSGleb SmirnoffSized to the largest prime number less than or equal to argument 162*abf68d1cSGleb Smirnoff.Fa size . 163*abf68d1cSGleb Smirnoff.El 164*abf68d1cSGleb Smirnoff.Pp 165*abf68d1cSGleb SmirnoffDefault is 166*abf68d1cSGleb Smirnoff.Dv HASH_TYPE_POWER2 . 167*abf68d1cSGleb Smirnoff.It Fa head 168*abf68d1cSGleb SmirnoffQueue header type for each bucket, a 169*abf68d1cSGleb Smirnoff.Xr queue 3 170*abf68d1cSGleb Smirnoffor 171*abf68d1cSGleb SmirnoffConcurrency-kit (CK) type. 172*abf68d1cSGleb Smirnoff.Bl -tag -width ".Dv HASH_HEAD_CK_STAILQ" 173*abf68d1cSGleb Smirnoff.It Dv HASH_HEAD_LIST 174*abf68d1cSGleb Smirnoff.Xr queue 3 175*abf68d1cSGleb Smirnoff.Fd LIST_HEAD 176*abf68d1cSGleb Smirnoff.It Dv HASH_HEAD_CK_LIST 177*abf68d1cSGleb SmirnoffConcurrency-kit 178*abf68d1cSGleb Smirnoff.Fd CK_LIST_HEAD 179*abf68d1cSGleb Smirnoff.It Dv HASH_HEAD_SLIST 180*abf68d1cSGleb Smirnoff.Xr queue 3 181*abf68d1cSGleb Smirnoff.Fd SLIST_HEAD 182*abf68d1cSGleb Smirnoff.It Dv HASH_HEAD_CK_SLIST 183*abf68d1cSGleb SmirnoffConcurrency-kit 184*abf68d1cSGleb Smirnoff.Fd CK_SLIST_HEAD 185*abf68d1cSGleb Smirnoff.It Dv HASH_HEAD_STAILQ 186*abf68d1cSGleb Smirnoff.Xr queue 3 187*abf68d1cSGleb Smirnoff.Fd STAILQ_HEAD 188*abf68d1cSGleb Smirnoff.It Dv HASH_HEAD_CK_STAILQ 189*abf68d1cSGleb SmirnoffConcurrency-kit 190*abf68d1cSGleb Smirnoff.Fd CK_STAILQ_HEAD 191*abf68d1cSGleb Smirnoff.It Dv HASH_HEAD_TAILQ 192*abf68d1cSGleb Smirnoff.Xr queue 3 193*abf68d1cSGleb Smirnoff.Fd TAILQ_HEAD 194*abf68d1cSGleb Smirnoff.El 195*abf68d1cSGleb Smirnoff.Pp 196*abf68d1cSGleb SmirnoffDefault is 197*abf68d1cSGleb Smirnoff.Dv HASH_HEAD_LIST . 198*abf68d1cSGleb Smirnoff.It Fa lock 199*abf68d1cSGleb SmirnoffSynchronization: 200*abf68d1cSGleb Smirnoff.Bl -tag -width ".Dv HASH_LOCK_RWLOCK" 201*abf68d1cSGleb Smirnoff.It Dv HASH_LOCK_NONE 202*abf68d1cSGleb SmirnoffNo per-bucket lock. 203*abf68d1cSGleb Smirnoff.It Dv HASH_LOCK_MTX 204*abf68d1cSGleb SmirnoffPer-bucket 205*abf68d1cSGleb Smirnoff.Xr mutex 9 . 206*abf68d1cSGleb Smirnoff.It Dv HASH_LOCK_RWLOCK 207*abf68d1cSGleb SmirnoffPer-bucket 208*abf68d1cSGleb Smirnoff.Xr rwlock 9 . 209*abf68d1cSGleb Smirnoff.It Dv HASH_LOCK_SX 210*abf68d1cSGleb SmirnoffPer-bucket 211*abf68d1cSGleb Smirnoff.Xr sx 9 . 212*abf68d1cSGleb Smirnoff.It Dv HASH_LOCK_RMLOCK 213*abf68d1cSGleb SmirnoffPer-bucket 214*abf68d1cSGleb Smirnoff.Xr rmlock 9 . 215*abf68d1cSGleb Smirnoff.It Dv HASH_LOCK_RMSLOCK 216*abf68d1cSGleb SmirnoffPer-bucket sleepable (rms) 217*abf68d1cSGleb Smirnoff.Xr rmlock 9 . 218*abf68d1cSGleb Smirnoff.El 219*abf68d1cSGleb Smirnoff.Pp 220*abf68d1cSGleb SmirnoffDefault is 221*abf68d1cSGleb Smirnoff.Dv HASH_LOCK_NONE . 222*abf68d1cSGleb Smirnoff.It Fa lopts 223*abf68d1cSGleb SmirnoffOptions passed to 224*abf68d1cSGleb Smirnoff.Xr mtx_init 9 , 225*abf68d1cSGleb Smirnoff.Xr rw_init 9 , 226*abf68d1cSGleb Smirnoff.Xr sx_init 9 , 227*abf68d1cSGleb Smirnoff.Xr rm_init 9 228*abf68d1cSGleb Smirnoffor 229*abf68d1cSGleb Smirnoff.Xr rms_init 9 230*abf68d1cSGleb Smirnoff(if locking is enabled). 231*abf68d1cSGleb Smirnoff.It Fa lname 232*abf68d1cSGleb SmirnoffLock name. 233*abf68d1cSGleb SmirnoffThis member is required unless 234*abf68d1cSGleb Smirnoff.Fa lock 235*abf68d1cSGleb Smirnoffis 236*abf68d1cSGleb Smirnoff.Dv HASH_LOCK_NONE . 237*abf68d1cSGleb Smirnoff.It Fa ctor 238*abf68d1cSGleb SmirnoffOptional constructor to be called by 239*abf68d1cSGleb Smirnoff.Fn hashalloc 240*abf68d1cSGleb Smirnofffor each bucket after list header and lock initialization. 241*abf68d1cSGleb SmirnoffMay fail with error code, yielding in a failure of 242*abf68d1cSGleb Smirnoff.Fn hashalloc . 243*abf68d1cSGleb Smirnoff.It Fa dtor 244*abf68d1cSGleb SmirnoffOptional destructor to be called by 245*abf68d1cSGleb Smirnoff.Fn hashfree 246*abf68d1cSGleb Smirnofffor each bucket before lock destructors and list emptyness checks. 247*abf68d1cSGleb Smirnoff.El 248*abf68d1cSGleb Smirnoff.Sh RETURN VALUES 249*abf68d1cSGleb Smirnoff.Fn hashalloc 250*abf68d1cSGleb Smirnoffreturns a pointer to the allocated and initialized hash table on success, or 251*abf68d1cSGleb Smirnoff.Dv NULL 252*abf68d1cSGleb Smirnoffon memory allocation failure or constructor failure. 253*abf68d1cSGleb SmirnoffThe 254*abf68d1cSGleb Smirnoff.Fa error 255*abf68d1cSGleb Smirnoffmember of 256*abf68d1cSGleb Smirnoff.Fa args 257*abf68d1cSGleb Smirnoffis set to appropriate error code. 258*abf68d1cSGleb SmirnoffWhen 259*abf68d1cSGleb Smirnoff.Fa mflags 260*abf68d1cSGleb Smirnoffin 261*abf68d1cSGleb Smirnoff.Fa args 262*abf68d1cSGleb Smirnoffcontain the 263*abf68d1cSGleb Smirnoff.Va M_WAITOK 264*abf68d1cSGleb Smirnoffflag and the 265*abf68d1cSGleb Smirnoff.Fa ctor 266*abf68d1cSGleb Smirnoffis either NULL or never fails, then 267*abf68d1cSGleb Smirnoff.Fn hashalloc 268*abf68d1cSGleb Smirnoffnever fails. 269*abf68d1cSGleb Smirnoff.Sh EXAMPLES 270*abf68d1cSGleb SmirnoffA simple mutex-protected hash table using TAILQ buckets: 271*abf68d1cSGleb Smirnoff.Bd -literal -offset indent 272*abf68d1cSGleb Smirnoffstruct bucket { 273*abf68d1cSGleb Smirnoff TAILQ_HEAD(, foo) head; 274*abf68d1cSGleb Smirnoff struct mtx lock; 275*abf68d1cSGleb Smirnoff} *table; 276*abf68d1cSGleb Smirnoff 277*abf68d1cSGleb Smirnoffstruct hashalloc_args args = { 278*abf68d1cSGleb Smirnoff .size = 9000, 279*abf68d1cSGleb Smirnoff .mflags = M_WAITOK, 280*abf68d1cSGleb Smirnoff .mtype = M_FOO, 281*abf68d1cSGleb Smirnoff .head = HASH_HEAD_TAILQ, 282*abf68d1cSGleb Smirnoff .lock = HASH_LOCK_MTX, 283*abf68d1cSGleb Smirnoff .lopts = MTX_DEF, 284*abf68d1cSGleb Smirnoff .lname = "bucket of foo", 285*abf68d1cSGleb Smirnoff}; 286*abf68d1cSGleb Smirnoff 287*abf68d1cSGleb Smirnofftable = hashalloc(&args); 288*abf68d1cSGleb Smirnoff/* Use table as array of struct bucket ... */ 289*abf68d1cSGleb Smirnoffmtx_lock(&table[hash].lock); 290*abf68d1cSGleb SmirnoffTAILQ_INSERT_HEAD(&table[hash].head, foo, next); 291*abf68d1cSGleb Smirnoff 292*abf68d1cSGleb Smirnoff/* Later */ 293*abf68d1cSGleb Smirnoffhashfree(table, &args); 294*abf68d1cSGleb Smirnoff.Ed 295*abf68d1cSGleb Smirnoff.Sh SEE ALSO 296*abf68d1cSGleb Smirnoff.Xr malloc 9 , 297*abf68d1cSGleb Smirnoff.Xr mutex 9 , 298*abf68d1cSGleb Smirnoff.Xr rmlock 9 , 299*abf68d1cSGleb Smirnoff.Xr rwlock 9 , 300*abf68d1cSGleb Smirnoff.Xr sx 9 , 301*abf68d1cSGleb Smirnoff.Xr queue 3 302*abf68d1cSGleb Smirnoff.Sh HISTORY 303*abf68d1cSGleb SmirnoffThe 304*abf68d1cSGleb Smirnoff.Nm 305*abf68d1cSGleb SmirnoffKPI first appeared in 306*abf68d1cSGleb Smirnoff.Fx 16.0 . 307*abf68d1cSGleb SmirnoffIt supersedes older interface 308*abf68d1cSGleb Smirnoff.Fn hashinit , 309*abf68d1cSGleb Smirnoffthat was available since 310*abf68d1cSGleb Smirnoff.Bx 4.4 , 311*abf68d1cSGleb Smirnoffby offering greater control over the hash table structure and locking 312*abf68d1cSGleb Smirnoffstrategy. 313*abf68d1cSGleb Smirnoff.Sh AUTHORS 314*abf68d1cSGleb Smirnoff.An Gleb Smirnoff Aq Mt glebius@FreeBSD.org 315