xref: /src/share/man/man9/hashalloc.9 (revision abf68d1cf02550c3c0341f5bb90be0d34f655a15)
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