xref: /freebsd/sys/kern/subr_unit.c (revision 6fe78ad434557a713ebf5159ac6ac4052ef3da1c)
1f6bde1fdSPoul-Henning Kamp /*-
28a36da99SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
38a36da99SPedro F. Giffuni  *
4f6bde1fdSPoul-Henning Kamp  * Copyright (c) 2004 Poul-Henning Kamp
5f6bde1fdSPoul-Henning Kamp  * All rights reserved.
6f6bde1fdSPoul-Henning Kamp  *
7f6bde1fdSPoul-Henning Kamp  * Redistribution and use in source and binary forms, with or without
8f6bde1fdSPoul-Henning Kamp  * modification, are permitted provided that the following conditions
9f6bde1fdSPoul-Henning Kamp  * are met:
10f6bde1fdSPoul-Henning Kamp  * 1. Redistributions of source code must retain the above copyright
11f6bde1fdSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer.
12f6bde1fdSPoul-Henning Kamp  * 2. Redistributions in binary form must reproduce the above copyright
13f6bde1fdSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer in the
14f6bde1fdSPoul-Henning Kamp  *    documentation and/or other materials provided with the distribution.
15f6bde1fdSPoul-Henning Kamp  *
16f6bde1fdSPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17f6bde1fdSPoul-Henning Kamp  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18f6bde1fdSPoul-Henning Kamp  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19f6bde1fdSPoul-Henning Kamp  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20f6bde1fdSPoul-Henning Kamp  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21f6bde1fdSPoul-Henning Kamp  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22f6bde1fdSPoul-Henning Kamp  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f6bde1fdSPoul-Henning Kamp  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24f6bde1fdSPoul-Henning Kamp  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25f6bde1fdSPoul-Henning Kamp  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26f6bde1fdSPoul-Henning Kamp  * SUCH DAMAGE.
27f6bde1fdSPoul-Henning Kamp  *
28f6bde1fdSPoul-Henning Kamp  * $FreeBSD$
29d9a54d5cSPoul-Henning Kamp  *
30d9a54d5cSPoul-Henning Kamp  *
31d9a54d5cSPoul-Henning Kamp  * Unit number allocation functions.
32f6bde1fdSPoul-Henning Kamp  *
33f6bde1fdSPoul-Henning Kamp  * These functions implement a mixed run-length/bitmap management of unit
34d9a54d5cSPoul-Henning Kamp  * number spaces in a very memory efficient manner.
35f6bde1fdSPoul-Henning Kamp  *
36d9a54d5cSPoul-Henning Kamp  * Allocation policy is always lowest free number first.
37f6bde1fdSPoul-Henning Kamp  *
38d9a54d5cSPoul-Henning Kamp  * A return value of -1 signals that no more unit numbers are available.
39f6bde1fdSPoul-Henning Kamp  *
40d9a54d5cSPoul-Henning Kamp  * There is no cost associated with the range of unitnumbers, so unless
41d9a54d5cSPoul-Henning Kamp  * the resource really is finite, specify INT_MAX to new_unrhdr() and
42d9a54d5cSPoul-Henning Kamp  * forget about checking the return value.
43f6bde1fdSPoul-Henning Kamp  *
44d9a54d5cSPoul-Henning Kamp  * If a mutex is not provided when the unit number space is created, a
45d9a54d5cSPoul-Henning Kamp  * default global mutex is used.  The advantage to passing a mutex in, is
466bccea7cSRebecca Cran  * that the alloc_unrl() function can be called with the mutex already
47d9a54d5cSPoul-Henning Kamp  * held (it will not be released by alloc_unrl()).
48d9a54d5cSPoul-Henning Kamp  *
49d9a54d5cSPoul-Henning Kamp  * The allocation function alloc_unr{l}() never sleeps (but it may block on
50d9a54d5cSPoul-Henning Kamp  * the mutex of course).
51d9a54d5cSPoul-Henning Kamp  *
52d9a54d5cSPoul-Henning Kamp  * Freeing a unit number may require allocating memory, and can therefore
53d9a54d5cSPoul-Henning Kamp  * sleep so the free_unr() function does not come in a pre-locked variant.
54f6bde1fdSPoul-Henning Kamp  *
55f6bde1fdSPoul-Henning Kamp  * A userland test program is included.
56f6bde1fdSPoul-Henning Kamp  *
576bccea7cSRebecca Cran  * Memory usage is a very complex function of the exact allocation
58d9a54d5cSPoul-Henning Kamp  * pattern, but always very compact:
59d9a54d5cSPoul-Henning Kamp  *    * For the very typical case where a single unbroken run of unit
60d9a54d5cSPoul-Henning Kamp  *      numbers are allocated 44 bytes are used on i386.
61d9a54d5cSPoul-Henning Kamp  *    * For a unit number space of 1000 units and the random pattern
62d9a54d5cSPoul-Henning Kamp  *      in the usermode test program included, the worst case usage
63d9a54d5cSPoul-Henning Kamp  *	was 252 bytes on i386 for 500 allocated and 500 free units.
64d9a54d5cSPoul-Henning Kamp  *    * For a unit number space of 10000 units and the random pattern
65d9a54d5cSPoul-Henning Kamp  *      in the usermode test program included, the worst case usage
66d9a54d5cSPoul-Henning Kamp  *	was 798 bytes on i386 for 5000 allocated and 5000 free units.
67d9a54d5cSPoul-Henning Kamp  *    * The worst case is where every other unit number is allocated and
6896240c89SEitan Adler  *	the rest are free.  In that case 44 + N/4 bytes are used where
69d9a54d5cSPoul-Henning Kamp  *	N is the number of the highest unit allocated.
70f6bde1fdSPoul-Henning Kamp  */
71f6bde1fdSPoul-Henning Kamp 
728907f744SAlan Somers #include <sys/param.h>
73f6bde1fdSPoul-Henning Kamp #include <sys/types.h>
74dc872d46SKonstantin Belousov #include <sys/_unrhdr.h>
75f6bde1fdSPoul-Henning Kamp 
76f6bde1fdSPoul-Henning Kamp #ifdef _KERNEL
77f6bde1fdSPoul-Henning Kamp 
78794277daSAlan Somers #include <sys/bitstring.h>
79f6bde1fdSPoul-Henning Kamp #include <sys/malloc.h>
80f6bde1fdSPoul-Henning Kamp #include <sys/kernel.h>
81f6bde1fdSPoul-Henning Kamp #include <sys/systm.h>
82d9a54d5cSPoul-Henning Kamp #include <sys/limits.h>
83d9a54d5cSPoul-Henning Kamp #include <sys/lock.h>
84d9a54d5cSPoul-Henning Kamp #include <sys/mutex.h>
85f6bde1fdSPoul-Henning Kamp 
86f6bde1fdSPoul-Henning Kamp /*
87f6bde1fdSPoul-Henning Kamp  * In theory it would be smarter to allocate the individual blocks
88f6bde1fdSPoul-Henning Kamp  * with the zone allocator, but at this time the expectation is that
89f6bde1fdSPoul-Henning Kamp  * there will typically not even be enough allocations to fill a single
90f6bde1fdSPoul-Henning Kamp  * page, so we stick with malloc for now.
91f6bde1fdSPoul-Henning Kamp  */
92f6bde1fdSPoul-Henning Kamp static MALLOC_DEFINE(M_UNIT, "Unitno", "Unit number allocation");
93f6bde1fdSPoul-Henning Kamp 
94f6bde1fdSPoul-Henning Kamp #define Malloc(foo) malloc(foo, M_UNIT, M_WAITOK | M_ZERO)
95f6bde1fdSPoul-Henning Kamp #define Free(foo) free(foo, M_UNIT)
96f6bde1fdSPoul-Henning Kamp 
97d9a54d5cSPoul-Henning Kamp static struct mtx unitmtx;
98d9a54d5cSPoul-Henning Kamp 
99d9a54d5cSPoul-Henning Kamp MTX_SYSINIT(unit, &unitmtx, "unit# allocation", MTX_DEF);
100d9a54d5cSPoul-Henning Kamp 
101435bef7aSMateusz Guzik #ifdef UNR64_LOCKED
102435bef7aSMateusz Guzik uint64_t
103435bef7aSMateusz Guzik alloc_unr64(struct unrhdr64 *unr64)
104435bef7aSMateusz Guzik {
105435bef7aSMateusz Guzik 	uint64_t item;
106435bef7aSMateusz Guzik 
107435bef7aSMateusz Guzik 	mtx_lock(&unitmtx);
108435bef7aSMateusz Guzik 	item = unr64->counter++;
109435bef7aSMateusz Guzik 	mtx_unlock(&unitmtx);
110435bef7aSMateusz Guzik 	return (item);
111435bef7aSMateusz Guzik }
112435bef7aSMateusz Guzik #endif
113435bef7aSMateusz Guzik 
114f6bde1fdSPoul-Henning Kamp #else /* ...USERLAND */
115f6bde1fdSPoul-Henning Kamp 
116794277daSAlan Somers #include <bitstring.h>
117794277daSAlan Somers #include <err.h>
118794277daSAlan Somers #include <errno.h>
119794277daSAlan Somers #include <getopt.h>
120794277daSAlan Somers #include <stdbool.h>
121f6bde1fdSPoul-Henning Kamp #include <stdio.h>
122f6bde1fdSPoul-Henning Kamp #include <stdlib.h>
123d9a54d5cSPoul-Henning Kamp #include <string.h>
124f6bde1fdSPoul-Henning Kamp 
125f6bde1fdSPoul-Henning Kamp #define KASSERT(cond, arg) \
126f6bde1fdSPoul-Henning Kamp 	do { \
127f6bde1fdSPoul-Henning Kamp 		if (!(cond)) { \
128f6bde1fdSPoul-Henning Kamp 			printf arg; \
129d9a54d5cSPoul-Henning Kamp 			abort(); \
130f6bde1fdSPoul-Henning Kamp 		} \
131f6bde1fdSPoul-Henning Kamp 	} while (0)
132f6bde1fdSPoul-Henning Kamp 
133d9a54d5cSPoul-Henning Kamp static int no_alloc;
134d9a54d5cSPoul-Henning Kamp #define Malloc(foo) _Malloc(foo, __LINE__)
135d9a54d5cSPoul-Henning Kamp static void *
136d9a54d5cSPoul-Henning Kamp _Malloc(size_t foo, int line)
137d9a54d5cSPoul-Henning Kamp {
138d9a54d5cSPoul-Henning Kamp 
139d9a54d5cSPoul-Henning Kamp 	KASSERT(no_alloc == 0, ("malloc in wrong place() line %d", line));
140d9a54d5cSPoul-Henning Kamp 	return (calloc(foo, 1));
141d9a54d5cSPoul-Henning Kamp }
142f6bde1fdSPoul-Henning Kamp #define Free(foo) free(foo)
143f6bde1fdSPoul-Henning Kamp 
144d9a54d5cSPoul-Henning Kamp struct unrhdr;
145d9a54d5cSPoul-Henning Kamp 
146*6fe78ad4SKonstantin Belousov #define	UNR_NO_MTX	((void *)(uintptr_t)-1)
147*6fe78ad4SKonstantin Belousov 
148d9a54d5cSPoul-Henning Kamp struct mtx {
149d9a54d5cSPoul-Henning Kamp 	int	state;
150d9a54d5cSPoul-Henning Kamp } unitmtx;
151d9a54d5cSPoul-Henning Kamp 
152d9a54d5cSPoul-Henning Kamp static void
153d9a54d5cSPoul-Henning Kamp mtx_lock(struct mtx *mp)
154d9a54d5cSPoul-Henning Kamp {
155d9a54d5cSPoul-Henning Kamp 	KASSERT(mp->state == 0, ("mutex already locked"));
156d9a54d5cSPoul-Henning Kamp 	mp->state = 1;
157d9a54d5cSPoul-Henning Kamp }
158d9a54d5cSPoul-Henning Kamp 
159d9a54d5cSPoul-Henning Kamp static void
160d9a54d5cSPoul-Henning Kamp mtx_unlock(struct mtx *mp)
161d9a54d5cSPoul-Henning Kamp {
162d9a54d5cSPoul-Henning Kamp 	KASSERT(mp->state == 1, ("mutex not locked"));
163d9a54d5cSPoul-Henning Kamp 	mp->state = 0;
164d9a54d5cSPoul-Henning Kamp }
165d9a54d5cSPoul-Henning Kamp 
166d9a54d5cSPoul-Henning Kamp #define MA_OWNED	9
167d9a54d5cSPoul-Henning Kamp 
168d9a54d5cSPoul-Henning Kamp static void
169d9a54d5cSPoul-Henning Kamp mtx_assert(struct mtx *mp, int flag)
170d9a54d5cSPoul-Henning Kamp {
171d9a54d5cSPoul-Henning Kamp 	if (flag == MA_OWNED) {
172d9a54d5cSPoul-Henning Kamp 		KASSERT(mp->state == 1, ("mtx_assert(MA_OWNED) not true"));
173d9a54d5cSPoul-Henning Kamp 	}
174d9a54d5cSPoul-Henning Kamp }
175d9a54d5cSPoul-Henning Kamp 
176d9a54d5cSPoul-Henning Kamp #define CTASSERT(foo)
17724e8eaf1SJaakko Heinonen #define WITNESS_WARN(flags, lock, fmt, ...)	(void)0
178d9a54d5cSPoul-Henning Kamp 
179d9a54d5cSPoul-Henning Kamp #endif /* USERLAND */
180f6bde1fdSPoul-Henning Kamp 
181f6bde1fdSPoul-Henning Kamp /*
182f6bde1fdSPoul-Henning Kamp  * This is our basic building block.
183f6bde1fdSPoul-Henning Kamp  *
184f6bde1fdSPoul-Henning Kamp  * It can be used in three different ways depending on the value of the ptr
185f6bde1fdSPoul-Henning Kamp  * element:
186f6bde1fdSPoul-Henning Kamp  *     If ptr is NULL, it represents a run of free items.
187f6bde1fdSPoul-Henning Kamp  *     If ptr points to the unrhdr it represents a run of allocated items.
1888907f744SAlan Somers  *     Otherwise it points to a bitstring of allocated items.
189f6bde1fdSPoul-Henning Kamp  *
190f6bde1fdSPoul-Henning Kamp  * For runs the len field is the length of the run.
191f6bde1fdSPoul-Henning Kamp  * For bitmaps the len field represents the number of allocated items.
192f6bde1fdSPoul-Henning Kamp  *
193f6bde1fdSPoul-Henning Kamp  * The bitmap is the same size as struct unr to optimize memory management.
194f6bde1fdSPoul-Henning Kamp  */
195f6bde1fdSPoul-Henning Kamp struct unr {
196f6bde1fdSPoul-Henning Kamp 	TAILQ_ENTRY(unr)	list;
197f6bde1fdSPoul-Henning Kamp 	u_int			len;
198f6bde1fdSPoul-Henning Kamp 	void			*ptr;
199f6bde1fdSPoul-Henning Kamp };
200f6bde1fdSPoul-Henning Kamp 
201d9a54d5cSPoul-Henning Kamp struct unrb {
2028907f744SAlan Somers 	bitstr_t		map[sizeof(struct unr) / sizeof(bitstr_t)];
203d9a54d5cSPoul-Henning Kamp };
204d9a54d5cSPoul-Henning Kamp 
2058907f744SAlan Somers CTASSERT((sizeof(struct unr) % sizeof(bitstr_t)) == 0);
206d9a54d5cSPoul-Henning Kamp 
2078907f744SAlan Somers /* Number of bits we can store in the bitmap */
2088907f744SAlan Somers #define NBITS (8 * sizeof(((struct unrb*)NULL)->map))
2098907f744SAlan Somers 
2108907f744SAlan Somers /* Is the unrb empty in at least the first len bits? */
2118907f744SAlan Somers static inline bool
2128907f744SAlan Somers ub_empty(struct unrb *ub, int len) {
2138907f744SAlan Somers 	int first_set;
2148907f744SAlan Somers 
2158907f744SAlan Somers 	bit_ffs(ub->map, len, &first_set);
2168907f744SAlan Somers 	return (first_set == -1);
2178907f744SAlan Somers }
2188907f744SAlan Somers 
2198907f744SAlan Somers /* Is the unrb full?  That is, is the number of set elements equal to len? */
2208907f744SAlan Somers static inline bool
2218907f744SAlan Somers ub_full(struct unrb *ub, int len)
2228907f744SAlan Somers {
2238907f744SAlan Somers 	int first_clear;
2248907f744SAlan Somers 
2258907f744SAlan Somers 	bit_ffc(ub->map, len, &first_clear);
2268907f744SAlan Somers 	return (first_clear == -1);
2278907f744SAlan Somers }
2288907f744SAlan Somers 
229f6bde1fdSPoul-Henning Kamp #if defined(DIAGNOSTIC) || !defined(_KERNEL)
230f6bde1fdSPoul-Henning Kamp /*
231f6bde1fdSPoul-Henning Kamp  * Consistency check function.
232f6bde1fdSPoul-Henning Kamp  *
233f6bde1fdSPoul-Henning Kamp  * Checks the internal consistency as well as we can.
234f6bde1fdSPoul-Henning Kamp  *
235f6bde1fdSPoul-Henning Kamp  * Called at all boundaries of this API.
236f6bde1fdSPoul-Henning Kamp  */
237f6bde1fdSPoul-Henning Kamp static void
238f6bde1fdSPoul-Henning Kamp check_unrhdr(struct unrhdr *uh, int line)
239f6bde1fdSPoul-Henning Kamp {
240f6bde1fdSPoul-Henning Kamp 	struct unr *up;
241d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
2421b82e02fSAlan Somers 	int w;
2431b82e02fSAlan Somers 	u_int y, z;
244f6bde1fdSPoul-Henning Kamp 
245d9a54d5cSPoul-Henning Kamp 	y = uh->first;
246f6bde1fdSPoul-Henning Kamp 	z = 0;
247f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
248f6bde1fdSPoul-Henning Kamp 		z++;
249f6bde1fdSPoul-Henning Kamp 		if (up->ptr != uh && up->ptr != NULL) {
250d9a54d5cSPoul-Henning Kamp 			ub = up->ptr;
251d9a54d5cSPoul-Henning Kamp 			KASSERT (up->len <= NBITS,
2528907f744SAlan Somers 			    ("UNR inconsistency: len %u max %zd (line %d)\n",
253d9a54d5cSPoul-Henning Kamp 			    up->len, NBITS, line));
254f6bde1fdSPoul-Henning Kamp 			z++;
255f6bde1fdSPoul-Henning Kamp 			w = 0;
2561b82e02fSAlan Somers 			bit_count(ub->map, 0, up->len, &w);
257d9a54d5cSPoul-Henning Kamp 			y += w;
258d9a54d5cSPoul-Henning Kamp 		} else if (up->ptr != NULL)
259f6bde1fdSPoul-Henning Kamp 			y += up->len;
260f6bde1fdSPoul-Henning Kamp 	}
261f6bde1fdSPoul-Henning Kamp 	KASSERT (y == uh->busy,
262f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: items %u found %u (line %d)\n",
263f6bde1fdSPoul-Henning Kamp 	    uh->busy, y, line));
264f6bde1fdSPoul-Henning Kamp 	KASSERT (z == uh->alloc,
265f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: chunks %u found %u (line %d)\n",
266f6bde1fdSPoul-Henning Kamp 	    uh->alloc, z, line));
267f6bde1fdSPoul-Henning Kamp }
268f6bde1fdSPoul-Henning Kamp 
269f6bde1fdSPoul-Henning Kamp #else
270f6bde1fdSPoul-Henning Kamp 
271f6bde1fdSPoul-Henning Kamp static __inline void
2728907f744SAlan Somers check_unrhdr(struct unrhdr *uh __unused, int line __unused)
273f6bde1fdSPoul-Henning Kamp {
274f6bde1fdSPoul-Henning Kamp 
275f6bde1fdSPoul-Henning Kamp }
276f6bde1fdSPoul-Henning Kamp 
277f6bde1fdSPoul-Henning Kamp #endif
278f6bde1fdSPoul-Henning Kamp 
279f6bde1fdSPoul-Henning Kamp /*
280f6bde1fdSPoul-Henning Kamp  * Userland memory management.  Just use calloc and keep track of how
281f6bde1fdSPoul-Henning Kamp  * many elements we have allocated for check_unrhdr().
282f6bde1fdSPoul-Henning Kamp  */
283f6bde1fdSPoul-Henning Kamp 
284f6bde1fdSPoul-Henning Kamp static __inline void *
285d9a54d5cSPoul-Henning Kamp new_unr(struct unrhdr *uh, void **p1, void **p2)
286f6bde1fdSPoul-Henning Kamp {
287d9a54d5cSPoul-Henning Kamp 	void *p;
288f6bde1fdSPoul-Henning Kamp 
289d9a54d5cSPoul-Henning Kamp 	uh->alloc++;
290d9a54d5cSPoul-Henning Kamp 	KASSERT(*p1 != NULL || *p2 != NULL, ("Out of cached memory"));
291d9a54d5cSPoul-Henning Kamp 	if (*p1 != NULL) {
292d9a54d5cSPoul-Henning Kamp 		p = *p1;
293d9a54d5cSPoul-Henning Kamp 		*p1 = NULL;
294d9a54d5cSPoul-Henning Kamp 		return (p);
295d9a54d5cSPoul-Henning Kamp 	} else {
296d9a54d5cSPoul-Henning Kamp 		p = *p2;
297d9a54d5cSPoul-Henning Kamp 		*p2 = NULL;
298d9a54d5cSPoul-Henning Kamp 		return (p);
299d9a54d5cSPoul-Henning Kamp 	}
300f6bde1fdSPoul-Henning Kamp }
301f6bde1fdSPoul-Henning Kamp 
302f6bde1fdSPoul-Henning Kamp static __inline void
303f6bde1fdSPoul-Henning Kamp delete_unr(struct unrhdr *uh, void *ptr)
304f6bde1fdSPoul-Henning Kamp {
30509828ba9SKonstantin Belousov 	struct unr *up;
306d9a54d5cSPoul-Henning Kamp 
307f6bde1fdSPoul-Henning Kamp 	uh->alloc--;
30809828ba9SKonstantin Belousov 	up = ptr;
30909828ba9SKonstantin Belousov 	TAILQ_INSERT_TAIL(&uh->ppfree, up, list);
31009828ba9SKonstantin Belousov }
31109828ba9SKonstantin Belousov 
31209828ba9SKonstantin Belousov void
31309828ba9SKonstantin Belousov clean_unrhdrl(struct unrhdr *uh)
31409828ba9SKonstantin Belousov {
31509828ba9SKonstantin Belousov 	struct unr *up;
31609828ba9SKonstantin Belousov 
317e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
31809828ba9SKonstantin Belousov 		mtx_assert(uh->mtx, MA_OWNED);
31909828ba9SKonstantin Belousov 	while ((up = TAILQ_FIRST(&uh->ppfree)) != NULL) {
32009828ba9SKonstantin Belousov 		TAILQ_REMOVE(&uh->ppfree, up, list);
321e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
32209828ba9SKonstantin Belousov 			mtx_unlock(uh->mtx);
32309828ba9SKonstantin Belousov 		Free(up);
324e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
32509828ba9SKonstantin Belousov 			mtx_lock(uh->mtx);
32609828ba9SKonstantin Belousov 	}
32709828ba9SKonstantin Belousov 
32809828ba9SKonstantin Belousov }
32909828ba9SKonstantin Belousov 
33009828ba9SKonstantin Belousov void
33109828ba9SKonstantin Belousov clean_unrhdr(struct unrhdr *uh)
33209828ba9SKonstantin Belousov {
33309828ba9SKonstantin Belousov 
334e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
33509828ba9SKonstantin Belousov 		mtx_lock(uh->mtx);
33609828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
337e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
33809828ba9SKonstantin Belousov 		mtx_unlock(uh->mtx);
339f6bde1fdSPoul-Henning Kamp }
340f6bde1fdSPoul-Henning Kamp 
341dc872d46SKonstantin Belousov void
342dc872d46SKonstantin Belousov init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex)
343f6bde1fdSPoul-Henning Kamp {
344f6bde1fdSPoul-Henning Kamp 
345831aa555SJaakko Heinonen 	KASSERT(low >= 0 && low <= high,
346501812f2SJaakko Heinonen 	    ("UNR: use error: new_unrhdr(%d, %d)", low, high));
347e59b940dSKonstantin Belousov 	if (mutex == UNR_NO_MTX)
348e59b940dSKonstantin Belousov 		uh->mtx = NULL;
349e59b940dSKonstantin Belousov 	else if (mutex != NULL)
350d9a54d5cSPoul-Henning Kamp 		uh->mtx = mutex;
351d9a54d5cSPoul-Henning Kamp 	else
352d9a54d5cSPoul-Henning Kamp 		uh->mtx = &unitmtx;
353f6bde1fdSPoul-Henning Kamp 	TAILQ_INIT(&uh->head);
35409828ba9SKonstantin Belousov 	TAILQ_INIT(&uh->ppfree);
355f6bde1fdSPoul-Henning Kamp 	uh->low = low;
356f6bde1fdSPoul-Henning Kamp 	uh->high = high;
357d9a54d5cSPoul-Henning Kamp 	uh->first = 0;
358d9a54d5cSPoul-Henning Kamp 	uh->last = 1 + (high - low);
359c4be460eSKonstantin Belousov 	uh->busy = 0;
360c4be460eSKonstantin Belousov 	uh->alloc = 0;
361f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
362dc872d46SKonstantin Belousov }
363dc872d46SKonstantin Belousov 
364dc872d46SKonstantin Belousov /*
365dc872d46SKonstantin Belousov  * Allocate a new unrheader set.
366dc872d46SKonstantin Belousov  *
367dc872d46SKonstantin Belousov  * Highest and lowest valid values given as parameters.
368dc872d46SKonstantin Belousov  */
369dc872d46SKonstantin Belousov 
370dc872d46SKonstantin Belousov struct unrhdr *
371dc872d46SKonstantin Belousov new_unrhdr(int low, int high, struct mtx *mutex)
372dc872d46SKonstantin Belousov {
373dc872d46SKonstantin Belousov 	struct unrhdr *uh;
374dc872d46SKonstantin Belousov 
375dc872d46SKonstantin Belousov 	uh = Malloc(sizeof *uh);
376dc872d46SKonstantin Belousov 	init_unrhdr(uh, low, high, mutex);
377f6bde1fdSPoul-Henning Kamp 	return (uh);
378f6bde1fdSPoul-Henning Kamp }
379f6bde1fdSPoul-Henning Kamp 
380e4fea39eSPoul-Henning Kamp void
381e4fea39eSPoul-Henning Kamp delete_unrhdr(struct unrhdr *uh)
382e4fea39eSPoul-Henning Kamp {
383e4fea39eSPoul-Henning Kamp 
384d9a54d5cSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
385e4fea39eSPoul-Henning Kamp 	KASSERT(uh->busy == 0, ("unrhdr has %u allocations", uh->busy));
386e4fea39eSPoul-Henning Kamp 	KASSERT(uh->alloc == 0, ("UNR memory leak in delete_unrhdr"));
38709828ba9SKonstantin Belousov 	KASSERT(TAILQ_FIRST(&uh->ppfree) == NULL,
38809828ba9SKonstantin Belousov 	    ("unrhdr has postponed item for free"));
389e4fea39eSPoul-Henning Kamp 	Free(uh);
390e4fea39eSPoul-Henning Kamp }
391e4fea39eSPoul-Henning Kamp 
392333dcaa4SMatt Joras void
393333dcaa4SMatt Joras clear_unrhdr(struct unrhdr *uh)
394333dcaa4SMatt Joras {
395333dcaa4SMatt Joras 	struct unr *up, *uq;
396333dcaa4SMatt Joras 
397333dcaa4SMatt Joras 	KASSERT(TAILQ_EMPTY(&uh->ppfree),
398333dcaa4SMatt Joras 	    ("unrhdr has postponed item for free"));
3990d8e0405SMatt Joras 	TAILQ_FOREACH_SAFE(up, &uh->head, list, uq) {
400333dcaa4SMatt Joras 		if (up->ptr != uh) {
401333dcaa4SMatt Joras 			Free(up->ptr);
402333dcaa4SMatt Joras 		}
403333dcaa4SMatt Joras 		Free(up);
404333dcaa4SMatt Joras 	}
405333dcaa4SMatt Joras 	uh->busy = 0;
406333dcaa4SMatt Joras 	uh->alloc = 0;
4070d8e0405SMatt Joras 	init_unrhdr(uh, uh->low, uh->high, uh->mtx);
4080d8e0405SMatt Joras 
4090d8e0405SMatt Joras 	check_unrhdr(uh, __LINE__);
410333dcaa4SMatt Joras }
411333dcaa4SMatt Joras 
412d9a54d5cSPoul-Henning Kamp static __inline int
413d9a54d5cSPoul-Henning Kamp is_bitmap(struct unrhdr *uh, struct unr *up)
414d9a54d5cSPoul-Henning Kamp {
415d9a54d5cSPoul-Henning Kamp 	return (up->ptr != uh && up->ptr != NULL);
416d9a54d5cSPoul-Henning Kamp }
417d9a54d5cSPoul-Henning Kamp 
418f6bde1fdSPoul-Henning Kamp /*
419d9a54d5cSPoul-Henning Kamp  * Look for sequence of items which can be combined into a bitmap, if
420d9a54d5cSPoul-Henning Kamp  * multiple are present, take the one which saves most memory.
421d9a54d5cSPoul-Henning Kamp  *
422d9a54d5cSPoul-Henning Kamp  * Return (1) if a sequence was found to indicate that another call
423d9a54d5cSPoul-Henning Kamp  * might be able to do more.  Return (0) if we found no suitable sequence.
424d9a54d5cSPoul-Henning Kamp  *
425d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
426d9a54d5cSPoul-Henning Kamp  */
427d9a54d5cSPoul-Henning Kamp static int
428d9a54d5cSPoul-Henning Kamp optimize_unr(struct unrhdr *uh)
429d9a54d5cSPoul-Henning Kamp {
430d9a54d5cSPoul-Henning Kamp 	struct unr *up, *uf, *us;
431d9a54d5cSPoul-Henning Kamp 	struct unrb *ub, *ubf;
432d9a54d5cSPoul-Henning Kamp 	u_int a, l, ba;
433d9a54d5cSPoul-Henning Kamp 
434d9a54d5cSPoul-Henning Kamp 	/*
435d9a54d5cSPoul-Henning Kamp 	 * Look for the run of items (if any) which when collapsed into
436d9a54d5cSPoul-Henning Kamp 	 * a bitmap would save most memory.
437d9a54d5cSPoul-Henning Kamp 	 */
438d9a54d5cSPoul-Henning Kamp 	us = NULL;
439d9a54d5cSPoul-Henning Kamp 	ba = 0;
440d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(uf, &uh->head, list) {
441d9a54d5cSPoul-Henning Kamp 		if (uf->len >= NBITS)
442d9a54d5cSPoul-Henning Kamp 			continue;
443d9a54d5cSPoul-Henning Kamp 		a = 1;
444d9a54d5cSPoul-Henning Kamp 		if (is_bitmap(uh, uf))
445d9a54d5cSPoul-Henning Kamp 			a++;
446d9a54d5cSPoul-Henning Kamp 		l = uf->len;
447d9a54d5cSPoul-Henning Kamp 		up = uf;
448d9a54d5cSPoul-Henning Kamp 		while (1) {
449d9a54d5cSPoul-Henning Kamp 			up = TAILQ_NEXT(up, list);
450d9a54d5cSPoul-Henning Kamp 			if (up == NULL)
451d9a54d5cSPoul-Henning Kamp 				break;
452d9a54d5cSPoul-Henning Kamp 			if ((up->len + l) > NBITS)
453d9a54d5cSPoul-Henning Kamp 				break;
454d9a54d5cSPoul-Henning Kamp 			a++;
455d9a54d5cSPoul-Henning Kamp 			if (is_bitmap(uh, up))
456d9a54d5cSPoul-Henning Kamp 				a++;
457d9a54d5cSPoul-Henning Kamp 			l += up->len;
458d9a54d5cSPoul-Henning Kamp 		}
459d9a54d5cSPoul-Henning Kamp 		if (a > ba) {
460d9a54d5cSPoul-Henning Kamp 			ba = a;
461d9a54d5cSPoul-Henning Kamp 			us = uf;
462d9a54d5cSPoul-Henning Kamp 		}
463d9a54d5cSPoul-Henning Kamp 	}
464d9a54d5cSPoul-Henning Kamp 	if (ba < 3)
465d9a54d5cSPoul-Henning Kamp 		return (0);
466d9a54d5cSPoul-Henning Kamp 
467d9a54d5cSPoul-Henning Kamp 	/*
468d9a54d5cSPoul-Henning Kamp 	 * If the first element is not a bitmap, make it one.
469d9a54d5cSPoul-Henning Kamp 	 * Trying to do so without allocating more memory complicates things
470d9a54d5cSPoul-Henning Kamp 	 * a bit
471d9a54d5cSPoul-Henning Kamp 	 */
472d9a54d5cSPoul-Henning Kamp 	if (!is_bitmap(uh, us)) {
473d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
474d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, us, list);
475d9a54d5cSPoul-Henning Kamp 		a = us->len;
476d9a54d5cSPoul-Henning Kamp 		l = us->ptr == uh ? 1 : 0;
477d9a54d5cSPoul-Henning Kamp 		ub = (void *)us;
4788907f744SAlan Somers 		bit_nclear(ub->map, 0, NBITS - 1);
4798907f744SAlan Somers 		if (l)
480d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, 0, a);
481d9a54d5cSPoul-Henning Kamp 		if (!is_bitmap(uh, uf)) {
4828907f744SAlan Somers 			if (uf->ptr == NULL)
483d9a54d5cSPoul-Henning Kamp 				bit_nclear(ub->map, a, a + uf->len - 1);
4848907f744SAlan Somers 			else
485d9a54d5cSPoul-Henning Kamp 				bit_nset(ub->map, a, a + uf->len - 1);
486d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
487d9a54d5cSPoul-Henning Kamp 			uf->len += a;
488d9a54d5cSPoul-Henning Kamp 			us = uf;
489d9a54d5cSPoul-Henning Kamp 		} else {
490d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
491d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, a++) {
4928907f744SAlan Somers 				if (bit_test(ubf->map, l))
493d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, a);
4948907f744SAlan Somers 				else
495d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, a);
496d9a54d5cSPoul-Henning Kamp 			}
497d9a54d5cSPoul-Henning Kamp 			uf->len = a;
498d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf->ptr);
499d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
500d9a54d5cSPoul-Henning Kamp 			us = uf;
501d9a54d5cSPoul-Henning Kamp 		}
502d9a54d5cSPoul-Henning Kamp 	}
503d9a54d5cSPoul-Henning Kamp 	ub = us->ptr;
504d9a54d5cSPoul-Henning Kamp 	while (1) {
505d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
506d9a54d5cSPoul-Henning Kamp 		if (uf == NULL)
507d9a54d5cSPoul-Henning Kamp 			return (1);
508d9a54d5cSPoul-Henning Kamp 		if (uf->len + us->len > NBITS)
509d9a54d5cSPoul-Henning Kamp 			return (1);
510d9a54d5cSPoul-Henning Kamp 		if (uf->ptr == NULL) {
511d9a54d5cSPoul-Henning Kamp 			bit_nclear(ub->map, us->len, us->len + uf->len - 1);
512d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
513d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
514d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
515d9a54d5cSPoul-Henning Kamp 		} else if (uf->ptr == uh) {
516d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, us->len, us->len + uf->len - 1);
517d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
518d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
519d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
520d9a54d5cSPoul-Henning Kamp 		} else {
521d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
522d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, us->len++) {
5238907f744SAlan Somers 				if (bit_test(ubf->map, l))
524d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, us->len);
5258907f744SAlan Somers 				else
526d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, us->len);
527d9a54d5cSPoul-Henning Kamp 			}
528d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
529d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, ubf);
530d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
531d9a54d5cSPoul-Henning Kamp 		}
532d9a54d5cSPoul-Henning Kamp 	}
533d9a54d5cSPoul-Henning Kamp }
534d9a54d5cSPoul-Henning Kamp 
535d9a54d5cSPoul-Henning Kamp /*
536d9a54d5cSPoul-Henning Kamp  * See if a given unr should be collapsed with a neighbor.
537d9a54d5cSPoul-Henning Kamp  *
538d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
539f6bde1fdSPoul-Henning Kamp  */
540f6bde1fdSPoul-Henning Kamp static void
541f6bde1fdSPoul-Henning Kamp collapse_unr(struct unrhdr *uh, struct unr *up)
542f6bde1fdSPoul-Henning Kamp {
543f6bde1fdSPoul-Henning Kamp 	struct unr *upp;
544d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
545f6bde1fdSPoul-Henning Kamp 
546d9a54d5cSPoul-Henning Kamp 	/* If bitmap is all set or clear, change it to runlength */
547d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
548d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
5498907f744SAlan Somers 		if (ub_full(ub, up->len)) {
550d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
551d9a54d5cSPoul-Henning Kamp 			up->ptr = uh;
5528907f744SAlan Somers 		} else if (ub_empty(ub, up->len)) {
553d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
554d9a54d5cSPoul-Henning Kamp 			up->ptr = NULL;
555d9a54d5cSPoul-Henning Kamp 		}
556d9a54d5cSPoul-Henning Kamp 	}
557d9a54d5cSPoul-Henning Kamp 
558d9a54d5cSPoul-Henning Kamp 	/* If nothing left in runlength, delete it */
559d9a54d5cSPoul-Henning Kamp 	if (up->len == 0) {
560d9a54d5cSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
561d9a54d5cSPoul-Henning Kamp 		if (upp == NULL)
562d9a54d5cSPoul-Henning Kamp 			upp = TAILQ_NEXT(up, list);
563d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, up, list);
564d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, up);
565d9a54d5cSPoul-Henning Kamp 		up = upp;
566d9a54d5cSPoul-Henning Kamp 	}
567d9a54d5cSPoul-Henning Kamp 
568d9a54d5cSPoul-Henning Kamp 	/* If we have "hot-spot" still, merge with neighbor if possible */
569d9a54d5cSPoul-Henning Kamp 	if (up != NULL) {
570f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
571f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
572f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
573f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
574f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
575f6bde1fdSPoul-Henning Kamp 			}
576f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_NEXT(up, list);
577f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
578f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
579f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
580f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
581f6bde1fdSPoul-Henning Kamp 		}
582f6bde1fdSPoul-Henning Kamp 	}
583f6bde1fdSPoul-Henning Kamp 
584d9a54d5cSPoul-Henning Kamp 	/* Merge into ->first if possible */
585d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
586d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == uh) {
587d9a54d5cSPoul-Henning Kamp 		uh->first += upp->len;
588d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
589d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
590d9a54d5cSPoul-Henning Kamp 		if (up == upp)
591d9a54d5cSPoul-Henning Kamp 			up = NULL;
592d9a54d5cSPoul-Henning Kamp 	}
593d9a54d5cSPoul-Henning Kamp 
594d9a54d5cSPoul-Henning Kamp 	/* Merge into ->last if possible */
595d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_LAST(&uh->head, unrhd);
596d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == NULL) {
597d9a54d5cSPoul-Henning Kamp 		uh->last += upp->len;
598d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
599d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
600d9a54d5cSPoul-Henning Kamp 		if (up == upp)
601d9a54d5cSPoul-Henning Kamp 			up = NULL;
602d9a54d5cSPoul-Henning Kamp 	}
603d9a54d5cSPoul-Henning Kamp 
604d9a54d5cSPoul-Henning Kamp 	/* Try to make bitmaps */
605d9a54d5cSPoul-Henning Kamp 	while (optimize_unr(uh))
606d9a54d5cSPoul-Henning Kamp 		continue;
607d9a54d5cSPoul-Henning Kamp }
608d9a54d5cSPoul-Henning Kamp 
609f6bde1fdSPoul-Henning Kamp /*
610f6bde1fdSPoul-Henning Kamp  * Allocate a free unr.
611f6bde1fdSPoul-Henning Kamp  */
612d9a54d5cSPoul-Henning Kamp int
613d9a54d5cSPoul-Henning Kamp alloc_unrl(struct unrhdr *uh)
614f6bde1fdSPoul-Henning Kamp {
615d9a54d5cSPoul-Henning Kamp 	struct unr *up;
616d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
617f6bde1fdSPoul-Henning Kamp 	u_int x;
618f6bde1fdSPoul-Henning Kamp 	int y;
619f6bde1fdSPoul-Henning Kamp 
620e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
621d9a54d5cSPoul-Henning Kamp 		mtx_assert(uh->mtx, MA_OWNED);
622f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
623d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
624d9a54d5cSPoul-Henning Kamp 
625f6bde1fdSPoul-Henning Kamp 	up = TAILQ_FIRST(&uh->head);
626f6bde1fdSPoul-Henning Kamp 
627d9a54d5cSPoul-Henning Kamp 	/*
628d9a54d5cSPoul-Henning Kamp 	 * If we have an ideal split, just adjust the first+last
629d9a54d5cSPoul-Henning Kamp 	 */
630d9a54d5cSPoul-Henning Kamp 	if (up == NULL && uh->last > 0) {
631d9a54d5cSPoul-Henning Kamp 		uh->first++;
632d9a54d5cSPoul-Henning Kamp 		uh->last--;
633f6bde1fdSPoul-Henning Kamp 		uh->busy++;
634f6bde1fdSPoul-Henning Kamp 		return (x);
635f6bde1fdSPoul-Henning Kamp 	}
636f6bde1fdSPoul-Henning Kamp 
637f6bde1fdSPoul-Henning Kamp 	/*
638d9a54d5cSPoul-Henning Kamp 	 * We can always allocate from the first list element, so if we have
639d9a54d5cSPoul-Henning Kamp 	 * nothing on the list, we must have run out of unit numbers.
640f6bde1fdSPoul-Henning Kamp 	 */
64193f6c81eSPoul-Henning Kamp 	if (up == NULL)
642d9a54d5cSPoul-Henning Kamp 		return (-1);
643d9a54d5cSPoul-Henning Kamp 
644d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr != uh, ("UNR first element is allocated"));
645d9a54d5cSPoul-Henning Kamp 
646d9a54d5cSPoul-Henning Kamp 	if (up->ptr == NULL) {	/* free run */
647d9a54d5cSPoul-Henning Kamp 		uh->first++;
648f6bde1fdSPoul-Henning Kamp 		up->len--;
649d9a54d5cSPoul-Henning Kamp 	} else {		/* bitmap */
650d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
651d9a54d5cSPoul-Henning Kamp 		bit_ffc(ub->map, up->len, &y);
652d9a54d5cSPoul-Henning Kamp 		KASSERT(y != -1, ("UNR corruption: No clear bit in bitmap."));
653d9a54d5cSPoul-Henning Kamp 		bit_set(ub->map, y);
654d9a54d5cSPoul-Henning Kamp 		x += y;
655d9a54d5cSPoul-Henning Kamp 	}
656f6bde1fdSPoul-Henning Kamp 	uh->busy++;
657d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
658f6bde1fdSPoul-Henning Kamp 	return (x);
659f6bde1fdSPoul-Henning Kamp }
660f6bde1fdSPoul-Henning Kamp 
661d9a54d5cSPoul-Henning Kamp int
662d9a54d5cSPoul-Henning Kamp alloc_unr(struct unrhdr *uh)
663d9a54d5cSPoul-Henning Kamp {
664d9a54d5cSPoul-Henning Kamp 	int i;
665d9a54d5cSPoul-Henning Kamp 
666e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
667d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
668d9a54d5cSPoul-Henning Kamp 	i = alloc_unrl(uh);
66909828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
670e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
671d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
672d9a54d5cSPoul-Henning Kamp 	return (i);
673d9a54d5cSPoul-Henning Kamp }
674d9a54d5cSPoul-Henning Kamp 
67513c02cbbSJaakko Heinonen static int
67613c02cbbSJaakko Heinonen alloc_unr_specificl(struct unrhdr *uh, u_int item, void **p1, void **p2)
67713c02cbbSJaakko Heinonen {
67813c02cbbSJaakko Heinonen 	struct unr *up, *upn;
67913c02cbbSJaakko Heinonen 	struct unrb *ub;
68013c02cbbSJaakko Heinonen 	u_int i, last, tl;
68113c02cbbSJaakko Heinonen 
682e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
68313c02cbbSJaakko Heinonen 		mtx_assert(uh->mtx, MA_OWNED);
68413c02cbbSJaakko Heinonen 
68513c02cbbSJaakko Heinonen 	if (item < uh->low + uh->first || item > uh->high)
68613c02cbbSJaakko Heinonen 		return (-1);
68713c02cbbSJaakko Heinonen 
68813c02cbbSJaakko Heinonen 	up = TAILQ_FIRST(&uh->head);
68913c02cbbSJaakko Heinonen 	/* Ideal split. */
69013c02cbbSJaakko Heinonen 	if (up == NULL && item - uh->low == uh->first) {
69113c02cbbSJaakko Heinonen 		uh->first++;
69213c02cbbSJaakko Heinonen 		uh->last--;
69313c02cbbSJaakko Heinonen 		uh->busy++;
69413c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
69513c02cbbSJaakko Heinonen 		return (item);
69613c02cbbSJaakko Heinonen 	}
69713c02cbbSJaakko Heinonen 
69813c02cbbSJaakko Heinonen 	i = item - uh->low - uh->first;
69913c02cbbSJaakko Heinonen 
70013c02cbbSJaakko Heinonen 	if (up == NULL) {
70113c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
70213c02cbbSJaakko Heinonen 		up->ptr = NULL;
70313c02cbbSJaakko Heinonen 		up->len = i;
70413c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
70513c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
70613c02cbbSJaakko Heinonen 		up->ptr = uh;
70713c02cbbSJaakko Heinonen 		up->len = 1;
70813c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
70913c02cbbSJaakko Heinonen 		uh->last = uh->high - uh->low - i;
71013c02cbbSJaakko Heinonen 		uh->busy++;
71113c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
71213c02cbbSJaakko Heinonen 		return (item);
71313c02cbbSJaakko Heinonen 	} else {
71413c02cbbSJaakko Heinonen 		/* Find the item which contains the unit we want to allocate. */
71513c02cbbSJaakko Heinonen 		TAILQ_FOREACH(up, &uh->head, list) {
71613c02cbbSJaakko Heinonen 			if (up->len > i)
71713c02cbbSJaakko Heinonen 				break;
71813c02cbbSJaakko Heinonen 			i -= up->len;
71913c02cbbSJaakko Heinonen 		}
72013c02cbbSJaakko Heinonen 	}
72113c02cbbSJaakko Heinonen 
72213c02cbbSJaakko Heinonen 	if (up == NULL) {
72313c02cbbSJaakko Heinonen 		if (i > 0) {
72413c02cbbSJaakko Heinonen 			up = new_unr(uh, p1, p2);
72513c02cbbSJaakko Heinonen 			up->ptr = NULL;
72613c02cbbSJaakko Heinonen 			up->len = i;
72713c02cbbSJaakko Heinonen 			TAILQ_INSERT_TAIL(&uh->head, up, list);
72813c02cbbSJaakko Heinonen 		}
72913c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
73013c02cbbSJaakko Heinonen 		up->ptr = uh;
73113c02cbbSJaakko Heinonen 		up->len = 1;
73213c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
73313c02cbbSJaakko Heinonen 		goto done;
73413c02cbbSJaakko Heinonen 	}
73513c02cbbSJaakko Heinonen 
73613c02cbbSJaakko Heinonen 	if (is_bitmap(uh, up)) {
73713c02cbbSJaakko Heinonen 		ub = up->ptr;
73813c02cbbSJaakko Heinonen 		if (bit_test(ub->map, i) == 0) {
73913c02cbbSJaakko Heinonen 			bit_set(ub->map, i);
74013c02cbbSJaakko Heinonen 			goto done;
74113c02cbbSJaakko Heinonen 		} else
74213c02cbbSJaakko Heinonen 			return (-1);
74313c02cbbSJaakko Heinonen 	} else if (up->ptr == uh)
74413c02cbbSJaakko Heinonen 		return (-1);
74513c02cbbSJaakko Heinonen 
74613c02cbbSJaakko Heinonen 	KASSERT(up->ptr == NULL,
74713c02cbbSJaakko Heinonen 	    ("alloc_unr_specificl: up->ptr != NULL (up=%p)", up));
74813c02cbbSJaakko Heinonen 
74913c02cbbSJaakko Heinonen 	/* Split off the tail end, if any. */
75013c02cbbSJaakko Heinonen 	tl = up->len - (1 + i);
75113c02cbbSJaakko Heinonen 	if (tl > 0) {
75213c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
75313c02cbbSJaakko Heinonen 		upn->ptr = NULL;
75413c02cbbSJaakko Heinonen 		upn->len = tl;
75513c02cbbSJaakko Heinonen 		TAILQ_INSERT_AFTER(&uh->head, up, upn, list);
75613c02cbbSJaakko Heinonen 	}
75713c02cbbSJaakko Heinonen 
75813c02cbbSJaakko Heinonen 	/* Split off head end, if any */
75913c02cbbSJaakko Heinonen 	if (i > 0) {
76013c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
76113c02cbbSJaakko Heinonen 		upn->len = i;
76213c02cbbSJaakko Heinonen 		upn->ptr = NULL;
76313c02cbbSJaakko Heinonen 		TAILQ_INSERT_BEFORE(up, upn, list);
76413c02cbbSJaakko Heinonen 	}
76513c02cbbSJaakko Heinonen 	up->len = 1;
76613c02cbbSJaakko Heinonen 	up->ptr = uh;
76713c02cbbSJaakko Heinonen 
76813c02cbbSJaakko Heinonen done:
76913c02cbbSJaakko Heinonen 	last = uh->high - uh->low - (item - uh->low);
77013c02cbbSJaakko Heinonen 	if (uh->last > last)
77113c02cbbSJaakko Heinonen 		uh->last = last;
77213c02cbbSJaakko Heinonen 	uh->busy++;
77313c02cbbSJaakko Heinonen 	collapse_unr(uh, up);
77413c02cbbSJaakko Heinonen 	check_unrhdr(uh, __LINE__);
77513c02cbbSJaakko Heinonen 	return (item);
77613c02cbbSJaakko Heinonen }
77713c02cbbSJaakko Heinonen 
77813c02cbbSJaakko Heinonen int
77913c02cbbSJaakko Heinonen alloc_unr_specific(struct unrhdr *uh, u_int item)
78013c02cbbSJaakko Heinonen {
78113c02cbbSJaakko Heinonen 	void *p1, *p2;
78213c02cbbSJaakko Heinonen 	int i;
78313c02cbbSJaakko Heinonen 
78413c02cbbSJaakko Heinonen 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "alloc_unr_specific");
78513c02cbbSJaakko Heinonen 
78613c02cbbSJaakko Heinonen 	p1 = Malloc(sizeof(struct unr));
78713c02cbbSJaakko Heinonen 	p2 = Malloc(sizeof(struct unr));
78813c02cbbSJaakko Heinonen 
789e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
79013c02cbbSJaakko Heinonen 		mtx_lock(uh->mtx);
79113c02cbbSJaakko Heinonen 	i = alloc_unr_specificl(uh, item, &p1, &p2);
792e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
79313c02cbbSJaakko Heinonen 		mtx_unlock(uh->mtx);
79413c02cbbSJaakko Heinonen 
79513c02cbbSJaakko Heinonen 	if (p1 != NULL)
79613c02cbbSJaakko Heinonen 		Free(p1);
79713c02cbbSJaakko Heinonen 	if (p2 != NULL)
79813c02cbbSJaakko Heinonen 		Free(p2);
79913c02cbbSJaakko Heinonen 
80013c02cbbSJaakko Heinonen 	return (i);
80113c02cbbSJaakko Heinonen }
80213c02cbbSJaakko Heinonen 
803f6bde1fdSPoul-Henning Kamp /*
804f6bde1fdSPoul-Henning Kamp  * Free a unr.
805f6bde1fdSPoul-Henning Kamp  *
806f6bde1fdSPoul-Henning Kamp  * If we can save unrs by using a bitmap, do so.
807f6bde1fdSPoul-Henning Kamp  */
808d9a54d5cSPoul-Henning Kamp static void
809d9a54d5cSPoul-Henning Kamp free_unrl(struct unrhdr *uh, u_int item, void **p1, void **p2)
810f6bde1fdSPoul-Henning Kamp {
811d9a54d5cSPoul-Henning Kamp 	struct unr *up, *upp, *upn;
812d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
813d9a54d5cSPoul-Henning Kamp 	u_int pl;
814f6bde1fdSPoul-Henning Kamp 
815f6bde1fdSPoul-Henning Kamp 	KASSERT(item >= uh->low && item <= uh->high,
816f6bde1fdSPoul-Henning Kamp 	    ("UNR: free_unr(%u) out of range [%u...%u]",
817f6bde1fdSPoul-Henning Kamp 	     item, uh->low, uh->high));
818f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
819f6bde1fdSPoul-Henning Kamp 	item -= uh->low;
820d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
821f6bde1fdSPoul-Henning Kamp 	/*
822d9a54d5cSPoul-Henning Kamp 	 * Freeing in the ideal split case
823f6bde1fdSPoul-Henning Kamp 	 */
824d9a54d5cSPoul-Henning Kamp 	if (item + 1 == uh->first && upp == NULL) {
825d9a54d5cSPoul-Henning Kamp 		uh->last++;
826d9a54d5cSPoul-Henning Kamp 		uh->first--;
827d9a54d5cSPoul-Henning Kamp 		uh->busy--;
828f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
829f6bde1fdSPoul-Henning Kamp 		return;
830f6bde1fdSPoul-Henning Kamp 	}
831d9a54d5cSPoul-Henning Kamp 	/*
832d9a54d5cSPoul-Henning Kamp  	 * Freeing in the ->first section.  Create a run starting at the
833d9a54d5cSPoul-Henning Kamp 	 * freed item.  The code below will subdivide it.
834d9a54d5cSPoul-Henning Kamp 	 */
835d9a54d5cSPoul-Henning Kamp 	if (item < uh->first) {
836d9a54d5cSPoul-Henning Kamp 		up = new_unr(uh, p1, p2);
837d9a54d5cSPoul-Henning Kamp 		up->ptr = uh;
838d9a54d5cSPoul-Henning Kamp 		up->len = uh->first - item;
839d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_HEAD(&uh->head, up, list);
840d9a54d5cSPoul-Henning Kamp 		uh->first -= up->len;
841f6bde1fdSPoul-Henning Kamp 	}
842f6bde1fdSPoul-Henning Kamp 
843d9a54d5cSPoul-Henning Kamp 	item -= uh->first;
844d9a54d5cSPoul-Henning Kamp 
845d9a54d5cSPoul-Henning Kamp 	/* Find the item which contains the unit we want to free */
846d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
847d9a54d5cSPoul-Henning Kamp 		if (up->len > item)
848d9a54d5cSPoul-Henning Kamp 			break;
849d9a54d5cSPoul-Henning Kamp 		item -= up->len;
850d9a54d5cSPoul-Henning Kamp 	}
851d9a54d5cSPoul-Henning Kamp 
852d9a54d5cSPoul-Henning Kamp 	/* Handle bitmap items */
853d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
854d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
855d9a54d5cSPoul-Henning Kamp 
856d9a54d5cSPoul-Henning Kamp 		KASSERT(bit_test(ub->map, item) != 0,
857d9a54d5cSPoul-Henning Kamp 		    ("UNR: Freeing free item %d (bitmap)\n", item));
858d9a54d5cSPoul-Henning Kamp 		bit_clear(ub->map, item);
859d9a54d5cSPoul-Henning Kamp 		uh->busy--;
860d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
861d9a54d5cSPoul-Henning Kamp 		return;
862d9a54d5cSPoul-Henning Kamp 	}
863d9a54d5cSPoul-Henning Kamp 
864d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr == uh, ("UNR Freeing free item %d (run))\n", item));
865f6bde1fdSPoul-Henning Kamp 
866f6bde1fdSPoul-Henning Kamp 	/* Just this one left, reap it */
867f6bde1fdSPoul-Henning Kamp 	if (up->len == 1) {
868f6bde1fdSPoul-Henning Kamp 		up->ptr = NULL;
869f6bde1fdSPoul-Henning Kamp 		uh->busy--;
870f6bde1fdSPoul-Henning Kamp 		collapse_unr(uh, up);
871f6bde1fdSPoul-Henning Kamp 		return;
872f6bde1fdSPoul-Henning Kamp 	}
873f6bde1fdSPoul-Henning Kamp 
874d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item into the previous 'free' run */
875f6bde1fdSPoul-Henning Kamp 	upp = TAILQ_PREV(up, unrhd, list);
876d9a54d5cSPoul-Henning Kamp 	if (item == 0 && upp != NULL && upp->ptr == NULL) {
877f6bde1fdSPoul-Henning Kamp 		upp->len++;
878f6bde1fdSPoul-Henning Kamp 		up->len--;
879f6bde1fdSPoul-Henning Kamp 		uh->busy--;
880d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
881f6bde1fdSPoul-Henning Kamp 		return;
882f6bde1fdSPoul-Henning Kamp 	}
883f6bde1fdSPoul-Henning Kamp 
884d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item to the next 'free' run */
885f6bde1fdSPoul-Henning Kamp 	upn = TAILQ_NEXT(up, list);
886d9a54d5cSPoul-Henning Kamp 	if (item == up->len - 1 && upn != NULL && upn->ptr == NULL) {
887f6bde1fdSPoul-Henning Kamp 		upn->len++;
888f6bde1fdSPoul-Henning Kamp 		up->len--;
889f6bde1fdSPoul-Henning Kamp 		uh->busy--;
890d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
891f6bde1fdSPoul-Henning Kamp 		return;
892f6bde1fdSPoul-Henning Kamp 	}
893f6bde1fdSPoul-Henning Kamp 
894f6bde1fdSPoul-Henning Kamp 	/* Split off the tail end, if any. */
895d9a54d5cSPoul-Henning Kamp 	pl = up->len - (1 + item);
896f6bde1fdSPoul-Henning Kamp 	if (pl > 0) {
897d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
898f6bde1fdSPoul-Henning Kamp 		upp->ptr = uh;
899f6bde1fdSPoul-Henning Kamp 		upp->len = pl;
900f6bde1fdSPoul-Henning Kamp 		TAILQ_INSERT_AFTER(&uh->head, up, upp, list);
901f6bde1fdSPoul-Henning Kamp 	}
902f6bde1fdSPoul-Henning Kamp 
903d9a54d5cSPoul-Henning Kamp 	/* Split off head end, if any */
904d9a54d5cSPoul-Henning Kamp 	if (item > 0) {
905d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
906d9a54d5cSPoul-Henning Kamp 		upp->len = item;
907d9a54d5cSPoul-Henning Kamp 		upp->ptr = uh;
908d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_BEFORE(up, upp, list);
909d9a54d5cSPoul-Henning Kamp 	}
910f6bde1fdSPoul-Henning Kamp 	up->len = 1;
911f6bde1fdSPoul-Henning Kamp 	up->ptr = NULL;
912f6bde1fdSPoul-Henning Kamp 	uh->busy--;
913d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
914f6bde1fdSPoul-Henning Kamp }
915f6bde1fdSPoul-Henning Kamp 
916d9a54d5cSPoul-Henning Kamp void
917d9a54d5cSPoul-Henning Kamp free_unr(struct unrhdr *uh, u_int item)
918d9a54d5cSPoul-Henning Kamp {
919d9a54d5cSPoul-Henning Kamp 	void *p1, *p2;
920f6bde1fdSPoul-Henning Kamp 
9217550e3eaSKonstantin Belousov 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "free_unr");
922d9a54d5cSPoul-Henning Kamp 	p1 = Malloc(sizeof(struct unr));
923d9a54d5cSPoul-Henning Kamp 	p2 = Malloc(sizeof(struct unr));
924e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
925d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
926d9a54d5cSPoul-Henning Kamp 	free_unrl(uh, item, &p1, &p2);
92709828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
928e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
929d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
930d9a54d5cSPoul-Henning Kamp 	if (p1 != NULL)
931d9a54d5cSPoul-Henning Kamp 		Free(p1);
932d9a54d5cSPoul-Henning Kamp 	if (p2 != NULL)
933d9a54d5cSPoul-Henning Kamp 		Free(p2);
934f6bde1fdSPoul-Henning Kamp }
935f6bde1fdSPoul-Henning Kamp 
93693f6c81eSPoul-Henning Kamp #ifndef _KERNEL	/* USERLAND test driver */
93793f6c81eSPoul-Henning Kamp 
938f6bde1fdSPoul-Henning Kamp /*
939794277daSAlan Somers  * Simple stochastic test driver for the above functions.  The code resides
940794277daSAlan Somers  * here so that it can access static functions and structures.
941f6bde1fdSPoul-Henning Kamp  */
942f6bde1fdSPoul-Henning Kamp 
943794277daSAlan Somers static bool verbose;
944794277daSAlan Somers #define VPRINTF(...)	{if (verbose) printf(__VA_ARGS__);}
945794277daSAlan Somers 
946f6bde1fdSPoul-Henning Kamp static void
947f6bde1fdSPoul-Henning Kamp print_unr(struct unrhdr *uh, struct unr *up)
948f6bde1fdSPoul-Henning Kamp {
949f6bde1fdSPoul-Henning Kamp 	u_int x;
950d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
951f6bde1fdSPoul-Henning Kamp 
952f6bde1fdSPoul-Henning Kamp 	printf("  %p len = %5u ", up, up->len);
953f6bde1fdSPoul-Henning Kamp 	if (up->ptr == NULL)
954f6bde1fdSPoul-Henning Kamp 		printf("free\n");
955f6bde1fdSPoul-Henning Kamp 	else if (up->ptr == uh)
956f6bde1fdSPoul-Henning Kamp 		printf("alloc\n");
957f6bde1fdSPoul-Henning Kamp 	else {
958d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
9598907f744SAlan Somers 		printf("bitmap [");
960d9a54d5cSPoul-Henning Kamp 		for (x = 0; x < up->len; x++) {
961d9a54d5cSPoul-Henning Kamp 			if (bit_test(ub->map, x))
962d9a54d5cSPoul-Henning Kamp 				printf("#");
963f6bde1fdSPoul-Henning Kamp 			else
964d9a54d5cSPoul-Henning Kamp 				printf(" ");
965f6bde1fdSPoul-Henning Kamp 		}
966f6bde1fdSPoul-Henning Kamp 		printf("]\n");
967f6bde1fdSPoul-Henning Kamp 	}
968f6bde1fdSPoul-Henning Kamp }
969f6bde1fdSPoul-Henning Kamp 
970f6bde1fdSPoul-Henning Kamp static void
971f6bde1fdSPoul-Henning Kamp print_unrhdr(struct unrhdr *uh)
972f6bde1fdSPoul-Henning Kamp {
973f6bde1fdSPoul-Henning Kamp 	struct unr *up;
974f6bde1fdSPoul-Henning Kamp 	u_int x;
975f6bde1fdSPoul-Henning Kamp 
976d9a54d5cSPoul-Henning Kamp 	printf(
977d9a54d5cSPoul-Henning Kamp 	    "%p low = %u high = %u first = %u last = %u busy %u chunks = %u\n",
978d9a54d5cSPoul-Henning Kamp 	    uh, uh->low, uh->high, uh->first, uh->last, uh->busy, uh->alloc);
979d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
980f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
981f6bde1fdSPoul-Henning Kamp 		printf("  from = %5u", x);
982f6bde1fdSPoul-Henning Kamp 		print_unr(uh, up);
983f6bde1fdSPoul-Henning Kamp 		if (up->ptr == NULL || up->ptr == uh)
984f6bde1fdSPoul-Henning Kamp 			x += up->len;
985f6bde1fdSPoul-Henning Kamp 		else
986f6bde1fdSPoul-Henning Kamp 			x += NBITS;
987f6bde1fdSPoul-Henning Kamp 	}
988f6bde1fdSPoul-Henning Kamp }
989f6bde1fdSPoul-Henning Kamp 
99013c02cbbSJaakko Heinonen static void
99113c02cbbSJaakko Heinonen test_alloc_unr(struct unrhdr *uh, u_int i, char a[])
99213c02cbbSJaakko Heinonen {
99313c02cbbSJaakko Heinonen 	int j;
99413c02cbbSJaakko Heinonen 
99513c02cbbSJaakko Heinonen 	if (a[i]) {
996794277daSAlan Somers 		VPRINTF("F %u\n", i);
99713c02cbbSJaakko Heinonen 		free_unr(uh, i);
99813c02cbbSJaakko Heinonen 		a[i] = 0;
99913c02cbbSJaakko Heinonen 	} else {
100013c02cbbSJaakko Heinonen 		no_alloc = 1;
100113c02cbbSJaakko Heinonen 		j = alloc_unr(uh);
100213c02cbbSJaakko Heinonen 		if (j != -1) {
100313c02cbbSJaakko Heinonen 			a[j] = 1;
1004794277daSAlan Somers 			VPRINTF("A %d\n", j);
100513c02cbbSJaakko Heinonen 		}
100613c02cbbSJaakko Heinonen 		no_alloc = 0;
100713c02cbbSJaakko Heinonen 	}
100813c02cbbSJaakko Heinonen }
100913c02cbbSJaakko Heinonen 
101013c02cbbSJaakko Heinonen static void
101113c02cbbSJaakko Heinonen test_alloc_unr_specific(struct unrhdr *uh, u_int i, char a[])
101213c02cbbSJaakko Heinonen {
101313c02cbbSJaakko Heinonen 	int j;
101413c02cbbSJaakko Heinonen 
101513c02cbbSJaakko Heinonen 	j = alloc_unr_specific(uh, i);
101613c02cbbSJaakko Heinonen 	if (j == -1) {
1017794277daSAlan Somers 		VPRINTF("F %u\n", i);
101813c02cbbSJaakko Heinonen 		a[i] = 0;
101913c02cbbSJaakko Heinonen 		free_unr(uh, i);
102013c02cbbSJaakko Heinonen 	} else {
102113c02cbbSJaakko Heinonen 		a[i] = 1;
1022794277daSAlan Somers 		VPRINTF("A %d\n", j);
102313c02cbbSJaakko Heinonen 	}
102413c02cbbSJaakko Heinonen }
102513c02cbbSJaakko Heinonen 
1026794277daSAlan Somers static void
1027794277daSAlan Somers usage(char** argv)
1028794277daSAlan Somers {
1029794277daSAlan Somers 	printf("%s [-h] [-r REPETITIONS] [-v]\n", argv[0]);
1030794277daSAlan Somers }
1031f6bde1fdSPoul-Henning Kamp 
1032f6bde1fdSPoul-Henning Kamp int
1033794277daSAlan Somers main(int argc, char **argv)
1034f6bde1fdSPoul-Henning Kamp {
1035f6bde1fdSPoul-Henning Kamp 	struct unrhdr *uh;
1036794277daSAlan Somers 	char *a;
1037794277daSAlan Somers 	long count = 10000;	/* Number of unrs to test */
103837f32e53SAlan Somers 	long reps = 1, m;
1039794277daSAlan Somers 	int ch;
1040cd565040SConrad Meyer 	u_int i;
1041794277daSAlan Somers 
1042794277daSAlan Somers 	verbose = false;
1043794277daSAlan Somers 
1044794277daSAlan Somers 	while ((ch = getopt(argc, argv, "hr:v")) != -1) {
1045794277daSAlan Somers 		switch (ch) {
1046794277daSAlan Somers 		case 'r':
1047794277daSAlan Somers 			errno = 0;
1048794277daSAlan Somers 			reps = strtol(optarg, NULL, 0);
1049794277daSAlan Somers 			if (errno == ERANGE || errno == EINVAL) {
1050794277daSAlan Somers 				usage(argv);
1051794277daSAlan Somers 				exit(2);
1052794277daSAlan Somers 			}
1053794277daSAlan Somers 
1054794277daSAlan Somers 			break;
1055794277daSAlan Somers 		case 'v':
1056794277daSAlan Somers 			verbose = true;
1057794277daSAlan Somers 			break;
1058794277daSAlan Somers 		case 'h':
1059794277daSAlan Somers 		default:
1060794277daSAlan Somers 			usage(argv);
1061794277daSAlan Somers 			exit(2);
1062794277daSAlan Somers 		}
1063794277daSAlan Somers 	}
1064f6bde1fdSPoul-Henning Kamp 
1065d9a54d5cSPoul-Henning Kamp 	setbuf(stdout, NULL);
1066794277daSAlan Somers 	uh = new_unrhdr(0, count - 1, NULL);
1067d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1068f6bde1fdSPoul-Henning Kamp 
1069794277daSAlan Somers 	a = calloc(count, sizeof(char));
1070794277daSAlan Somers 	if (a == NULL)
1071794277daSAlan Somers 		err(1, "calloc failed");
1072f6bde1fdSPoul-Henning Kamp 
1073794277daSAlan Somers 	printf("sizeof(struct unr) %zu\n", sizeof(struct unr));
1074794277daSAlan Somers 	printf("sizeof(struct unrb) %zu\n", sizeof(struct unrb));
1075794277daSAlan Somers 	printf("sizeof(struct unrhdr) %zu\n", sizeof(struct unrhdr));
1076b4f69365SEd Maste 	printf("NBITS %lu\n", (unsigned long)NBITS);
1077794277daSAlan Somers 	for (m = 0; m < count * reps; m++) {
1078cd565040SConrad Meyer 		i = arc4random_uniform(count);
1079d9a54d5cSPoul-Henning Kamp #if 0
1080d9a54d5cSPoul-Henning Kamp 		if (a[i] && (j & 1))
1081d9a54d5cSPoul-Henning Kamp 			continue;
1082d9a54d5cSPoul-Henning Kamp #endif
1083cd565040SConrad Meyer 		if ((arc4random() & 1) != 0)
108413c02cbbSJaakko Heinonen 			test_alloc_unr(uh, i, a);
108513c02cbbSJaakko Heinonen 		else
108613c02cbbSJaakko Heinonen 			test_alloc_unr_specific(uh, i, a);
108713c02cbbSJaakko Heinonen 
1088794277daSAlan Somers 		if (verbose)
1089f6bde1fdSPoul-Henning Kamp 			print_unrhdr(uh);
1090f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
1091f6bde1fdSPoul-Henning Kamp 	}
109237f32e53SAlan Somers 	for (i = 0; i < (u_int)count; i++) {
1093d9a54d5cSPoul-Henning Kamp 		if (a[i]) {
1094794277daSAlan Somers 			if (verbose) {
1095d9a54d5cSPoul-Henning Kamp 				printf("C %u\n", i);
1096e4fea39eSPoul-Henning Kamp 				print_unrhdr(uh);
1097d9a54d5cSPoul-Henning Kamp 			}
1098794277daSAlan Somers 			free_unr(uh, i);
1099794277daSAlan Somers 		}
1100d9a54d5cSPoul-Henning Kamp 	}
1101d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1102e4fea39eSPoul-Henning Kamp 	delete_unrhdr(uh);
1103794277daSAlan Somers 	free(a);
1104f6bde1fdSPoul-Henning Kamp 	return (0);
1105f6bde1fdSPoul-Henning Kamp }
1106f6bde1fdSPoul-Henning Kamp #endif
1107