xref: /freebsd/sys/kern/subr_unit.c (revision a014e0a3987a277a0e56c7fa5b9d895f735a8d1e)
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.
181d44f4770SKonstantin Belousov  *
182d44f4770SKonstantin Belousov  * Two special ranges are not covered by unrs:
183d44f4770SKonstantin Belousov  * - at the start of the allocator space, all elements in [low, low + first)
184d44f4770SKonstantin Belousov  *   are allocated;
185d44f4770SKonstantin Belousov  * - at the end of the allocator space, all elements in [high - last, high]
186d44f4770SKonstantin Belousov  *   are free.
187f6bde1fdSPoul-Henning Kamp  */
188f6bde1fdSPoul-Henning Kamp struct unr {
189f6bde1fdSPoul-Henning Kamp 	TAILQ_ENTRY(unr)	list;
190f6bde1fdSPoul-Henning Kamp 	u_int			len;
191f6bde1fdSPoul-Henning Kamp 	void			*ptr;
192f6bde1fdSPoul-Henning Kamp };
193f6bde1fdSPoul-Henning Kamp 
194d9a54d5cSPoul-Henning Kamp struct unrb {
1958907f744SAlan Somers 	bitstr_t		map[sizeof(struct unr) / sizeof(bitstr_t)];
196d9a54d5cSPoul-Henning Kamp };
197d9a54d5cSPoul-Henning Kamp 
1988907f744SAlan Somers CTASSERT((sizeof(struct unr) % sizeof(bitstr_t)) == 0);
199d9a54d5cSPoul-Henning Kamp 
2008907f744SAlan Somers /* Number of bits we can store in the bitmap */
201042ec55fSKonstantin Belousov #define NBITS (NBBY * sizeof(((struct unrb *)NULL)->map))
2028907f744SAlan Somers 
20336b1f8a8SKonstantin Belousov static inline bool
20436b1f8a8SKonstantin Belousov is_bitmap(struct unrhdr *uh, struct unr *up)
20536b1f8a8SKonstantin Belousov {
20636b1f8a8SKonstantin Belousov 	return (up->ptr != uh && up->ptr != NULL);
20736b1f8a8SKonstantin Belousov }
20836b1f8a8SKonstantin Belousov 
2098907f744SAlan Somers /* Is the unrb empty in at least the first len bits? */
2108907f744SAlan Somers static inline bool
2118907f744SAlan Somers ub_empty(struct unrb *ub, int len) {
2128907f744SAlan Somers 	int first_set;
2138907f744SAlan Somers 
2148907f744SAlan Somers 	bit_ffs(ub->map, len, &first_set);
2158907f744SAlan Somers 	return (first_set == -1);
2168907f744SAlan Somers }
2178907f744SAlan Somers 
2188907f744SAlan Somers /* Is the unrb full?  That is, is the number of set elements equal to len? */
2198907f744SAlan Somers static inline bool
2208907f744SAlan Somers ub_full(struct unrb *ub, int len)
2218907f744SAlan Somers {
2228907f744SAlan Somers 	int first_clear;
2238907f744SAlan Somers 
2248907f744SAlan Somers 	bit_ffc(ub->map, len, &first_clear);
2258907f744SAlan Somers 	return (first_clear == -1);
2268907f744SAlan Somers }
2278907f744SAlan Somers 
228*a014e0a3SKonstantin Belousov /*
229*a014e0a3SKonstantin Belousov  * start: ipos = -1, upos = NULL;
230*a014e0a3SKonstantin Belousov  * end:   ipos = -1, upos = uh
231*a014e0a3SKonstantin Belousov  */
232*a014e0a3SKonstantin Belousov struct unrhdr_iter {
233*a014e0a3SKonstantin Belousov 	struct unrhdr *uh;
234*a014e0a3SKonstantin Belousov 	int ipos;
235*a014e0a3SKonstantin Belousov 	int upos_first_item;
236*a014e0a3SKonstantin Belousov 	void *upos;
237*a014e0a3SKonstantin Belousov };
238*a014e0a3SKonstantin Belousov 
239*a014e0a3SKonstantin Belousov void *
240*a014e0a3SKonstantin Belousov create_iter_unr(struct unrhdr *uh)
241*a014e0a3SKonstantin Belousov {
242*a014e0a3SKonstantin Belousov 	struct unrhdr_iter *iter;
243*a014e0a3SKonstantin Belousov 
244*a014e0a3SKonstantin Belousov 	iter = Malloc(sizeof(*iter));
245*a014e0a3SKonstantin Belousov 	iter->ipos = -1;
246*a014e0a3SKonstantin Belousov 	iter->uh = uh;
247*a014e0a3SKonstantin Belousov 	iter->upos = NULL;
248*a014e0a3SKonstantin Belousov 	iter->upos_first_item = -1;
249*a014e0a3SKonstantin Belousov 	return (iter);
250*a014e0a3SKonstantin Belousov }
251*a014e0a3SKonstantin Belousov 
252*a014e0a3SKonstantin Belousov static void
253*a014e0a3SKonstantin Belousov next_iter_unrl(struct unrhdr *uh, struct unrhdr_iter *iter)
254*a014e0a3SKonstantin Belousov {
255*a014e0a3SKonstantin Belousov 	struct unr *up;
256*a014e0a3SKonstantin Belousov 	struct unrb *ub;
257*a014e0a3SKonstantin Belousov 	u_int y;
258*a014e0a3SKonstantin Belousov 	int c;
259*a014e0a3SKonstantin Belousov 
260*a014e0a3SKonstantin Belousov 	if (iter->ipos == -1) {
261*a014e0a3SKonstantin Belousov 		if (iter->upos == uh)
262*a014e0a3SKonstantin Belousov 			return;
263*a014e0a3SKonstantin Belousov 		y = uh->low - 1;
264*a014e0a3SKonstantin Belousov 		if (uh->first == 0) {
265*a014e0a3SKonstantin Belousov 			up = TAILQ_FIRST(&uh->head);
266*a014e0a3SKonstantin Belousov 			if (up == NULL) {
267*a014e0a3SKonstantin Belousov 				iter->upos = uh;
268*a014e0a3SKonstantin Belousov 				return;
269*a014e0a3SKonstantin Belousov 			}
270*a014e0a3SKonstantin Belousov 			iter->upos = up;
271*a014e0a3SKonstantin Belousov 			if (up->ptr == NULL)
272*a014e0a3SKonstantin Belousov 				iter->upos = NULL;
273*a014e0a3SKonstantin Belousov 			else
274*a014e0a3SKonstantin Belousov 				iter->upos_first_item = uh->low;
275*a014e0a3SKonstantin Belousov 		}
276*a014e0a3SKonstantin Belousov 	} else {
277*a014e0a3SKonstantin Belousov 		y = iter->ipos;
278*a014e0a3SKonstantin Belousov 	}
279*a014e0a3SKonstantin Belousov 
280*a014e0a3SKonstantin Belousov 	up = iter->upos;
281*a014e0a3SKonstantin Belousov 
282*a014e0a3SKonstantin Belousov 	/* Special case for the compacted [low, first) run. */
283*a014e0a3SKonstantin Belousov 	if (up == NULL) {
284*a014e0a3SKonstantin Belousov 		if (y + 1 < uh->low + uh->first) {
285*a014e0a3SKonstantin Belousov 			iter->ipos = y + 1;
286*a014e0a3SKonstantin Belousov 			return;
287*a014e0a3SKonstantin Belousov 		}
288*a014e0a3SKonstantin Belousov 		up = iter->upos = TAILQ_FIRST(&uh->head);
289*a014e0a3SKonstantin Belousov 		iter->upos_first_item = uh->low + uh->first;
290*a014e0a3SKonstantin Belousov 	}
291*a014e0a3SKonstantin Belousov 
292*a014e0a3SKonstantin Belousov 	for (;;) {
293*a014e0a3SKonstantin Belousov 		if (y + 1 < iter->upos_first_item + up->len) {
294*a014e0a3SKonstantin Belousov 			if (up->ptr == uh) {
295*a014e0a3SKonstantin Belousov 				iter->ipos = y + 1;
296*a014e0a3SKonstantin Belousov 				return;
297*a014e0a3SKonstantin Belousov 			} else if (is_bitmap(uh, up)) {
298*a014e0a3SKonstantin Belousov 				ub = up->ptr;
299*a014e0a3SKonstantin Belousov 				bit_ffs_at(&ub->map[0],
300*a014e0a3SKonstantin Belousov 				    y + 1 - iter->upos_first_item,
301*a014e0a3SKonstantin Belousov 				    up->len, &c);
302*a014e0a3SKonstantin Belousov 				if (c != -1) {
303*a014e0a3SKonstantin Belousov 					iter->ipos = iter->upos_first_item + c;
304*a014e0a3SKonstantin Belousov 					return;
305*a014e0a3SKonstantin Belousov 				}
306*a014e0a3SKonstantin Belousov 			}
307*a014e0a3SKonstantin Belousov 		}
308*a014e0a3SKonstantin Belousov 		iter->upos_first_item += up->len;
309*a014e0a3SKonstantin Belousov 		y = iter->upos_first_item - 1;
310*a014e0a3SKonstantin Belousov 		up = iter->upos = TAILQ_NEXT((struct unr *)iter->upos, list);
311*a014e0a3SKonstantin Belousov 		if (iter->upos == NULL) {
312*a014e0a3SKonstantin Belousov 			iter->ipos = -1;
313*a014e0a3SKonstantin Belousov 			iter->upos = uh;
314*a014e0a3SKonstantin Belousov 			return;
315*a014e0a3SKonstantin Belousov 		}
316*a014e0a3SKonstantin Belousov 	}
317*a014e0a3SKonstantin Belousov }
318*a014e0a3SKonstantin Belousov 
319*a014e0a3SKonstantin Belousov /*
320*a014e0a3SKonstantin Belousov  * returns -1 on end, otherwise the next element
321*a014e0a3SKonstantin Belousov  */
322*a014e0a3SKonstantin Belousov int
323*a014e0a3SKonstantin Belousov next_iter_unr(void *handle)
324*a014e0a3SKonstantin Belousov {
325*a014e0a3SKonstantin Belousov 	struct unrhdr *uh;
326*a014e0a3SKonstantin Belousov 	struct unrhdr_iter *iter;
327*a014e0a3SKonstantin Belousov 
328*a014e0a3SKonstantin Belousov 	iter = handle;
329*a014e0a3SKonstantin Belousov 	uh = iter->uh;
330*a014e0a3SKonstantin Belousov 	if (uh->mtx != NULL)
331*a014e0a3SKonstantin Belousov 		mtx_lock(uh->mtx);
332*a014e0a3SKonstantin Belousov 	next_iter_unrl(uh, iter);
333*a014e0a3SKonstantin Belousov 	if (uh->mtx != NULL)
334*a014e0a3SKonstantin Belousov 		mtx_unlock(uh->mtx);
335*a014e0a3SKonstantin Belousov 	return (iter->ipos);
336*a014e0a3SKonstantin Belousov }
337*a014e0a3SKonstantin Belousov 
338*a014e0a3SKonstantin Belousov void
339*a014e0a3SKonstantin Belousov free_iter_unr(void *handle)
340*a014e0a3SKonstantin Belousov {
341*a014e0a3SKonstantin Belousov 	Free(handle);
342*a014e0a3SKonstantin Belousov }
343*a014e0a3SKonstantin Belousov 
344f6bde1fdSPoul-Henning Kamp #if defined(DIAGNOSTIC) || !defined(_KERNEL)
345f6bde1fdSPoul-Henning Kamp /*
346f6bde1fdSPoul-Henning Kamp  * Consistency check function.
347f6bde1fdSPoul-Henning Kamp  *
348f6bde1fdSPoul-Henning Kamp  * Checks the internal consistency as well as we can.
349f6bde1fdSPoul-Henning Kamp  *
350f6bde1fdSPoul-Henning Kamp  * Called at all boundaries of this API.
351f6bde1fdSPoul-Henning Kamp  */
352f6bde1fdSPoul-Henning Kamp static void
353f6bde1fdSPoul-Henning Kamp check_unrhdr(struct unrhdr *uh, int line)
354f6bde1fdSPoul-Henning Kamp {
355f6bde1fdSPoul-Henning Kamp 	struct unr *up;
356d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
3571b82e02fSAlan Somers 	int w;
3581b82e02fSAlan Somers 	u_int y, z;
359f6bde1fdSPoul-Henning Kamp 
360d9a54d5cSPoul-Henning Kamp 	y = uh->first;
361f6bde1fdSPoul-Henning Kamp 	z = 0;
362f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
363f6bde1fdSPoul-Henning Kamp 		z++;
36436b1f8a8SKonstantin Belousov 		if (is_bitmap(uh, up)) {
365d9a54d5cSPoul-Henning Kamp 			ub = up->ptr;
366d9a54d5cSPoul-Henning Kamp 			KASSERT (up->len <= NBITS,
3678907f744SAlan Somers 			    ("UNR inconsistency: len %u max %zd (line %d)\n",
368d9a54d5cSPoul-Henning Kamp 			    up->len, NBITS, line));
369f6bde1fdSPoul-Henning Kamp 			z++;
370f6bde1fdSPoul-Henning Kamp 			w = 0;
3711b82e02fSAlan Somers 			bit_count(ub->map, 0, up->len, &w);
372d9a54d5cSPoul-Henning Kamp 			y += w;
373d9a54d5cSPoul-Henning Kamp 		} else if (up->ptr != NULL)
374f6bde1fdSPoul-Henning Kamp 			y += up->len;
375f6bde1fdSPoul-Henning Kamp 	}
376f6bde1fdSPoul-Henning Kamp 	KASSERT (y == uh->busy,
377f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: items %u found %u (line %d)\n",
378f6bde1fdSPoul-Henning Kamp 	    uh->busy, y, line));
379f6bde1fdSPoul-Henning Kamp 	KASSERT (z == uh->alloc,
380f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: chunks %u found %u (line %d)\n",
381f6bde1fdSPoul-Henning Kamp 	    uh->alloc, z, line));
382f6bde1fdSPoul-Henning Kamp }
383f6bde1fdSPoul-Henning Kamp 
384f6bde1fdSPoul-Henning Kamp #else
385f6bde1fdSPoul-Henning Kamp 
386f6bde1fdSPoul-Henning Kamp static __inline void
3878907f744SAlan Somers check_unrhdr(struct unrhdr *uh __unused, int line __unused)
388f6bde1fdSPoul-Henning Kamp {
389f6bde1fdSPoul-Henning Kamp 
390f6bde1fdSPoul-Henning Kamp }
391f6bde1fdSPoul-Henning Kamp 
392f6bde1fdSPoul-Henning Kamp #endif
393f6bde1fdSPoul-Henning Kamp 
394f6bde1fdSPoul-Henning Kamp /*
395f6bde1fdSPoul-Henning Kamp  * Userland memory management.  Just use calloc and keep track of how
396f6bde1fdSPoul-Henning Kamp  * many elements we have allocated for check_unrhdr().
397f6bde1fdSPoul-Henning Kamp  */
398f6bde1fdSPoul-Henning Kamp 
399f6bde1fdSPoul-Henning Kamp static __inline void *
400d9a54d5cSPoul-Henning Kamp new_unr(struct unrhdr *uh, void **p1, void **p2)
401f6bde1fdSPoul-Henning Kamp {
402d9a54d5cSPoul-Henning Kamp 	void *p;
403f6bde1fdSPoul-Henning Kamp 
404d9a54d5cSPoul-Henning Kamp 	uh->alloc++;
405d9a54d5cSPoul-Henning Kamp 	KASSERT(*p1 != NULL || *p2 != NULL, ("Out of cached memory"));
406d9a54d5cSPoul-Henning Kamp 	if (*p1 != NULL) {
407d9a54d5cSPoul-Henning Kamp 		p = *p1;
408d9a54d5cSPoul-Henning Kamp 		*p1 = NULL;
409d9a54d5cSPoul-Henning Kamp 		return (p);
410d9a54d5cSPoul-Henning Kamp 	} else {
411d9a54d5cSPoul-Henning Kamp 		p = *p2;
412d9a54d5cSPoul-Henning Kamp 		*p2 = NULL;
413d9a54d5cSPoul-Henning Kamp 		return (p);
414d9a54d5cSPoul-Henning Kamp 	}
415f6bde1fdSPoul-Henning Kamp }
416f6bde1fdSPoul-Henning Kamp 
417f6bde1fdSPoul-Henning Kamp static __inline void
418f6bde1fdSPoul-Henning Kamp delete_unr(struct unrhdr *uh, void *ptr)
419f6bde1fdSPoul-Henning Kamp {
42009828ba9SKonstantin Belousov 	struct unr *up;
421d9a54d5cSPoul-Henning Kamp 
422f6bde1fdSPoul-Henning Kamp 	uh->alloc--;
42309828ba9SKonstantin Belousov 	up = ptr;
42409828ba9SKonstantin Belousov 	TAILQ_INSERT_TAIL(&uh->ppfree, up, list);
42509828ba9SKonstantin Belousov }
42609828ba9SKonstantin Belousov 
42709828ba9SKonstantin Belousov void
42809828ba9SKonstantin Belousov clean_unrhdrl(struct unrhdr *uh)
42909828ba9SKonstantin Belousov {
43009828ba9SKonstantin Belousov 	struct unr *up;
43109828ba9SKonstantin Belousov 
432e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
43309828ba9SKonstantin Belousov 		mtx_assert(uh->mtx, MA_OWNED);
43409828ba9SKonstantin Belousov 	while ((up = TAILQ_FIRST(&uh->ppfree)) != NULL) {
43509828ba9SKonstantin Belousov 		TAILQ_REMOVE(&uh->ppfree, up, list);
436e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
43709828ba9SKonstantin Belousov 			mtx_unlock(uh->mtx);
43809828ba9SKonstantin Belousov 		Free(up);
439e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
44009828ba9SKonstantin Belousov 			mtx_lock(uh->mtx);
44109828ba9SKonstantin Belousov 	}
44209828ba9SKonstantin Belousov 
44309828ba9SKonstantin Belousov }
44409828ba9SKonstantin Belousov 
44509828ba9SKonstantin Belousov void
44609828ba9SKonstantin Belousov clean_unrhdr(struct unrhdr *uh)
44709828ba9SKonstantin Belousov {
44809828ba9SKonstantin Belousov 
449e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
45009828ba9SKonstantin Belousov 		mtx_lock(uh->mtx);
45109828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
452e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
45309828ba9SKonstantin Belousov 		mtx_unlock(uh->mtx);
454f6bde1fdSPoul-Henning Kamp }
455f6bde1fdSPoul-Henning Kamp 
456dc872d46SKonstantin Belousov void
457dc872d46SKonstantin Belousov init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex)
458f6bde1fdSPoul-Henning Kamp {
459f6bde1fdSPoul-Henning Kamp 
460831aa555SJaakko Heinonen 	KASSERT(low >= 0 && low <= high,
461501812f2SJaakko Heinonen 	    ("UNR: use error: new_unrhdr(%d, %d)", low, high));
462e59b940dSKonstantin Belousov 	if (mutex == UNR_NO_MTX)
463e59b940dSKonstantin Belousov 		uh->mtx = NULL;
464e59b940dSKonstantin Belousov 	else if (mutex != NULL)
465d9a54d5cSPoul-Henning Kamp 		uh->mtx = mutex;
466d9a54d5cSPoul-Henning Kamp 	else
467d9a54d5cSPoul-Henning Kamp 		uh->mtx = &unitmtx;
468f6bde1fdSPoul-Henning Kamp 	TAILQ_INIT(&uh->head);
46909828ba9SKonstantin Belousov 	TAILQ_INIT(&uh->ppfree);
470f6bde1fdSPoul-Henning Kamp 	uh->low = low;
471f6bde1fdSPoul-Henning Kamp 	uh->high = high;
472d9a54d5cSPoul-Henning Kamp 	uh->first = 0;
473d9a54d5cSPoul-Henning Kamp 	uh->last = 1 + (high - low);
474c4be460eSKonstantin Belousov 	uh->busy = 0;
475c4be460eSKonstantin Belousov 	uh->alloc = 0;
476f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
477dc872d46SKonstantin Belousov }
478dc872d46SKonstantin Belousov 
479dc872d46SKonstantin Belousov /*
480dc872d46SKonstantin Belousov  * Allocate a new unrheader set.
481dc872d46SKonstantin Belousov  *
482dc872d46SKonstantin Belousov  * Highest and lowest valid values given as parameters.
483dc872d46SKonstantin Belousov  */
484dc872d46SKonstantin Belousov 
485dc872d46SKonstantin Belousov struct unrhdr *
486dc872d46SKonstantin Belousov new_unrhdr(int low, int high, struct mtx *mutex)
487dc872d46SKonstantin Belousov {
488dc872d46SKonstantin Belousov 	struct unrhdr *uh;
489dc872d46SKonstantin Belousov 
490dc872d46SKonstantin Belousov 	uh = Malloc(sizeof *uh);
491dc872d46SKonstantin Belousov 	init_unrhdr(uh, low, high, mutex);
492f6bde1fdSPoul-Henning Kamp 	return (uh);
493f6bde1fdSPoul-Henning Kamp }
494f6bde1fdSPoul-Henning Kamp 
495e4fea39eSPoul-Henning Kamp void
496e4fea39eSPoul-Henning Kamp delete_unrhdr(struct unrhdr *uh)
497e4fea39eSPoul-Henning Kamp {
498e4fea39eSPoul-Henning Kamp 
499d9a54d5cSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
500e4fea39eSPoul-Henning Kamp 	KASSERT(uh->busy == 0, ("unrhdr has %u allocations", uh->busy));
501e4fea39eSPoul-Henning Kamp 	KASSERT(uh->alloc == 0, ("UNR memory leak in delete_unrhdr"));
50209828ba9SKonstantin Belousov 	KASSERT(TAILQ_FIRST(&uh->ppfree) == NULL,
50309828ba9SKonstantin Belousov 	    ("unrhdr has postponed item for free"));
504e4fea39eSPoul-Henning Kamp 	Free(uh);
505e4fea39eSPoul-Henning Kamp }
506e4fea39eSPoul-Henning Kamp 
507333dcaa4SMatt Joras void
508333dcaa4SMatt Joras clear_unrhdr(struct unrhdr *uh)
509333dcaa4SMatt Joras {
510333dcaa4SMatt Joras 	struct unr *up, *uq;
511333dcaa4SMatt Joras 
512333dcaa4SMatt Joras 	KASSERT(TAILQ_EMPTY(&uh->ppfree),
513333dcaa4SMatt Joras 	    ("unrhdr has postponed item for free"));
5140d8e0405SMatt Joras 	TAILQ_FOREACH_SAFE(up, &uh->head, list, uq) {
515333dcaa4SMatt Joras 		if (up->ptr != uh) {
516333dcaa4SMatt Joras 			Free(up->ptr);
517333dcaa4SMatt Joras 		}
518333dcaa4SMatt Joras 		Free(up);
519333dcaa4SMatt Joras 	}
520333dcaa4SMatt Joras 	uh->busy = 0;
521333dcaa4SMatt Joras 	uh->alloc = 0;
5220d8e0405SMatt Joras 	init_unrhdr(uh, uh->low, uh->high, uh->mtx);
5230d8e0405SMatt Joras 
5240d8e0405SMatt Joras 	check_unrhdr(uh, __LINE__);
525333dcaa4SMatt Joras }
526333dcaa4SMatt Joras 
527f6bde1fdSPoul-Henning Kamp /*
528d9a54d5cSPoul-Henning Kamp  * Look for sequence of items which can be combined into a bitmap, if
529d9a54d5cSPoul-Henning Kamp  * multiple are present, take the one which saves most memory.
530d9a54d5cSPoul-Henning Kamp  *
531d9a54d5cSPoul-Henning Kamp  * Return (1) if a sequence was found to indicate that another call
532d9a54d5cSPoul-Henning Kamp  * might be able to do more.  Return (0) if we found no suitable sequence.
533d9a54d5cSPoul-Henning Kamp  *
534d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
535d9a54d5cSPoul-Henning Kamp  */
536d9a54d5cSPoul-Henning Kamp static int
537d9a54d5cSPoul-Henning Kamp optimize_unr(struct unrhdr *uh)
538d9a54d5cSPoul-Henning Kamp {
539d9a54d5cSPoul-Henning Kamp 	struct unr *up, *uf, *us;
540d9a54d5cSPoul-Henning Kamp 	struct unrb *ub, *ubf;
541d9a54d5cSPoul-Henning Kamp 	u_int a, l, ba;
542d9a54d5cSPoul-Henning Kamp 
543d9a54d5cSPoul-Henning Kamp 	/*
544d9a54d5cSPoul-Henning Kamp 	 * Look for the run of items (if any) which when collapsed into
545d9a54d5cSPoul-Henning Kamp 	 * a bitmap would save most memory.
546d9a54d5cSPoul-Henning Kamp 	 */
547d9a54d5cSPoul-Henning Kamp 	us = NULL;
548d9a54d5cSPoul-Henning Kamp 	ba = 0;
549d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(uf, &uh->head, list) {
550d9a54d5cSPoul-Henning Kamp 		if (uf->len >= NBITS)
551d9a54d5cSPoul-Henning Kamp 			continue;
552d9a54d5cSPoul-Henning Kamp 		a = 1;
553d9a54d5cSPoul-Henning Kamp 		if (is_bitmap(uh, uf))
554d9a54d5cSPoul-Henning Kamp 			a++;
555d9a54d5cSPoul-Henning Kamp 		l = uf->len;
556d9a54d5cSPoul-Henning Kamp 		up = uf;
557d9a54d5cSPoul-Henning Kamp 		while (1) {
558d9a54d5cSPoul-Henning Kamp 			up = TAILQ_NEXT(up, list);
559d9a54d5cSPoul-Henning Kamp 			if (up == NULL)
560d9a54d5cSPoul-Henning Kamp 				break;
561d9a54d5cSPoul-Henning Kamp 			if ((up->len + l) > NBITS)
562d9a54d5cSPoul-Henning Kamp 				break;
563d9a54d5cSPoul-Henning Kamp 			a++;
564d9a54d5cSPoul-Henning Kamp 			if (is_bitmap(uh, up))
565d9a54d5cSPoul-Henning Kamp 				a++;
566d9a54d5cSPoul-Henning Kamp 			l += up->len;
567d9a54d5cSPoul-Henning Kamp 		}
568d9a54d5cSPoul-Henning Kamp 		if (a > ba) {
569d9a54d5cSPoul-Henning Kamp 			ba = a;
570d9a54d5cSPoul-Henning Kamp 			us = uf;
571d9a54d5cSPoul-Henning Kamp 		}
572d9a54d5cSPoul-Henning Kamp 	}
573d9a54d5cSPoul-Henning Kamp 	if (ba < 3)
574d9a54d5cSPoul-Henning Kamp 		return (0);
575d9a54d5cSPoul-Henning Kamp 
576d9a54d5cSPoul-Henning Kamp 	/*
577d9a54d5cSPoul-Henning Kamp 	 * If the first element is not a bitmap, make it one.
578d9a54d5cSPoul-Henning Kamp 	 * Trying to do so without allocating more memory complicates things
579d9a54d5cSPoul-Henning Kamp 	 * a bit
580d9a54d5cSPoul-Henning Kamp 	 */
581d9a54d5cSPoul-Henning Kamp 	if (!is_bitmap(uh, us)) {
582d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
583d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, us, list);
584d9a54d5cSPoul-Henning Kamp 		a = us->len;
585d9a54d5cSPoul-Henning Kamp 		l = us->ptr == uh ? 1 : 0;
586d9a54d5cSPoul-Henning Kamp 		ub = (void *)us;
5878907f744SAlan Somers 		bit_nclear(ub->map, 0, NBITS - 1);
5888907f744SAlan Somers 		if (l)
589d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, 0, a);
590d9a54d5cSPoul-Henning Kamp 		if (!is_bitmap(uh, uf)) {
5918907f744SAlan Somers 			if (uf->ptr == NULL)
592d9a54d5cSPoul-Henning Kamp 				bit_nclear(ub->map, a, a + uf->len - 1);
5938907f744SAlan Somers 			else
594d9a54d5cSPoul-Henning Kamp 				bit_nset(ub->map, a, a + uf->len - 1);
595d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
596d9a54d5cSPoul-Henning Kamp 			uf->len += a;
597d9a54d5cSPoul-Henning Kamp 			us = uf;
598d9a54d5cSPoul-Henning Kamp 		} else {
599d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
600d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, a++) {
6018907f744SAlan Somers 				if (bit_test(ubf->map, l))
602d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, a);
6038907f744SAlan Somers 				else
604d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, a);
605d9a54d5cSPoul-Henning Kamp 			}
606d9a54d5cSPoul-Henning Kamp 			uf->len = a;
607d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf->ptr);
608d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
609d9a54d5cSPoul-Henning Kamp 			us = uf;
610d9a54d5cSPoul-Henning Kamp 		}
611d9a54d5cSPoul-Henning Kamp 	}
612d9a54d5cSPoul-Henning Kamp 	ub = us->ptr;
613d9a54d5cSPoul-Henning Kamp 	while (1) {
614d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
615d9a54d5cSPoul-Henning Kamp 		if (uf == NULL)
616d9a54d5cSPoul-Henning Kamp 			return (1);
617d9a54d5cSPoul-Henning Kamp 		if (uf->len + us->len > NBITS)
618d9a54d5cSPoul-Henning Kamp 			return (1);
619d9a54d5cSPoul-Henning Kamp 		if (uf->ptr == NULL) {
620d9a54d5cSPoul-Henning Kamp 			bit_nclear(ub->map, us->len, us->len + uf->len - 1);
621d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
622d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
623d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
624d9a54d5cSPoul-Henning Kamp 		} else if (uf->ptr == uh) {
625d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, us->len, us->len + uf->len - 1);
626d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
627d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
628d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
629d9a54d5cSPoul-Henning Kamp 		} else {
630d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
631d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, us->len++) {
6328907f744SAlan Somers 				if (bit_test(ubf->map, l))
633d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, us->len);
6348907f744SAlan Somers 				else
635d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, us->len);
636d9a54d5cSPoul-Henning Kamp 			}
637d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
638d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, ubf);
639d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
640d9a54d5cSPoul-Henning Kamp 		}
641d9a54d5cSPoul-Henning Kamp 	}
642d9a54d5cSPoul-Henning Kamp }
643d9a54d5cSPoul-Henning Kamp 
644d9a54d5cSPoul-Henning Kamp /*
645d9a54d5cSPoul-Henning Kamp  * See if a given unr should be collapsed with a neighbor.
646d9a54d5cSPoul-Henning Kamp  *
647d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
648f6bde1fdSPoul-Henning Kamp  */
649f6bde1fdSPoul-Henning Kamp static void
650f6bde1fdSPoul-Henning Kamp collapse_unr(struct unrhdr *uh, struct unr *up)
651f6bde1fdSPoul-Henning Kamp {
652f6bde1fdSPoul-Henning Kamp 	struct unr *upp;
653d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
654f6bde1fdSPoul-Henning Kamp 
655d9a54d5cSPoul-Henning Kamp 	/* If bitmap is all set or clear, change it to runlength */
656d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
657d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
6588907f744SAlan Somers 		if (ub_full(ub, up->len)) {
659d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
660d9a54d5cSPoul-Henning Kamp 			up->ptr = uh;
6618907f744SAlan Somers 		} else if (ub_empty(ub, up->len)) {
662d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
663d9a54d5cSPoul-Henning Kamp 			up->ptr = NULL;
664d9a54d5cSPoul-Henning Kamp 		}
665d9a54d5cSPoul-Henning Kamp 	}
666d9a54d5cSPoul-Henning Kamp 
667d9a54d5cSPoul-Henning Kamp 	/* If nothing left in runlength, delete it */
668d9a54d5cSPoul-Henning Kamp 	if (up->len == 0) {
669d9a54d5cSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
670d9a54d5cSPoul-Henning Kamp 		if (upp == NULL)
671d9a54d5cSPoul-Henning Kamp 			upp = TAILQ_NEXT(up, list);
672d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, up, list);
673d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, up);
674d9a54d5cSPoul-Henning Kamp 		up = upp;
675d9a54d5cSPoul-Henning Kamp 	}
676d9a54d5cSPoul-Henning Kamp 
677d9a54d5cSPoul-Henning Kamp 	/* If we have "hot-spot" still, merge with neighbor if possible */
678d9a54d5cSPoul-Henning Kamp 	if (up != NULL) {
679f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
680f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
681f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
682f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
683f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
684f6bde1fdSPoul-Henning Kamp 			}
685f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_NEXT(up, list);
686f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
687f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
688f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
689f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
690f6bde1fdSPoul-Henning Kamp 		}
691f6bde1fdSPoul-Henning Kamp 	}
692f6bde1fdSPoul-Henning Kamp 
693d9a54d5cSPoul-Henning Kamp 	/* Merge into ->first if possible */
694d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
695d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == uh) {
696d9a54d5cSPoul-Henning Kamp 		uh->first += upp->len;
697d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
698d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
699d9a54d5cSPoul-Henning Kamp 		if (up == upp)
700d9a54d5cSPoul-Henning Kamp 			up = NULL;
701d9a54d5cSPoul-Henning Kamp 	}
702d9a54d5cSPoul-Henning Kamp 
703d9a54d5cSPoul-Henning Kamp 	/* Merge into ->last if possible */
704d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_LAST(&uh->head, unrhd);
705d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == NULL) {
706d9a54d5cSPoul-Henning Kamp 		uh->last += upp->len;
707d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
708d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
709d9a54d5cSPoul-Henning Kamp 		if (up == upp)
710d9a54d5cSPoul-Henning Kamp 			up = NULL;
711d9a54d5cSPoul-Henning Kamp 	}
712d9a54d5cSPoul-Henning Kamp 
713d9a54d5cSPoul-Henning Kamp 	/* Try to make bitmaps */
714d9a54d5cSPoul-Henning Kamp 	while (optimize_unr(uh))
715d9a54d5cSPoul-Henning Kamp 		continue;
716d9a54d5cSPoul-Henning Kamp }
717d9a54d5cSPoul-Henning Kamp 
718f6bde1fdSPoul-Henning Kamp /*
719f6bde1fdSPoul-Henning Kamp  * Allocate a free unr.
720f6bde1fdSPoul-Henning Kamp  */
721d9a54d5cSPoul-Henning Kamp int
722d9a54d5cSPoul-Henning Kamp alloc_unrl(struct unrhdr *uh)
723f6bde1fdSPoul-Henning Kamp {
724d9a54d5cSPoul-Henning Kamp 	struct unr *up;
725d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
726f6bde1fdSPoul-Henning Kamp 	u_int x;
727f6bde1fdSPoul-Henning Kamp 	int y;
728f6bde1fdSPoul-Henning Kamp 
729e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
730d9a54d5cSPoul-Henning Kamp 		mtx_assert(uh->mtx, MA_OWNED);
731f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
732d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
733d9a54d5cSPoul-Henning Kamp 
734f6bde1fdSPoul-Henning Kamp 	up = TAILQ_FIRST(&uh->head);
735f6bde1fdSPoul-Henning Kamp 
736d9a54d5cSPoul-Henning Kamp 	/*
737d9a54d5cSPoul-Henning Kamp 	 * If we have an ideal split, just adjust the first+last
738d9a54d5cSPoul-Henning Kamp 	 */
739d9a54d5cSPoul-Henning Kamp 	if (up == NULL && uh->last > 0) {
740d9a54d5cSPoul-Henning Kamp 		uh->first++;
741d9a54d5cSPoul-Henning Kamp 		uh->last--;
742f6bde1fdSPoul-Henning Kamp 		uh->busy++;
743f6bde1fdSPoul-Henning Kamp 		return (x);
744f6bde1fdSPoul-Henning Kamp 	}
745f6bde1fdSPoul-Henning Kamp 
746f6bde1fdSPoul-Henning Kamp 	/*
747d9a54d5cSPoul-Henning Kamp 	 * We can always allocate from the first list element, so if we have
748d9a54d5cSPoul-Henning Kamp 	 * nothing on the list, we must have run out of unit numbers.
749f6bde1fdSPoul-Henning Kamp 	 */
75093f6c81eSPoul-Henning Kamp 	if (up == NULL)
751d9a54d5cSPoul-Henning Kamp 		return (-1);
752d9a54d5cSPoul-Henning Kamp 
753d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr != uh, ("UNR first element is allocated"));
754d9a54d5cSPoul-Henning Kamp 
755d9a54d5cSPoul-Henning Kamp 	if (up->ptr == NULL) {	/* free run */
756d9a54d5cSPoul-Henning Kamp 		uh->first++;
757f6bde1fdSPoul-Henning Kamp 		up->len--;
758d9a54d5cSPoul-Henning Kamp 	} else {		/* bitmap */
759d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
760d9a54d5cSPoul-Henning Kamp 		bit_ffc(ub->map, up->len, &y);
761d9a54d5cSPoul-Henning Kamp 		KASSERT(y != -1, ("UNR corruption: No clear bit in bitmap."));
762d9a54d5cSPoul-Henning Kamp 		bit_set(ub->map, y);
763d9a54d5cSPoul-Henning Kamp 		x += y;
764d9a54d5cSPoul-Henning Kamp 	}
765f6bde1fdSPoul-Henning Kamp 	uh->busy++;
766d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
767f6bde1fdSPoul-Henning Kamp 	return (x);
768f6bde1fdSPoul-Henning Kamp }
769f6bde1fdSPoul-Henning Kamp 
770d9a54d5cSPoul-Henning Kamp int
771d9a54d5cSPoul-Henning Kamp alloc_unr(struct unrhdr *uh)
772d9a54d5cSPoul-Henning Kamp {
773d9a54d5cSPoul-Henning Kamp 	int i;
774d9a54d5cSPoul-Henning Kamp 
775e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
776d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
777d9a54d5cSPoul-Henning Kamp 	i = alloc_unrl(uh);
77809828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
779e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
780d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
781d9a54d5cSPoul-Henning Kamp 	return (i);
782d9a54d5cSPoul-Henning Kamp }
783d9a54d5cSPoul-Henning Kamp 
78413c02cbbSJaakko Heinonen static int
78513c02cbbSJaakko Heinonen alloc_unr_specificl(struct unrhdr *uh, u_int item, void **p1, void **p2)
78613c02cbbSJaakko Heinonen {
78713c02cbbSJaakko Heinonen 	struct unr *up, *upn;
78813c02cbbSJaakko Heinonen 	struct unrb *ub;
78913c02cbbSJaakko Heinonen 	u_int i, last, tl;
79013c02cbbSJaakko Heinonen 
791e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
79213c02cbbSJaakko Heinonen 		mtx_assert(uh->mtx, MA_OWNED);
79313c02cbbSJaakko Heinonen 
79413c02cbbSJaakko Heinonen 	if (item < uh->low + uh->first || item > uh->high)
79513c02cbbSJaakko Heinonen 		return (-1);
79613c02cbbSJaakko Heinonen 
79713c02cbbSJaakko Heinonen 	up = TAILQ_FIRST(&uh->head);
79813c02cbbSJaakko Heinonen 	/* Ideal split. */
79913c02cbbSJaakko Heinonen 	if (up == NULL && item - uh->low == uh->first) {
80013c02cbbSJaakko Heinonen 		uh->first++;
80113c02cbbSJaakko Heinonen 		uh->last--;
80213c02cbbSJaakko Heinonen 		uh->busy++;
80313c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
80413c02cbbSJaakko Heinonen 		return (item);
80513c02cbbSJaakko Heinonen 	}
80613c02cbbSJaakko Heinonen 
80713c02cbbSJaakko Heinonen 	i = item - uh->low - uh->first;
80813c02cbbSJaakko Heinonen 
80913c02cbbSJaakko Heinonen 	if (up == NULL) {
81013c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
81113c02cbbSJaakko Heinonen 		up->ptr = NULL;
81213c02cbbSJaakko Heinonen 		up->len = i;
81313c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
81413c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
81513c02cbbSJaakko Heinonen 		up->ptr = uh;
81613c02cbbSJaakko Heinonen 		up->len = 1;
81713c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
81813c02cbbSJaakko Heinonen 		uh->last = uh->high - uh->low - i;
81913c02cbbSJaakko Heinonen 		uh->busy++;
82013c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
82113c02cbbSJaakko Heinonen 		return (item);
82213c02cbbSJaakko Heinonen 	} else {
82313c02cbbSJaakko Heinonen 		/* Find the item which contains the unit we want to allocate. */
82413c02cbbSJaakko Heinonen 		TAILQ_FOREACH(up, &uh->head, list) {
82513c02cbbSJaakko Heinonen 			if (up->len > i)
82613c02cbbSJaakko Heinonen 				break;
82713c02cbbSJaakko Heinonen 			i -= up->len;
82813c02cbbSJaakko Heinonen 		}
82913c02cbbSJaakko Heinonen 	}
83013c02cbbSJaakko Heinonen 
83113c02cbbSJaakko Heinonen 	if (up == NULL) {
83213c02cbbSJaakko Heinonen 		if (i > 0) {
83313c02cbbSJaakko Heinonen 			up = new_unr(uh, p1, p2);
83413c02cbbSJaakko Heinonen 			up->ptr = NULL;
83513c02cbbSJaakko Heinonen 			up->len = i;
83613c02cbbSJaakko Heinonen 			TAILQ_INSERT_TAIL(&uh->head, up, list);
83713c02cbbSJaakko Heinonen 		}
83813c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
83913c02cbbSJaakko Heinonen 		up->ptr = uh;
84013c02cbbSJaakko Heinonen 		up->len = 1;
84113c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
84213c02cbbSJaakko Heinonen 		goto done;
84313c02cbbSJaakko Heinonen 	}
84413c02cbbSJaakko Heinonen 
84513c02cbbSJaakko Heinonen 	if (is_bitmap(uh, up)) {
84613c02cbbSJaakko Heinonen 		ub = up->ptr;
84713c02cbbSJaakko Heinonen 		if (bit_test(ub->map, i) == 0) {
84813c02cbbSJaakko Heinonen 			bit_set(ub->map, i);
84913c02cbbSJaakko Heinonen 			goto done;
85013c02cbbSJaakko Heinonen 		} else
85113c02cbbSJaakko Heinonen 			return (-1);
85213c02cbbSJaakko Heinonen 	} else if (up->ptr == uh)
85313c02cbbSJaakko Heinonen 		return (-1);
85413c02cbbSJaakko Heinonen 
85513c02cbbSJaakko Heinonen 	KASSERT(up->ptr == NULL,
85613c02cbbSJaakko Heinonen 	    ("alloc_unr_specificl: up->ptr != NULL (up=%p)", up));
85713c02cbbSJaakko Heinonen 
85813c02cbbSJaakko Heinonen 	/* Split off the tail end, if any. */
85913c02cbbSJaakko Heinonen 	tl = up->len - (1 + i);
86013c02cbbSJaakko Heinonen 	if (tl > 0) {
86113c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
86213c02cbbSJaakko Heinonen 		upn->ptr = NULL;
86313c02cbbSJaakko Heinonen 		upn->len = tl;
86413c02cbbSJaakko Heinonen 		TAILQ_INSERT_AFTER(&uh->head, up, upn, list);
86513c02cbbSJaakko Heinonen 	}
86613c02cbbSJaakko Heinonen 
86713c02cbbSJaakko Heinonen 	/* Split off head end, if any */
86813c02cbbSJaakko Heinonen 	if (i > 0) {
86913c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
87013c02cbbSJaakko Heinonen 		upn->len = i;
87113c02cbbSJaakko Heinonen 		upn->ptr = NULL;
87213c02cbbSJaakko Heinonen 		TAILQ_INSERT_BEFORE(up, upn, list);
87313c02cbbSJaakko Heinonen 	}
87413c02cbbSJaakko Heinonen 	up->len = 1;
87513c02cbbSJaakko Heinonen 	up->ptr = uh;
87613c02cbbSJaakko Heinonen 
87713c02cbbSJaakko Heinonen done:
87813c02cbbSJaakko Heinonen 	last = uh->high - uh->low - (item - uh->low);
87913c02cbbSJaakko Heinonen 	if (uh->last > last)
88013c02cbbSJaakko Heinonen 		uh->last = last;
88113c02cbbSJaakko Heinonen 	uh->busy++;
88213c02cbbSJaakko Heinonen 	collapse_unr(uh, up);
88313c02cbbSJaakko Heinonen 	check_unrhdr(uh, __LINE__);
88413c02cbbSJaakko Heinonen 	return (item);
88513c02cbbSJaakko Heinonen }
88613c02cbbSJaakko Heinonen 
88713c02cbbSJaakko Heinonen int
88813c02cbbSJaakko Heinonen alloc_unr_specific(struct unrhdr *uh, u_int item)
88913c02cbbSJaakko Heinonen {
89013c02cbbSJaakko Heinonen 	void *p1, *p2;
89113c02cbbSJaakko Heinonen 	int i;
89213c02cbbSJaakko Heinonen 
89313c02cbbSJaakko Heinonen 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "alloc_unr_specific");
89413c02cbbSJaakko Heinonen 
89513c02cbbSJaakko Heinonen 	p1 = Malloc(sizeof(struct unr));
89613c02cbbSJaakko Heinonen 	p2 = Malloc(sizeof(struct unr));
89713c02cbbSJaakko Heinonen 
898e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
89913c02cbbSJaakko Heinonen 		mtx_lock(uh->mtx);
90013c02cbbSJaakko Heinonen 	i = alloc_unr_specificl(uh, item, &p1, &p2);
901e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
90213c02cbbSJaakko Heinonen 		mtx_unlock(uh->mtx);
90313c02cbbSJaakko Heinonen 
90413c02cbbSJaakko Heinonen 	if (p1 != NULL)
90513c02cbbSJaakko Heinonen 		Free(p1);
90613c02cbbSJaakko Heinonen 	if (p2 != NULL)
90713c02cbbSJaakko Heinonen 		Free(p2);
90813c02cbbSJaakko Heinonen 
90913c02cbbSJaakko Heinonen 	return (i);
91013c02cbbSJaakko Heinonen }
91113c02cbbSJaakko Heinonen 
912f6bde1fdSPoul-Henning Kamp /*
913f6bde1fdSPoul-Henning Kamp  * Free a unr.
914f6bde1fdSPoul-Henning Kamp  *
915f6bde1fdSPoul-Henning Kamp  * If we can save unrs by using a bitmap, do so.
916f6bde1fdSPoul-Henning Kamp  */
917d9a54d5cSPoul-Henning Kamp static void
918d9a54d5cSPoul-Henning Kamp free_unrl(struct unrhdr *uh, u_int item, void **p1, void **p2)
919f6bde1fdSPoul-Henning Kamp {
920d9a54d5cSPoul-Henning Kamp 	struct unr *up, *upp, *upn;
921d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
922d9a54d5cSPoul-Henning Kamp 	u_int pl;
923f6bde1fdSPoul-Henning Kamp 
924f6bde1fdSPoul-Henning Kamp 	KASSERT(item >= uh->low && item <= uh->high,
925f6bde1fdSPoul-Henning Kamp 	    ("UNR: free_unr(%u) out of range [%u...%u]",
926f6bde1fdSPoul-Henning Kamp 	     item, uh->low, uh->high));
927f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
928f6bde1fdSPoul-Henning Kamp 	item -= uh->low;
929d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
930f6bde1fdSPoul-Henning Kamp 	/*
931d9a54d5cSPoul-Henning Kamp 	 * Freeing in the ideal split case
932f6bde1fdSPoul-Henning Kamp 	 */
933d9a54d5cSPoul-Henning Kamp 	if (item + 1 == uh->first && upp == NULL) {
934d9a54d5cSPoul-Henning Kamp 		uh->last++;
935d9a54d5cSPoul-Henning Kamp 		uh->first--;
936d9a54d5cSPoul-Henning Kamp 		uh->busy--;
937f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
938f6bde1fdSPoul-Henning Kamp 		return;
939f6bde1fdSPoul-Henning Kamp 	}
940d9a54d5cSPoul-Henning Kamp 	/*
941d9a54d5cSPoul-Henning Kamp  	 * Freeing in the ->first section.  Create a run starting at the
942d9a54d5cSPoul-Henning Kamp 	 * freed item.  The code below will subdivide it.
943d9a54d5cSPoul-Henning Kamp 	 */
944d9a54d5cSPoul-Henning Kamp 	if (item < uh->first) {
945d9a54d5cSPoul-Henning Kamp 		up = new_unr(uh, p1, p2);
946d9a54d5cSPoul-Henning Kamp 		up->ptr = uh;
947d9a54d5cSPoul-Henning Kamp 		up->len = uh->first - item;
948d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_HEAD(&uh->head, up, list);
949d9a54d5cSPoul-Henning Kamp 		uh->first -= up->len;
950f6bde1fdSPoul-Henning Kamp 	}
951f6bde1fdSPoul-Henning Kamp 
952d9a54d5cSPoul-Henning Kamp 	item -= uh->first;
953d9a54d5cSPoul-Henning Kamp 
954d9a54d5cSPoul-Henning Kamp 	/* Find the item which contains the unit we want to free */
955d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
956d9a54d5cSPoul-Henning Kamp 		if (up->len > item)
957d9a54d5cSPoul-Henning Kamp 			break;
958d9a54d5cSPoul-Henning Kamp 		item -= up->len;
959d9a54d5cSPoul-Henning Kamp 	}
960d9a54d5cSPoul-Henning Kamp 
961d9a54d5cSPoul-Henning Kamp 	/* Handle bitmap items */
962d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
963d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
964d9a54d5cSPoul-Henning Kamp 
965d9a54d5cSPoul-Henning Kamp 		KASSERT(bit_test(ub->map, item) != 0,
966d9a54d5cSPoul-Henning Kamp 		    ("UNR: Freeing free item %d (bitmap)\n", item));
967d9a54d5cSPoul-Henning Kamp 		bit_clear(ub->map, item);
968d9a54d5cSPoul-Henning Kamp 		uh->busy--;
969d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
970d9a54d5cSPoul-Henning Kamp 		return;
971d9a54d5cSPoul-Henning Kamp 	}
972d9a54d5cSPoul-Henning Kamp 
973d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr == uh, ("UNR Freeing free item %d (run))\n", item));
974f6bde1fdSPoul-Henning Kamp 
975f6bde1fdSPoul-Henning Kamp 	/* Just this one left, reap it */
976f6bde1fdSPoul-Henning Kamp 	if (up->len == 1) {
977f6bde1fdSPoul-Henning Kamp 		up->ptr = NULL;
978f6bde1fdSPoul-Henning Kamp 		uh->busy--;
979f6bde1fdSPoul-Henning Kamp 		collapse_unr(uh, up);
980f6bde1fdSPoul-Henning Kamp 		return;
981f6bde1fdSPoul-Henning Kamp 	}
982f6bde1fdSPoul-Henning Kamp 
983d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item into the previous 'free' run */
984f6bde1fdSPoul-Henning Kamp 	upp = TAILQ_PREV(up, unrhd, list);
985d9a54d5cSPoul-Henning Kamp 	if (item == 0 && upp != NULL && upp->ptr == NULL) {
986f6bde1fdSPoul-Henning Kamp 		upp->len++;
987f6bde1fdSPoul-Henning Kamp 		up->len--;
988f6bde1fdSPoul-Henning Kamp 		uh->busy--;
989d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
990f6bde1fdSPoul-Henning Kamp 		return;
991f6bde1fdSPoul-Henning Kamp 	}
992f6bde1fdSPoul-Henning Kamp 
993d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item to the next 'free' run */
994f6bde1fdSPoul-Henning Kamp 	upn = TAILQ_NEXT(up, list);
995d9a54d5cSPoul-Henning Kamp 	if (item == up->len - 1 && upn != NULL && upn->ptr == NULL) {
996f6bde1fdSPoul-Henning Kamp 		upn->len++;
997f6bde1fdSPoul-Henning Kamp 		up->len--;
998f6bde1fdSPoul-Henning Kamp 		uh->busy--;
999d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
1000f6bde1fdSPoul-Henning Kamp 		return;
1001f6bde1fdSPoul-Henning Kamp 	}
1002f6bde1fdSPoul-Henning Kamp 
1003f6bde1fdSPoul-Henning Kamp 	/* Split off the tail end, if any. */
1004d9a54d5cSPoul-Henning Kamp 	pl = up->len - (1 + item);
1005f6bde1fdSPoul-Henning Kamp 	if (pl > 0) {
1006d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
1007f6bde1fdSPoul-Henning Kamp 		upp->ptr = uh;
1008f6bde1fdSPoul-Henning Kamp 		upp->len = pl;
1009f6bde1fdSPoul-Henning Kamp 		TAILQ_INSERT_AFTER(&uh->head, up, upp, list);
1010f6bde1fdSPoul-Henning Kamp 	}
1011f6bde1fdSPoul-Henning Kamp 
1012d9a54d5cSPoul-Henning Kamp 	/* Split off head end, if any */
1013d9a54d5cSPoul-Henning Kamp 	if (item > 0) {
1014d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
1015d9a54d5cSPoul-Henning Kamp 		upp->len = item;
1016d9a54d5cSPoul-Henning Kamp 		upp->ptr = uh;
1017d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_BEFORE(up, upp, list);
1018d9a54d5cSPoul-Henning Kamp 	}
1019f6bde1fdSPoul-Henning Kamp 	up->len = 1;
1020f6bde1fdSPoul-Henning Kamp 	up->ptr = NULL;
1021f6bde1fdSPoul-Henning Kamp 	uh->busy--;
1022d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
1023f6bde1fdSPoul-Henning Kamp }
1024f6bde1fdSPoul-Henning Kamp 
1025d9a54d5cSPoul-Henning Kamp void
1026d9a54d5cSPoul-Henning Kamp free_unr(struct unrhdr *uh, u_int item)
1027d9a54d5cSPoul-Henning Kamp {
1028d9a54d5cSPoul-Henning Kamp 	void *p1, *p2;
1029f6bde1fdSPoul-Henning Kamp 
10307550e3eaSKonstantin Belousov 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "free_unr");
1031d9a54d5cSPoul-Henning Kamp 	p1 = Malloc(sizeof(struct unr));
1032d9a54d5cSPoul-Henning Kamp 	p2 = Malloc(sizeof(struct unr));
1033e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
1034d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
1035d9a54d5cSPoul-Henning Kamp 	free_unrl(uh, item, &p1, &p2);
103609828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
1037e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
1038d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
1039d9a54d5cSPoul-Henning Kamp 	if (p1 != NULL)
1040d9a54d5cSPoul-Henning Kamp 		Free(p1);
1041d9a54d5cSPoul-Henning Kamp 	if (p2 != NULL)
1042d9a54d5cSPoul-Henning Kamp 		Free(p2);
1043f6bde1fdSPoul-Henning Kamp }
1044f6bde1fdSPoul-Henning Kamp 
1045f386b277SKonstantin Belousov #ifdef _KERNEL
1046f386b277SKonstantin Belousov #include "opt_ddb.h"
1047f386b277SKonstantin Belousov #ifdef DDB
1048f386b277SKonstantin Belousov #include <ddb/ddb.h>
1049f386b277SKonstantin Belousov #endif
1050f386b277SKonstantin Belousov #endif
105193f6c81eSPoul-Henning Kamp 
1052f386b277SKonstantin Belousov #if (defined(_KERNEL) && defined(DDB)) || !defined(_KERNEL)
1053f6bde1fdSPoul-Henning Kamp 
1054f386b277SKonstantin Belousov #if !defined(_KERNEL)
1055f386b277SKonstantin Belousov #define db_printf printf
1056f386b277SKonstantin Belousov #endif
1057794277daSAlan Somers 
1058f6bde1fdSPoul-Henning Kamp static void
1059f6bde1fdSPoul-Henning Kamp print_unr(struct unrhdr *uh, struct unr *up)
1060f6bde1fdSPoul-Henning Kamp {
1061f6bde1fdSPoul-Henning Kamp 	u_int x;
1062d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
1063f6bde1fdSPoul-Henning Kamp 
1064f386b277SKonstantin Belousov 	db_printf("  %p len = %5u ", up, up->len);
1065f6bde1fdSPoul-Henning Kamp 	if (up->ptr == NULL)
1066f386b277SKonstantin Belousov 		db_printf("free\n");
1067f6bde1fdSPoul-Henning Kamp 	else if (up->ptr == uh)
1068f386b277SKonstantin Belousov 		db_printf("alloc\n");
1069f6bde1fdSPoul-Henning Kamp 	else {
1070d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
1071f386b277SKonstantin Belousov 		db_printf("bitmap [");
1072d9a54d5cSPoul-Henning Kamp 		for (x = 0; x < up->len; x++) {
1073d9a54d5cSPoul-Henning Kamp 			if (bit_test(ub->map, x))
1074f386b277SKonstantin Belousov 				db_printf("#");
1075f6bde1fdSPoul-Henning Kamp 			else
1076f386b277SKonstantin Belousov 				db_printf(" ");
1077f6bde1fdSPoul-Henning Kamp 		}
1078f386b277SKonstantin Belousov 		db_printf("]\n");
1079f6bde1fdSPoul-Henning Kamp 	}
1080f6bde1fdSPoul-Henning Kamp }
1081f6bde1fdSPoul-Henning Kamp 
1082f6bde1fdSPoul-Henning Kamp static void
1083f6bde1fdSPoul-Henning Kamp print_unrhdr(struct unrhdr *uh)
1084f6bde1fdSPoul-Henning Kamp {
1085f6bde1fdSPoul-Henning Kamp 	struct unr *up;
1086f6bde1fdSPoul-Henning Kamp 	u_int x;
1087f6bde1fdSPoul-Henning Kamp 
1088f386b277SKonstantin Belousov 	db_printf(
1089d9a54d5cSPoul-Henning Kamp 	    "%p low = %u high = %u first = %u last = %u busy %u chunks = %u\n",
1090d9a54d5cSPoul-Henning Kamp 	    uh, uh->low, uh->high, uh->first, uh->last, uh->busy, uh->alloc);
1091d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
1092f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
1093f386b277SKonstantin Belousov 		db_printf("  from = %5u", x);
1094f6bde1fdSPoul-Henning Kamp 		print_unr(uh, up);
1095f6bde1fdSPoul-Henning Kamp 		if (up->ptr == NULL || up->ptr == uh)
1096f6bde1fdSPoul-Henning Kamp 			x += up->len;
1097f6bde1fdSPoul-Henning Kamp 		else
1098f6bde1fdSPoul-Henning Kamp 			x += NBITS;
1099f6bde1fdSPoul-Henning Kamp 	}
1100f6bde1fdSPoul-Henning Kamp }
1101f6bde1fdSPoul-Henning Kamp 
1102f386b277SKonstantin Belousov #endif
1103f386b277SKonstantin Belousov 
1104f386b277SKonstantin Belousov #if defined(_KERNEL) && defined(DDB)
1105f386b277SKonstantin Belousov DB_SHOW_COMMAND(unrhdr, unrhdr_print_unrhdr)
1106f386b277SKonstantin Belousov {
1107f386b277SKonstantin Belousov 	if (!have_addr) {
1108f386b277SKonstantin Belousov 		db_printf("show unrhdr addr\n");
1109f386b277SKonstantin Belousov 		return;
1110f386b277SKonstantin Belousov 	}
1111f386b277SKonstantin Belousov 
1112f386b277SKonstantin Belousov 	print_unrhdr((struct unrhdr *)addr);
1113f386b277SKonstantin Belousov }
1114f386b277SKonstantin Belousov #endif
1115f386b277SKonstantin Belousov 
1116f386b277SKonstantin Belousov #ifndef _KERNEL	/* USERLAND test driver */
1117f386b277SKonstantin Belousov 
1118f386b277SKonstantin Belousov /*
1119f386b277SKonstantin Belousov  * Simple stochastic test driver for the above functions.  The code resides
1120f386b277SKonstantin Belousov  * here so that it can access static functions and structures.
1121f386b277SKonstantin Belousov  */
1122f386b277SKonstantin Belousov 
1123f386b277SKonstantin Belousov static bool verbose;
1124f386b277SKonstantin Belousov #define VPRINTF(...)	{if (verbose) printf(__VA_ARGS__);}
1125f386b277SKonstantin Belousov 
112613c02cbbSJaakko Heinonen static void
112713c02cbbSJaakko Heinonen test_alloc_unr(struct unrhdr *uh, u_int i, char a[])
112813c02cbbSJaakko Heinonen {
112913c02cbbSJaakko Heinonen 	int j;
113013c02cbbSJaakko Heinonen 
113113c02cbbSJaakko Heinonen 	if (a[i]) {
1132794277daSAlan Somers 		VPRINTF("F %u\n", i);
113313c02cbbSJaakko Heinonen 		free_unr(uh, i);
113413c02cbbSJaakko Heinonen 		a[i] = 0;
113513c02cbbSJaakko Heinonen 	} else {
113613c02cbbSJaakko Heinonen 		no_alloc = 1;
113713c02cbbSJaakko Heinonen 		j = alloc_unr(uh);
113813c02cbbSJaakko Heinonen 		if (j != -1) {
113913c02cbbSJaakko Heinonen 			a[j] = 1;
1140794277daSAlan Somers 			VPRINTF("A %d\n", j);
114113c02cbbSJaakko Heinonen 		}
114213c02cbbSJaakko Heinonen 		no_alloc = 0;
114313c02cbbSJaakko Heinonen 	}
114413c02cbbSJaakko Heinonen }
114513c02cbbSJaakko Heinonen 
114613c02cbbSJaakko Heinonen static void
114713c02cbbSJaakko Heinonen test_alloc_unr_specific(struct unrhdr *uh, u_int i, char a[])
114813c02cbbSJaakko Heinonen {
114913c02cbbSJaakko Heinonen 	int j;
115013c02cbbSJaakko Heinonen 
115113c02cbbSJaakko Heinonen 	j = alloc_unr_specific(uh, i);
115213c02cbbSJaakko Heinonen 	if (j == -1) {
1153794277daSAlan Somers 		VPRINTF("F %u\n", i);
115413c02cbbSJaakko Heinonen 		a[i] = 0;
115513c02cbbSJaakko Heinonen 		free_unr(uh, i);
115613c02cbbSJaakko Heinonen 	} else {
115713c02cbbSJaakko Heinonen 		a[i] = 1;
1158794277daSAlan Somers 		VPRINTF("A %d\n", j);
115913c02cbbSJaakko Heinonen 	}
116013c02cbbSJaakko Heinonen }
116113c02cbbSJaakko Heinonen 
1162794277daSAlan Somers static void
1163*a014e0a3SKonstantin Belousov test_iter(void)
1164*a014e0a3SKonstantin Belousov {
1165*a014e0a3SKonstantin Belousov }
1166*a014e0a3SKonstantin Belousov 
1167*a014e0a3SKonstantin Belousov static void
1168794277daSAlan Somers usage(char **argv)
1169794277daSAlan Somers {
1170794277daSAlan Somers 	printf("%s [-h] [-r REPETITIONS] [-v]\n", argv[0]);
1171794277daSAlan Somers }
1172f6bde1fdSPoul-Henning Kamp 
1173f6bde1fdSPoul-Henning Kamp int
1174794277daSAlan Somers main(int argc, char **argv)
1175f6bde1fdSPoul-Henning Kamp {
1176f6bde1fdSPoul-Henning Kamp 	struct unrhdr *uh;
1177794277daSAlan Somers 	char *a;
1178794277daSAlan Somers 	long count = 10000;	/* Number of unrs to test */
117937f32e53SAlan Somers 	long reps = 1, m;
1180794277daSAlan Somers 	int ch;
1181cd565040SConrad Meyer 	u_int i;
1182794277daSAlan Somers 
1183794277daSAlan Somers 	verbose = false;
1184794277daSAlan Somers 
1185794277daSAlan Somers 	while ((ch = getopt(argc, argv, "hr:v")) != -1) {
1186794277daSAlan Somers 		switch (ch) {
1187794277daSAlan Somers 		case 'r':
1188794277daSAlan Somers 			errno = 0;
1189794277daSAlan Somers 			reps = strtol(optarg, NULL, 0);
1190794277daSAlan Somers 			if (errno == ERANGE || errno == EINVAL) {
1191794277daSAlan Somers 				usage(argv);
1192794277daSAlan Somers 				exit(2);
1193794277daSAlan Somers 			}
1194794277daSAlan Somers 
1195794277daSAlan Somers 			break;
1196794277daSAlan Somers 		case 'v':
1197794277daSAlan Somers 			verbose = true;
1198794277daSAlan Somers 			break;
1199794277daSAlan Somers 		case 'h':
1200794277daSAlan Somers 		default:
1201794277daSAlan Somers 			usage(argv);
1202794277daSAlan Somers 			exit(2);
1203794277daSAlan Somers 		}
1204794277daSAlan Somers 	}
1205f6bde1fdSPoul-Henning Kamp 
1206d9a54d5cSPoul-Henning Kamp 	setbuf(stdout, NULL);
1207794277daSAlan Somers 	uh = new_unrhdr(0, count - 1, NULL);
1208d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1209f6bde1fdSPoul-Henning Kamp 
1210794277daSAlan Somers 	a = calloc(count, sizeof(char));
1211794277daSAlan Somers 	if (a == NULL)
1212794277daSAlan Somers 		err(1, "calloc failed");
1213f6bde1fdSPoul-Henning Kamp 
1214794277daSAlan Somers 	printf("sizeof(struct unr) %zu\n", sizeof(struct unr));
1215794277daSAlan Somers 	printf("sizeof(struct unrb) %zu\n", sizeof(struct unrb));
1216794277daSAlan Somers 	printf("sizeof(struct unrhdr) %zu\n", sizeof(struct unrhdr));
1217b4f69365SEd Maste 	printf("NBITS %lu\n", (unsigned long)NBITS);
1218794277daSAlan Somers 	for (m = 0; m < count * reps; m++) {
1219cd565040SConrad Meyer 		i = arc4random_uniform(count);
1220d9a54d5cSPoul-Henning Kamp #if 0
1221d9a54d5cSPoul-Henning Kamp 		if (a[i] && (j & 1))
1222d9a54d5cSPoul-Henning Kamp 			continue;
1223d9a54d5cSPoul-Henning Kamp #endif
1224cd565040SConrad Meyer 		if ((arc4random() & 1) != 0)
122513c02cbbSJaakko Heinonen 			test_alloc_unr(uh, i, a);
122613c02cbbSJaakko Heinonen 		else
122713c02cbbSJaakko Heinonen 			test_alloc_unr_specific(uh, i, a);
122813c02cbbSJaakko Heinonen 
1229794277daSAlan Somers 		if (verbose)
1230f6bde1fdSPoul-Henning Kamp 			print_unrhdr(uh);
1231f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
1232f6bde1fdSPoul-Henning Kamp 	}
123337f32e53SAlan Somers 	for (i = 0; i < (u_int)count; i++) {
1234d9a54d5cSPoul-Henning Kamp 		if (a[i]) {
1235794277daSAlan Somers 			if (verbose) {
1236d9a54d5cSPoul-Henning Kamp 				printf("C %u\n", i);
1237e4fea39eSPoul-Henning Kamp 				print_unrhdr(uh);
1238d9a54d5cSPoul-Henning Kamp 			}
1239794277daSAlan Somers 			free_unr(uh, i);
1240794277daSAlan Somers 		}
1241d9a54d5cSPoul-Henning Kamp 	}
1242d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1243e4fea39eSPoul-Henning Kamp 	delete_unrhdr(uh);
1244794277daSAlan Somers 	free(a);
1245f6bde1fdSPoul-Henning Kamp 	return (0);
1246f6bde1fdSPoul-Henning Kamp }
1247f6bde1fdSPoul-Henning Kamp #endif
1248