xref: /freebsd/sys/kern/subr_unit.c (revision e59b940dcb45b887974aeae8d8f86665aed2c913)
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 
146d9a54d5cSPoul-Henning Kamp struct mtx {
147d9a54d5cSPoul-Henning Kamp 	int	state;
148d9a54d5cSPoul-Henning Kamp } unitmtx;
149d9a54d5cSPoul-Henning Kamp 
150d9a54d5cSPoul-Henning Kamp static void
151d9a54d5cSPoul-Henning Kamp mtx_lock(struct mtx *mp)
152d9a54d5cSPoul-Henning Kamp {
153d9a54d5cSPoul-Henning Kamp 	KASSERT(mp->state == 0, ("mutex already locked"));
154d9a54d5cSPoul-Henning Kamp 	mp->state = 1;
155d9a54d5cSPoul-Henning Kamp }
156d9a54d5cSPoul-Henning Kamp 
157d9a54d5cSPoul-Henning Kamp static void
158d9a54d5cSPoul-Henning Kamp mtx_unlock(struct mtx *mp)
159d9a54d5cSPoul-Henning Kamp {
160d9a54d5cSPoul-Henning Kamp 	KASSERT(mp->state == 1, ("mutex not locked"));
161d9a54d5cSPoul-Henning Kamp 	mp->state = 0;
162d9a54d5cSPoul-Henning Kamp }
163d9a54d5cSPoul-Henning Kamp 
164d9a54d5cSPoul-Henning Kamp #define MA_OWNED	9
165d9a54d5cSPoul-Henning Kamp 
166d9a54d5cSPoul-Henning Kamp static void
167d9a54d5cSPoul-Henning Kamp mtx_assert(struct mtx *mp, int flag)
168d9a54d5cSPoul-Henning Kamp {
169d9a54d5cSPoul-Henning Kamp 	if (flag == MA_OWNED) {
170d9a54d5cSPoul-Henning Kamp 		KASSERT(mp->state == 1, ("mtx_assert(MA_OWNED) not true"));
171d9a54d5cSPoul-Henning Kamp 	}
172d9a54d5cSPoul-Henning Kamp }
173d9a54d5cSPoul-Henning Kamp 
174d9a54d5cSPoul-Henning Kamp #define CTASSERT(foo)
17524e8eaf1SJaakko Heinonen #define WITNESS_WARN(flags, lock, fmt, ...)	(void)0
176d9a54d5cSPoul-Henning Kamp 
177d9a54d5cSPoul-Henning Kamp #endif /* USERLAND */
178f6bde1fdSPoul-Henning Kamp 
179f6bde1fdSPoul-Henning Kamp /*
180f6bde1fdSPoul-Henning Kamp  * This is our basic building block.
181f6bde1fdSPoul-Henning Kamp  *
182f6bde1fdSPoul-Henning Kamp  * It can be used in three different ways depending on the value of the ptr
183f6bde1fdSPoul-Henning Kamp  * element:
184f6bde1fdSPoul-Henning Kamp  *     If ptr is NULL, it represents a run of free items.
185f6bde1fdSPoul-Henning Kamp  *     If ptr points to the unrhdr it represents a run of allocated items.
1868907f744SAlan Somers  *     Otherwise it points to a bitstring of allocated items.
187f6bde1fdSPoul-Henning Kamp  *
188f6bde1fdSPoul-Henning Kamp  * For runs the len field is the length of the run.
189f6bde1fdSPoul-Henning Kamp  * For bitmaps the len field represents the number of allocated items.
190f6bde1fdSPoul-Henning Kamp  *
191f6bde1fdSPoul-Henning Kamp  * The bitmap is the same size as struct unr to optimize memory management.
192f6bde1fdSPoul-Henning Kamp  */
193f6bde1fdSPoul-Henning Kamp struct unr {
194f6bde1fdSPoul-Henning Kamp 	TAILQ_ENTRY(unr)	list;
195f6bde1fdSPoul-Henning Kamp 	u_int			len;
196f6bde1fdSPoul-Henning Kamp 	void			*ptr;
197f6bde1fdSPoul-Henning Kamp };
198f6bde1fdSPoul-Henning Kamp 
199d9a54d5cSPoul-Henning Kamp struct unrb {
2008907f744SAlan Somers 	bitstr_t		map[sizeof(struct unr) / sizeof(bitstr_t)];
201d9a54d5cSPoul-Henning Kamp };
202d9a54d5cSPoul-Henning Kamp 
2038907f744SAlan Somers CTASSERT((sizeof(struct unr) % sizeof(bitstr_t)) == 0);
204d9a54d5cSPoul-Henning Kamp 
2058907f744SAlan Somers /* Number of bits we can store in the bitmap */
2068907f744SAlan Somers #define NBITS (8 * sizeof(((struct unrb*)NULL)->map))
2078907f744SAlan Somers 
2088907f744SAlan Somers /* Is the unrb empty in at least the first len bits? */
2098907f744SAlan Somers static inline bool
2108907f744SAlan Somers ub_empty(struct unrb *ub, int len) {
2118907f744SAlan Somers 	int first_set;
2128907f744SAlan Somers 
2138907f744SAlan Somers 	bit_ffs(ub->map, len, &first_set);
2148907f744SAlan Somers 	return (first_set == -1);
2158907f744SAlan Somers }
2168907f744SAlan Somers 
2178907f744SAlan Somers /* Is the unrb full?  That is, is the number of set elements equal to len? */
2188907f744SAlan Somers static inline bool
2198907f744SAlan Somers ub_full(struct unrb *ub, int len)
2208907f744SAlan Somers {
2218907f744SAlan Somers 	int first_clear;
2228907f744SAlan Somers 
2238907f744SAlan Somers 	bit_ffc(ub->map, len, &first_clear);
2248907f744SAlan Somers 	return (first_clear == -1);
2258907f744SAlan Somers }
2268907f744SAlan Somers 
227f6bde1fdSPoul-Henning Kamp #if defined(DIAGNOSTIC) || !defined(_KERNEL)
228f6bde1fdSPoul-Henning Kamp /*
229f6bde1fdSPoul-Henning Kamp  * Consistency check function.
230f6bde1fdSPoul-Henning Kamp  *
231f6bde1fdSPoul-Henning Kamp  * Checks the internal consistency as well as we can.
232f6bde1fdSPoul-Henning Kamp  *
233f6bde1fdSPoul-Henning Kamp  * Called at all boundaries of this API.
234f6bde1fdSPoul-Henning Kamp  */
235f6bde1fdSPoul-Henning Kamp static void
236f6bde1fdSPoul-Henning Kamp check_unrhdr(struct unrhdr *uh, int line)
237f6bde1fdSPoul-Henning Kamp {
238f6bde1fdSPoul-Henning Kamp 	struct unr *up;
239d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
2401b82e02fSAlan Somers 	int w;
2411b82e02fSAlan Somers 	u_int y, z;
242f6bde1fdSPoul-Henning Kamp 
243d9a54d5cSPoul-Henning Kamp 	y = uh->first;
244f6bde1fdSPoul-Henning Kamp 	z = 0;
245f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
246f6bde1fdSPoul-Henning Kamp 		z++;
247f6bde1fdSPoul-Henning Kamp 		if (up->ptr != uh && up->ptr != NULL) {
248d9a54d5cSPoul-Henning Kamp 			ub = up->ptr;
249d9a54d5cSPoul-Henning Kamp 			KASSERT (up->len <= NBITS,
2508907f744SAlan Somers 			    ("UNR inconsistency: len %u max %zd (line %d)\n",
251d9a54d5cSPoul-Henning Kamp 			    up->len, NBITS, line));
252f6bde1fdSPoul-Henning Kamp 			z++;
253f6bde1fdSPoul-Henning Kamp 			w = 0;
2541b82e02fSAlan Somers 			bit_count(ub->map, 0, up->len, &w);
255d9a54d5cSPoul-Henning Kamp 			y += w;
256d9a54d5cSPoul-Henning Kamp 		} else if (up->ptr != NULL)
257f6bde1fdSPoul-Henning Kamp 			y += up->len;
258f6bde1fdSPoul-Henning Kamp 	}
259f6bde1fdSPoul-Henning Kamp 	KASSERT (y == uh->busy,
260f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: items %u found %u (line %d)\n",
261f6bde1fdSPoul-Henning Kamp 	    uh->busy, y, line));
262f6bde1fdSPoul-Henning Kamp 	KASSERT (z == uh->alloc,
263f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: chunks %u found %u (line %d)\n",
264f6bde1fdSPoul-Henning Kamp 	    uh->alloc, z, line));
265f6bde1fdSPoul-Henning Kamp }
266f6bde1fdSPoul-Henning Kamp 
267f6bde1fdSPoul-Henning Kamp #else
268f6bde1fdSPoul-Henning Kamp 
269f6bde1fdSPoul-Henning Kamp static __inline void
2708907f744SAlan Somers check_unrhdr(struct unrhdr *uh __unused, int line __unused)
271f6bde1fdSPoul-Henning Kamp {
272f6bde1fdSPoul-Henning Kamp 
273f6bde1fdSPoul-Henning Kamp }
274f6bde1fdSPoul-Henning Kamp 
275f6bde1fdSPoul-Henning Kamp #endif
276f6bde1fdSPoul-Henning Kamp 
277f6bde1fdSPoul-Henning Kamp /*
278f6bde1fdSPoul-Henning Kamp  * Userland memory management.  Just use calloc and keep track of how
279f6bde1fdSPoul-Henning Kamp  * many elements we have allocated for check_unrhdr().
280f6bde1fdSPoul-Henning Kamp  */
281f6bde1fdSPoul-Henning Kamp 
282f6bde1fdSPoul-Henning Kamp static __inline void *
283d9a54d5cSPoul-Henning Kamp new_unr(struct unrhdr *uh, void **p1, void **p2)
284f6bde1fdSPoul-Henning Kamp {
285d9a54d5cSPoul-Henning Kamp 	void *p;
286f6bde1fdSPoul-Henning Kamp 
287d9a54d5cSPoul-Henning Kamp 	uh->alloc++;
288d9a54d5cSPoul-Henning Kamp 	KASSERT(*p1 != NULL || *p2 != NULL, ("Out of cached memory"));
289d9a54d5cSPoul-Henning Kamp 	if (*p1 != NULL) {
290d9a54d5cSPoul-Henning Kamp 		p = *p1;
291d9a54d5cSPoul-Henning Kamp 		*p1 = NULL;
292d9a54d5cSPoul-Henning Kamp 		return (p);
293d9a54d5cSPoul-Henning Kamp 	} else {
294d9a54d5cSPoul-Henning Kamp 		p = *p2;
295d9a54d5cSPoul-Henning Kamp 		*p2 = NULL;
296d9a54d5cSPoul-Henning Kamp 		return (p);
297d9a54d5cSPoul-Henning Kamp 	}
298f6bde1fdSPoul-Henning Kamp }
299f6bde1fdSPoul-Henning Kamp 
300f6bde1fdSPoul-Henning Kamp static __inline void
301f6bde1fdSPoul-Henning Kamp delete_unr(struct unrhdr *uh, void *ptr)
302f6bde1fdSPoul-Henning Kamp {
30309828ba9SKonstantin Belousov 	struct unr *up;
304d9a54d5cSPoul-Henning Kamp 
305f6bde1fdSPoul-Henning Kamp 	uh->alloc--;
30609828ba9SKonstantin Belousov 	up = ptr;
30709828ba9SKonstantin Belousov 	TAILQ_INSERT_TAIL(&uh->ppfree, up, list);
30809828ba9SKonstantin Belousov }
30909828ba9SKonstantin Belousov 
31009828ba9SKonstantin Belousov void
31109828ba9SKonstantin Belousov clean_unrhdrl(struct unrhdr *uh)
31209828ba9SKonstantin Belousov {
31309828ba9SKonstantin Belousov 	struct unr *up;
31409828ba9SKonstantin Belousov 
315*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
31609828ba9SKonstantin Belousov 		mtx_assert(uh->mtx, MA_OWNED);
31709828ba9SKonstantin Belousov 	while ((up = TAILQ_FIRST(&uh->ppfree)) != NULL) {
31809828ba9SKonstantin Belousov 		TAILQ_REMOVE(&uh->ppfree, up, list);
319*e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
32009828ba9SKonstantin Belousov 			mtx_unlock(uh->mtx);
32109828ba9SKonstantin Belousov 		Free(up);
322*e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
32309828ba9SKonstantin Belousov 			mtx_lock(uh->mtx);
32409828ba9SKonstantin Belousov 	}
32509828ba9SKonstantin Belousov 
32609828ba9SKonstantin Belousov }
32709828ba9SKonstantin Belousov 
32809828ba9SKonstantin Belousov void
32909828ba9SKonstantin Belousov clean_unrhdr(struct unrhdr *uh)
33009828ba9SKonstantin Belousov {
33109828ba9SKonstantin Belousov 
332*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
33309828ba9SKonstantin Belousov 		mtx_lock(uh->mtx);
33409828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
335*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
33609828ba9SKonstantin Belousov 		mtx_unlock(uh->mtx);
337f6bde1fdSPoul-Henning Kamp }
338f6bde1fdSPoul-Henning Kamp 
339dc872d46SKonstantin Belousov void
340dc872d46SKonstantin Belousov init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex)
341f6bde1fdSPoul-Henning Kamp {
342f6bde1fdSPoul-Henning Kamp 
343831aa555SJaakko Heinonen 	KASSERT(low >= 0 && low <= high,
344501812f2SJaakko Heinonen 	    ("UNR: use error: new_unrhdr(%d, %d)", low, high));
345*e59b940dSKonstantin Belousov 	if (mutex == UNR_NO_MTX)
346*e59b940dSKonstantin Belousov 		uh->mtx = NULL;
347*e59b940dSKonstantin Belousov 	else if (mutex != NULL)
348d9a54d5cSPoul-Henning Kamp 		uh->mtx = mutex;
349d9a54d5cSPoul-Henning Kamp 	else
350d9a54d5cSPoul-Henning Kamp 		uh->mtx = &unitmtx;
351f6bde1fdSPoul-Henning Kamp 	TAILQ_INIT(&uh->head);
35209828ba9SKonstantin Belousov 	TAILQ_INIT(&uh->ppfree);
353f6bde1fdSPoul-Henning Kamp 	uh->low = low;
354f6bde1fdSPoul-Henning Kamp 	uh->high = high;
355d9a54d5cSPoul-Henning Kamp 	uh->first = 0;
356d9a54d5cSPoul-Henning Kamp 	uh->last = 1 + (high - low);
357c4be460eSKonstantin Belousov 	uh->busy = 0;
358c4be460eSKonstantin Belousov 	uh->alloc = 0;
359f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
360dc872d46SKonstantin Belousov }
361dc872d46SKonstantin Belousov 
362dc872d46SKonstantin Belousov /*
363dc872d46SKonstantin Belousov  * Allocate a new unrheader set.
364dc872d46SKonstantin Belousov  *
365dc872d46SKonstantin Belousov  * Highest and lowest valid values given as parameters.
366dc872d46SKonstantin Belousov  */
367dc872d46SKonstantin Belousov 
368dc872d46SKonstantin Belousov struct unrhdr *
369dc872d46SKonstantin Belousov new_unrhdr(int low, int high, struct mtx *mutex)
370dc872d46SKonstantin Belousov {
371dc872d46SKonstantin Belousov 	struct unrhdr *uh;
372dc872d46SKonstantin Belousov 
373dc872d46SKonstantin Belousov 	uh = Malloc(sizeof *uh);
374dc872d46SKonstantin Belousov 	init_unrhdr(uh, low, high, mutex);
375f6bde1fdSPoul-Henning Kamp 	return (uh);
376f6bde1fdSPoul-Henning Kamp }
377f6bde1fdSPoul-Henning Kamp 
378e4fea39eSPoul-Henning Kamp void
379e4fea39eSPoul-Henning Kamp delete_unrhdr(struct unrhdr *uh)
380e4fea39eSPoul-Henning Kamp {
381e4fea39eSPoul-Henning Kamp 
382d9a54d5cSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
383e4fea39eSPoul-Henning Kamp 	KASSERT(uh->busy == 0, ("unrhdr has %u allocations", uh->busy));
384e4fea39eSPoul-Henning Kamp 	KASSERT(uh->alloc == 0, ("UNR memory leak in delete_unrhdr"));
38509828ba9SKonstantin Belousov 	KASSERT(TAILQ_FIRST(&uh->ppfree) == NULL,
38609828ba9SKonstantin Belousov 	    ("unrhdr has postponed item for free"));
387e4fea39eSPoul-Henning Kamp 	Free(uh);
388e4fea39eSPoul-Henning Kamp }
389e4fea39eSPoul-Henning Kamp 
390333dcaa4SMatt Joras void
391333dcaa4SMatt Joras clear_unrhdr(struct unrhdr *uh)
392333dcaa4SMatt Joras {
393333dcaa4SMatt Joras 	struct unr *up, *uq;
394333dcaa4SMatt Joras 
395333dcaa4SMatt Joras 	KASSERT(TAILQ_EMPTY(&uh->ppfree),
396333dcaa4SMatt Joras 	    ("unrhdr has postponed item for free"));
3970d8e0405SMatt Joras 	TAILQ_FOREACH_SAFE(up, &uh->head, list, uq) {
398333dcaa4SMatt Joras 		if (up->ptr != uh) {
399333dcaa4SMatt Joras 			Free(up->ptr);
400333dcaa4SMatt Joras 		}
401333dcaa4SMatt Joras 		Free(up);
402333dcaa4SMatt Joras 	}
403333dcaa4SMatt Joras 	uh->busy = 0;
404333dcaa4SMatt Joras 	uh->alloc = 0;
4050d8e0405SMatt Joras 	init_unrhdr(uh, uh->low, uh->high, uh->mtx);
4060d8e0405SMatt Joras 
4070d8e0405SMatt Joras 	check_unrhdr(uh, __LINE__);
408333dcaa4SMatt Joras }
409333dcaa4SMatt Joras 
410d9a54d5cSPoul-Henning Kamp static __inline int
411d9a54d5cSPoul-Henning Kamp is_bitmap(struct unrhdr *uh, struct unr *up)
412d9a54d5cSPoul-Henning Kamp {
413d9a54d5cSPoul-Henning Kamp 	return (up->ptr != uh && up->ptr != NULL);
414d9a54d5cSPoul-Henning Kamp }
415d9a54d5cSPoul-Henning Kamp 
416f6bde1fdSPoul-Henning Kamp /*
417d9a54d5cSPoul-Henning Kamp  * Look for sequence of items which can be combined into a bitmap, if
418d9a54d5cSPoul-Henning Kamp  * multiple are present, take the one which saves most memory.
419d9a54d5cSPoul-Henning Kamp  *
420d9a54d5cSPoul-Henning Kamp  * Return (1) if a sequence was found to indicate that another call
421d9a54d5cSPoul-Henning Kamp  * might be able to do more.  Return (0) if we found no suitable sequence.
422d9a54d5cSPoul-Henning Kamp  *
423d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
424d9a54d5cSPoul-Henning Kamp  */
425d9a54d5cSPoul-Henning Kamp static int
426d9a54d5cSPoul-Henning Kamp optimize_unr(struct unrhdr *uh)
427d9a54d5cSPoul-Henning Kamp {
428d9a54d5cSPoul-Henning Kamp 	struct unr *up, *uf, *us;
429d9a54d5cSPoul-Henning Kamp 	struct unrb *ub, *ubf;
430d9a54d5cSPoul-Henning Kamp 	u_int a, l, ba;
431d9a54d5cSPoul-Henning Kamp 
432d9a54d5cSPoul-Henning Kamp 	/*
433d9a54d5cSPoul-Henning Kamp 	 * Look for the run of items (if any) which when collapsed into
434d9a54d5cSPoul-Henning Kamp 	 * a bitmap would save most memory.
435d9a54d5cSPoul-Henning Kamp 	 */
436d9a54d5cSPoul-Henning Kamp 	us = NULL;
437d9a54d5cSPoul-Henning Kamp 	ba = 0;
438d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(uf, &uh->head, list) {
439d9a54d5cSPoul-Henning Kamp 		if (uf->len >= NBITS)
440d9a54d5cSPoul-Henning Kamp 			continue;
441d9a54d5cSPoul-Henning Kamp 		a = 1;
442d9a54d5cSPoul-Henning Kamp 		if (is_bitmap(uh, uf))
443d9a54d5cSPoul-Henning Kamp 			a++;
444d9a54d5cSPoul-Henning Kamp 		l = uf->len;
445d9a54d5cSPoul-Henning Kamp 		up = uf;
446d9a54d5cSPoul-Henning Kamp 		while (1) {
447d9a54d5cSPoul-Henning Kamp 			up = TAILQ_NEXT(up, list);
448d9a54d5cSPoul-Henning Kamp 			if (up == NULL)
449d9a54d5cSPoul-Henning Kamp 				break;
450d9a54d5cSPoul-Henning Kamp 			if ((up->len + l) > NBITS)
451d9a54d5cSPoul-Henning Kamp 				break;
452d9a54d5cSPoul-Henning Kamp 			a++;
453d9a54d5cSPoul-Henning Kamp 			if (is_bitmap(uh, up))
454d9a54d5cSPoul-Henning Kamp 				a++;
455d9a54d5cSPoul-Henning Kamp 			l += up->len;
456d9a54d5cSPoul-Henning Kamp 		}
457d9a54d5cSPoul-Henning Kamp 		if (a > ba) {
458d9a54d5cSPoul-Henning Kamp 			ba = a;
459d9a54d5cSPoul-Henning Kamp 			us = uf;
460d9a54d5cSPoul-Henning Kamp 		}
461d9a54d5cSPoul-Henning Kamp 	}
462d9a54d5cSPoul-Henning Kamp 	if (ba < 3)
463d9a54d5cSPoul-Henning Kamp 		return (0);
464d9a54d5cSPoul-Henning Kamp 
465d9a54d5cSPoul-Henning Kamp 	/*
466d9a54d5cSPoul-Henning Kamp 	 * If the first element is not a bitmap, make it one.
467d9a54d5cSPoul-Henning Kamp 	 * Trying to do so without allocating more memory complicates things
468d9a54d5cSPoul-Henning Kamp 	 * a bit
469d9a54d5cSPoul-Henning Kamp 	 */
470d9a54d5cSPoul-Henning Kamp 	if (!is_bitmap(uh, us)) {
471d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
472d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, us, list);
473d9a54d5cSPoul-Henning Kamp 		a = us->len;
474d9a54d5cSPoul-Henning Kamp 		l = us->ptr == uh ? 1 : 0;
475d9a54d5cSPoul-Henning Kamp 		ub = (void *)us;
4768907f744SAlan Somers 		bit_nclear(ub->map, 0, NBITS - 1);
4778907f744SAlan Somers 		if (l)
478d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, 0, a);
479d9a54d5cSPoul-Henning Kamp 		if (!is_bitmap(uh, uf)) {
4808907f744SAlan Somers 			if (uf->ptr == NULL)
481d9a54d5cSPoul-Henning Kamp 				bit_nclear(ub->map, a, a + uf->len - 1);
4828907f744SAlan Somers 			else
483d9a54d5cSPoul-Henning Kamp 				bit_nset(ub->map, a, a + uf->len - 1);
484d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
485d9a54d5cSPoul-Henning Kamp 			uf->len += a;
486d9a54d5cSPoul-Henning Kamp 			us = uf;
487d9a54d5cSPoul-Henning Kamp 		} else {
488d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
489d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, a++) {
4908907f744SAlan Somers 				if (bit_test(ubf->map, l))
491d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, a);
4928907f744SAlan Somers 				else
493d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, a);
494d9a54d5cSPoul-Henning Kamp 			}
495d9a54d5cSPoul-Henning Kamp 			uf->len = a;
496d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf->ptr);
497d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
498d9a54d5cSPoul-Henning Kamp 			us = uf;
499d9a54d5cSPoul-Henning Kamp 		}
500d9a54d5cSPoul-Henning Kamp 	}
501d9a54d5cSPoul-Henning Kamp 	ub = us->ptr;
502d9a54d5cSPoul-Henning Kamp 	while (1) {
503d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
504d9a54d5cSPoul-Henning Kamp 		if (uf == NULL)
505d9a54d5cSPoul-Henning Kamp 			return (1);
506d9a54d5cSPoul-Henning Kamp 		if (uf->len + us->len > NBITS)
507d9a54d5cSPoul-Henning Kamp 			return (1);
508d9a54d5cSPoul-Henning Kamp 		if (uf->ptr == NULL) {
509d9a54d5cSPoul-Henning Kamp 			bit_nclear(ub->map, us->len, us->len + uf->len - 1);
510d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
511d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
512d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
513d9a54d5cSPoul-Henning Kamp 		} else if (uf->ptr == uh) {
514d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, us->len, us->len + uf->len - 1);
515d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
516d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
517d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
518d9a54d5cSPoul-Henning Kamp 		} else {
519d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
520d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, us->len++) {
5218907f744SAlan Somers 				if (bit_test(ubf->map, l))
522d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, us->len);
5238907f744SAlan Somers 				else
524d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, us->len);
525d9a54d5cSPoul-Henning Kamp 			}
526d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
527d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, ubf);
528d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
529d9a54d5cSPoul-Henning Kamp 		}
530d9a54d5cSPoul-Henning Kamp 	}
531d9a54d5cSPoul-Henning Kamp }
532d9a54d5cSPoul-Henning Kamp 
533d9a54d5cSPoul-Henning Kamp /*
534d9a54d5cSPoul-Henning Kamp  * See if a given unr should be collapsed with a neighbor.
535d9a54d5cSPoul-Henning Kamp  *
536d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
537f6bde1fdSPoul-Henning Kamp  */
538f6bde1fdSPoul-Henning Kamp static void
539f6bde1fdSPoul-Henning Kamp collapse_unr(struct unrhdr *uh, struct unr *up)
540f6bde1fdSPoul-Henning Kamp {
541f6bde1fdSPoul-Henning Kamp 	struct unr *upp;
542d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
543f6bde1fdSPoul-Henning Kamp 
544d9a54d5cSPoul-Henning Kamp 	/* If bitmap is all set or clear, change it to runlength */
545d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
546d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
5478907f744SAlan Somers 		if (ub_full(ub, up->len)) {
548d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
549d9a54d5cSPoul-Henning Kamp 			up->ptr = uh;
5508907f744SAlan Somers 		} else if (ub_empty(ub, up->len)) {
551d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
552d9a54d5cSPoul-Henning Kamp 			up->ptr = NULL;
553d9a54d5cSPoul-Henning Kamp 		}
554d9a54d5cSPoul-Henning Kamp 	}
555d9a54d5cSPoul-Henning Kamp 
556d9a54d5cSPoul-Henning Kamp 	/* If nothing left in runlength, delete it */
557d9a54d5cSPoul-Henning Kamp 	if (up->len == 0) {
558d9a54d5cSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
559d9a54d5cSPoul-Henning Kamp 		if (upp == NULL)
560d9a54d5cSPoul-Henning Kamp 			upp = TAILQ_NEXT(up, list);
561d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, up, list);
562d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, up);
563d9a54d5cSPoul-Henning Kamp 		up = upp;
564d9a54d5cSPoul-Henning Kamp 	}
565d9a54d5cSPoul-Henning Kamp 
566d9a54d5cSPoul-Henning Kamp 	/* If we have "hot-spot" still, merge with neighbor if possible */
567d9a54d5cSPoul-Henning Kamp 	if (up != NULL) {
568f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
569f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
570f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
571f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
572f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
573f6bde1fdSPoul-Henning Kamp 			}
574f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_NEXT(up, list);
575f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
576f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
577f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
578f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
579f6bde1fdSPoul-Henning Kamp 		}
580f6bde1fdSPoul-Henning Kamp 	}
581f6bde1fdSPoul-Henning Kamp 
582d9a54d5cSPoul-Henning Kamp 	/* Merge into ->first if possible */
583d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
584d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == uh) {
585d9a54d5cSPoul-Henning Kamp 		uh->first += upp->len;
586d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
587d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
588d9a54d5cSPoul-Henning Kamp 		if (up == upp)
589d9a54d5cSPoul-Henning Kamp 			up = NULL;
590d9a54d5cSPoul-Henning Kamp 	}
591d9a54d5cSPoul-Henning Kamp 
592d9a54d5cSPoul-Henning Kamp 	/* Merge into ->last if possible */
593d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_LAST(&uh->head, unrhd);
594d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == NULL) {
595d9a54d5cSPoul-Henning Kamp 		uh->last += upp->len;
596d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
597d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
598d9a54d5cSPoul-Henning Kamp 		if (up == upp)
599d9a54d5cSPoul-Henning Kamp 			up = NULL;
600d9a54d5cSPoul-Henning Kamp 	}
601d9a54d5cSPoul-Henning Kamp 
602d9a54d5cSPoul-Henning Kamp 	/* Try to make bitmaps */
603d9a54d5cSPoul-Henning Kamp 	while (optimize_unr(uh))
604d9a54d5cSPoul-Henning Kamp 		continue;
605d9a54d5cSPoul-Henning Kamp }
606d9a54d5cSPoul-Henning Kamp 
607f6bde1fdSPoul-Henning Kamp /*
608f6bde1fdSPoul-Henning Kamp  * Allocate a free unr.
609f6bde1fdSPoul-Henning Kamp  */
610d9a54d5cSPoul-Henning Kamp int
611d9a54d5cSPoul-Henning Kamp alloc_unrl(struct unrhdr *uh)
612f6bde1fdSPoul-Henning Kamp {
613d9a54d5cSPoul-Henning Kamp 	struct unr *up;
614d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
615f6bde1fdSPoul-Henning Kamp 	u_int x;
616f6bde1fdSPoul-Henning Kamp 	int y;
617f6bde1fdSPoul-Henning Kamp 
618*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
619d9a54d5cSPoul-Henning Kamp 		mtx_assert(uh->mtx, MA_OWNED);
620f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
621d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
622d9a54d5cSPoul-Henning Kamp 
623f6bde1fdSPoul-Henning Kamp 	up = TAILQ_FIRST(&uh->head);
624f6bde1fdSPoul-Henning Kamp 
625d9a54d5cSPoul-Henning Kamp 	/*
626d9a54d5cSPoul-Henning Kamp 	 * If we have an ideal split, just adjust the first+last
627d9a54d5cSPoul-Henning Kamp 	 */
628d9a54d5cSPoul-Henning Kamp 	if (up == NULL && uh->last > 0) {
629d9a54d5cSPoul-Henning Kamp 		uh->first++;
630d9a54d5cSPoul-Henning Kamp 		uh->last--;
631f6bde1fdSPoul-Henning Kamp 		uh->busy++;
632f6bde1fdSPoul-Henning Kamp 		return (x);
633f6bde1fdSPoul-Henning Kamp 	}
634f6bde1fdSPoul-Henning Kamp 
635f6bde1fdSPoul-Henning Kamp 	/*
636d9a54d5cSPoul-Henning Kamp 	 * We can always allocate from the first list element, so if we have
637d9a54d5cSPoul-Henning Kamp 	 * nothing on the list, we must have run out of unit numbers.
638f6bde1fdSPoul-Henning Kamp 	 */
63993f6c81eSPoul-Henning Kamp 	if (up == NULL)
640d9a54d5cSPoul-Henning Kamp 		return (-1);
641d9a54d5cSPoul-Henning Kamp 
642d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr != uh, ("UNR first element is allocated"));
643d9a54d5cSPoul-Henning Kamp 
644d9a54d5cSPoul-Henning Kamp 	if (up->ptr == NULL) {	/* free run */
645d9a54d5cSPoul-Henning Kamp 		uh->first++;
646f6bde1fdSPoul-Henning Kamp 		up->len--;
647d9a54d5cSPoul-Henning Kamp 	} else {		/* bitmap */
648d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
649d9a54d5cSPoul-Henning Kamp 		bit_ffc(ub->map, up->len, &y);
650d9a54d5cSPoul-Henning Kamp 		KASSERT(y != -1, ("UNR corruption: No clear bit in bitmap."));
651d9a54d5cSPoul-Henning Kamp 		bit_set(ub->map, y);
652d9a54d5cSPoul-Henning Kamp 		x += y;
653d9a54d5cSPoul-Henning Kamp 	}
654f6bde1fdSPoul-Henning Kamp 	uh->busy++;
655d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
656f6bde1fdSPoul-Henning Kamp 	return (x);
657f6bde1fdSPoul-Henning Kamp }
658f6bde1fdSPoul-Henning Kamp 
659d9a54d5cSPoul-Henning Kamp int
660d9a54d5cSPoul-Henning Kamp alloc_unr(struct unrhdr *uh)
661d9a54d5cSPoul-Henning Kamp {
662d9a54d5cSPoul-Henning Kamp 	int i;
663d9a54d5cSPoul-Henning Kamp 
664*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
665d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
666d9a54d5cSPoul-Henning Kamp 	i = alloc_unrl(uh);
66709828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
668*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
669d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
670d9a54d5cSPoul-Henning Kamp 	return (i);
671d9a54d5cSPoul-Henning Kamp }
672d9a54d5cSPoul-Henning Kamp 
67313c02cbbSJaakko Heinonen static int
67413c02cbbSJaakko Heinonen alloc_unr_specificl(struct unrhdr *uh, u_int item, void **p1, void **p2)
67513c02cbbSJaakko Heinonen {
67613c02cbbSJaakko Heinonen 	struct unr *up, *upn;
67713c02cbbSJaakko Heinonen 	struct unrb *ub;
67813c02cbbSJaakko Heinonen 	u_int i, last, tl;
67913c02cbbSJaakko Heinonen 
680*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
68113c02cbbSJaakko Heinonen 		mtx_assert(uh->mtx, MA_OWNED);
68213c02cbbSJaakko Heinonen 
68313c02cbbSJaakko Heinonen 	if (item < uh->low + uh->first || item > uh->high)
68413c02cbbSJaakko Heinonen 		return (-1);
68513c02cbbSJaakko Heinonen 
68613c02cbbSJaakko Heinonen 	up = TAILQ_FIRST(&uh->head);
68713c02cbbSJaakko Heinonen 	/* Ideal split. */
68813c02cbbSJaakko Heinonen 	if (up == NULL && item - uh->low == uh->first) {
68913c02cbbSJaakko Heinonen 		uh->first++;
69013c02cbbSJaakko Heinonen 		uh->last--;
69113c02cbbSJaakko Heinonen 		uh->busy++;
69213c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
69313c02cbbSJaakko Heinonen 		return (item);
69413c02cbbSJaakko Heinonen 	}
69513c02cbbSJaakko Heinonen 
69613c02cbbSJaakko Heinonen 	i = item - uh->low - uh->first;
69713c02cbbSJaakko Heinonen 
69813c02cbbSJaakko Heinonen 	if (up == NULL) {
69913c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
70013c02cbbSJaakko Heinonen 		up->ptr = NULL;
70113c02cbbSJaakko Heinonen 		up->len = i;
70213c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
70313c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
70413c02cbbSJaakko Heinonen 		up->ptr = uh;
70513c02cbbSJaakko Heinonen 		up->len = 1;
70613c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
70713c02cbbSJaakko Heinonen 		uh->last = uh->high - uh->low - i;
70813c02cbbSJaakko Heinonen 		uh->busy++;
70913c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
71013c02cbbSJaakko Heinonen 		return (item);
71113c02cbbSJaakko Heinonen 	} else {
71213c02cbbSJaakko Heinonen 		/* Find the item which contains the unit we want to allocate. */
71313c02cbbSJaakko Heinonen 		TAILQ_FOREACH(up, &uh->head, list) {
71413c02cbbSJaakko Heinonen 			if (up->len > i)
71513c02cbbSJaakko Heinonen 				break;
71613c02cbbSJaakko Heinonen 			i -= up->len;
71713c02cbbSJaakko Heinonen 		}
71813c02cbbSJaakko Heinonen 	}
71913c02cbbSJaakko Heinonen 
72013c02cbbSJaakko Heinonen 	if (up == NULL) {
72113c02cbbSJaakko Heinonen 		if (i > 0) {
72213c02cbbSJaakko Heinonen 			up = new_unr(uh, p1, p2);
72313c02cbbSJaakko Heinonen 			up->ptr = NULL;
72413c02cbbSJaakko Heinonen 			up->len = i;
72513c02cbbSJaakko Heinonen 			TAILQ_INSERT_TAIL(&uh->head, up, list);
72613c02cbbSJaakko Heinonen 		}
72713c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
72813c02cbbSJaakko Heinonen 		up->ptr = uh;
72913c02cbbSJaakko Heinonen 		up->len = 1;
73013c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
73113c02cbbSJaakko Heinonen 		goto done;
73213c02cbbSJaakko Heinonen 	}
73313c02cbbSJaakko Heinonen 
73413c02cbbSJaakko Heinonen 	if (is_bitmap(uh, up)) {
73513c02cbbSJaakko Heinonen 		ub = up->ptr;
73613c02cbbSJaakko Heinonen 		if (bit_test(ub->map, i) == 0) {
73713c02cbbSJaakko Heinonen 			bit_set(ub->map, i);
73813c02cbbSJaakko Heinonen 			goto done;
73913c02cbbSJaakko Heinonen 		} else
74013c02cbbSJaakko Heinonen 			return (-1);
74113c02cbbSJaakko Heinonen 	} else if (up->ptr == uh)
74213c02cbbSJaakko Heinonen 		return (-1);
74313c02cbbSJaakko Heinonen 
74413c02cbbSJaakko Heinonen 	KASSERT(up->ptr == NULL,
74513c02cbbSJaakko Heinonen 	    ("alloc_unr_specificl: up->ptr != NULL (up=%p)", up));
74613c02cbbSJaakko Heinonen 
74713c02cbbSJaakko Heinonen 	/* Split off the tail end, if any. */
74813c02cbbSJaakko Heinonen 	tl = up->len - (1 + i);
74913c02cbbSJaakko Heinonen 	if (tl > 0) {
75013c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
75113c02cbbSJaakko Heinonen 		upn->ptr = NULL;
75213c02cbbSJaakko Heinonen 		upn->len = tl;
75313c02cbbSJaakko Heinonen 		TAILQ_INSERT_AFTER(&uh->head, up, upn, list);
75413c02cbbSJaakko Heinonen 	}
75513c02cbbSJaakko Heinonen 
75613c02cbbSJaakko Heinonen 	/* Split off head end, if any */
75713c02cbbSJaakko Heinonen 	if (i > 0) {
75813c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
75913c02cbbSJaakko Heinonen 		upn->len = i;
76013c02cbbSJaakko Heinonen 		upn->ptr = NULL;
76113c02cbbSJaakko Heinonen 		TAILQ_INSERT_BEFORE(up, upn, list);
76213c02cbbSJaakko Heinonen 	}
76313c02cbbSJaakko Heinonen 	up->len = 1;
76413c02cbbSJaakko Heinonen 	up->ptr = uh;
76513c02cbbSJaakko Heinonen 
76613c02cbbSJaakko Heinonen done:
76713c02cbbSJaakko Heinonen 	last = uh->high - uh->low - (item - uh->low);
76813c02cbbSJaakko Heinonen 	if (uh->last > last)
76913c02cbbSJaakko Heinonen 		uh->last = last;
77013c02cbbSJaakko Heinonen 	uh->busy++;
77113c02cbbSJaakko Heinonen 	collapse_unr(uh, up);
77213c02cbbSJaakko Heinonen 	check_unrhdr(uh, __LINE__);
77313c02cbbSJaakko Heinonen 	return (item);
77413c02cbbSJaakko Heinonen }
77513c02cbbSJaakko Heinonen 
77613c02cbbSJaakko Heinonen int
77713c02cbbSJaakko Heinonen alloc_unr_specific(struct unrhdr *uh, u_int item)
77813c02cbbSJaakko Heinonen {
77913c02cbbSJaakko Heinonen 	void *p1, *p2;
78013c02cbbSJaakko Heinonen 	int i;
78113c02cbbSJaakko Heinonen 
78213c02cbbSJaakko Heinonen 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "alloc_unr_specific");
78313c02cbbSJaakko Heinonen 
78413c02cbbSJaakko Heinonen 	p1 = Malloc(sizeof(struct unr));
78513c02cbbSJaakko Heinonen 	p2 = Malloc(sizeof(struct unr));
78613c02cbbSJaakko Heinonen 
787*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
78813c02cbbSJaakko Heinonen 		mtx_lock(uh->mtx);
78913c02cbbSJaakko Heinonen 	i = alloc_unr_specificl(uh, item, &p1, &p2);
790*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
79113c02cbbSJaakko Heinonen 		mtx_unlock(uh->mtx);
79213c02cbbSJaakko Heinonen 
79313c02cbbSJaakko Heinonen 	if (p1 != NULL)
79413c02cbbSJaakko Heinonen 		Free(p1);
79513c02cbbSJaakko Heinonen 	if (p2 != NULL)
79613c02cbbSJaakko Heinonen 		Free(p2);
79713c02cbbSJaakko Heinonen 
79813c02cbbSJaakko Heinonen 	return (i);
79913c02cbbSJaakko Heinonen }
80013c02cbbSJaakko Heinonen 
801f6bde1fdSPoul-Henning Kamp /*
802f6bde1fdSPoul-Henning Kamp  * Free a unr.
803f6bde1fdSPoul-Henning Kamp  *
804f6bde1fdSPoul-Henning Kamp  * If we can save unrs by using a bitmap, do so.
805f6bde1fdSPoul-Henning Kamp  */
806d9a54d5cSPoul-Henning Kamp static void
807d9a54d5cSPoul-Henning Kamp free_unrl(struct unrhdr *uh, u_int item, void **p1, void **p2)
808f6bde1fdSPoul-Henning Kamp {
809d9a54d5cSPoul-Henning Kamp 	struct unr *up, *upp, *upn;
810d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
811d9a54d5cSPoul-Henning Kamp 	u_int pl;
812f6bde1fdSPoul-Henning Kamp 
813f6bde1fdSPoul-Henning Kamp 	KASSERT(item >= uh->low && item <= uh->high,
814f6bde1fdSPoul-Henning Kamp 	    ("UNR: free_unr(%u) out of range [%u...%u]",
815f6bde1fdSPoul-Henning Kamp 	     item, uh->low, uh->high));
816f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
817f6bde1fdSPoul-Henning Kamp 	item -= uh->low;
818d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
819f6bde1fdSPoul-Henning Kamp 	/*
820d9a54d5cSPoul-Henning Kamp 	 * Freeing in the ideal split case
821f6bde1fdSPoul-Henning Kamp 	 */
822d9a54d5cSPoul-Henning Kamp 	if (item + 1 == uh->first && upp == NULL) {
823d9a54d5cSPoul-Henning Kamp 		uh->last++;
824d9a54d5cSPoul-Henning Kamp 		uh->first--;
825d9a54d5cSPoul-Henning Kamp 		uh->busy--;
826f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
827f6bde1fdSPoul-Henning Kamp 		return;
828f6bde1fdSPoul-Henning Kamp 	}
829d9a54d5cSPoul-Henning Kamp 	/*
830d9a54d5cSPoul-Henning Kamp  	 * Freeing in the ->first section.  Create a run starting at the
831d9a54d5cSPoul-Henning Kamp 	 * freed item.  The code below will subdivide it.
832d9a54d5cSPoul-Henning Kamp 	 */
833d9a54d5cSPoul-Henning Kamp 	if (item < uh->first) {
834d9a54d5cSPoul-Henning Kamp 		up = new_unr(uh, p1, p2);
835d9a54d5cSPoul-Henning Kamp 		up->ptr = uh;
836d9a54d5cSPoul-Henning Kamp 		up->len = uh->first - item;
837d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_HEAD(&uh->head, up, list);
838d9a54d5cSPoul-Henning Kamp 		uh->first -= up->len;
839f6bde1fdSPoul-Henning Kamp 	}
840f6bde1fdSPoul-Henning Kamp 
841d9a54d5cSPoul-Henning Kamp 	item -= uh->first;
842d9a54d5cSPoul-Henning Kamp 
843d9a54d5cSPoul-Henning Kamp 	/* Find the item which contains the unit we want to free */
844d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
845d9a54d5cSPoul-Henning Kamp 		if (up->len > item)
846d9a54d5cSPoul-Henning Kamp 			break;
847d9a54d5cSPoul-Henning Kamp 		item -= up->len;
848d9a54d5cSPoul-Henning Kamp 	}
849d9a54d5cSPoul-Henning Kamp 
850d9a54d5cSPoul-Henning Kamp 	/* Handle bitmap items */
851d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
852d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
853d9a54d5cSPoul-Henning Kamp 
854d9a54d5cSPoul-Henning Kamp 		KASSERT(bit_test(ub->map, item) != 0,
855d9a54d5cSPoul-Henning Kamp 		    ("UNR: Freeing free item %d (bitmap)\n", item));
856d9a54d5cSPoul-Henning Kamp 		bit_clear(ub->map, item);
857d9a54d5cSPoul-Henning Kamp 		uh->busy--;
858d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
859d9a54d5cSPoul-Henning Kamp 		return;
860d9a54d5cSPoul-Henning Kamp 	}
861d9a54d5cSPoul-Henning Kamp 
862d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr == uh, ("UNR Freeing free item %d (run))\n", item));
863f6bde1fdSPoul-Henning Kamp 
864f6bde1fdSPoul-Henning Kamp 	/* Just this one left, reap it */
865f6bde1fdSPoul-Henning Kamp 	if (up->len == 1) {
866f6bde1fdSPoul-Henning Kamp 		up->ptr = NULL;
867f6bde1fdSPoul-Henning Kamp 		uh->busy--;
868f6bde1fdSPoul-Henning Kamp 		collapse_unr(uh, up);
869f6bde1fdSPoul-Henning Kamp 		return;
870f6bde1fdSPoul-Henning Kamp 	}
871f6bde1fdSPoul-Henning Kamp 
872d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item into the previous 'free' run */
873f6bde1fdSPoul-Henning Kamp 	upp = TAILQ_PREV(up, unrhd, list);
874d9a54d5cSPoul-Henning Kamp 	if (item == 0 && upp != NULL && upp->ptr == NULL) {
875f6bde1fdSPoul-Henning Kamp 		upp->len++;
876f6bde1fdSPoul-Henning Kamp 		up->len--;
877f6bde1fdSPoul-Henning Kamp 		uh->busy--;
878d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
879f6bde1fdSPoul-Henning Kamp 		return;
880f6bde1fdSPoul-Henning Kamp 	}
881f6bde1fdSPoul-Henning Kamp 
882d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item to the next 'free' run */
883f6bde1fdSPoul-Henning Kamp 	upn = TAILQ_NEXT(up, list);
884d9a54d5cSPoul-Henning Kamp 	if (item == up->len - 1 && upn != NULL && upn->ptr == NULL) {
885f6bde1fdSPoul-Henning Kamp 		upn->len++;
886f6bde1fdSPoul-Henning Kamp 		up->len--;
887f6bde1fdSPoul-Henning Kamp 		uh->busy--;
888d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
889f6bde1fdSPoul-Henning Kamp 		return;
890f6bde1fdSPoul-Henning Kamp 	}
891f6bde1fdSPoul-Henning Kamp 
892f6bde1fdSPoul-Henning Kamp 	/* Split off the tail end, if any. */
893d9a54d5cSPoul-Henning Kamp 	pl = up->len - (1 + item);
894f6bde1fdSPoul-Henning Kamp 	if (pl > 0) {
895d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
896f6bde1fdSPoul-Henning Kamp 		upp->ptr = uh;
897f6bde1fdSPoul-Henning Kamp 		upp->len = pl;
898f6bde1fdSPoul-Henning Kamp 		TAILQ_INSERT_AFTER(&uh->head, up, upp, list);
899f6bde1fdSPoul-Henning Kamp 	}
900f6bde1fdSPoul-Henning Kamp 
901d9a54d5cSPoul-Henning Kamp 	/* Split off head end, if any */
902d9a54d5cSPoul-Henning Kamp 	if (item > 0) {
903d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
904d9a54d5cSPoul-Henning Kamp 		upp->len = item;
905d9a54d5cSPoul-Henning Kamp 		upp->ptr = uh;
906d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_BEFORE(up, upp, list);
907d9a54d5cSPoul-Henning Kamp 	}
908f6bde1fdSPoul-Henning Kamp 	up->len = 1;
909f6bde1fdSPoul-Henning Kamp 	up->ptr = NULL;
910f6bde1fdSPoul-Henning Kamp 	uh->busy--;
911d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
912f6bde1fdSPoul-Henning Kamp }
913f6bde1fdSPoul-Henning Kamp 
914d9a54d5cSPoul-Henning Kamp void
915d9a54d5cSPoul-Henning Kamp free_unr(struct unrhdr *uh, u_int item)
916d9a54d5cSPoul-Henning Kamp {
917d9a54d5cSPoul-Henning Kamp 	void *p1, *p2;
918f6bde1fdSPoul-Henning Kamp 
9197550e3eaSKonstantin Belousov 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "free_unr");
920d9a54d5cSPoul-Henning Kamp 	p1 = Malloc(sizeof(struct unr));
921d9a54d5cSPoul-Henning Kamp 	p2 = Malloc(sizeof(struct unr));
922*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
923d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
924d9a54d5cSPoul-Henning Kamp 	free_unrl(uh, item, &p1, &p2);
92509828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
926*e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
927d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
928d9a54d5cSPoul-Henning Kamp 	if (p1 != NULL)
929d9a54d5cSPoul-Henning Kamp 		Free(p1);
930d9a54d5cSPoul-Henning Kamp 	if (p2 != NULL)
931d9a54d5cSPoul-Henning Kamp 		Free(p2);
932f6bde1fdSPoul-Henning Kamp }
933f6bde1fdSPoul-Henning Kamp 
93493f6c81eSPoul-Henning Kamp #ifndef _KERNEL	/* USERLAND test driver */
93593f6c81eSPoul-Henning Kamp 
936f6bde1fdSPoul-Henning Kamp /*
937794277daSAlan Somers  * Simple stochastic test driver for the above functions.  The code resides
938794277daSAlan Somers  * here so that it can access static functions and structures.
939f6bde1fdSPoul-Henning Kamp  */
940f6bde1fdSPoul-Henning Kamp 
941794277daSAlan Somers static bool verbose;
942794277daSAlan Somers #define VPRINTF(...)	{if (verbose) printf(__VA_ARGS__);}
943794277daSAlan Somers 
944f6bde1fdSPoul-Henning Kamp static void
945f6bde1fdSPoul-Henning Kamp print_unr(struct unrhdr *uh, struct unr *up)
946f6bde1fdSPoul-Henning Kamp {
947f6bde1fdSPoul-Henning Kamp 	u_int x;
948d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
949f6bde1fdSPoul-Henning Kamp 
950f6bde1fdSPoul-Henning Kamp 	printf("  %p len = %5u ", up, up->len);
951f6bde1fdSPoul-Henning Kamp 	if (up->ptr == NULL)
952f6bde1fdSPoul-Henning Kamp 		printf("free\n");
953f6bde1fdSPoul-Henning Kamp 	else if (up->ptr == uh)
954f6bde1fdSPoul-Henning Kamp 		printf("alloc\n");
955f6bde1fdSPoul-Henning Kamp 	else {
956d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
9578907f744SAlan Somers 		printf("bitmap [");
958d9a54d5cSPoul-Henning Kamp 		for (x = 0; x < up->len; x++) {
959d9a54d5cSPoul-Henning Kamp 			if (bit_test(ub->map, x))
960d9a54d5cSPoul-Henning Kamp 				printf("#");
961f6bde1fdSPoul-Henning Kamp 			else
962d9a54d5cSPoul-Henning Kamp 				printf(" ");
963f6bde1fdSPoul-Henning Kamp 		}
964f6bde1fdSPoul-Henning Kamp 		printf("]\n");
965f6bde1fdSPoul-Henning Kamp 	}
966f6bde1fdSPoul-Henning Kamp }
967f6bde1fdSPoul-Henning Kamp 
968f6bde1fdSPoul-Henning Kamp static void
969f6bde1fdSPoul-Henning Kamp print_unrhdr(struct unrhdr *uh)
970f6bde1fdSPoul-Henning Kamp {
971f6bde1fdSPoul-Henning Kamp 	struct unr *up;
972f6bde1fdSPoul-Henning Kamp 	u_int x;
973f6bde1fdSPoul-Henning Kamp 
974d9a54d5cSPoul-Henning Kamp 	printf(
975d9a54d5cSPoul-Henning Kamp 	    "%p low = %u high = %u first = %u last = %u busy %u chunks = %u\n",
976d9a54d5cSPoul-Henning Kamp 	    uh, uh->low, uh->high, uh->first, uh->last, uh->busy, uh->alloc);
977d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
978f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
979f6bde1fdSPoul-Henning Kamp 		printf("  from = %5u", x);
980f6bde1fdSPoul-Henning Kamp 		print_unr(uh, up);
981f6bde1fdSPoul-Henning Kamp 		if (up->ptr == NULL || up->ptr == uh)
982f6bde1fdSPoul-Henning Kamp 			x += up->len;
983f6bde1fdSPoul-Henning Kamp 		else
984f6bde1fdSPoul-Henning Kamp 			x += NBITS;
985f6bde1fdSPoul-Henning Kamp 	}
986f6bde1fdSPoul-Henning Kamp }
987f6bde1fdSPoul-Henning Kamp 
98813c02cbbSJaakko Heinonen static void
98913c02cbbSJaakko Heinonen test_alloc_unr(struct unrhdr *uh, u_int i, char a[])
99013c02cbbSJaakko Heinonen {
99113c02cbbSJaakko Heinonen 	int j;
99213c02cbbSJaakko Heinonen 
99313c02cbbSJaakko Heinonen 	if (a[i]) {
994794277daSAlan Somers 		VPRINTF("F %u\n", i);
99513c02cbbSJaakko Heinonen 		free_unr(uh, i);
99613c02cbbSJaakko Heinonen 		a[i] = 0;
99713c02cbbSJaakko Heinonen 	} else {
99813c02cbbSJaakko Heinonen 		no_alloc = 1;
99913c02cbbSJaakko Heinonen 		j = alloc_unr(uh);
100013c02cbbSJaakko Heinonen 		if (j != -1) {
100113c02cbbSJaakko Heinonen 			a[j] = 1;
1002794277daSAlan Somers 			VPRINTF("A %d\n", j);
100313c02cbbSJaakko Heinonen 		}
100413c02cbbSJaakko Heinonen 		no_alloc = 0;
100513c02cbbSJaakko Heinonen 	}
100613c02cbbSJaakko Heinonen }
100713c02cbbSJaakko Heinonen 
100813c02cbbSJaakko Heinonen static void
100913c02cbbSJaakko Heinonen test_alloc_unr_specific(struct unrhdr *uh, u_int i, char a[])
101013c02cbbSJaakko Heinonen {
101113c02cbbSJaakko Heinonen 	int j;
101213c02cbbSJaakko Heinonen 
101313c02cbbSJaakko Heinonen 	j = alloc_unr_specific(uh, i);
101413c02cbbSJaakko Heinonen 	if (j == -1) {
1015794277daSAlan Somers 		VPRINTF("F %u\n", i);
101613c02cbbSJaakko Heinonen 		a[i] = 0;
101713c02cbbSJaakko Heinonen 		free_unr(uh, i);
101813c02cbbSJaakko Heinonen 	} else {
101913c02cbbSJaakko Heinonen 		a[i] = 1;
1020794277daSAlan Somers 		VPRINTF("A %d\n", j);
102113c02cbbSJaakko Heinonen 	}
102213c02cbbSJaakko Heinonen }
102313c02cbbSJaakko Heinonen 
1024794277daSAlan Somers static void
1025794277daSAlan Somers usage(char** argv)
1026794277daSAlan Somers {
1027794277daSAlan Somers 	printf("%s [-h] [-r REPETITIONS] [-v]\n", argv[0]);
1028794277daSAlan Somers }
1029f6bde1fdSPoul-Henning Kamp 
1030f6bde1fdSPoul-Henning Kamp int
1031794277daSAlan Somers main(int argc, char **argv)
1032f6bde1fdSPoul-Henning Kamp {
1033f6bde1fdSPoul-Henning Kamp 	struct unrhdr *uh;
1034794277daSAlan Somers 	char *a;
1035794277daSAlan Somers 	long count = 10000;	/* Number of unrs to test */
103637f32e53SAlan Somers 	long reps = 1, m;
1037794277daSAlan Somers 	int ch;
1038cd565040SConrad Meyer 	u_int i;
1039794277daSAlan Somers 
1040794277daSAlan Somers 	verbose = false;
1041794277daSAlan Somers 
1042794277daSAlan Somers 	while ((ch = getopt(argc, argv, "hr:v")) != -1) {
1043794277daSAlan Somers 		switch (ch) {
1044794277daSAlan Somers 		case 'r':
1045794277daSAlan Somers 			errno = 0;
1046794277daSAlan Somers 			reps = strtol(optarg, NULL, 0);
1047794277daSAlan Somers 			if (errno == ERANGE || errno == EINVAL) {
1048794277daSAlan Somers 				usage(argv);
1049794277daSAlan Somers 				exit(2);
1050794277daSAlan Somers 			}
1051794277daSAlan Somers 
1052794277daSAlan Somers 			break;
1053794277daSAlan Somers 		case 'v':
1054794277daSAlan Somers 			verbose = true;
1055794277daSAlan Somers 			break;
1056794277daSAlan Somers 		case 'h':
1057794277daSAlan Somers 		default:
1058794277daSAlan Somers 			usage(argv);
1059794277daSAlan Somers 			exit(2);
1060794277daSAlan Somers 		}
1061794277daSAlan Somers 	}
1062f6bde1fdSPoul-Henning Kamp 
1063d9a54d5cSPoul-Henning Kamp 	setbuf(stdout, NULL);
1064794277daSAlan Somers 	uh = new_unrhdr(0, count - 1, NULL);
1065d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1066f6bde1fdSPoul-Henning Kamp 
1067794277daSAlan Somers 	a = calloc(count, sizeof(char));
1068794277daSAlan Somers 	if (a == NULL)
1069794277daSAlan Somers 		err(1, "calloc failed");
1070f6bde1fdSPoul-Henning Kamp 
1071794277daSAlan Somers 	printf("sizeof(struct unr) %zu\n", sizeof(struct unr));
1072794277daSAlan Somers 	printf("sizeof(struct unrb) %zu\n", sizeof(struct unrb));
1073794277daSAlan Somers 	printf("sizeof(struct unrhdr) %zu\n", sizeof(struct unrhdr));
1074b4f69365SEd Maste 	printf("NBITS %lu\n", (unsigned long)NBITS);
1075794277daSAlan Somers 	for (m = 0; m < count * reps; m++) {
1076cd565040SConrad Meyer 		i = arc4random_uniform(count);
1077d9a54d5cSPoul-Henning Kamp #if 0
1078d9a54d5cSPoul-Henning Kamp 		if (a[i] && (j & 1))
1079d9a54d5cSPoul-Henning Kamp 			continue;
1080d9a54d5cSPoul-Henning Kamp #endif
1081cd565040SConrad Meyer 		if ((arc4random() & 1) != 0)
108213c02cbbSJaakko Heinonen 			test_alloc_unr(uh, i, a);
108313c02cbbSJaakko Heinonen 		else
108413c02cbbSJaakko Heinonen 			test_alloc_unr_specific(uh, i, a);
108513c02cbbSJaakko Heinonen 
1086794277daSAlan Somers 		if (verbose)
1087f6bde1fdSPoul-Henning Kamp 			print_unrhdr(uh);
1088f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
1089f6bde1fdSPoul-Henning Kamp 	}
109037f32e53SAlan Somers 	for (i = 0; i < (u_int)count; i++) {
1091d9a54d5cSPoul-Henning Kamp 		if (a[i]) {
1092794277daSAlan Somers 			if (verbose) {
1093d9a54d5cSPoul-Henning Kamp 				printf("C %u\n", i);
1094e4fea39eSPoul-Henning Kamp 				print_unrhdr(uh);
1095d9a54d5cSPoul-Henning Kamp 			}
1096794277daSAlan Somers 			free_unr(uh, i);
1097794277daSAlan Somers 		}
1098d9a54d5cSPoul-Henning Kamp 	}
1099d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1100e4fea39eSPoul-Henning Kamp 	delete_unrhdr(uh);
1101794277daSAlan Somers 	free(a);
1102f6bde1fdSPoul-Henning Kamp 	return (0);
1103f6bde1fdSPoul-Henning Kamp }
1104f6bde1fdSPoul-Henning Kamp #endif
1105