xref: /freebsd/sys/kern/subr_unit.c (revision 36b1f8a81ef96b42ce446efb79cffd577f1819f7)
1f6bde1fdSPoul-Henning Kamp /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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 
101f6bde1fdSPoul-Henning Kamp #else /* ...USERLAND */
102f6bde1fdSPoul-Henning Kamp 
103794277daSAlan Somers #include <bitstring.h>
104794277daSAlan Somers #include <err.h>
105794277daSAlan Somers #include <errno.h>
106794277daSAlan Somers #include <getopt.h>
107794277daSAlan Somers #include <stdbool.h>
108f6bde1fdSPoul-Henning Kamp #include <stdio.h>
109f6bde1fdSPoul-Henning Kamp #include <stdlib.h>
110d9a54d5cSPoul-Henning Kamp #include <string.h>
111f6bde1fdSPoul-Henning Kamp 
112f6bde1fdSPoul-Henning Kamp #define KASSERT(cond, arg) \
113f6bde1fdSPoul-Henning Kamp 	do { \
114f6bde1fdSPoul-Henning Kamp 		if (!(cond)) { \
115f6bde1fdSPoul-Henning Kamp 			printf arg; \
116d9a54d5cSPoul-Henning Kamp 			abort(); \
117f6bde1fdSPoul-Henning Kamp 		} \
118f6bde1fdSPoul-Henning Kamp 	} while (0)
119f6bde1fdSPoul-Henning Kamp 
120d9a54d5cSPoul-Henning Kamp static int no_alloc;
121d9a54d5cSPoul-Henning Kamp #define Malloc(foo) _Malloc(foo, __LINE__)
122d9a54d5cSPoul-Henning Kamp static void *
123d9a54d5cSPoul-Henning Kamp _Malloc(size_t foo, int line)
124d9a54d5cSPoul-Henning Kamp {
125d9a54d5cSPoul-Henning Kamp 
126d9a54d5cSPoul-Henning Kamp 	KASSERT(no_alloc == 0, ("malloc in wrong place() line %d", line));
127d9a54d5cSPoul-Henning Kamp 	return (calloc(foo, 1));
128d9a54d5cSPoul-Henning Kamp }
129f6bde1fdSPoul-Henning Kamp #define Free(foo) free(foo)
130f6bde1fdSPoul-Henning Kamp 
131d9a54d5cSPoul-Henning Kamp struct unrhdr;
132d9a54d5cSPoul-Henning Kamp 
1336fe78ad4SKonstantin Belousov #define	UNR_NO_MTX	((void *)(uintptr_t)-1)
1346fe78ad4SKonstantin Belousov 
135d9a54d5cSPoul-Henning Kamp struct mtx {
136d9a54d5cSPoul-Henning Kamp 	int	state;
137d9a54d5cSPoul-Henning Kamp } unitmtx;
138d9a54d5cSPoul-Henning Kamp 
139d9a54d5cSPoul-Henning Kamp static void
140d9a54d5cSPoul-Henning Kamp mtx_lock(struct mtx *mp)
141d9a54d5cSPoul-Henning Kamp {
142d9a54d5cSPoul-Henning Kamp 	KASSERT(mp->state == 0, ("mutex already locked"));
143d9a54d5cSPoul-Henning Kamp 	mp->state = 1;
144d9a54d5cSPoul-Henning Kamp }
145d9a54d5cSPoul-Henning Kamp 
146d9a54d5cSPoul-Henning Kamp static void
147d9a54d5cSPoul-Henning Kamp mtx_unlock(struct mtx *mp)
148d9a54d5cSPoul-Henning Kamp {
149d9a54d5cSPoul-Henning Kamp 	KASSERT(mp->state == 1, ("mutex not locked"));
150d9a54d5cSPoul-Henning Kamp 	mp->state = 0;
151d9a54d5cSPoul-Henning Kamp }
152d9a54d5cSPoul-Henning Kamp 
153d9a54d5cSPoul-Henning Kamp #define MA_OWNED	9
154d9a54d5cSPoul-Henning Kamp 
155d9a54d5cSPoul-Henning Kamp static void
156d9a54d5cSPoul-Henning Kamp mtx_assert(struct mtx *mp, int flag)
157d9a54d5cSPoul-Henning Kamp {
158d9a54d5cSPoul-Henning Kamp 	if (flag == MA_OWNED) {
159d9a54d5cSPoul-Henning Kamp 		KASSERT(mp->state == 1, ("mtx_assert(MA_OWNED) not true"));
160d9a54d5cSPoul-Henning Kamp 	}
161d9a54d5cSPoul-Henning Kamp }
162d9a54d5cSPoul-Henning Kamp 
163d9a54d5cSPoul-Henning Kamp #define CTASSERT(foo)
16424e8eaf1SJaakko Heinonen #define WITNESS_WARN(flags, lock, fmt, ...)	(void)0
165d9a54d5cSPoul-Henning Kamp 
166d9a54d5cSPoul-Henning Kamp #endif /* USERLAND */
167f6bde1fdSPoul-Henning Kamp 
168f6bde1fdSPoul-Henning Kamp /*
169f6bde1fdSPoul-Henning Kamp  * This is our basic building block.
170f6bde1fdSPoul-Henning Kamp  *
171f6bde1fdSPoul-Henning Kamp  * It can be used in three different ways depending on the value of the ptr
172f6bde1fdSPoul-Henning Kamp  * element:
173f6bde1fdSPoul-Henning Kamp  *     If ptr is NULL, it represents a run of free items.
174f6bde1fdSPoul-Henning Kamp  *     If ptr points to the unrhdr it represents a run of allocated items.
1758907f744SAlan Somers  *     Otherwise it points to a bitstring of allocated items.
176f6bde1fdSPoul-Henning Kamp  *
177f6bde1fdSPoul-Henning Kamp  * For runs the len field is the length of the run.
178f6bde1fdSPoul-Henning Kamp  * For bitmaps the len field represents the number of allocated items.
179f6bde1fdSPoul-Henning Kamp  *
180f6bde1fdSPoul-Henning Kamp  * The bitmap is the same size as struct unr to optimize memory management.
181f6bde1fdSPoul-Henning Kamp  */
182f6bde1fdSPoul-Henning Kamp struct unr {
183f6bde1fdSPoul-Henning Kamp 	TAILQ_ENTRY(unr)	list;
184f6bde1fdSPoul-Henning Kamp 	u_int			len;
185f6bde1fdSPoul-Henning Kamp 	void			*ptr;
186f6bde1fdSPoul-Henning Kamp };
187f6bde1fdSPoul-Henning Kamp 
188d9a54d5cSPoul-Henning Kamp struct unrb {
1898907f744SAlan Somers 	bitstr_t		map[sizeof(struct unr) / sizeof(bitstr_t)];
190d9a54d5cSPoul-Henning Kamp };
191d9a54d5cSPoul-Henning Kamp 
1928907f744SAlan Somers CTASSERT((sizeof(struct unr) % sizeof(bitstr_t)) == 0);
193d9a54d5cSPoul-Henning Kamp 
1948907f744SAlan Somers /* Number of bits we can store in the bitmap */
195042ec55fSKonstantin Belousov #define NBITS (NBBY * sizeof(((struct unrb *)NULL)->map))
1968907f744SAlan Somers 
197*36b1f8a8SKonstantin Belousov static inline bool
198*36b1f8a8SKonstantin Belousov is_bitmap(struct unrhdr *uh, struct unr *up)
199*36b1f8a8SKonstantin Belousov {
200*36b1f8a8SKonstantin Belousov 	return (up->ptr != uh && up->ptr != NULL);
201*36b1f8a8SKonstantin Belousov }
202*36b1f8a8SKonstantin Belousov 
2038907f744SAlan Somers /* Is the unrb empty in at least the first len bits? */
2048907f744SAlan Somers static inline bool
2058907f744SAlan Somers ub_empty(struct unrb *ub, int len) {
2068907f744SAlan Somers 	int first_set;
2078907f744SAlan Somers 
2088907f744SAlan Somers 	bit_ffs(ub->map, len, &first_set);
2098907f744SAlan Somers 	return (first_set == -1);
2108907f744SAlan Somers }
2118907f744SAlan Somers 
2128907f744SAlan Somers /* Is the unrb full?  That is, is the number of set elements equal to len? */
2138907f744SAlan Somers static inline bool
2148907f744SAlan Somers ub_full(struct unrb *ub, int len)
2158907f744SAlan Somers {
2168907f744SAlan Somers 	int first_clear;
2178907f744SAlan Somers 
2188907f744SAlan Somers 	bit_ffc(ub->map, len, &first_clear);
2198907f744SAlan Somers 	return (first_clear == -1);
2208907f744SAlan Somers }
2218907f744SAlan Somers 
222f6bde1fdSPoul-Henning Kamp #if defined(DIAGNOSTIC) || !defined(_KERNEL)
223f6bde1fdSPoul-Henning Kamp /*
224f6bde1fdSPoul-Henning Kamp  * Consistency check function.
225f6bde1fdSPoul-Henning Kamp  *
226f6bde1fdSPoul-Henning Kamp  * Checks the internal consistency as well as we can.
227f6bde1fdSPoul-Henning Kamp  *
228f6bde1fdSPoul-Henning Kamp  * Called at all boundaries of this API.
229f6bde1fdSPoul-Henning Kamp  */
230f6bde1fdSPoul-Henning Kamp static void
231f6bde1fdSPoul-Henning Kamp check_unrhdr(struct unrhdr *uh, int line)
232f6bde1fdSPoul-Henning Kamp {
233f6bde1fdSPoul-Henning Kamp 	struct unr *up;
234d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
2351b82e02fSAlan Somers 	int w;
2361b82e02fSAlan Somers 	u_int y, z;
237f6bde1fdSPoul-Henning Kamp 
238d9a54d5cSPoul-Henning Kamp 	y = uh->first;
239f6bde1fdSPoul-Henning Kamp 	z = 0;
240f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
241f6bde1fdSPoul-Henning Kamp 		z++;
242*36b1f8a8SKonstantin Belousov 		if (is_bitmap(uh, up)) {
243d9a54d5cSPoul-Henning Kamp 			ub = up->ptr;
244d9a54d5cSPoul-Henning Kamp 			KASSERT (up->len <= NBITS,
2458907f744SAlan Somers 			    ("UNR inconsistency: len %u max %zd (line %d)\n",
246d9a54d5cSPoul-Henning Kamp 			    up->len, NBITS, line));
247f6bde1fdSPoul-Henning Kamp 			z++;
248f6bde1fdSPoul-Henning Kamp 			w = 0;
2491b82e02fSAlan Somers 			bit_count(ub->map, 0, up->len, &w);
250d9a54d5cSPoul-Henning Kamp 			y += w;
251d9a54d5cSPoul-Henning Kamp 		} else if (up->ptr != NULL)
252f6bde1fdSPoul-Henning Kamp 			y += up->len;
253f6bde1fdSPoul-Henning Kamp 	}
254f6bde1fdSPoul-Henning Kamp 	KASSERT (y == uh->busy,
255f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: items %u found %u (line %d)\n",
256f6bde1fdSPoul-Henning Kamp 	    uh->busy, y, line));
257f6bde1fdSPoul-Henning Kamp 	KASSERT (z == uh->alloc,
258f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: chunks %u found %u (line %d)\n",
259f6bde1fdSPoul-Henning Kamp 	    uh->alloc, z, line));
260f6bde1fdSPoul-Henning Kamp }
261f6bde1fdSPoul-Henning Kamp 
262f6bde1fdSPoul-Henning Kamp #else
263f6bde1fdSPoul-Henning Kamp 
264f6bde1fdSPoul-Henning Kamp static __inline void
2658907f744SAlan Somers check_unrhdr(struct unrhdr *uh __unused, int line __unused)
266f6bde1fdSPoul-Henning Kamp {
267f6bde1fdSPoul-Henning Kamp 
268f6bde1fdSPoul-Henning Kamp }
269f6bde1fdSPoul-Henning Kamp 
270f6bde1fdSPoul-Henning Kamp #endif
271f6bde1fdSPoul-Henning Kamp 
272f6bde1fdSPoul-Henning Kamp /*
273f6bde1fdSPoul-Henning Kamp  * Userland memory management.  Just use calloc and keep track of how
274f6bde1fdSPoul-Henning Kamp  * many elements we have allocated for check_unrhdr().
275f6bde1fdSPoul-Henning Kamp  */
276f6bde1fdSPoul-Henning Kamp 
277f6bde1fdSPoul-Henning Kamp static __inline void *
278d9a54d5cSPoul-Henning Kamp new_unr(struct unrhdr *uh, void **p1, void **p2)
279f6bde1fdSPoul-Henning Kamp {
280d9a54d5cSPoul-Henning Kamp 	void *p;
281f6bde1fdSPoul-Henning Kamp 
282d9a54d5cSPoul-Henning Kamp 	uh->alloc++;
283d9a54d5cSPoul-Henning Kamp 	KASSERT(*p1 != NULL || *p2 != NULL, ("Out of cached memory"));
284d9a54d5cSPoul-Henning Kamp 	if (*p1 != NULL) {
285d9a54d5cSPoul-Henning Kamp 		p = *p1;
286d9a54d5cSPoul-Henning Kamp 		*p1 = NULL;
287d9a54d5cSPoul-Henning Kamp 		return (p);
288d9a54d5cSPoul-Henning Kamp 	} else {
289d9a54d5cSPoul-Henning Kamp 		p = *p2;
290d9a54d5cSPoul-Henning Kamp 		*p2 = NULL;
291d9a54d5cSPoul-Henning Kamp 		return (p);
292d9a54d5cSPoul-Henning Kamp 	}
293f6bde1fdSPoul-Henning Kamp }
294f6bde1fdSPoul-Henning Kamp 
295f6bde1fdSPoul-Henning Kamp static __inline void
296f6bde1fdSPoul-Henning Kamp delete_unr(struct unrhdr *uh, void *ptr)
297f6bde1fdSPoul-Henning Kamp {
29809828ba9SKonstantin Belousov 	struct unr *up;
299d9a54d5cSPoul-Henning Kamp 
300f6bde1fdSPoul-Henning Kamp 	uh->alloc--;
30109828ba9SKonstantin Belousov 	up = ptr;
30209828ba9SKonstantin Belousov 	TAILQ_INSERT_TAIL(&uh->ppfree, up, list);
30309828ba9SKonstantin Belousov }
30409828ba9SKonstantin Belousov 
30509828ba9SKonstantin Belousov void
30609828ba9SKonstantin Belousov clean_unrhdrl(struct unrhdr *uh)
30709828ba9SKonstantin Belousov {
30809828ba9SKonstantin Belousov 	struct unr *up;
30909828ba9SKonstantin Belousov 
310e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
31109828ba9SKonstantin Belousov 		mtx_assert(uh->mtx, MA_OWNED);
31209828ba9SKonstantin Belousov 	while ((up = TAILQ_FIRST(&uh->ppfree)) != NULL) {
31309828ba9SKonstantin Belousov 		TAILQ_REMOVE(&uh->ppfree, up, list);
314e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
31509828ba9SKonstantin Belousov 			mtx_unlock(uh->mtx);
31609828ba9SKonstantin Belousov 		Free(up);
317e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
31809828ba9SKonstantin Belousov 			mtx_lock(uh->mtx);
31909828ba9SKonstantin Belousov 	}
32009828ba9SKonstantin Belousov 
32109828ba9SKonstantin Belousov }
32209828ba9SKonstantin Belousov 
32309828ba9SKonstantin Belousov void
32409828ba9SKonstantin Belousov clean_unrhdr(struct unrhdr *uh)
32509828ba9SKonstantin Belousov {
32609828ba9SKonstantin Belousov 
327e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
32809828ba9SKonstantin Belousov 		mtx_lock(uh->mtx);
32909828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
330e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
33109828ba9SKonstantin Belousov 		mtx_unlock(uh->mtx);
332f6bde1fdSPoul-Henning Kamp }
333f6bde1fdSPoul-Henning Kamp 
334dc872d46SKonstantin Belousov void
335dc872d46SKonstantin Belousov init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex)
336f6bde1fdSPoul-Henning Kamp {
337f6bde1fdSPoul-Henning Kamp 
338831aa555SJaakko Heinonen 	KASSERT(low >= 0 && low <= high,
339501812f2SJaakko Heinonen 	    ("UNR: use error: new_unrhdr(%d, %d)", low, high));
340e59b940dSKonstantin Belousov 	if (mutex == UNR_NO_MTX)
341e59b940dSKonstantin Belousov 		uh->mtx = NULL;
342e59b940dSKonstantin Belousov 	else if (mutex != NULL)
343d9a54d5cSPoul-Henning Kamp 		uh->mtx = mutex;
344d9a54d5cSPoul-Henning Kamp 	else
345d9a54d5cSPoul-Henning Kamp 		uh->mtx = &unitmtx;
346f6bde1fdSPoul-Henning Kamp 	TAILQ_INIT(&uh->head);
34709828ba9SKonstantin Belousov 	TAILQ_INIT(&uh->ppfree);
348f6bde1fdSPoul-Henning Kamp 	uh->low = low;
349f6bde1fdSPoul-Henning Kamp 	uh->high = high;
350d9a54d5cSPoul-Henning Kamp 	uh->first = 0;
351d9a54d5cSPoul-Henning Kamp 	uh->last = 1 + (high - low);
352c4be460eSKonstantin Belousov 	uh->busy = 0;
353c4be460eSKonstantin Belousov 	uh->alloc = 0;
354f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
355dc872d46SKonstantin Belousov }
356dc872d46SKonstantin Belousov 
357dc872d46SKonstantin Belousov /*
358dc872d46SKonstantin Belousov  * Allocate a new unrheader set.
359dc872d46SKonstantin Belousov  *
360dc872d46SKonstantin Belousov  * Highest and lowest valid values given as parameters.
361dc872d46SKonstantin Belousov  */
362dc872d46SKonstantin Belousov 
363dc872d46SKonstantin Belousov struct unrhdr *
364dc872d46SKonstantin Belousov new_unrhdr(int low, int high, struct mtx *mutex)
365dc872d46SKonstantin Belousov {
366dc872d46SKonstantin Belousov 	struct unrhdr *uh;
367dc872d46SKonstantin Belousov 
368dc872d46SKonstantin Belousov 	uh = Malloc(sizeof *uh);
369dc872d46SKonstantin Belousov 	init_unrhdr(uh, low, high, mutex);
370f6bde1fdSPoul-Henning Kamp 	return (uh);
371f6bde1fdSPoul-Henning Kamp }
372f6bde1fdSPoul-Henning Kamp 
373e4fea39eSPoul-Henning Kamp void
374e4fea39eSPoul-Henning Kamp delete_unrhdr(struct unrhdr *uh)
375e4fea39eSPoul-Henning Kamp {
376e4fea39eSPoul-Henning Kamp 
377d9a54d5cSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
378e4fea39eSPoul-Henning Kamp 	KASSERT(uh->busy == 0, ("unrhdr has %u allocations", uh->busy));
379e4fea39eSPoul-Henning Kamp 	KASSERT(uh->alloc == 0, ("UNR memory leak in delete_unrhdr"));
38009828ba9SKonstantin Belousov 	KASSERT(TAILQ_FIRST(&uh->ppfree) == NULL,
38109828ba9SKonstantin Belousov 	    ("unrhdr has postponed item for free"));
382e4fea39eSPoul-Henning Kamp 	Free(uh);
383e4fea39eSPoul-Henning Kamp }
384e4fea39eSPoul-Henning Kamp 
385333dcaa4SMatt Joras void
386333dcaa4SMatt Joras clear_unrhdr(struct unrhdr *uh)
387333dcaa4SMatt Joras {
388333dcaa4SMatt Joras 	struct unr *up, *uq;
389333dcaa4SMatt Joras 
390333dcaa4SMatt Joras 	KASSERT(TAILQ_EMPTY(&uh->ppfree),
391333dcaa4SMatt Joras 	    ("unrhdr has postponed item for free"));
3920d8e0405SMatt Joras 	TAILQ_FOREACH_SAFE(up, &uh->head, list, uq) {
393333dcaa4SMatt Joras 		if (up->ptr != uh) {
394333dcaa4SMatt Joras 			Free(up->ptr);
395333dcaa4SMatt Joras 		}
396333dcaa4SMatt Joras 		Free(up);
397333dcaa4SMatt Joras 	}
398333dcaa4SMatt Joras 	uh->busy = 0;
399333dcaa4SMatt Joras 	uh->alloc = 0;
4000d8e0405SMatt Joras 	init_unrhdr(uh, uh->low, uh->high, uh->mtx);
4010d8e0405SMatt Joras 
4020d8e0405SMatt Joras 	check_unrhdr(uh, __LINE__);
403333dcaa4SMatt Joras }
404333dcaa4SMatt Joras 
405f6bde1fdSPoul-Henning Kamp /*
406d9a54d5cSPoul-Henning Kamp  * Look for sequence of items which can be combined into a bitmap, if
407d9a54d5cSPoul-Henning Kamp  * multiple are present, take the one which saves most memory.
408d9a54d5cSPoul-Henning Kamp  *
409d9a54d5cSPoul-Henning Kamp  * Return (1) if a sequence was found to indicate that another call
410d9a54d5cSPoul-Henning Kamp  * might be able to do more.  Return (0) if we found no suitable sequence.
411d9a54d5cSPoul-Henning Kamp  *
412d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
413d9a54d5cSPoul-Henning Kamp  */
414d9a54d5cSPoul-Henning Kamp static int
415d9a54d5cSPoul-Henning Kamp optimize_unr(struct unrhdr *uh)
416d9a54d5cSPoul-Henning Kamp {
417d9a54d5cSPoul-Henning Kamp 	struct unr *up, *uf, *us;
418d9a54d5cSPoul-Henning Kamp 	struct unrb *ub, *ubf;
419d9a54d5cSPoul-Henning Kamp 	u_int a, l, ba;
420d9a54d5cSPoul-Henning Kamp 
421d9a54d5cSPoul-Henning Kamp 	/*
422d9a54d5cSPoul-Henning Kamp 	 * Look for the run of items (if any) which when collapsed into
423d9a54d5cSPoul-Henning Kamp 	 * a bitmap would save most memory.
424d9a54d5cSPoul-Henning Kamp 	 */
425d9a54d5cSPoul-Henning Kamp 	us = NULL;
426d9a54d5cSPoul-Henning Kamp 	ba = 0;
427d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(uf, &uh->head, list) {
428d9a54d5cSPoul-Henning Kamp 		if (uf->len >= NBITS)
429d9a54d5cSPoul-Henning Kamp 			continue;
430d9a54d5cSPoul-Henning Kamp 		a = 1;
431d9a54d5cSPoul-Henning Kamp 		if (is_bitmap(uh, uf))
432d9a54d5cSPoul-Henning Kamp 			a++;
433d9a54d5cSPoul-Henning Kamp 		l = uf->len;
434d9a54d5cSPoul-Henning Kamp 		up = uf;
435d9a54d5cSPoul-Henning Kamp 		while (1) {
436d9a54d5cSPoul-Henning Kamp 			up = TAILQ_NEXT(up, list);
437d9a54d5cSPoul-Henning Kamp 			if (up == NULL)
438d9a54d5cSPoul-Henning Kamp 				break;
439d9a54d5cSPoul-Henning Kamp 			if ((up->len + l) > NBITS)
440d9a54d5cSPoul-Henning Kamp 				break;
441d9a54d5cSPoul-Henning Kamp 			a++;
442d9a54d5cSPoul-Henning Kamp 			if (is_bitmap(uh, up))
443d9a54d5cSPoul-Henning Kamp 				a++;
444d9a54d5cSPoul-Henning Kamp 			l += up->len;
445d9a54d5cSPoul-Henning Kamp 		}
446d9a54d5cSPoul-Henning Kamp 		if (a > ba) {
447d9a54d5cSPoul-Henning Kamp 			ba = a;
448d9a54d5cSPoul-Henning Kamp 			us = uf;
449d9a54d5cSPoul-Henning Kamp 		}
450d9a54d5cSPoul-Henning Kamp 	}
451d9a54d5cSPoul-Henning Kamp 	if (ba < 3)
452d9a54d5cSPoul-Henning Kamp 		return (0);
453d9a54d5cSPoul-Henning Kamp 
454d9a54d5cSPoul-Henning Kamp 	/*
455d9a54d5cSPoul-Henning Kamp 	 * If the first element is not a bitmap, make it one.
456d9a54d5cSPoul-Henning Kamp 	 * Trying to do so without allocating more memory complicates things
457d9a54d5cSPoul-Henning Kamp 	 * a bit
458d9a54d5cSPoul-Henning Kamp 	 */
459d9a54d5cSPoul-Henning Kamp 	if (!is_bitmap(uh, us)) {
460d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
461d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, us, list);
462d9a54d5cSPoul-Henning Kamp 		a = us->len;
463d9a54d5cSPoul-Henning Kamp 		l = us->ptr == uh ? 1 : 0;
464d9a54d5cSPoul-Henning Kamp 		ub = (void *)us;
4658907f744SAlan Somers 		bit_nclear(ub->map, 0, NBITS - 1);
4668907f744SAlan Somers 		if (l)
467d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, 0, a);
468d9a54d5cSPoul-Henning Kamp 		if (!is_bitmap(uh, uf)) {
4698907f744SAlan Somers 			if (uf->ptr == NULL)
470d9a54d5cSPoul-Henning Kamp 				bit_nclear(ub->map, a, a + uf->len - 1);
4718907f744SAlan Somers 			else
472d9a54d5cSPoul-Henning Kamp 				bit_nset(ub->map, a, a + uf->len - 1);
473d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
474d9a54d5cSPoul-Henning Kamp 			uf->len += a;
475d9a54d5cSPoul-Henning Kamp 			us = uf;
476d9a54d5cSPoul-Henning Kamp 		} else {
477d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
478d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, a++) {
4798907f744SAlan Somers 				if (bit_test(ubf->map, l))
480d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, a);
4818907f744SAlan Somers 				else
482d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, a);
483d9a54d5cSPoul-Henning Kamp 			}
484d9a54d5cSPoul-Henning Kamp 			uf->len = a;
485d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf->ptr);
486d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
487d9a54d5cSPoul-Henning Kamp 			us = uf;
488d9a54d5cSPoul-Henning Kamp 		}
489d9a54d5cSPoul-Henning Kamp 	}
490d9a54d5cSPoul-Henning Kamp 	ub = us->ptr;
491d9a54d5cSPoul-Henning Kamp 	while (1) {
492d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
493d9a54d5cSPoul-Henning Kamp 		if (uf == NULL)
494d9a54d5cSPoul-Henning Kamp 			return (1);
495d9a54d5cSPoul-Henning Kamp 		if (uf->len + us->len > NBITS)
496d9a54d5cSPoul-Henning Kamp 			return (1);
497d9a54d5cSPoul-Henning Kamp 		if (uf->ptr == NULL) {
498d9a54d5cSPoul-Henning Kamp 			bit_nclear(ub->map, us->len, us->len + uf->len - 1);
499d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
500d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
501d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
502d9a54d5cSPoul-Henning Kamp 		} else if (uf->ptr == uh) {
503d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, us->len, us->len + uf->len - 1);
504d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
505d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
506d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
507d9a54d5cSPoul-Henning Kamp 		} else {
508d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
509d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, us->len++) {
5108907f744SAlan Somers 				if (bit_test(ubf->map, l))
511d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, us->len);
5128907f744SAlan Somers 				else
513d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, us->len);
514d9a54d5cSPoul-Henning Kamp 			}
515d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
516d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, ubf);
517d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
518d9a54d5cSPoul-Henning Kamp 		}
519d9a54d5cSPoul-Henning Kamp 	}
520d9a54d5cSPoul-Henning Kamp }
521d9a54d5cSPoul-Henning Kamp 
522d9a54d5cSPoul-Henning Kamp /*
523d9a54d5cSPoul-Henning Kamp  * See if a given unr should be collapsed with a neighbor.
524d9a54d5cSPoul-Henning Kamp  *
525d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
526f6bde1fdSPoul-Henning Kamp  */
527f6bde1fdSPoul-Henning Kamp static void
528f6bde1fdSPoul-Henning Kamp collapse_unr(struct unrhdr *uh, struct unr *up)
529f6bde1fdSPoul-Henning Kamp {
530f6bde1fdSPoul-Henning Kamp 	struct unr *upp;
531d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
532f6bde1fdSPoul-Henning Kamp 
533d9a54d5cSPoul-Henning Kamp 	/* If bitmap is all set or clear, change it to runlength */
534d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
535d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
5368907f744SAlan Somers 		if (ub_full(ub, up->len)) {
537d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
538d9a54d5cSPoul-Henning Kamp 			up->ptr = uh;
5398907f744SAlan Somers 		} else if (ub_empty(ub, up->len)) {
540d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
541d9a54d5cSPoul-Henning Kamp 			up->ptr = NULL;
542d9a54d5cSPoul-Henning Kamp 		}
543d9a54d5cSPoul-Henning Kamp 	}
544d9a54d5cSPoul-Henning Kamp 
545d9a54d5cSPoul-Henning Kamp 	/* If nothing left in runlength, delete it */
546d9a54d5cSPoul-Henning Kamp 	if (up->len == 0) {
547d9a54d5cSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
548d9a54d5cSPoul-Henning Kamp 		if (upp == NULL)
549d9a54d5cSPoul-Henning Kamp 			upp = TAILQ_NEXT(up, list);
550d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, up, list);
551d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, up);
552d9a54d5cSPoul-Henning Kamp 		up = upp;
553d9a54d5cSPoul-Henning Kamp 	}
554d9a54d5cSPoul-Henning Kamp 
555d9a54d5cSPoul-Henning Kamp 	/* If we have "hot-spot" still, merge with neighbor if possible */
556d9a54d5cSPoul-Henning Kamp 	if (up != NULL) {
557f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
558f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
559f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
560f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
561f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
562f6bde1fdSPoul-Henning Kamp 			}
563f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_NEXT(up, list);
564f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
565f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
566f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
567f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
568f6bde1fdSPoul-Henning Kamp 		}
569f6bde1fdSPoul-Henning Kamp 	}
570f6bde1fdSPoul-Henning Kamp 
571d9a54d5cSPoul-Henning Kamp 	/* Merge into ->first if possible */
572d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
573d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == uh) {
574d9a54d5cSPoul-Henning Kamp 		uh->first += upp->len;
575d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
576d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
577d9a54d5cSPoul-Henning Kamp 		if (up == upp)
578d9a54d5cSPoul-Henning Kamp 			up = NULL;
579d9a54d5cSPoul-Henning Kamp 	}
580d9a54d5cSPoul-Henning Kamp 
581d9a54d5cSPoul-Henning Kamp 	/* Merge into ->last if possible */
582d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_LAST(&uh->head, unrhd);
583d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == NULL) {
584d9a54d5cSPoul-Henning Kamp 		uh->last += upp->len;
585d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
586d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
587d9a54d5cSPoul-Henning Kamp 		if (up == upp)
588d9a54d5cSPoul-Henning Kamp 			up = NULL;
589d9a54d5cSPoul-Henning Kamp 	}
590d9a54d5cSPoul-Henning Kamp 
591d9a54d5cSPoul-Henning Kamp 	/* Try to make bitmaps */
592d9a54d5cSPoul-Henning Kamp 	while (optimize_unr(uh))
593d9a54d5cSPoul-Henning Kamp 		continue;
594d9a54d5cSPoul-Henning Kamp }
595d9a54d5cSPoul-Henning Kamp 
596f6bde1fdSPoul-Henning Kamp /*
597f6bde1fdSPoul-Henning Kamp  * Allocate a free unr.
598f6bde1fdSPoul-Henning Kamp  */
599d9a54d5cSPoul-Henning Kamp int
600d9a54d5cSPoul-Henning Kamp alloc_unrl(struct unrhdr *uh)
601f6bde1fdSPoul-Henning Kamp {
602d9a54d5cSPoul-Henning Kamp 	struct unr *up;
603d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
604f6bde1fdSPoul-Henning Kamp 	u_int x;
605f6bde1fdSPoul-Henning Kamp 	int y;
606f6bde1fdSPoul-Henning Kamp 
607e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
608d9a54d5cSPoul-Henning Kamp 		mtx_assert(uh->mtx, MA_OWNED);
609f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
610d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
611d9a54d5cSPoul-Henning Kamp 
612f6bde1fdSPoul-Henning Kamp 	up = TAILQ_FIRST(&uh->head);
613f6bde1fdSPoul-Henning Kamp 
614d9a54d5cSPoul-Henning Kamp 	/*
615d9a54d5cSPoul-Henning Kamp 	 * If we have an ideal split, just adjust the first+last
616d9a54d5cSPoul-Henning Kamp 	 */
617d9a54d5cSPoul-Henning Kamp 	if (up == NULL && uh->last > 0) {
618d9a54d5cSPoul-Henning Kamp 		uh->first++;
619d9a54d5cSPoul-Henning Kamp 		uh->last--;
620f6bde1fdSPoul-Henning Kamp 		uh->busy++;
621f6bde1fdSPoul-Henning Kamp 		return (x);
622f6bde1fdSPoul-Henning Kamp 	}
623f6bde1fdSPoul-Henning Kamp 
624f6bde1fdSPoul-Henning Kamp 	/*
625d9a54d5cSPoul-Henning Kamp 	 * We can always allocate from the first list element, so if we have
626d9a54d5cSPoul-Henning Kamp 	 * nothing on the list, we must have run out of unit numbers.
627f6bde1fdSPoul-Henning Kamp 	 */
62893f6c81eSPoul-Henning Kamp 	if (up == NULL)
629d9a54d5cSPoul-Henning Kamp 		return (-1);
630d9a54d5cSPoul-Henning Kamp 
631d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr != uh, ("UNR first element is allocated"));
632d9a54d5cSPoul-Henning Kamp 
633d9a54d5cSPoul-Henning Kamp 	if (up->ptr == NULL) {	/* free run */
634d9a54d5cSPoul-Henning Kamp 		uh->first++;
635f6bde1fdSPoul-Henning Kamp 		up->len--;
636d9a54d5cSPoul-Henning Kamp 	} else {		/* bitmap */
637d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
638d9a54d5cSPoul-Henning Kamp 		bit_ffc(ub->map, up->len, &y);
639d9a54d5cSPoul-Henning Kamp 		KASSERT(y != -1, ("UNR corruption: No clear bit in bitmap."));
640d9a54d5cSPoul-Henning Kamp 		bit_set(ub->map, y);
641d9a54d5cSPoul-Henning Kamp 		x += y;
642d9a54d5cSPoul-Henning Kamp 	}
643f6bde1fdSPoul-Henning Kamp 	uh->busy++;
644d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
645f6bde1fdSPoul-Henning Kamp 	return (x);
646f6bde1fdSPoul-Henning Kamp }
647f6bde1fdSPoul-Henning Kamp 
648d9a54d5cSPoul-Henning Kamp int
649d9a54d5cSPoul-Henning Kamp alloc_unr(struct unrhdr *uh)
650d9a54d5cSPoul-Henning Kamp {
651d9a54d5cSPoul-Henning Kamp 	int i;
652d9a54d5cSPoul-Henning Kamp 
653e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
654d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
655d9a54d5cSPoul-Henning Kamp 	i = alloc_unrl(uh);
65609828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
657e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
658d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
659d9a54d5cSPoul-Henning Kamp 	return (i);
660d9a54d5cSPoul-Henning Kamp }
661d9a54d5cSPoul-Henning Kamp 
66213c02cbbSJaakko Heinonen static int
66313c02cbbSJaakko Heinonen alloc_unr_specificl(struct unrhdr *uh, u_int item, void **p1, void **p2)
66413c02cbbSJaakko Heinonen {
66513c02cbbSJaakko Heinonen 	struct unr *up, *upn;
66613c02cbbSJaakko Heinonen 	struct unrb *ub;
66713c02cbbSJaakko Heinonen 	u_int i, last, tl;
66813c02cbbSJaakko Heinonen 
669e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
67013c02cbbSJaakko Heinonen 		mtx_assert(uh->mtx, MA_OWNED);
67113c02cbbSJaakko Heinonen 
67213c02cbbSJaakko Heinonen 	if (item < uh->low + uh->first || item > uh->high)
67313c02cbbSJaakko Heinonen 		return (-1);
67413c02cbbSJaakko Heinonen 
67513c02cbbSJaakko Heinonen 	up = TAILQ_FIRST(&uh->head);
67613c02cbbSJaakko Heinonen 	/* Ideal split. */
67713c02cbbSJaakko Heinonen 	if (up == NULL && item - uh->low == uh->first) {
67813c02cbbSJaakko Heinonen 		uh->first++;
67913c02cbbSJaakko Heinonen 		uh->last--;
68013c02cbbSJaakko Heinonen 		uh->busy++;
68113c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
68213c02cbbSJaakko Heinonen 		return (item);
68313c02cbbSJaakko Heinonen 	}
68413c02cbbSJaakko Heinonen 
68513c02cbbSJaakko Heinonen 	i = item - uh->low - uh->first;
68613c02cbbSJaakko Heinonen 
68713c02cbbSJaakko Heinonen 	if (up == NULL) {
68813c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
68913c02cbbSJaakko Heinonen 		up->ptr = NULL;
69013c02cbbSJaakko Heinonen 		up->len = i;
69113c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
69213c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
69313c02cbbSJaakko Heinonen 		up->ptr = uh;
69413c02cbbSJaakko Heinonen 		up->len = 1;
69513c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
69613c02cbbSJaakko Heinonen 		uh->last = uh->high - uh->low - i;
69713c02cbbSJaakko Heinonen 		uh->busy++;
69813c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
69913c02cbbSJaakko Heinonen 		return (item);
70013c02cbbSJaakko Heinonen 	} else {
70113c02cbbSJaakko Heinonen 		/* Find the item which contains the unit we want to allocate. */
70213c02cbbSJaakko Heinonen 		TAILQ_FOREACH(up, &uh->head, list) {
70313c02cbbSJaakko Heinonen 			if (up->len > i)
70413c02cbbSJaakko Heinonen 				break;
70513c02cbbSJaakko Heinonen 			i -= up->len;
70613c02cbbSJaakko Heinonen 		}
70713c02cbbSJaakko Heinonen 	}
70813c02cbbSJaakko Heinonen 
70913c02cbbSJaakko Heinonen 	if (up == NULL) {
71013c02cbbSJaakko Heinonen 		if (i > 0) {
71113c02cbbSJaakko Heinonen 			up = new_unr(uh, p1, p2);
71213c02cbbSJaakko Heinonen 			up->ptr = NULL;
71313c02cbbSJaakko Heinonen 			up->len = i;
71413c02cbbSJaakko Heinonen 			TAILQ_INSERT_TAIL(&uh->head, up, list);
71513c02cbbSJaakko Heinonen 		}
71613c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
71713c02cbbSJaakko Heinonen 		up->ptr = uh;
71813c02cbbSJaakko Heinonen 		up->len = 1;
71913c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
72013c02cbbSJaakko Heinonen 		goto done;
72113c02cbbSJaakko Heinonen 	}
72213c02cbbSJaakko Heinonen 
72313c02cbbSJaakko Heinonen 	if (is_bitmap(uh, up)) {
72413c02cbbSJaakko Heinonen 		ub = up->ptr;
72513c02cbbSJaakko Heinonen 		if (bit_test(ub->map, i) == 0) {
72613c02cbbSJaakko Heinonen 			bit_set(ub->map, i);
72713c02cbbSJaakko Heinonen 			goto done;
72813c02cbbSJaakko Heinonen 		} else
72913c02cbbSJaakko Heinonen 			return (-1);
73013c02cbbSJaakko Heinonen 	} else if (up->ptr == uh)
73113c02cbbSJaakko Heinonen 		return (-1);
73213c02cbbSJaakko Heinonen 
73313c02cbbSJaakko Heinonen 	KASSERT(up->ptr == NULL,
73413c02cbbSJaakko Heinonen 	    ("alloc_unr_specificl: up->ptr != NULL (up=%p)", up));
73513c02cbbSJaakko Heinonen 
73613c02cbbSJaakko Heinonen 	/* Split off the tail end, if any. */
73713c02cbbSJaakko Heinonen 	tl = up->len - (1 + i);
73813c02cbbSJaakko Heinonen 	if (tl > 0) {
73913c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
74013c02cbbSJaakko Heinonen 		upn->ptr = NULL;
74113c02cbbSJaakko Heinonen 		upn->len = tl;
74213c02cbbSJaakko Heinonen 		TAILQ_INSERT_AFTER(&uh->head, up, upn, list);
74313c02cbbSJaakko Heinonen 	}
74413c02cbbSJaakko Heinonen 
74513c02cbbSJaakko Heinonen 	/* Split off head end, if any */
74613c02cbbSJaakko Heinonen 	if (i > 0) {
74713c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
74813c02cbbSJaakko Heinonen 		upn->len = i;
74913c02cbbSJaakko Heinonen 		upn->ptr = NULL;
75013c02cbbSJaakko Heinonen 		TAILQ_INSERT_BEFORE(up, upn, list);
75113c02cbbSJaakko Heinonen 	}
75213c02cbbSJaakko Heinonen 	up->len = 1;
75313c02cbbSJaakko Heinonen 	up->ptr = uh;
75413c02cbbSJaakko Heinonen 
75513c02cbbSJaakko Heinonen done:
75613c02cbbSJaakko Heinonen 	last = uh->high - uh->low - (item - uh->low);
75713c02cbbSJaakko Heinonen 	if (uh->last > last)
75813c02cbbSJaakko Heinonen 		uh->last = last;
75913c02cbbSJaakko Heinonen 	uh->busy++;
76013c02cbbSJaakko Heinonen 	collapse_unr(uh, up);
76113c02cbbSJaakko Heinonen 	check_unrhdr(uh, __LINE__);
76213c02cbbSJaakko Heinonen 	return (item);
76313c02cbbSJaakko Heinonen }
76413c02cbbSJaakko Heinonen 
76513c02cbbSJaakko Heinonen int
76613c02cbbSJaakko Heinonen alloc_unr_specific(struct unrhdr *uh, u_int item)
76713c02cbbSJaakko Heinonen {
76813c02cbbSJaakko Heinonen 	void *p1, *p2;
76913c02cbbSJaakko Heinonen 	int i;
77013c02cbbSJaakko Heinonen 
77113c02cbbSJaakko Heinonen 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "alloc_unr_specific");
77213c02cbbSJaakko Heinonen 
77313c02cbbSJaakko Heinonen 	p1 = Malloc(sizeof(struct unr));
77413c02cbbSJaakko Heinonen 	p2 = Malloc(sizeof(struct unr));
77513c02cbbSJaakko Heinonen 
776e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
77713c02cbbSJaakko Heinonen 		mtx_lock(uh->mtx);
77813c02cbbSJaakko Heinonen 	i = alloc_unr_specificl(uh, item, &p1, &p2);
779e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
78013c02cbbSJaakko Heinonen 		mtx_unlock(uh->mtx);
78113c02cbbSJaakko Heinonen 
78213c02cbbSJaakko Heinonen 	if (p1 != NULL)
78313c02cbbSJaakko Heinonen 		Free(p1);
78413c02cbbSJaakko Heinonen 	if (p2 != NULL)
78513c02cbbSJaakko Heinonen 		Free(p2);
78613c02cbbSJaakko Heinonen 
78713c02cbbSJaakko Heinonen 	return (i);
78813c02cbbSJaakko Heinonen }
78913c02cbbSJaakko Heinonen 
790f6bde1fdSPoul-Henning Kamp /*
791f6bde1fdSPoul-Henning Kamp  * Free a unr.
792f6bde1fdSPoul-Henning Kamp  *
793f6bde1fdSPoul-Henning Kamp  * If we can save unrs by using a bitmap, do so.
794f6bde1fdSPoul-Henning Kamp  */
795d9a54d5cSPoul-Henning Kamp static void
796d9a54d5cSPoul-Henning Kamp free_unrl(struct unrhdr *uh, u_int item, void **p1, void **p2)
797f6bde1fdSPoul-Henning Kamp {
798d9a54d5cSPoul-Henning Kamp 	struct unr *up, *upp, *upn;
799d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
800d9a54d5cSPoul-Henning Kamp 	u_int pl;
801f6bde1fdSPoul-Henning Kamp 
802f6bde1fdSPoul-Henning Kamp 	KASSERT(item >= uh->low && item <= uh->high,
803f6bde1fdSPoul-Henning Kamp 	    ("UNR: free_unr(%u) out of range [%u...%u]",
804f6bde1fdSPoul-Henning Kamp 	     item, uh->low, uh->high));
805f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
806f6bde1fdSPoul-Henning Kamp 	item -= uh->low;
807d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
808f6bde1fdSPoul-Henning Kamp 	/*
809d9a54d5cSPoul-Henning Kamp 	 * Freeing in the ideal split case
810f6bde1fdSPoul-Henning Kamp 	 */
811d9a54d5cSPoul-Henning Kamp 	if (item + 1 == uh->first && upp == NULL) {
812d9a54d5cSPoul-Henning Kamp 		uh->last++;
813d9a54d5cSPoul-Henning Kamp 		uh->first--;
814d9a54d5cSPoul-Henning Kamp 		uh->busy--;
815f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
816f6bde1fdSPoul-Henning Kamp 		return;
817f6bde1fdSPoul-Henning Kamp 	}
818d9a54d5cSPoul-Henning Kamp 	/*
819d9a54d5cSPoul-Henning Kamp  	 * Freeing in the ->first section.  Create a run starting at the
820d9a54d5cSPoul-Henning Kamp 	 * freed item.  The code below will subdivide it.
821d9a54d5cSPoul-Henning Kamp 	 */
822d9a54d5cSPoul-Henning Kamp 	if (item < uh->first) {
823d9a54d5cSPoul-Henning Kamp 		up = new_unr(uh, p1, p2);
824d9a54d5cSPoul-Henning Kamp 		up->ptr = uh;
825d9a54d5cSPoul-Henning Kamp 		up->len = uh->first - item;
826d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_HEAD(&uh->head, up, list);
827d9a54d5cSPoul-Henning Kamp 		uh->first -= up->len;
828f6bde1fdSPoul-Henning Kamp 	}
829f6bde1fdSPoul-Henning Kamp 
830d9a54d5cSPoul-Henning Kamp 	item -= uh->first;
831d9a54d5cSPoul-Henning Kamp 
832d9a54d5cSPoul-Henning Kamp 	/* Find the item which contains the unit we want to free */
833d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
834d9a54d5cSPoul-Henning Kamp 		if (up->len > item)
835d9a54d5cSPoul-Henning Kamp 			break;
836d9a54d5cSPoul-Henning Kamp 		item -= up->len;
837d9a54d5cSPoul-Henning Kamp 	}
838d9a54d5cSPoul-Henning Kamp 
839d9a54d5cSPoul-Henning Kamp 	/* Handle bitmap items */
840d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
841d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
842d9a54d5cSPoul-Henning Kamp 
843d9a54d5cSPoul-Henning Kamp 		KASSERT(bit_test(ub->map, item) != 0,
844d9a54d5cSPoul-Henning Kamp 		    ("UNR: Freeing free item %d (bitmap)\n", item));
845d9a54d5cSPoul-Henning Kamp 		bit_clear(ub->map, item);
846d9a54d5cSPoul-Henning Kamp 		uh->busy--;
847d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
848d9a54d5cSPoul-Henning Kamp 		return;
849d9a54d5cSPoul-Henning Kamp 	}
850d9a54d5cSPoul-Henning Kamp 
851d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr == uh, ("UNR Freeing free item %d (run))\n", item));
852f6bde1fdSPoul-Henning Kamp 
853f6bde1fdSPoul-Henning Kamp 	/* Just this one left, reap it */
854f6bde1fdSPoul-Henning Kamp 	if (up->len == 1) {
855f6bde1fdSPoul-Henning Kamp 		up->ptr = NULL;
856f6bde1fdSPoul-Henning Kamp 		uh->busy--;
857f6bde1fdSPoul-Henning Kamp 		collapse_unr(uh, up);
858f6bde1fdSPoul-Henning Kamp 		return;
859f6bde1fdSPoul-Henning Kamp 	}
860f6bde1fdSPoul-Henning Kamp 
861d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item into the previous 'free' run */
862f6bde1fdSPoul-Henning Kamp 	upp = TAILQ_PREV(up, unrhd, list);
863d9a54d5cSPoul-Henning Kamp 	if (item == 0 && upp != NULL && upp->ptr == NULL) {
864f6bde1fdSPoul-Henning Kamp 		upp->len++;
865f6bde1fdSPoul-Henning Kamp 		up->len--;
866f6bde1fdSPoul-Henning Kamp 		uh->busy--;
867d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
868f6bde1fdSPoul-Henning Kamp 		return;
869f6bde1fdSPoul-Henning Kamp 	}
870f6bde1fdSPoul-Henning Kamp 
871d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item to the next 'free' run */
872f6bde1fdSPoul-Henning Kamp 	upn = TAILQ_NEXT(up, list);
873d9a54d5cSPoul-Henning Kamp 	if (item == up->len - 1 && upn != NULL && upn->ptr == NULL) {
874f6bde1fdSPoul-Henning Kamp 		upn->len++;
875f6bde1fdSPoul-Henning Kamp 		up->len--;
876f6bde1fdSPoul-Henning Kamp 		uh->busy--;
877d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
878f6bde1fdSPoul-Henning Kamp 		return;
879f6bde1fdSPoul-Henning Kamp 	}
880f6bde1fdSPoul-Henning Kamp 
881f6bde1fdSPoul-Henning Kamp 	/* Split off the tail end, if any. */
882d9a54d5cSPoul-Henning Kamp 	pl = up->len - (1 + item);
883f6bde1fdSPoul-Henning Kamp 	if (pl > 0) {
884d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
885f6bde1fdSPoul-Henning Kamp 		upp->ptr = uh;
886f6bde1fdSPoul-Henning Kamp 		upp->len = pl;
887f6bde1fdSPoul-Henning Kamp 		TAILQ_INSERT_AFTER(&uh->head, up, upp, list);
888f6bde1fdSPoul-Henning Kamp 	}
889f6bde1fdSPoul-Henning Kamp 
890d9a54d5cSPoul-Henning Kamp 	/* Split off head end, if any */
891d9a54d5cSPoul-Henning Kamp 	if (item > 0) {
892d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
893d9a54d5cSPoul-Henning Kamp 		upp->len = item;
894d9a54d5cSPoul-Henning Kamp 		upp->ptr = uh;
895d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_BEFORE(up, upp, list);
896d9a54d5cSPoul-Henning Kamp 	}
897f6bde1fdSPoul-Henning Kamp 	up->len = 1;
898f6bde1fdSPoul-Henning Kamp 	up->ptr = NULL;
899f6bde1fdSPoul-Henning Kamp 	uh->busy--;
900d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
901f6bde1fdSPoul-Henning Kamp }
902f6bde1fdSPoul-Henning Kamp 
903d9a54d5cSPoul-Henning Kamp void
904d9a54d5cSPoul-Henning Kamp free_unr(struct unrhdr *uh, u_int item)
905d9a54d5cSPoul-Henning Kamp {
906d9a54d5cSPoul-Henning Kamp 	void *p1, *p2;
907f6bde1fdSPoul-Henning Kamp 
9087550e3eaSKonstantin Belousov 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "free_unr");
909d9a54d5cSPoul-Henning Kamp 	p1 = Malloc(sizeof(struct unr));
910d9a54d5cSPoul-Henning Kamp 	p2 = Malloc(sizeof(struct unr));
911e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
912d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
913d9a54d5cSPoul-Henning Kamp 	free_unrl(uh, item, &p1, &p2);
91409828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
915e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
916d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
917d9a54d5cSPoul-Henning Kamp 	if (p1 != NULL)
918d9a54d5cSPoul-Henning Kamp 		Free(p1);
919d9a54d5cSPoul-Henning Kamp 	if (p2 != NULL)
920d9a54d5cSPoul-Henning Kamp 		Free(p2);
921f6bde1fdSPoul-Henning Kamp }
922f6bde1fdSPoul-Henning Kamp 
92393f6c81eSPoul-Henning Kamp #ifndef _KERNEL	/* USERLAND test driver */
92493f6c81eSPoul-Henning Kamp 
925f6bde1fdSPoul-Henning Kamp /*
926794277daSAlan Somers  * Simple stochastic test driver for the above functions.  The code resides
927794277daSAlan Somers  * here so that it can access static functions and structures.
928f6bde1fdSPoul-Henning Kamp  */
929f6bde1fdSPoul-Henning Kamp 
930794277daSAlan Somers static bool verbose;
931794277daSAlan Somers #define VPRINTF(...)	{if (verbose) printf(__VA_ARGS__);}
932794277daSAlan Somers 
933f6bde1fdSPoul-Henning Kamp static void
934f6bde1fdSPoul-Henning Kamp print_unr(struct unrhdr *uh, struct unr *up)
935f6bde1fdSPoul-Henning Kamp {
936f6bde1fdSPoul-Henning Kamp 	u_int x;
937d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
938f6bde1fdSPoul-Henning Kamp 
939f6bde1fdSPoul-Henning Kamp 	printf("  %p len = %5u ", up, up->len);
940f6bde1fdSPoul-Henning Kamp 	if (up->ptr == NULL)
941f6bde1fdSPoul-Henning Kamp 		printf("free\n");
942f6bde1fdSPoul-Henning Kamp 	else if (up->ptr == uh)
943f6bde1fdSPoul-Henning Kamp 		printf("alloc\n");
944f6bde1fdSPoul-Henning Kamp 	else {
945d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
9468907f744SAlan Somers 		printf("bitmap [");
947d9a54d5cSPoul-Henning Kamp 		for (x = 0; x < up->len; x++) {
948d9a54d5cSPoul-Henning Kamp 			if (bit_test(ub->map, x))
949d9a54d5cSPoul-Henning Kamp 				printf("#");
950f6bde1fdSPoul-Henning Kamp 			else
951d9a54d5cSPoul-Henning Kamp 				printf(" ");
952f6bde1fdSPoul-Henning Kamp 		}
953f6bde1fdSPoul-Henning Kamp 		printf("]\n");
954f6bde1fdSPoul-Henning Kamp 	}
955f6bde1fdSPoul-Henning Kamp }
956f6bde1fdSPoul-Henning Kamp 
957f6bde1fdSPoul-Henning Kamp static void
958f6bde1fdSPoul-Henning Kamp print_unrhdr(struct unrhdr *uh)
959f6bde1fdSPoul-Henning Kamp {
960f6bde1fdSPoul-Henning Kamp 	struct unr *up;
961f6bde1fdSPoul-Henning Kamp 	u_int x;
962f6bde1fdSPoul-Henning Kamp 
963d9a54d5cSPoul-Henning Kamp 	printf(
964d9a54d5cSPoul-Henning Kamp 	    "%p low = %u high = %u first = %u last = %u busy %u chunks = %u\n",
965d9a54d5cSPoul-Henning Kamp 	    uh, uh->low, uh->high, uh->first, uh->last, uh->busy, uh->alloc);
966d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
967f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
968f6bde1fdSPoul-Henning Kamp 		printf("  from = %5u", x);
969f6bde1fdSPoul-Henning Kamp 		print_unr(uh, up);
970f6bde1fdSPoul-Henning Kamp 		if (up->ptr == NULL || up->ptr == uh)
971f6bde1fdSPoul-Henning Kamp 			x += up->len;
972f6bde1fdSPoul-Henning Kamp 		else
973f6bde1fdSPoul-Henning Kamp 			x += NBITS;
974f6bde1fdSPoul-Henning Kamp 	}
975f6bde1fdSPoul-Henning Kamp }
976f6bde1fdSPoul-Henning Kamp 
97713c02cbbSJaakko Heinonen static void
97813c02cbbSJaakko Heinonen test_alloc_unr(struct unrhdr *uh, u_int i, char a[])
97913c02cbbSJaakko Heinonen {
98013c02cbbSJaakko Heinonen 	int j;
98113c02cbbSJaakko Heinonen 
98213c02cbbSJaakko Heinonen 	if (a[i]) {
983794277daSAlan Somers 		VPRINTF("F %u\n", i);
98413c02cbbSJaakko Heinonen 		free_unr(uh, i);
98513c02cbbSJaakko Heinonen 		a[i] = 0;
98613c02cbbSJaakko Heinonen 	} else {
98713c02cbbSJaakko Heinonen 		no_alloc = 1;
98813c02cbbSJaakko Heinonen 		j = alloc_unr(uh);
98913c02cbbSJaakko Heinonen 		if (j != -1) {
99013c02cbbSJaakko Heinonen 			a[j] = 1;
991794277daSAlan Somers 			VPRINTF("A %d\n", j);
99213c02cbbSJaakko Heinonen 		}
99313c02cbbSJaakko Heinonen 		no_alloc = 0;
99413c02cbbSJaakko Heinonen 	}
99513c02cbbSJaakko Heinonen }
99613c02cbbSJaakko Heinonen 
99713c02cbbSJaakko Heinonen static void
99813c02cbbSJaakko Heinonen test_alloc_unr_specific(struct unrhdr *uh, u_int i, char a[])
99913c02cbbSJaakko Heinonen {
100013c02cbbSJaakko Heinonen 	int j;
100113c02cbbSJaakko Heinonen 
100213c02cbbSJaakko Heinonen 	j = alloc_unr_specific(uh, i);
100313c02cbbSJaakko Heinonen 	if (j == -1) {
1004794277daSAlan Somers 		VPRINTF("F %u\n", i);
100513c02cbbSJaakko Heinonen 		a[i] = 0;
100613c02cbbSJaakko Heinonen 		free_unr(uh, i);
100713c02cbbSJaakko Heinonen 	} else {
100813c02cbbSJaakko Heinonen 		a[i] = 1;
1009794277daSAlan Somers 		VPRINTF("A %d\n", j);
101013c02cbbSJaakko Heinonen 	}
101113c02cbbSJaakko Heinonen }
101213c02cbbSJaakko Heinonen 
1013794277daSAlan Somers static void
1014794277daSAlan Somers usage(char **argv)
1015794277daSAlan Somers {
1016794277daSAlan Somers 	printf("%s [-h] [-r REPETITIONS] [-v]\n", argv[0]);
1017794277daSAlan Somers }
1018f6bde1fdSPoul-Henning Kamp 
1019f6bde1fdSPoul-Henning Kamp int
1020794277daSAlan Somers main(int argc, char **argv)
1021f6bde1fdSPoul-Henning Kamp {
1022f6bde1fdSPoul-Henning Kamp 	struct unrhdr *uh;
1023794277daSAlan Somers 	char *a;
1024794277daSAlan Somers 	long count = 10000;	/* Number of unrs to test */
102537f32e53SAlan Somers 	long reps = 1, m;
1026794277daSAlan Somers 	int ch;
1027cd565040SConrad Meyer 	u_int i;
1028794277daSAlan Somers 
1029794277daSAlan Somers 	verbose = false;
1030794277daSAlan Somers 
1031794277daSAlan Somers 	while ((ch = getopt(argc, argv, "hr:v")) != -1) {
1032794277daSAlan Somers 		switch (ch) {
1033794277daSAlan Somers 		case 'r':
1034794277daSAlan Somers 			errno = 0;
1035794277daSAlan Somers 			reps = strtol(optarg, NULL, 0);
1036794277daSAlan Somers 			if (errno == ERANGE || errno == EINVAL) {
1037794277daSAlan Somers 				usage(argv);
1038794277daSAlan Somers 				exit(2);
1039794277daSAlan Somers 			}
1040794277daSAlan Somers 
1041794277daSAlan Somers 			break;
1042794277daSAlan Somers 		case 'v':
1043794277daSAlan Somers 			verbose = true;
1044794277daSAlan Somers 			break;
1045794277daSAlan Somers 		case 'h':
1046794277daSAlan Somers 		default:
1047794277daSAlan Somers 			usage(argv);
1048794277daSAlan Somers 			exit(2);
1049794277daSAlan Somers 		}
1050794277daSAlan Somers 	}
1051f6bde1fdSPoul-Henning Kamp 
1052d9a54d5cSPoul-Henning Kamp 	setbuf(stdout, NULL);
1053794277daSAlan Somers 	uh = new_unrhdr(0, count - 1, NULL);
1054d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1055f6bde1fdSPoul-Henning Kamp 
1056794277daSAlan Somers 	a = calloc(count, sizeof(char));
1057794277daSAlan Somers 	if (a == NULL)
1058794277daSAlan Somers 		err(1, "calloc failed");
1059f6bde1fdSPoul-Henning Kamp 
1060794277daSAlan Somers 	printf("sizeof(struct unr) %zu\n", sizeof(struct unr));
1061794277daSAlan Somers 	printf("sizeof(struct unrb) %zu\n", sizeof(struct unrb));
1062794277daSAlan Somers 	printf("sizeof(struct unrhdr) %zu\n", sizeof(struct unrhdr));
1063b4f69365SEd Maste 	printf("NBITS %lu\n", (unsigned long)NBITS);
1064794277daSAlan Somers 	for (m = 0; m < count * reps; m++) {
1065cd565040SConrad Meyer 		i = arc4random_uniform(count);
1066d9a54d5cSPoul-Henning Kamp #if 0
1067d9a54d5cSPoul-Henning Kamp 		if (a[i] && (j & 1))
1068d9a54d5cSPoul-Henning Kamp 			continue;
1069d9a54d5cSPoul-Henning Kamp #endif
1070cd565040SConrad Meyer 		if ((arc4random() & 1) != 0)
107113c02cbbSJaakko Heinonen 			test_alloc_unr(uh, i, a);
107213c02cbbSJaakko Heinonen 		else
107313c02cbbSJaakko Heinonen 			test_alloc_unr_specific(uh, i, a);
107413c02cbbSJaakko Heinonen 
1075794277daSAlan Somers 		if (verbose)
1076f6bde1fdSPoul-Henning Kamp 			print_unrhdr(uh);
1077f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
1078f6bde1fdSPoul-Henning Kamp 	}
107937f32e53SAlan Somers 	for (i = 0; i < (u_int)count; i++) {
1080d9a54d5cSPoul-Henning Kamp 		if (a[i]) {
1081794277daSAlan Somers 			if (verbose) {
1082d9a54d5cSPoul-Henning Kamp 				printf("C %u\n", i);
1083e4fea39eSPoul-Henning Kamp 				print_unrhdr(uh);
1084d9a54d5cSPoul-Henning Kamp 			}
1085794277daSAlan Somers 			free_unr(uh, i);
1086794277daSAlan Somers 		}
1087d9a54d5cSPoul-Henning Kamp 	}
1088d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1089e4fea39eSPoul-Henning Kamp 	delete_unrhdr(uh);
1090794277daSAlan Somers 	free(a);
1091f6bde1fdSPoul-Henning Kamp 	return (0);
1092f6bde1fdSPoul-Henning Kamp }
1093f6bde1fdSPoul-Henning Kamp #endif
1094