xref: /freebsd/sys/kern/subr_unit.c (revision 1384a0b940e87876d36d50ad58581c24dc642714)
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  *
28d9a54d5cSPoul-Henning Kamp  *
29d9a54d5cSPoul-Henning Kamp  * Unit number allocation functions.
30f6bde1fdSPoul-Henning Kamp  *
31f6bde1fdSPoul-Henning Kamp  * These functions implement a mixed run-length/bitmap management of unit
32d9a54d5cSPoul-Henning Kamp  * number spaces in a very memory efficient manner.
33f6bde1fdSPoul-Henning Kamp  *
34d9a54d5cSPoul-Henning Kamp  * Allocation policy is always lowest free number first.
35f6bde1fdSPoul-Henning Kamp  *
36d9a54d5cSPoul-Henning Kamp  * A return value of -1 signals that no more unit numbers are available.
37f6bde1fdSPoul-Henning Kamp  *
38d9a54d5cSPoul-Henning Kamp  * There is no cost associated with the range of unitnumbers, so unless
39d9a54d5cSPoul-Henning Kamp  * the resource really is finite, specify INT_MAX to new_unrhdr() and
40d9a54d5cSPoul-Henning Kamp  * forget about checking the return value.
41f6bde1fdSPoul-Henning Kamp  *
42d9a54d5cSPoul-Henning Kamp  * If a mutex is not provided when the unit number space is created, a
43d9a54d5cSPoul-Henning Kamp  * default global mutex is used.  The advantage to passing a mutex in, is
446bccea7cSRebecca Cran  * that the alloc_unrl() function can be called with the mutex already
45d9a54d5cSPoul-Henning Kamp  * held (it will not be released by alloc_unrl()).
46d9a54d5cSPoul-Henning Kamp  *
47d9a54d5cSPoul-Henning Kamp  * The allocation function alloc_unr{l}() never sleeps (but it may block on
48d9a54d5cSPoul-Henning Kamp  * the mutex of course).
49d9a54d5cSPoul-Henning Kamp  *
50d9a54d5cSPoul-Henning Kamp  * Freeing a unit number may require allocating memory, and can therefore
51d9a54d5cSPoul-Henning Kamp  * sleep so the free_unr() function does not come in a pre-locked variant.
52f6bde1fdSPoul-Henning Kamp  *
53f6bde1fdSPoul-Henning Kamp  * A userland test program is included.
54f6bde1fdSPoul-Henning Kamp  *
556bccea7cSRebecca Cran  * Memory usage is a very complex function of the exact allocation
56d9a54d5cSPoul-Henning Kamp  * pattern, but always very compact:
57d9a54d5cSPoul-Henning Kamp  *    * For the very typical case where a single unbroken run of unit
58d9a54d5cSPoul-Henning Kamp  *      numbers are allocated 44 bytes are used on i386.
59d9a54d5cSPoul-Henning Kamp  *    * For a unit number space of 1000 units and the random pattern
60d9a54d5cSPoul-Henning Kamp  *      in the usermode test program included, the worst case usage
61d9a54d5cSPoul-Henning Kamp  *	was 252 bytes on i386 for 500 allocated and 500 free units.
62d9a54d5cSPoul-Henning Kamp  *    * For a unit number space of 10000 units and the random pattern
63d9a54d5cSPoul-Henning Kamp  *      in the usermode test program included, the worst case usage
64d9a54d5cSPoul-Henning Kamp  *	was 798 bytes on i386 for 5000 allocated and 5000 free units.
65d9a54d5cSPoul-Henning Kamp  *    * The worst case is where every other unit number is allocated and
6696240c89SEitan Adler  *	the rest are free.  In that case 44 + N/4 bytes are used where
67d9a54d5cSPoul-Henning Kamp  *	N is the number of the highest unit allocated.
68f6bde1fdSPoul-Henning Kamp  */
69f6bde1fdSPoul-Henning Kamp 
708907f744SAlan Somers #include <sys/param.h>
71f6bde1fdSPoul-Henning Kamp #include <sys/types.h>
72dc872d46SKonstantin Belousov #include <sys/_unrhdr.h>
73f6bde1fdSPoul-Henning Kamp 
74f6bde1fdSPoul-Henning Kamp #ifdef _KERNEL
75f6bde1fdSPoul-Henning Kamp 
76794277daSAlan Somers #include <sys/bitstring.h>
77f6bde1fdSPoul-Henning Kamp #include <sys/malloc.h>
78f6bde1fdSPoul-Henning Kamp #include <sys/kernel.h>
79f6bde1fdSPoul-Henning Kamp #include <sys/systm.h>
80d9a54d5cSPoul-Henning Kamp #include <sys/limits.h>
81d9a54d5cSPoul-Henning Kamp #include <sys/lock.h>
82d9a54d5cSPoul-Henning Kamp #include <sys/mutex.h>
83f6bde1fdSPoul-Henning Kamp 
84f6bde1fdSPoul-Henning Kamp /*
85f6bde1fdSPoul-Henning Kamp  * In theory it would be smarter to allocate the individual blocks
86f6bde1fdSPoul-Henning Kamp  * with the zone allocator, but at this time the expectation is that
87f6bde1fdSPoul-Henning Kamp  * there will typically not even be enough allocations to fill a single
88f6bde1fdSPoul-Henning Kamp  * page, so we stick with malloc for now.
89f6bde1fdSPoul-Henning Kamp  */
90f6bde1fdSPoul-Henning Kamp static MALLOC_DEFINE(M_UNIT, "Unitno", "Unit number allocation");
91f6bde1fdSPoul-Henning Kamp 
92f6bde1fdSPoul-Henning Kamp #define Malloc(foo) malloc(foo, M_UNIT, M_WAITOK | M_ZERO)
93f6bde1fdSPoul-Henning Kamp #define Free(foo) free(foo, M_UNIT)
94f6bde1fdSPoul-Henning Kamp 
95d9a54d5cSPoul-Henning Kamp static struct mtx unitmtx;
96d9a54d5cSPoul-Henning Kamp 
97d9a54d5cSPoul-Henning Kamp MTX_SYSINIT(unit, &unitmtx, "unit# allocation", MTX_DEF);
98d9a54d5cSPoul-Henning Kamp 
99f6bde1fdSPoul-Henning Kamp #else /* ...USERLAND */
100f6bde1fdSPoul-Henning Kamp 
101794277daSAlan Somers #include <bitstring.h>
102794277daSAlan Somers #include <err.h>
103794277daSAlan Somers #include <errno.h>
104794277daSAlan Somers #include <getopt.h>
105794277daSAlan Somers #include <stdbool.h>
106f6bde1fdSPoul-Henning Kamp #include <stdio.h>
107f6bde1fdSPoul-Henning Kamp #include <stdlib.h>
108d9a54d5cSPoul-Henning Kamp #include <string.h>
109f6bde1fdSPoul-Henning Kamp 
110f6bde1fdSPoul-Henning Kamp #define KASSERT(cond, arg) \
111f6bde1fdSPoul-Henning Kamp 	do { \
112f6bde1fdSPoul-Henning Kamp 		if (!(cond)) { \
113f6bde1fdSPoul-Henning Kamp 			printf arg; \
114d9a54d5cSPoul-Henning Kamp 			abort(); \
115f6bde1fdSPoul-Henning Kamp 		} \
116f6bde1fdSPoul-Henning Kamp 	} while (0)
117f6bde1fdSPoul-Henning Kamp 
118d9a54d5cSPoul-Henning Kamp static int no_alloc;
119d9a54d5cSPoul-Henning Kamp #define Malloc(foo) _Malloc(foo, __LINE__)
120d9a54d5cSPoul-Henning Kamp static void *
121d9a54d5cSPoul-Henning Kamp _Malloc(size_t foo, int line)
122d9a54d5cSPoul-Henning Kamp {
123d9a54d5cSPoul-Henning Kamp 
124d9a54d5cSPoul-Henning Kamp 	KASSERT(no_alloc == 0, ("malloc in wrong place() line %d", line));
125d9a54d5cSPoul-Henning Kamp 	return (calloc(foo, 1));
126d9a54d5cSPoul-Henning Kamp }
127f6bde1fdSPoul-Henning Kamp #define Free(foo) free(foo)
128f6bde1fdSPoul-Henning Kamp 
129d9a54d5cSPoul-Henning Kamp struct unrhdr;
130d9a54d5cSPoul-Henning Kamp 
1316fe78ad4SKonstantin Belousov #define	UNR_NO_MTX	((void *)(uintptr_t)-1)
1326fe78ad4SKonstantin Belousov 
133d9a54d5cSPoul-Henning Kamp struct mtx {
134d9a54d5cSPoul-Henning Kamp 	int	state;
135d9a54d5cSPoul-Henning Kamp } unitmtx;
136d9a54d5cSPoul-Henning Kamp 
137d9a54d5cSPoul-Henning Kamp static void
138d9a54d5cSPoul-Henning Kamp mtx_lock(struct mtx *mp)
139d9a54d5cSPoul-Henning Kamp {
140d9a54d5cSPoul-Henning Kamp 	KASSERT(mp->state == 0, ("mutex already locked"));
141d9a54d5cSPoul-Henning Kamp 	mp->state = 1;
142d9a54d5cSPoul-Henning Kamp }
143d9a54d5cSPoul-Henning Kamp 
144d9a54d5cSPoul-Henning Kamp static void
145d9a54d5cSPoul-Henning Kamp mtx_unlock(struct mtx *mp)
146d9a54d5cSPoul-Henning Kamp {
147d9a54d5cSPoul-Henning Kamp 	KASSERT(mp->state == 1, ("mutex not locked"));
148d9a54d5cSPoul-Henning Kamp 	mp->state = 0;
149d9a54d5cSPoul-Henning Kamp }
150d9a54d5cSPoul-Henning Kamp 
151d9a54d5cSPoul-Henning Kamp #define MA_OWNED	9
152d9a54d5cSPoul-Henning Kamp 
153d9a54d5cSPoul-Henning Kamp static void
154d9a54d5cSPoul-Henning Kamp mtx_assert(struct mtx *mp, int flag)
155d9a54d5cSPoul-Henning Kamp {
156d9a54d5cSPoul-Henning Kamp 	if (flag == MA_OWNED) {
157d9a54d5cSPoul-Henning Kamp 		KASSERT(mp->state == 1, ("mtx_assert(MA_OWNED) not true"));
158d9a54d5cSPoul-Henning Kamp 	}
159d9a54d5cSPoul-Henning Kamp }
160d9a54d5cSPoul-Henning Kamp 
161d9a54d5cSPoul-Henning Kamp #define CTASSERT(foo)
16224e8eaf1SJaakko Heinonen #define WITNESS_WARN(flags, lock, fmt, ...)	(void)0
163d9a54d5cSPoul-Henning Kamp 
164d9a54d5cSPoul-Henning Kamp #endif /* USERLAND */
165f6bde1fdSPoul-Henning Kamp 
166f6bde1fdSPoul-Henning Kamp /*
167f6bde1fdSPoul-Henning Kamp  * This is our basic building block.
168f6bde1fdSPoul-Henning Kamp  *
169f6bde1fdSPoul-Henning Kamp  * It can be used in three different ways depending on the value of the ptr
170f6bde1fdSPoul-Henning Kamp  * element:
171f6bde1fdSPoul-Henning Kamp  *     If ptr is NULL, it represents a run of free items.
172f6bde1fdSPoul-Henning Kamp  *     If ptr points to the unrhdr it represents a run of allocated items.
1738907f744SAlan Somers  *     Otherwise it points to a bitstring of allocated items.
174f6bde1fdSPoul-Henning Kamp  *
175f6bde1fdSPoul-Henning Kamp  * For runs the len field is the length of the run.
176f6bde1fdSPoul-Henning Kamp  * For bitmaps the len field represents the number of allocated items.
177f6bde1fdSPoul-Henning Kamp  *
178f6bde1fdSPoul-Henning Kamp  * The bitmap is the same size as struct unr to optimize memory management.
179d44f4770SKonstantin Belousov  *
180d44f4770SKonstantin Belousov  * Two special ranges are not covered by unrs:
181d44f4770SKonstantin Belousov  * - at the start of the allocator space, all elements in [low, low + first)
182d44f4770SKonstantin Belousov  *   are allocated;
183d44f4770SKonstantin Belousov  * - at the end of the allocator space, all elements in [high - last, high]
184d44f4770SKonstantin Belousov  *   are free.
185f6bde1fdSPoul-Henning Kamp  */
186f6bde1fdSPoul-Henning Kamp struct unr {
187f6bde1fdSPoul-Henning Kamp 	TAILQ_ENTRY(unr)	list;
188f6bde1fdSPoul-Henning Kamp 	u_int			len;
189f6bde1fdSPoul-Henning Kamp 	void			*ptr;
190f6bde1fdSPoul-Henning Kamp };
191f6bde1fdSPoul-Henning Kamp 
192d9a54d5cSPoul-Henning Kamp struct unrb {
1938907f744SAlan Somers 	bitstr_t		map[sizeof(struct unr) / sizeof(bitstr_t)];
194d9a54d5cSPoul-Henning Kamp };
195d9a54d5cSPoul-Henning Kamp 
1968907f744SAlan Somers CTASSERT((sizeof(struct unr) % sizeof(bitstr_t)) == 0);
197d9a54d5cSPoul-Henning Kamp 
1988907f744SAlan Somers /* Number of bits we can store in the bitmap */
199042ec55fSKonstantin Belousov #define NBITS (NBBY * sizeof(((struct unrb *)NULL)->map))
2008907f744SAlan Somers 
20136b1f8a8SKonstantin Belousov static inline bool
20236b1f8a8SKonstantin Belousov is_bitmap(struct unrhdr *uh, struct unr *up)
20336b1f8a8SKonstantin Belousov {
20436b1f8a8SKonstantin Belousov 	return (up->ptr != uh && up->ptr != NULL);
20536b1f8a8SKonstantin Belousov }
20636b1f8a8SKonstantin Belousov 
2078907f744SAlan Somers /* Is the unrb empty in at least the first len bits? */
2088907f744SAlan Somers static inline bool
2098907f744SAlan Somers ub_empty(struct unrb *ub, int len) {
2108907f744SAlan Somers 	int first_set;
2118907f744SAlan Somers 
2128907f744SAlan Somers 	bit_ffs(ub->map, len, &first_set);
2138907f744SAlan Somers 	return (first_set == -1);
2148907f744SAlan Somers }
2158907f744SAlan Somers 
2168907f744SAlan Somers /* Is the unrb full?  That is, is the number of set elements equal to len? */
2178907f744SAlan Somers static inline bool
2188907f744SAlan Somers ub_full(struct unrb *ub, int len)
2198907f744SAlan Somers {
2208907f744SAlan Somers 	int first_clear;
2218907f744SAlan Somers 
2228907f744SAlan Somers 	bit_ffc(ub->map, len, &first_clear);
2238907f744SAlan Somers 	return (first_clear == -1);
2248907f744SAlan Somers }
2258907f744SAlan Somers 
226a014e0a3SKonstantin Belousov /*
227a014e0a3SKonstantin Belousov  * start: ipos = -1, upos = NULL;
228a014e0a3SKonstantin Belousov  * end:   ipos = -1, upos = uh
229a014e0a3SKonstantin Belousov  */
230a014e0a3SKonstantin Belousov struct unrhdr_iter {
231a014e0a3SKonstantin Belousov 	struct unrhdr *uh;
232a014e0a3SKonstantin Belousov 	int ipos;
233a014e0a3SKonstantin Belousov 	int upos_first_item;
234a014e0a3SKonstantin Belousov 	void *upos;
235a014e0a3SKonstantin Belousov };
236a014e0a3SKonstantin Belousov 
237a014e0a3SKonstantin Belousov void *
238a014e0a3SKonstantin Belousov create_iter_unr(struct unrhdr *uh)
239a014e0a3SKonstantin Belousov {
240a014e0a3SKonstantin Belousov 	struct unrhdr_iter *iter;
241a014e0a3SKonstantin Belousov 
242a014e0a3SKonstantin Belousov 	iter = Malloc(sizeof(*iter));
243a014e0a3SKonstantin Belousov 	iter->ipos = -1;
244a014e0a3SKonstantin Belousov 	iter->uh = uh;
245a014e0a3SKonstantin Belousov 	iter->upos = NULL;
246a014e0a3SKonstantin Belousov 	iter->upos_first_item = -1;
247a014e0a3SKonstantin Belousov 	return (iter);
248a014e0a3SKonstantin Belousov }
249a014e0a3SKonstantin Belousov 
250a014e0a3SKonstantin Belousov static void
251a014e0a3SKonstantin Belousov next_iter_unrl(struct unrhdr *uh, struct unrhdr_iter *iter)
252a014e0a3SKonstantin Belousov {
253a014e0a3SKonstantin Belousov 	struct unr *up;
254a014e0a3SKonstantin Belousov 	struct unrb *ub;
255a014e0a3SKonstantin Belousov 	u_int y;
256a014e0a3SKonstantin Belousov 	int c;
257a014e0a3SKonstantin Belousov 
258a014e0a3SKonstantin Belousov 	if (iter->ipos == -1) {
259a014e0a3SKonstantin Belousov 		if (iter->upos == uh)
260a014e0a3SKonstantin Belousov 			return;
261a014e0a3SKonstantin Belousov 		y = uh->low - 1;
262a014e0a3SKonstantin Belousov 		if (uh->first == 0) {
263a014e0a3SKonstantin Belousov 			up = TAILQ_FIRST(&uh->head);
264a014e0a3SKonstantin Belousov 			if (up == NULL) {
265a014e0a3SKonstantin Belousov 				iter->upos = uh;
266a014e0a3SKonstantin Belousov 				return;
267a014e0a3SKonstantin Belousov 			}
268a014e0a3SKonstantin Belousov 			iter->upos = up;
269a014e0a3SKonstantin Belousov 			if (up->ptr == NULL)
270a014e0a3SKonstantin Belousov 				iter->upos = NULL;
271a014e0a3SKonstantin Belousov 			else
272a014e0a3SKonstantin Belousov 				iter->upos_first_item = uh->low;
273a014e0a3SKonstantin Belousov 		}
274a014e0a3SKonstantin Belousov 	} else {
275a014e0a3SKonstantin Belousov 		y = iter->ipos;
276a014e0a3SKonstantin Belousov 	}
277a014e0a3SKonstantin Belousov 
278a014e0a3SKonstantin Belousov 	up = iter->upos;
279a014e0a3SKonstantin Belousov 
280a014e0a3SKonstantin Belousov 	/* Special case for the compacted [low, first) run. */
281a014e0a3SKonstantin Belousov 	if (up == NULL) {
282a014e0a3SKonstantin Belousov 		if (y + 1 < uh->low + uh->first) {
283a014e0a3SKonstantin Belousov 			iter->ipos = y + 1;
284a014e0a3SKonstantin Belousov 			return;
285a014e0a3SKonstantin Belousov 		}
286a014e0a3SKonstantin Belousov 		up = iter->upos = TAILQ_FIRST(&uh->head);
287a014e0a3SKonstantin Belousov 		iter->upos_first_item = uh->low + uh->first;
288a014e0a3SKonstantin Belousov 	}
289a014e0a3SKonstantin Belousov 
290a014e0a3SKonstantin Belousov 	for (;;) {
291a014e0a3SKonstantin Belousov 		if (y + 1 < iter->upos_first_item + up->len) {
292a014e0a3SKonstantin Belousov 			if (up->ptr == uh) {
293a014e0a3SKonstantin Belousov 				iter->ipos = y + 1;
294a014e0a3SKonstantin Belousov 				return;
295a014e0a3SKonstantin Belousov 			} else if (is_bitmap(uh, up)) {
296a014e0a3SKonstantin Belousov 				ub = up->ptr;
297a014e0a3SKonstantin Belousov 				bit_ffs_at(&ub->map[0],
298a014e0a3SKonstantin Belousov 				    y + 1 - iter->upos_first_item,
299a014e0a3SKonstantin Belousov 				    up->len, &c);
300a014e0a3SKonstantin Belousov 				if (c != -1) {
301a014e0a3SKonstantin Belousov 					iter->ipos = iter->upos_first_item + c;
302a014e0a3SKonstantin Belousov 					return;
303a014e0a3SKonstantin Belousov 				}
304a014e0a3SKonstantin Belousov 			}
305a014e0a3SKonstantin Belousov 		}
306a014e0a3SKonstantin Belousov 		iter->upos_first_item += up->len;
307a014e0a3SKonstantin Belousov 		y = iter->upos_first_item - 1;
308a014e0a3SKonstantin Belousov 		up = iter->upos = TAILQ_NEXT((struct unr *)iter->upos, list);
309a014e0a3SKonstantin Belousov 		if (iter->upos == NULL) {
310a014e0a3SKonstantin Belousov 			iter->ipos = -1;
311a014e0a3SKonstantin Belousov 			iter->upos = uh;
312a014e0a3SKonstantin Belousov 			return;
313a014e0a3SKonstantin Belousov 		}
314a014e0a3SKonstantin Belousov 	}
315a014e0a3SKonstantin Belousov }
316a014e0a3SKonstantin Belousov 
317a014e0a3SKonstantin Belousov /*
318a014e0a3SKonstantin Belousov  * returns -1 on end, otherwise the next element
319a014e0a3SKonstantin Belousov  */
320a014e0a3SKonstantin Belousov int
321a014e0a3SKonstantin Belousov next_iter_unr(void *handle)
322a014e0a3SKonstantin Belousov {
323a014e0a3SKonstantin Belousov 	struct unrhdr *uh;
324a014e0a3SKonstantin Belousov 	struct unrhdr_iter *iter;
325a014e0a3SKonstantin Belousov 
326a014e0a3SKonstantin Belousov 	iter = handle;
327a014e0a3SKonstantin Belousov 	uh = iter->uh;
328a014e0a3SKonstantin Belousov 	if (uh->mtx != NULL)
329a014e0a3SKonstantin Belousov 		mtx_lock(uh->mtx);
330a014e0a3SKonstantin Belousov 	next_iter_unrl(uh, iter);
331a014e0a3SKonstantin Belousov 	if (uh->mtx != NULL)
332a014e0a3SKonstantin Belousov 		mtx_unlock(uh->mtx);
333a014e0a3SKonstantin Belousov 	return (iter->ipos);
334a014e0a3SKonstantin Belousov }
335a014e0a3SKonstantin Belousov 
336a014e0a3SKonstantin Belousov void
337a014e0a3SKonstantin Belousov free_iter_unr(void *handle)
338a014e0a3SKonstantin Belousov {
339a014e0a3SKonstantin Belousov 	Free(handle);
340a014e0a3SKonstantin Belousov }
341a014e0a3SKonstantin Belousov 
342f6bde1fdSPoul-Henning Kamp #if defined(DIAGNOSTIC) || !defined(_KERNEL)
343f6bde1fdSPoul-Henning Kamp /*
344f6bde1fdSPoul-Henning Kamp  * Consistency check function.
345f6bde1fdSPoul-Henning Kamp  *
346f6bde1fdSPoul-Henning Kamp  * Checks the internal consistency as well as we can.
347f6bde1fdSPoul-Henning Kamp  *
348f6bde1fdSPoul-Henning Kamp  * Called at all boundaries of this API.
349f6bde1fdSPoul-Henning Kamp  */
350f6bde1fdSPoul-Henning Kamp static void
351f6bde1fdSPoul-Henning Kamp check_unrhdr(struct unrhdr *uh, int line)
352f6bde1fdSPoul-Henning Kamp {
353f6bde1fdSPoul-Henning Kamp 	struct unr *up;
354d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
3551b82e02fSAlan Somers 	int w;
356*1384a0b9SKonstantin Belousov 	u_int y __diagused, z __diagused;
357f6bde1fdSPoul-Henning Kamp 
358d9a54d5cSPoul-Henning Kamp 	y = uh->first;
359f6bde1fdSPoul-Henning Kamp 	z = 0;
360f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
361f6bde1fdSPoul-Henning Kamp 		z++;
36236b1f8a8SKonstantin Belousov 		if (is_bitmap(uh, up)) {
363d9a54d5cSPoul-Henning Kamp 			ub = up->ptr;
364d9a54d5cSPoul-Henning Kamp 			KASSERT (up->len <= NBITS,
3658907f744SAlan Somers 			    ("UNR inconsistency: len %u max %zd (line %d)\n",
366d9a54d5cSPoul-Henning Kamp 			    up->len, NBITS, line));
367f6bde1fdSPoul-Henning Kamp 			z++;
368f6bde1fdSPoul-Henning Kamp 			w = 0;
3691b82e02fSAlan Somers 			bit_count(ub->map, 0, up->len, &w);
370d9a54d5cSPoul-Henning Kamp 			y += w;
371d9a54d5cSPoul-Henning Kamp 		} else if (up->ptr != NULL)
372f6bde1fdSPoul-Henning Kamp 			y += up->len;
373f6bde1fdSPoul-Henning Kamp 	}
374f6bde1fdSPoul-Henning Kamp 	KASSERT (y == uh->busy,
375f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: items %u found %u (line %d)\n",
376f6bde1fdSPoul-Henning Kamp 	    uh->busy, y, line));
377f6bde1fdSPoul-Henning Kamp 	KASSERT (z == uh->alloc,
378f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: chunks %u found %u (line %d)\n",
379f6bde1fdSPoul-Henning Kamp 	    uh->alloc, z, line));
380f6bde1fdSPoul-Henning Kamp }
381f6bde1fdSPoul-Henning Kamp 
382f6bde1fdSPoul-Henning Kamp #else
383f6bde1fdSPoul-Henning Kamp 
384f6bde1fdSPoul-Henning Kamp static __inline void
3858907f744SAlan Somers check_unrhdr(struct unrhdr *uh __unused, int line __unused)
386f6bde1fdSPoul-Henning Kamp {
387f6bde1fdSPoul-Henning Kamp 
388f6bde1fdSPoul-Henning Kamp }
389f6bde1fdSPoul-Henning Kamp 
390f6bde1fdSPoul-Henning Kamp #endif
391f6bde1fdSPoul-Henning Kamp 
392f6bde1fdSPoul-Henning Kamp /*
393f6bde1fdSPoul-Henning Kamp  * Userland memory management.  Just use calloc and keep track of how
394f6bde1fdSPoul-Henning Kamp  * many elements we have allocated for check_unrhdr().
395f6bde1fdSPoul-Henning Kamp  */
396f6bde1fdSPoul-Henning Kamp 
397f6bde1fdSPoul-Henning Kamp static __inline void *
398d9a54d5cSPoul-Henning Kamp new_unr(struct unrhdr *uh, void **p1, void **p2)
399f6bde1fdSPoul-Henning Kamp {
400d9a54d5cSPoul-Henning Kamp 	void *p;
401f6bde1fdSPoul-Henning Kamp 
402d9a54d5cSPoul-Henning Kamp 	uh->alloc++;
403d9a54d5cSPoul-Henning Kamp 	KASSERT(*p1 != NULL || *p2 != NULL, ("Out of cached memory"));
404d9a54d5cSPoul-Henning Kamp 	if (*p1 != NULL) {
405d9a54d5cSPoul-Henning Kamp 		p = *p1;
406d9a54d5cSPoul-Henning Kamp 		*p1 = NULL;
407d9a54d5cSPoul-Henning Kamp 		return (p);
408d9a54d5cSPoul-Henning Kamp 	} else {
409d9a54d5cSPoul-Henning Kamp 		p = *p2;
410d9a54d5cSPoul-Henning Kamp 		*p2 = NULL;
411d9a54d5cSPoul-Henning Kamp 		return (p);
412d9a54d5cSPoul-Henning Kamp 	}
413f6bde1fdSPoul-Henning Kamp }
414f6bde1fdSPoul-Henning Kamp 
415f6bde1fdSPoul-Henning Kamp static __inline void
416f6bde1fdSPoul-Henning Kamp delete_unr(struct unrhdr *uh, void *ptr)
417f6bde1fdSPoul-Henning Kamp {
41809828ba9SKonstantin Belousov 	struct unr *up;
419d9a54d5cSPoul-Henning Kamp 
420f6bde1fdSPoul-Henning Kamp 	uh->alloc--;
42109828ba9SKonstantin Belousov 	up = ptr;
42209828ba9SKonstantin Belousov 	TAILQ_INSERT_TAIL(&uh->ppfree, up, list);
42309828ba9SKonstantin Belousov }
42409828ba9SKonstantin Belousov 
42509828ba9SKonstantin Belousov void
42609828ba9SKonstantin Belousov clean_unrhdrl(struct unrhdr *uh)
42709828ba9SKonstantin Belousov {
42809828ba9SKonstantin Belousov 	struct unr *up;
42909828ba9SKonstantin Belousov 
430e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
43109828ba9SKonstantin Belousov 		mtx_assert(uh->mtx, MA_OWNED);
43209828ba9SKonstantin Belousov 	while ((up = TAILQ_FIRST(&uh->ppfree)) != NULL) {
43309828ba9SKonstantin Belousov 		TAILQ_REMOVE(&uh->ppfree, up, list);
434e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
43509828ba9SKonstantin Belousov 			mtx_unlock(uh->mtx);
43609828ba9SKonstantin Belousov 		Free(up);
437e59b940dSKonstantin Belousov 		if (uh->mtx != NULL)
43809828ba9SKonstantin Belousov 			mtx_lock(uh->mtx);
43909828ba9SKonstantin Belousov 	}
44009828ba9SKonstantin Belousov 
44109828ba9SKonstantin Belousov }
44209828ba9SKonstantin Belousov 
44309828ba9SKonstantin Belousov void
44409828ba9SKonstantin Belousov clean_unrhdr(struct unrhdr *uh)
44509828ba9SKonstantin Belousov {
44609828ba9SKonstantin Belousov 
447e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
44809828ba9SKonstantin Belousov 		mtx_lock(uh->mtx);
44909828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
450e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
45109828ba9SKonstantin Belousov 		mtx_unlock(uh->mtx);
452f6bde1fdSPoul-Henning Kamp }
453f6bde1fdSPoul-Henning Kamp 
454dc872d46SKonstantin Belousov void
455dc872d46SKonstantin Belousov init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex)
456f6bde1fdSPoul-Henning Kamp {
457f6bde1fdSPoul-Henning Kamp 
458831aa555SJaakko Heinonen 	KASSERT(low >= 0 && low <= high,
459501812f2SJaakko Heinonen 	    ("UNR: use error: new_unrhdr(%d, %d)", low, high));
460e59b940dSKonstantin Belousov 	if (mutex == UNR_NO_MTX)
461e59b940dSKonstantin Belousov 		uh->mtx = NULL;
462e59b940dSKonstantin Belousov 	else if (mutex != NULL)
463d9a54d5cSPoul-Henning Kamp 		uh->mtx = mutex;
464d9a54d5cSPoul-Henning Kamp 	else
465d9a54d5cSPoul-Henning Kamp 		uh->mtx = &unitmtx;
466f6bde1fdSPoul-Henning Kamp 	TAILQ_INIT(&uh->head);
46709828ba9SKonstantin Belousov 	TAILQ_INIT(&uh->ppfree);
468f6bde1fdSPoul-Henning Kamp 	uh->low = low;
469f6bde1fdSPoul-Henning Kamp 	uh->high = high;
470d9a54d5cSPoul-Henning Kamp 	uh->first = 0;
471d9a54d5cSPoul-Henning Kamp 	uh->last = 1 + (high - low);
472c4be460eSKonstantin Belousov 	uh->busy = 0;
473c4be460eSKonstantin Belousov 	uh->alloc = 0;
474f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
475dc872d46SKonstantin Belousov }
476dc872d46SKonstantin Belousov 
477dc872d46SKonstantin Belousov /*
478dc872d46SKonstantin Belousov  * Allocate a new unrheader set.
479dc872d46SKonstantin Belousov  *
480dc872d46SKonstantin Belousov  * Highest and lowest valid values given as parameters.
481dc872d46SKonstantin Belousov  */
482dc872d46SKonstantin Belousov 
483dc872d46SKonstantin Belousov struct unrhdr *
484dc872d46SKonstantin Belousov new_unrhdr(int low, int high, struct mtx *mutex)
485dc872d46SKonstantin Belousov {
486dc872d46SKonstantin Belousov 	struct unrhdr *uh;
487dc872d46SKonstantin Belousov 
488dc872d46SKonstantin Belousov 	uh = Malloc(sizeof *uh);
489dc872d46SKonstantin Belousov 	init_unrhdr(uh, low, high, mutex);
490f6bde1fdSPoul-Henning Kamp 	return (uh);
491f6bde1fdSPoul-Henning Kamp }
492f6bde1fdSPoul-Henning Kamp 
493e4fea39eSPoul-Henning Kamp void
494e4fea39eSPoul-Henning Kamp delete_unrhdr(struct unrhdr *uh)
495e4fea39eSPoul-Henning Kamp {
496e4fea39eSPoul-Henning Kamp 
497d9a54d5cSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
498e4fea39eSPoul-Henning Kamp 	KASSERT(uh->busy == 0, ("unrhdr has %u allocations", uh->busy));
499e4fea39eSPoul-Henning Kamp 	KASSERT(uh->alloc == 0, ("UNR memory leak in delete_unrhdr"));
50009828ba9SKonstantin Belousov 	KASSERT(TAILQ_FIRST(&uh->ppfree) == NULL,
50109828ba9SKonstantin Belousov 	    ("unrhdr has postponed item for free"));
502e4fea39eSPoul-Henning Kamp 	Free(uh);
503e4fea39eSPoul-Henning Kamp }
504e4fea39eSPoul-Henning Kamp 
505333dcaa4SMatt Joras void
506333dcaa4SMatt Joras clear_unrhdr(struct unrhdr *uh)
507333dcaa4SMatt Joras {
508333dcaa4SMatt Joras 	struct unr *up, *uq;
509333dcaa4SMatt Joras 
510333dcaa4SMatt Joras 	KASSERT(TAILQ_EMPTY(&uh->ppfree),
511333dcaa4SMatt Joras 	    ("unrhdr has postponed item for free"));
5120d8e0405SMatt Joras 	TAILQ_FOREACH_SAFE(up, &uh->head, list, uq) {
513333dcaa4SMatt Joras 		if (up->ptr != uh) {
514333dcaa4SMatt Joras 			Free(up->ptr);
515333dcaa4SMatt Joras 		}
516333dcaa4SMatt Joras 		Free(up);
517333dcaa4SMatt Joras 	}
518333dcaa4SMatt Joras 	uh->busy = 0;
519333dcaa4SMatt Joras 	uh->alloc = 0;
5200d8e0405SMatt Joras 	init_unrhdr(uh, uh->low, uh->high, uh->mtx);
5210d8e0405SMatt Joras 
5220d8e0405SMatt Joras 	check_unrhdr(uh, __LINE__);
523333dcaa4SMatt Joras }
524333dcaa4SMatt Joras 
525f6bde1fdSPoul-Henning Kamp /*
526d9a54d5cSPoul-Henning Kamp  * Look for sequence of items which can be combined into a bitmap, if
527d9a54d5cSPoul-Henning Kamp  * multiple are present, take the one which saves most memory.
528d9a54d5cSPoul-Henning Kamp  *
529d9a54d5cSPoul-Henning Kamp  * Return (1) if a sequence was found to indicate that another call
530d9a54d5cSPoul-Henning Kamp  * might be able to do more.  Return (0) if we found no suitable sequence.
531d9a54d5cSPoul-Henning Kamp  *
532d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
533d9a54d5cSPoul-Henning Kamp  */
534d9a54d5cSPoul-Henning Kamp static int
535d9a54d5cSPoul-Henning Kamp optimize_unr(struct unrhdr *uh)
536d9a54d5cSPoul-Henning Kamp {
537d9a54d5cSPoul-Henning Kamp 	struct unr *up, *uf, *us;
538d9a54d5cSPoul-Henning Kamp 	struct unrb *ub, *ubf;
539d9a54d5cSPoul-Henning Kamp 	u_int a, l, ba;
540d9a54d5cSPoul-Henning Kamp 
541d9a54d5cSPoul-Henning Kamp 	/*
542d9a54d5cSPoul-Henning Kamp 	 * Look for the run of items (if any) which when collapsed into
543d9a54d5cSPoul-Henning Kamp 	 * a bitmap would save most memory.
544d9a54d5cSPoul-Henning Kamp 	 */
545d9a54d5cSPoul-Henning Kamp 	us = NULL;
546d9a54d5cSPoul-Henning Kamp 	ba = 0;
547d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(uf, &uh->head, list) {
548d9a54d5cSPoul-Henning Kamp 		if (uf->len >= NBITS)
549d9a54d5cSPoul-Henning Kamp 			continue;
550d9a54d5cSPoul-Henning Kamp 		a = 1;
551d9a54d5cSPoul-Henning Kamp 		if (is_bitmap(uh, uf))
552d9a54d5cSPoul-Henning Kamp 			a++;
553d9a54d5cSPoul-Henning Kamp 		l = uf->len;
554d9a54d5cSPoul-Henning Kamp 		up = uf;
555d9a54d5cSPoul-Henning Kamp 		while (1) {
556d9a54d5cSPoul-Henning Kamp 			up = TAILQ_NEXT(up, list);
557d9a54d5cSPoul-Henning Kamp 			if (up == NULL)
558d9a54d5cSPoul-Henning Kamp 				break;
559d9a54d5cSPoul-Henning Kamp 			if ((up->len + l) > NBITS)
560d9a54d5cSPoul-Henning Kamp 				break;
561d9a54d5cSPoul-Henning Kamp 			a++;
562d9a54d5cSPoul-Henning Kamp 			if (is_bitmap(uh, up))
563d9a54d5cSPoul-Henning Kamp 				a++;
564d9a54d5cSPoul-Henning Kamp 			l += up->len;
565d9a54d5cSPoul-Henning Kamp 		}
566d9a54d5cSPoul-Henning Kamp 		if (a > ba) {
567d9a54d5cSPoul-Henning Kamp 			ba = a;
568d9a54d5cSPoul-Henning Kamp 			us = uf;
569d9a54d5cSPoul-Henning Kamp 		}
570d9a54d5cSPoul-Henning Kamp 	}
571d9a54d5cSPoul-Henning Kamp 	if (ba < 3)
572d9a54d5cSPoul-Henning Kamp 		return (0);
573d9a54d5cSPoul-Henning Kamp 
574d9a54d5cSPoul-Henning Kamp 	/*
575d9a54d5cSPoul-Henning Kamp 	 * If the first element is not a bitmap, make it one.
576d9a54d5cSPoul-Henning Kamp 	 * Trying to do so without allocating more memory complicates things
577d9a54d5cSPoul-Henning Kamp 	 * a bit
578d9a54d5cSPoul-Henning Kamp 	 */
579d9a54d5cSPoul-Henning Kamp 	if (!is_bitmap(uh, us)) {
580d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
581d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, us, list);
582d9a54d5cSPoul-Henning Kamp 		a = us->len;
583d9a54d5cSPoul-Henning Kamp 		l = us->ptr == uh ? 1 : 0;
584d9a54d5cSPoul-Henning Kamp 		ub = (void *)us;
5858907f744SAlan Somers 		bit_nclear(ub->map, 0, NBITS - 1);
5868907f744SAlan Somers 		if (l)
587d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, 0, a);
588d9a54d5cSPoul-Henning Kamp 		if (!is_bitmap(uh, uf)) {
5898907f744SAlan Somers 			if (uf->ptr == NULL)
590d9a54d5cSPoul-Henning Kamp 				bit_nclear(ub->map, a, a + uf->len - 1);
5918907f744SAlan Somers 			else
592d9a54d5cSPoul-Henning Kamp 				bit_nset(ub->map, a, a + uf->len - 1);
593d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
594d9a54d5cSPoul-Henning Kamp 			uf->len += a;
595d9a54d5cSPoul-Henning Kamp 			us = uf;
596d9a54d5cSPoul-Henning Kamp 		} else {
597d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
598d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, a++) {
5998907f744SAlan Somers 				if (bit_test(ubf->map, l))
600d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, a);
6018907f744SAlan Somers 				else
602d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, a);
603d9a54d5cSPoul-Henning Kamp 			}
604d9a54d5cSPoul-Henning Kamp 			uf->len = a;
605d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf->ptr);
606d9a54d5cSPoul-Henning Kamp 			uf->ptr = ub;
607d9a54d5cSPoul-Henning Kamp 			us = uf;
608d9a54d5cSPoul-Henning Kamp 		}
609d9a54d5cSPoul-Henning Kamp 	}
610d9a54d5cSPoul-Henning Kamp 	ub = us->ptr;
611d9a54d5cSPoul-Henning Kamp 	while (1) {
612d9a54d5cSPoul-Henning Kamp 		uf = TAILQ_NEXT(us, list);
613d9a54d5cSPoul-Henning Kamp 		if (uf == NULL)
614d9a54d5cSPoul-Henning Kamp 			return (1);
615d9a54d5cSPoul-Henning Kamp 		if (uf->len + us->len > NBITS)
616d9a54d5cSPoul-Henning Kamp 			return (1);
617d9a54d5cSPoul-Henning Kamp 		if (uf->ptr == NULL) {
618d9a54d5cSPoul-Henning Kamp 			bit_nclear(ub->map, us->len, us->len + uf->len - 1);
619d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
620d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
621d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
622d9a54d5cSPoul-Henning Kamp 		} else if (uf->ptr == uh) {
623d9a54d5cSPoul-Henning Kamp 			bit_nset(ub->map, us->len, us->len + uf->len - 1);
624d9a54d5cSPoul-Henning Kamp 			us->len += uf->len;
625d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
626d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
627d9a54d5cSPoul-Henning Kamp 		} else {
628d9a54d5cSPoul-Henning Kamp 			ubf = uf->ptr;
629d9a54d5cSPoul-Henning Kamp 			for (l = 0; l < uf->len; l++, us->len++) {
6308907f744SAlan Somers 				if (bit_test(ubf->map, l))
631d9a54d5cSPoul-Henning Kamp 					bit_set(ub->map, us->len);
6328907f744SAlan Somers 				else
633d9a54d5cSPoul-Henning Kamp 					bit_clear(ub->map, us->len);
634d9a54d5cSPoul-Henning Kamp 			}
635d9a54d5cSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, uf, list);
636d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, ubf);
637d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, uf);
638d9a54d5cSPoul-Henning Kamp 		}
639d9a54d5cSPoul-Henning Kamp 	}
640d9a54d5cSPoul-Henning Kamp }
641d9a54d5cSPoul-Henning Kamp 
642d9a54d5cSPoul-Henning Kamp /*
643d9a54d5cSPoul-Henning Kamp  * See if a given unr should be collapsed with a neighbor.
644d9a54d5cSPoul-Henning Kamp  *
645d9a54d5cSPoul-Henning Kamp  * NB: called from alloc_unr(), no new memory allocation allowed.
646f6bde1fdSPoul-Henning Kamp  */
647f6bde1fdSPoul-Henning Kamp static void
648f6bde1fdSPoul-Henning Kamp collapse_unr(struct unrhdr *uh, struct unr *up)
649f6bde1fdSPoul-Henning Kamp {
650f6bde1fdSPoul-Henning Kamp 	struct unr *upp;
651d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
652f6bde1fdSPoul-Henning Kamp 
653d9a54d5cSPoul-Henning Kamp 	/* If bitmap is all set or clear, change it to runlength */
654d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
655d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
6568907f744SAlan Somers 		if (ub_full(ub, up->len)) {
657d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
658d9a54d5cSPoul-Henning Kamp 			up->ptr = uh;
6598907f744SAlan Somers 		} else if (ub_empty(ub, up->len)) {
660d9a54d5cSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
661d9a54d5cSPoul-Henning Kamp 			up->ptr = NULL;
662d9a54d5cSPoul-Henning Kamp 		}
663d9a54d5cSPoul-Henning Kamp 	}
664d9a54d5cSPoul-Henning Kamp 
665d9a54d5cSPoul-Henning Kamp 	/* If nothing left in runlength, delete it */
666d9a54d5cSPoul-Henning Kamp 	if (up->len == 0) {
667d9a54d5cSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
668d9a54d5cSPoul-Henning Kamp 		if (upp == NULL)
669d9a54d5cSPoul-Henning Kamp 			upp = TAILQ_NEXT(up, list);
670d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, up, list);
671d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, up);
672d9a54d5cSPoul-Henning Kamp 		up = upp;
673d9a54d5cSPoul-Henning Kamp 	}
674d9a54d5cSPoul-Henning Kamp 
675d9a54d5cSPoul-Henning Kamp 	/* If we have "hot-spot" still, merge with neighbor if possible */
676d9a54d5cSPoul-Henning Kamp 	if (up != NULL) {
677f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
678f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
679f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
680f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
681f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
682f6bde1fdSPoul-Henning Kamp 			}
683f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_NEXT(up, list);
684f6bde1fdSPoul-Henning Kamp 		if (upp != NULL && up->ptr == upp->ptr) {
685f6bde1fdSPoul-Henning Kamp 			up->len += upp->len;
686f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upp, list);
687f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upp);
688f6bde1fdSPoul-Henning Kamp 		}
689f6bde1fdSPoul-Henning Kamp 	}
690f6bde1fdSPoul-Henning Kamp 
691d9a54d5cSPoul-Henning Kamp 	/* Merge into ->first if possible */
692d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
693d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == uh) {
694d9a54d5cSPoul-Henning Kamp 		uh->first += upp->len;
695d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
696d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
697d9a54d5cSPoul-Henning Kamp 		if (up == upp)
698d9a54d5cSPoul-Henning Kamp 			up = NULL;
699d9a54d5cSPoul-Henning Kamp 	}
700d9a54d5cSPoul-Henning Kamp 
701d9a54d5cSPoul-Henning Kamp 	/* Merge into ->last if possible */
702d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_LAST(&uh->head, unrhd);
703d9a54d5cSPoul-Henning Kamp 	if (upp != NULL && upp->ptr == NULL) {
704d9a54d5cSPoul-Henning Kamp 		uh->last += upp->len;
705d9a54d5cSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
706d9a54d5cSPoul-Henning Kamp 		delete_unr(uh, upp);
707d9a54d5cSPoul-Henning Kamp 		if (up == upp)
708d9a54d5cSPoul-Henning Kamp 			up = NULL;
709d9a54d5cSPoul-Henning Kamp 	}
710d9a54d5cSPoul-Henning Kamp 
711d9a54d5cSPoul-Henning Kamp 	/* Try to make bitmaps */
712d9a54d5cSPoul-Henning Kamp 	while (optimize_unr(uh))
713d9a54d5cSPoul-Henning Kamp 		continue;
714d9a54d5cSPoul-Henning Kamp }
715d9a54d5cSPoul-Henning Kamp 
716f6bde1fdSPoul-Henning Kamp /*
717f6bde1fdSPoul-Henning Kamp  * Allocate a free unr.
718f6bde1fdSPoul-Henning Kamp  */
719d9a54d5cSPoul-Henning Kamp int
720d9a54d5cSPoul-Henning Kamp alloc_unrl(struct unrhdr *uh)
721f6bde1fdSPoul-Henning Kamp {
722d9a54d5cSPoul-Henning Kamp 	struct unr *up;
723d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
724f6bde1fdSPoul-Henning Kamp 	u_int x;
725f6bde1fdSPoul-Henning Kamp 	int y;
726f6bde1fdSPoul-Henning Kamp 
727e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
728d9a54d5cSPoul-Henning Kamp 		mtx_assert(uh->mtx, MA_OWNED);
729f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
730d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
731d9a54d5cSPoul-Henning Kamp 
732f6bde1fdSPoul-Henning Kamp 	up = TAILQ_FIRST(&uh->head);
733f6bde1fdSPoul-Henning Kamp 
734d9a54d5cSPoul-Henning Kamp 	/*
735d9a54d5cSPoul-Henning Kamp 	 * If we have an ideal split, just adjust the first+last
736d9a54d5cSPoul-Henning Kamp 	 */
737d9a54d5cSPoul-Henning Kamp 	if (up == NULL && uh->last > 0) {
738d9a54d5cSPoul-Henning Kamp 		uh->first++;
739d9a54d5cSPoul-Henning Kamp 		uh->last--;
740f6bde1fdSPoul-Henning Kamp 		uh->busy++;
741f6bde1fdSPoul-Henning Kamp 		return (x);
742f6bde1fdSPoul-Henning Kamp 	}
743f6bde1fdSPoul-Henning Kamp 
744f6bde1fdSPoul-Henning Kamp 	/*
745d9a54d5cSPoul-Henning Kamp 	 * We can always allocate from the first list element, so if we have
746d9a54d5cSPoul-Henning Kamp 	 * nothing on the list, we must have run out of unit numbers.
747f6bde1fdSPoul-Henning Kamp 	 */
74893f6c81eSPoul-Henning Kamp 	if (up == NULL)
749d9a54d5cSPoul-Henning Kamp 		return (-1);
750d9a54d5cSPoul-Henning Kamp 
751d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr != uh, ("UNR first element is allocated"));
752d9a54d5cSPoul-Henning Kamp 
753d9a54d5cSPoul-Henning Kamp 	if (up->ptr == NULL) {	/* free run */
754d9a54d5cSPoul-Henning Kamp 		uh->first++;
755f6bde1fdSPoul-Henning Kamp 		up->len--;
756d9a54d5cSPoul-Henning Kamp 	} else {		/* bitmap */
757d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
758d9a54d5cSPoul-Henning Kamp 		bit_ffc(ub->map, up->len, &y);
759d9a54d5cSPoul-Henning Kamp 		KASSERT(y != -1, ("UNR corruption: No clear bit in bitmap."));
760d9a54d5cSPoul-Henning Kamp 		bit_set(ub->map, y);
761d9a54d5cSPoul-Henning Kamp 		x += y;
762d9a54d5cSPoul-Henning Kamp 	}
763f6bde1fdSPoul-Henning Kamp 	uh->busy++;
764d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
765f6bde1fdSPoul-Henning Kamp 	return (x);
766f6bde1fdSPoul-Henning Kamp }
767f6bde1fdSPoul-Henning Kamp 
768d9a54d5cSPoul-Henning Kamp int
769d9a54d5cSPoul-Henning Kamp alloc_unr(struct unrhdr *uh)
770d9a54d5cSPoul-Henning Kamp {
771d9a54d5cSPoul-Henning Kamp 	int i;
772d9a54d5cSPoul-Henning Kamp 
773e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
774d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
775d9a54d5cSPoul-Henning Kamp 	i = alloc_unrl(uh);
77609828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
777e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
778d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
779d9a54d5cSPoul-Henning Kamp 	return (i);
780d9a54d5cSPoul-Henning Kamp }
781d9a54d5cSPoul-Henning Kamp 
78213c02cbbSJaakko Heinonen static int
78313c02cbbSJaakko Heinonen alloc_unr_specificl(struct unrhdr *uh, u_int item, void **p1, void **p2)
78413c02cbbSJaakko Heinonen {
78513c02cbbSJaakko Heinonen 	struct unr *up, *upn;
78613c02cbbSJaakko Heinonen 	struct unrb *ub;
78713c02cbbSJaakko Heinonen 	u_int i, last, tl;
78813c02cbbSJaakko Heinonen 
789e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
79013c02cbbSJaakko Heinonen 		mtx_assert(uh->mtx, MA_OWNED);
79113c02cbbSJaakko Heinonen 
79213c02cbbSJaakko Heinonen 	if (item < uh->low + uh->first || item > uh->high)
79313c02cbbSJaakko Heinonen 		return (-1);
79413c02cbbSJaakko Heinonen 
79513c02cbbSJaakko Heinonen 	up = TAILQ_FIRST(&uh->head);
79613c02cbbSJaakko Heinonen 	/* Ideal split. */
79713c02cbbSJaakko Heinonen 	if (up == NULL && item - uh->low == uh->first) {
79813c02cbbSJaakko Heinonen 		uh->first++;
79913c02cbbSJaakko Heinonen 		uh->last--;
80013c02cbbSJaakko Heinonen 		uh->busy++;
80113c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
80213c02cbbSJaakko Heinonen 		return (item);
80313c02cbbSJaakko Heinonen 	}
80413c02cbbSJaakko Heinonen 
80513c02cbbSJaakko Heinonen 	i = item - uh->low - uh->first;
80613c02cbbSJaakko Heinonen 
80713c02cbbSJaakko Heinonen 	if (up == NULL) {
80813c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
80913c02cbbSJaakko Heinonen 		up->ptr = NULL;
81013c02cbbSJaakko Heinonen 		up->len = i;
81113c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
81213c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
81313c02cbbSJaakko Heinonen 		up->ptr = uh;
81413c02cbbSJaakko Heinonen 		up->len = 1;
81513c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
81613c02cbbSJaakko Heinonen 		uh->last = uh->high - uh->low - i;
81713c02cbbSJaakko Heinonen 		uh->busy++;
81813c02cbbSJaakko Heinonen 		check_unrhdr(uh, __LINE__);
81913c02cbbSJaakko Heinonen 		return (item);
82013c02cbbSJaakko Heinonen 	} else {
82113c02cbbSJaakko Heinonen 		/* Find the item which contains the unit we want to allocate. */
82213c02cbbSJaakko Heinonen 		TAILQ_FOREACH(up, &uh->head, list) {
82313c02cbbSJaakko Heinonen 			if (up->len > i)
82413c02cbbSJaakko Heinonen 				break;
82513c02cbbSJaakko Heinonen 			i -= up->len;
82613c02cbbSJaakko Heinonen 		}
82713c02cbbSJaakko Heinonen 	}
82813c02cbbSJaakko Heinonen 
82913c02cbbSJaakko Heinonen 	if (up == NULL) {
83013c02cbbSJaakko Heinonen 		if (i > 0) {
83113c02cbbSJaakko Heinonen 			up = new_unr(uh, p1, p2);
83213c02cbbSJaakko Heinonen 			up->ptr = NULL;
83313c02cbbSJaakko Heinonen 			up->len = i;
83413c02cbbSJaakko Heinonen 			TAILQ_INSERT_TAIL(&uh->head, up, list);
83513c02cbbSJaakko Heinonen 		}
83613c02cbbSJaakko Heinonen 		up = new_unr(uh, p1, p2);
83713c02cbbSJaakko Heinonen 		up->ptr = uh;
83813c02cbbSJaakko Heinonen 		up->len = 1;
83913c02cbbSJaakko Heinonen 		TAILQ_INSERT_TAIL(&uh->head, up, list);
84013c02cbbSJaakko Heinonen 		goto done;
84113c02cbbSJaakko Heinonen 	}
84213c02cbbSJaakko Heinonen 
84313c02cbbSJaakko Heinonen 	if (is_bitmap(uh, up)) {
84413c02cbbSJaakko Heinonen 		ub = up->ptr;
84513c02cbbSJaakko Heinonen 		if (bit_test(ub->map, i) == 0) {
84613c02cbbSJaakko Heinonen 			bit_set(ub->map, i);
84713c02cbbSJaakko Heinonen 			goto done;
84813c02cbbSJaakko Heinonen 		} else
84913c02cbbSJaakko Heinonen 			return (-1);
85013c02cbbSJaakko Heinonen 	} else if (up->ptr == uh)
85113c02cbbSJaakko Heinonen 		return (-1);
85213c02cbbSJaakko Heinonen 
85313c02cbbSJaakko Heinonen 	KASSERT(up->ptr == NULL,
85413c02cbbSJaakko Heinonen 	    ("alloc_unr_specificl: up->ptr != NULL (up=%p)", up));
85513c02cbbSJaakko Heinonen 
85613c02cbbSJaakko Heinonen 	/* Split off the tail end, if any. */
85713c02cbbSJaakko Heinonen 	tl = up->len - (1 + i);
85813c02cbbSJaakko Heinonen 	if (tl > 0) {
85913c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
86013c02cbbSJaakko Heinonen 		upn->ptr = NULL;
86113c02cbbSJaakko Heinonen 		upn->len = tl;
86213c02cbbSJaakko Heinonen 		TAILQ_INSERT_AFTER(&uh->head, up, upn, list);
86313c02cbbSJaakko Heinonen 	}
86413c02cbbSJaakko Heinonen 
86513c02cbbSJaakko Heinonen 	/* Split off head end, if any */
86613c02cbbSJaakko Heinonen 	if (i > 0) {
86713c02cbbSJaakko Heinonen 		upn = new_unr(uh, p1, p2);
86813c02cbbSJaakko Heinonen 		upn->len = i;
86913c02cbbSJaakko Heinonen 		upn->ptr = NULL;
87013c02cbbSJaakko Heinonen 		TAILQ_INSERT_BEFORE(up, upn, list);
87113c02cbbSJaakko Heinonen 	}
87213c02cbbSJaakko Heinonen 	up->len = 1;
87313c02cbbSJaakko Heinonen 	up->ptr = uh;
87413c02cbbSJaakko Heinonen 
87513c02cbbSJaakko Heinonen done:
87613c02cbbSJaakko Heinonen 	last = uh->high - uh->low - (item - uh->low);
87713c02cbbSJaakko Heinonen 	if (uh->last > last)
87813c02cbbSJaakko Heinonen 		uh->last = last;
87913c02cbbSJaakko Heinonen 	uh->busy++;
88013c02cbbSJaakko Heinonen 	collapse_unr(uh, up);
88113c02cbbSJaakko Heinonen 	check_unrhdr(uh, __LINE__);
88213c02cbbSJaakko Heinonen 	return (item);
88313c02cbbSJaakko Heinonen }
88413c02cbbSJaakko Heinonen 
88513c02cbbSJaakko Heinonen int
88613c02cbbSJaakko Heinonen alloc_unr_specific(struct unrhdr *uh, u_int item)
88713c02cbbSJaakko Heinonen {
88813c02cbbSJaakko Heinonen 	void *p1, *p2;
88913c02cbbSJaakko Heinonen 	int i;
89013c02cbbSJaakko Heinonen 
89113c02cbbSJaakko Heinonen 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "alloc_unr_specific");
89213c02cbbSJaakko Heinonen 
89313c02cbbSJaakko Heinonen 	p1 = Malloc(sizeof(struct unr));
89413c02cbbSJaakko Heinonen 	p2 = Malloc(sizeof(struct unr));
89513c02cbbSJaakko Heinonen 
896e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
89713c02cbbSJaakko Heinonen 		mtx_lock(uh->mtx);
89813c02cbbSJaakko Heinonen 	i = alloc_unr_specificl(uh, item, &p1, &p2);
899e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
90013c02cbbSJaakko Heinonen 		mtx_unlock(uh->mtx);
90113c02cbbSJaakko Heinonen 
90213c02cbbSJaakko Heinonen 	if (p1 != NULL)
90313c02cbbSJaakko Heinonen 		Free(p1);
90413c02cbbSJaakko Heinonen 	if (p2 != NULL)
90513c02cbbSJaakko Heinonen 		Free(p2);
90613c02cbbSJaakko Heinonen 
90713c02cbbSJaakko Heinonen 	return (i);
90813c02cbbSJaakko Heinonen }
90913c02cbbSJaakko Heinonen 
910f6bde1fdSPoul-Henning Kamp /*
911f6bde1fdSPoul-Henning Kamp  * Free a unr.
912f6bde1fdSPoul-Henning Kamp  *
913f6bde1fdSPoul-Henning Kamp  * If we can save unrs by using a bitmap, do so.
914f6bde1fdSPoul-Henning Kamp  */
915d9a54d5cSPoul-Henning Kamp static void
916d9a54d5cSPoul-Henning Kamp free_unrl(struct unrhdr *uh, u_int item, void **p1, void **p2)
917f6bde1fdSPoul-Henning Kamp {
918d9a54d5cSPoul-Henning Kamp 	struct unr *up, *upp, *upn;
919d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
920d9a54d5cSPoul-Henning Kamp 	u_int pl;
921f6bde1fdSPoul-Henning Kamp 
922f6bde1fdSPoul-Henning Kamp 	KASSERT(item >= uh->low && item <= uh->high,
923f6bde1fdSPoul-Henning Kamp 	    ("UNR: free_unr(%u) out of range [%u...%u]",
924f6bde1fdSPoul-Henning Kamp 	     item, uh->low, uh->high));
925f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
926f6bde1fdSPoul-Henning Kamp 	item -= uh->low;
927d9a54d5cSPoul-Henning Kamp 	upp = TAILQ_FIRST(&uh->head);
928f6bde1fdSPoul-Henning Kamp 	/*
929d9a54d5cSPoul-Henning Kamp 	 * Freeing in the ideal split case
930f6bde1fdSPoul-Henning Kamp 	 */
931d9a54d5cSPoul-Henning Kamp 	if (item + 1 == uh->first && upp == NULL) {
932d9a54d5cSPoul-Henning Kamp 		uh->last++;
933d9a54d5cSPoul-Henning Kamp 		uh->first--;
934d9a54d5cSPoul-Henning Kamp 		uh->busy--;
935f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
936f6bde1fdSPoul-Henning Kamp 		return;
937f6bde1fdSPoul-Henning Kamp 	}
938d9a54d5cSPoul-Henning Kamp 	/*
939d9a54d5cSPoul-Henning Kamp  	 * Freeing in the ->first section.  Create a run starting at the
940d9a54d5cSPoul-Henning Kamp 	 * freed item.  The code below will subdivide it.
941d9a54d5cSPoul-Henning Kamp 	 */
942d9a54d5cSPoul-Henning Kamp 	if (item < uh->first) {
943d9a54d5cSPoul-Henning Kamp 		up = new_unr(uh, p1, p2);
944d9a54d5cSPoul-Henning Kamp 		up->ptr = uh;
945d9a54d5cSPoul-Henning Kamp 		up->len = uh->first - item;
946d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_HEAD(&uh->head, up, list);
947d9a54d5cSPoul-Henning Kamp 		uh->first -= up->len;
948f6bde1fdSPoul-Henning Kamp 	}
949f6bde1fdSPoul-Henning Kamp 
950d9a54d5cSPoul-Henning Kamp 	item -= uh->first;
951d9a54d5cSPoul-Henning Kamp 
952d9a54d5cSPoul-Henning Kamp 	/* Find the item which contains the unit we want to free */
953d9a54d5cSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
954d9a54d5cSPoul-Henning Kamp 		if (up->len > item)
955d9a54d5cSPoul-Henning Kamp 			break;
956d9a54d5cSPoul-Henning Kamp 		item -= up->len;
957d9a54d5cSPoul-Henning Kamp 	}
958d9a54d5cSPoul-Henning Kamp 
959d9a54d5cSPoul-Henning Kamp 	/* Handle bitmap items */
960d9a54d5cSPoul-Henning Kamp 	if (is_bitmap(uh, up)) {
961d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
962d9a54d5cSPoul-Henning Kamp 
963d9a54d5cSPoul-Henning Kamp 		KASSERT(bit_test(ub->map, item) != 0,
964d9a54d5cSPoul-Henning Kamp 		    ("UNR: Freeing free item %d (bitmap)\n", item));
965d9a54d5cSPoul-Henning Kamp 		bit_clear(ub->map, item);
966d9a54d5cSPoul-Henning Kamp 		uh->busy--;
967d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
968d9a54d5cSPoul-Henning Kamp 		return;
969d9a54d5cSPoul-Henning Kamp 	}
970d9a54d5cSPoul-Henning Kamp 
971d9a54d5cSPoul-Henning Kamp 	KASSERT(up->ptr == uh, ("UNR Freeing free item %d (run))\n", item));
972f6bde1fdSPoul-Henning Kamp 
973f6bde1fdSPoul-Henning Kamp 	/* Just this one left, reap it */
974f6bde1fdSPoul-Henning Kamp 	if (up->len == 1) {
975f6bde1fdSPoul-Henning Kamp 		up->ptr = NULL;
976f6bde1fdSPoul-Henning Kamp 		uh->busy--;
977f6bde1fdSPoul-Henning Kamp 		collapse_unr(uh, up);
978f6bde1fdSPoul-Henning Kamp 		return;
979f6bde1fdSPoul-Henning Kamp 	}
980f6bde1fdSPoul-Henning Kamp 
981d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item into the previous 'free' run */
982f6bde1fdSPoul-Henning Kamp 	upp = TAILQ_PREV(up, unrhd, list);
983d9a54d5cSPoul-Henning Kamp 	if (item == 0 && upp != NULL && upp->ptr == NULL) {
984f6bde1fdSPoul-Henning Kamp 		upp->len++;
985f6bde1fdSPoul-Henning Kamp 		up->len--;
986f6bde1fdSPoul-Henning Kamp 		uh->busy--;
987d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
988f6bde1fdSPoul-Henning Kamp 		return;
989f6bde1fdSPoul-Henning Kamp 	}
990f6bde1fdSPoul-Henning Kamp 
991d9a54d5cSPoul-Henning Kamp 	/* Check if we can shift the item to the next 'free' run */
992f6bde1fdSPoul-Henning Kamp 	upn = TAILQ_NEXT(up, list);
993d9a54d5cSPoul-Henning Kamp 	if (item == up->len - 1 && upn != NULL && upn->ptr == NULL) {
994f6bde1fdSPoul-Henning Kamp 		upn->len++;
995f6bde1fdSPoul-Henning Kamp 		up->len--;
996f6bde1fdSPoul-Henning Kamp 		uh->busy--;
997d9a54d5cSPoul-Henning Kamp 		collapse_unr(uh, up);
998f6bde1fdSPoul-Henning Kamp 		return;
999f6bde1fdSPoul-Henning Kamp 	}
1000f6bde1fdSPoul-Henning Kamp 
1001f6bde1fdSPoul-Henning Kamp 	/* Split off the tail end, if any. */
1002d9a54d5cSPoul-Henning Kamp 	pl = up->len - (1 + item);
1003f6bde1fdSPoul-Henning Kamp 	if (pl > 0) {
1004d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
1005f6bde1fdSPoul-Henning Kamp 		upp->ptr = uh;
1006f6bde1fdSPoul-Henning Kamp 		upp->len = pl;
1007f6bde1fdSPoul-Henning Kamp 		TAILQ_INSERT_AFTER(&uh->head, up, upp, list);
1008f6bde1fdSPoul-Henning Kamp 	}
1009f6bde1fdSPoul-Henning Kamp 
1010d9a54d5cSPoul-Henning Kamp 	/* Split off head end, if any */
1011d9a54d5cSPoul-Henning Kamp 	if (item > 0) {
1012d9a54d5cSPoul-Henning Kamp 		upp = new_unr(uh, p1, p2);
1013d9a54d5cSPoul-Henning Kamp 		upp->len = item;
1014d9a54d5cSPoul-Henning Kamp 		upp->ptr = uh;
1015d9a54d5cSPoul-Henning Kamp 		TAILQ_INSERT_BEFORE(up, upp, list);
1016d9a54d5cSPoul-Henning Kamp 	}
1017f6bde1fdSPoul-Henning Kamp 	up->len = 1;
1018f6bde1fdSPoul-Henning Kamp 	up->ptr = NULL;
1019f6bde1fdSPoul-Henning Kamp 	uh->busy--;
1020d9a54d5cSPoul-Henning Kamp 	collapse_unr(uh, up);
1021f6bde1fdSPoul-Henning Kamp }
1022f6bde1fdSPoul-Henning Kamp 
1023d9a54d5cSPoul-Henning Kamp void
1024d9a54d5cSPoul-Henning Kamp free_unr(struct unrhdr *uh, u_int item)
1025d9a54d5cSPoul-Henning Kamp {
1026d9a54d5cSPoul-Henning Kamp 	void *p1, *p2;
1027f6bde1fdSPoul-Henning Kamp 
10287550e3eaSKonstantin Belousov 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "free_unr");
1029d9a54d5cSPoul-Henning Kamp 	p1 = Malloc(sizeof(struct unr));
1030d9a54d5cSPoul-Henning Kamp 	p2 = Malloc(sizeof(struct unr));
1031e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
1032d9a54d5cSPoul-Henning Kamp 		mtx_lock(uh->mtx);
1033d9a54d5cSPoul-Henning Kamp 	free_unrl(uh, item, &p1, &p2);
103409828ba9SKonstantin Belousov 	clean_unrhdrl(uh);
1035e59b940dSKonstantin Belousov 	if (uh->mtx != NULL)
1036d9a54d5cSPoul-Henning Kamp 		mtx_unlock(uh->mtx);
1037d9a54d5cSPoul-Henning Kamp 	if (p1 != NULL)
1038d9a54d5cSPoul-Henning Kamp 		Free(p1);
1039d9a54d5cSPoul-Henning Kamp 	if (p2 != NULL)
1040d9a54d5cSPoul-Henning Kamp 		Free(p2);
1041f6bde1fdSPoul-Henning Kamp }
1042f6bde1fdSPoul-Henning Kamp 
1043f386b277SKonstantin Belousov #ifdef _KERNEL
1044f386b277SKonstantin Belousov #include "opt_ddb.h"
1045f386b277SKonstantin Belousov #ifdef DDB
1046f386b277SKonstantin Belousov #include <ddb/ddb.h>
1047f386b277SKonstantin Belousov #endif
1048f386b277SKonstantin Belousov #endif
104993f6c81eSPoul-Henning Kamp 
1050f386b277SKonstantin Belousov #if (defined(_KERNEL) && defined(DDB)) || !defined(_KERNEL)
1051f6bde1fdSPoul-Henning Kamp 
1052f386b277SKonstantin Belousov #if !defined(_KERNEL)
1053f386b277SKonstantin Belousov #define db_printf printf
1054f386b277SKonstantin Belousov #endif
1055794277daSAlan Somers 
1056f6bde1fdSPoul-Henning Kamp static void
1057f6bde1fdSPoul-Henning Kamp print_unr(struct unrhdr *uh, struct unr *up)
1058f6bde1fdSPoul-Henning Kamp {
1059f6bde1fdSPoul-Henning Kamp 	u_int x;
1060d9a54d5cSPoul-Henning Kamp 	struct unrb *ub;
1061f6bde1fdSPoul-Henning Kamp 
1062f386b277SKonstantin Belousov 	db_printf("  %p len = %5u ", up, up->len);
1063f6bde1fdSPoul-Henning Kamp 	if (up->ptr == NULL)
1064f386b277SKonstantin Belousov 		db_printf("free\n");
1065f6bde1fdSPoul-Henning Kamp 	else if (up->ptr == uh)
1066f386b277SKonstantin Belousov 		db_printf("alloc\n");
1067f6bde1fdSPoul-Henning Kamp 	else {
1068d9a54d5cSPoul-Henning Kamp 		ub = up->ptr;
1069f386b277SKonstantin Belousov 		db_printf("bitmap [");
1070d9a54d5cSPoul-Henning Kamp 		for (x = 0; x < up->len; x++) {
1071d9a54d5cSPoul-Henning Kamp 			if (bit_test(ub->map, x))
1072f386b277SKonstantin Belousov 				db_printf("#");
1073f6bde1fdSPoul-Henning Kamp 			else
1074f386b277SKonstantin Belousov 				db_printf(" ");
1075f6bde1fdSPoul-Henning Kamp 		}
1076f386b277SKonstantin Belousov 		db_printf("]\n");
1077f6bde1fdSPoul-Henning Kamp 	}
1078f6bde1fdSPoul-Henning Kamp }
1079f6bde1fdSPoul-Henning Kamp 
1080f6bde1fdSPoul-Henning Kamp static void
1081f6bde1fdSPoul-Henning Kamp print_unrhdr(struct unrhdr *uh)
1082f6bde1fdSPoul-Henning Kamp {
1083f6bde1fdSPoul-Henning Kamp 	struct unr *up;
1084f6bde1fdSPoul-Henning Kamp 	u_int x;
1085f6bde1fdSPoul-Henning Kamp 
1086f386b277SKonstantin Belousov 	db_printf(
1087d9a54d5cSPoul-Henning Kamp 	    "%p low = %u high = %u first = %u last = %u busy %u chunks = %u\n",
1088d9a54d5cSPoul-Henning Kamp 	    uh, uh->low, uh->high, uh->first, uh->last, uh->busy, uh->alloc);
1089d9a54d5cSPoul-Henning Kamp 	x = uh->low + uh->first;
1090f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
1091f386b277SKonstantin Belousov 		db_printf("  from = %5u", x);
1092f6bde1fdSPoul-Henning Kamp 		print_unr(uh, up);
1093f6bde1fdSPoul-Henning Kamp 		if (up->ptr == NULL || up->ptr == uh)
1094f6bde1fdSPoul-Henning Kamp 			x += up->len;
1095f6bde1fdSPoul-Henning Kamp 		else
1096f6bde1fdSPoul-Henning Kamp 			x += NBITS;
1097f6bde1fdSPoul-Henning Kamp 	}
1098f6bde1fdSPoul-Henning Kamp }
1099f6bde1fdSPoul-Henning Kamp 
1100f386b277SKonstantin Belousov #endif
1101f386b277SKonstantin Belousov 
1102f386b277SKonstantin Belousov #if defined(_KERNEL) && defined(DDB)
1103f386b277SKonstantin Belousov DB_SHOW_COMMAND(unrhdr, unrhdr_print_unrhdr)
1104f386b277SKonstantin Belousov {
1105f386b277SKonstantin Belousov 	if (!have_addr) {
1106f386b277SKonstantin Belousov 		db_printf("show unrhdr addr\n");
1107f386b277SKonstantin Belousov 		return;
1108f386b277SKonstantin Belousov 	}
1109f386b277SKonstantin Belousov 
1110f386b277SKonstantin Belousov 	print_unrhdr((struct unrhdr *)addr);
1111f386b277SKonstantin Belousov }
1112c4cc0cabSKonstantin Belousov 
1113c4cc0cabSKonstantin Belousov static void
1114c4cc0cabSKonstantin Belousov print_unrhdr_iter(struct unrhdr_iter *iter)
1115c4cc0cabSKonstantin Belousov {
1116c4cc0cabSKonstantin Belousov 	db_printf("iter %p unrhdr %p ipos %d upos %p ufi %d\n",
1117c4cc0cabSKonstantin Belousov 	    iter, iter->uh, iter->ipos, iter->upos, iter->upos_first_item);
1118c4cc0cabSKonstantin Belousov }
1119c4cc0cabSKonstantin Belousov 
1120c4cc0cabSKonstantin Belousov DB_SHOW_COMMAND(unrhdr_iter, unrhdr_print_iter)
1121c4cc0cabSKonstantin Belousov {
1122c4cc0cabSKonstantin Belousov 	if (!have_addr) {
1123c4cc0cabSKonstantin Belousov 		db_printf("show unrhdr_iter addr\n");
1124c4cc0cabSKonstantin Belousov 		return;
1125c4cc0cabSKonstantin Belousov 	}
1126c4cc0cabSKonstantin Belousov 
1127c4cc0cabSKonstantin Belousov 	print_unrhdr_iter((struct unrhdr_iter *)addr);
1128c4cc0cabSKonstantin Belousov }
1129f386b277SKonstantin Belousov #endif
1130f386b277SKonstantin Belousov 
1131f386b277SKonstantin Belousov #ifndef _KERNEL	/* USERLAND test driver */
1132f386b277SKonstantin Belousov 
1133f386b277SKonstantin Belousov /*
1134f386b277SKonstantin Belousov  * Simple stochastic test driver for the above functions.  The code resides
1135f386b277SKonstantin Belousov  * here so that it can access static functions and structures.
1136f386b277SKonstantin Belousov  */
1137f386b277SKonstantin Belousov 
1138f386b277SKonstantin Belousov static bool verbose;
1139f386b277SKonstantin Belousov #define VPRINTF(...)	{if (verbose) printf(__VA_ARGS__);}
1140f386b277SKonstantin Belousov 
114113c02cbbSJaakko Heinonen static void
114213c02cbbSJaakko Heinonen test_alloc_unr(struct unrhdr *uh, u_int i, char a[])
114313c02cbbSJaakko Heinonen {
114413c02cbbSJaakko Heinonen 	int j;
114513c02cbbSJaakko Heinonen 
114613c02cbbSJaakko Heinonen 	if (a[i]) {
1147794277daSAlan Somers 		VPRINTF("F %u\n", i);
114813c02cbbSJaakko Heinonen 		free_unr(uh, i);
114913c02cbbSJaakko Heinonen 		a[i] = 0;
115013c02cbbSJaakko Heinonen 	} else {
115113c02cbbSJaakko Heinonen 		no_alloc = 1;
115213c02cbbSJaakko Heinonen 		j = alloc_unr(uh);
115313c02cbbSJaakko Heinonen 		if (j != -1) {
115413c02cbbSJaakko Heinonen 			a[j] = 1;
1155794277daSAlan Somers 			VPRINTF("A %d\n", j);
115613c02cbbSJaakko Heinonen 		}
115713c02cbbSJaakko Heinonen 		no_alloc = 0;
115813c02cbbSJaakko Heinonen 	}
115913c02cbbSJaakko Heinonen }
116013c02cbbSJaakko Heinonen 
116113c02cbbSJaakko Heinonen static void
116213c02cbbSJaakko Heinonen test_alloc_unr_specific(struct unrhdr *uh, u_int i, char a[])
116313c02cbbSJaakko Heinonen {
116413c02cbbSJaakko Heinonen 	int j;
116513c02cbbSJaakko Heinonen 
116613c02cbbSJaakko Heinonen 	j = alloc_unr_specific(uh, i);
116713c02cbbSJaakko Heinonen 	if (j == -1) {
1168794277daSAlan Somers 		VPRINTF("F %u\n", i);
116913c02cbbSJaakko Heinonen 		a[i] = 0;
117013c02cbbSJaakko Heinonen 		free_unr(uh, i);
117113c02cbbSJaakko Heinonen 	} else {
117213c02cbbSJaakko Heinonen 		a[i] = 1;
1173794277daSAlan Somers 		VPRINTF("A %d\n", j);
117413c02cbbSJaakko Heinonen 	}
117513c02cbbSJaakko Heinonen }
117613c02cbbSJaakko Heinonen 
117712db3c91SKonstantin Belousov #define	TBASE	7
117812db3c91SKonstantin Belousov #define	XSIZE	10
117912db3c91SKonstantin Belousov #define	ISIZE	1000
118012db3c91SKonstantin Belousov 
118112db3c91SKonstantin Belousov static int
118212db3c91SKonstantin Belousov test_iter_compar(const void *a, const void *b)
118312db3c91SKonstantin Belousov {
118412db3c91SKonstantin Belousov 	return (*(const int *)a - *(const int *)b);
118512db3c91SKonstantin Belousov }
118612db3c91SKonstantin Belousov 
118712db3c91SKonstantin Belousov static void
118812db3c91SKonstantin Belousov test_iter_fill(int *vals, struct unrhdr *uh, int i, int v, int *res)
118912db3c91SKonstantin Belousov {
119012db3c91SKonstantin Belousov 	int x;
119112db3c91SKonstantin Belousov 
119212db3c91SKonstantin Belousov 	vals[i] = v;
119312db3c91SKonstantin Belousov 	x = alloc_unr_specific(uh, v);
119412db3c91SKonstantin Belousov 	if (x != v) {
119512db3c91SKonstantin Belousov 		VPRINTF("alloc_unr_specific failed %d %d\n", x, v);
119612db3c91SKonstantin Belousov 		*res = 1;
119712db3c91SKonstantin Belousov 	}
119812db3c91SKonstantin Belousov }
119912db3c91SKonstantin Belousov 
1200794277daSAlan Somers static void
1201a014e0a3SKonstantin Belousov test_iter(void)
1202a014e0a3SKonstantin Belousov {
120312db3c91SKonstantin Belousov 	struct unrhdr *uh;
120412db3c91SKonstantin Belousov 	void *ihandle;
120512db3c91SKonstantin Belousov 	int vals[ISIZE];
120612db3c91SKonstantin Belousov 	int i, j, v, x, res;
120712db3c91SKonstantin Belousov 
120812db3c91SKonstantin Belousov 	res = 0;
120912db3c91SKonstantin Belousov 	uh = new_unrhdr(TBASE, INT_MAX, NULL);
121012db3c91SKonstantin Belousov 	for (i = 0; i < XSIZE; i++) {
121112db3c91SKonstantin Belousov 		vals[i] = i + TBASE;
121212db3c91SKonstantin Belousov 		x = alloc_unr_specific(uh, i + TBASE);
121312db3c91SKonstantin Belousov 		if (x != i + TBASE) {
121412db3c91SKonstantin Belousov 			VPRINTF("alloc_unr_specific failed %d %d\n", x,
121512db3c91SKonstantin Belousov 			    i + TBASE);
121612db3c91SKonstantin Belousov 			res = 1;
121712db3c91SKonstantin Belousov 		}
121812db3c91SKonstantin Belousov 	}
121912db3c91SKonstantin Belousov 	for (; i < ISIZE; i++) {
122012db3c91SKonstantin Belousov 		for (;;) {
122112db3c91SKonstantin Belousov again:
122212db3c91SKonstantin Belousov 			v = arc4random_uniform(INT_MAX);
122312db3c91SKonstantin Belousov 			if (v < TBASE)
122412db3c91SKonstantin Belousov 				goto again;
122512db3c91SKonstantin Belousov 			for (j = 0; j < i; j++) {
122612db3c91SKonstantin Belousov 				if (v == vals[j] || v + 1 == vals[j])
122712db3c91SKonstantin Belousov 					goto again;
122812db3c91SKonstantin Belousov 			}
122912db3c91SKonstantin Belousov 			break;
123012db3c91SKonstantin Belousov 		}
123112db3c91SKonstantin Belousov 		test_iter_fill(vals, uh, i, v, &res);
123212db3c91SKonstantin Belousov 		i++, v++;
123312db3c91SKonstantin Belousov 		if (i < ISIZE)
123412db3c91SKonstantin Belousov 			test_iter_fill(vals, uh, i, v, &res);
123512db3c91SKonstantin Belousov 	}
123612db3c91SKonstantin Belousov 	qsort(vals, ISIZE, sizeof(vals[0]), test_iter_compar);
123712db3c91SKonstantin Belousov 
123812db3c91SKonstantin Belousov 	ihandle = create_iter_unr(uh);
123912db3c91SKonstantin Belousov 	i = 0;
124012db3c91SKonstantin Belousov 	while ((v = next_iter_unr(ihandle)) != -1) {
124112db3c91SKonstantin Belousov 		if (vals[i] != v) {
124212db3c91SKonstantin Belousov 			VPRINTF("iter %d: iter %d != val %d\n", i, v, vals[i]);
124312db3c91SKonstantin Belousov 			if (res == 0) {
124412db3c91SKonstantin Belousov 				if (verbose)
124512db3c91SKonstantin Belousov 					print_unrhdr(uh);
124612db3c91SKonstantin Belousov 				res = 1;
124712db3c91SKonstantin Belousov 			}
124812db3c91SKonstantin Belousov 		} else {
124912db3c91SKonstantin Belousov 			VPRINTF("iter %d: val %d\n", i, v);
125012db3c91SKonstantin Belousov 		}
125112db3c91SKonstantin Belousov 		i++;
125212db3c91SKonstantin Belousov 	}
125312db3c91SKonstantin Belousov 	free_iter_unr(ihandle);
125412db3c91SKonstantin Belousov 	clean_unrhdr(uh);
125512db3c91SKonstantin Belousov 	clear_unrhdr(uh);
125612db3c91SKonstantin Belousov 	delete_unrhdr(uh);
125712db3c91SKonstantin Belousov 	exit(res);
1258a014e0a3SKonstantin Belousov }
1259a014e0a3SKonstantin Belousov 
1260a014e0a3SKonstantin Belousov static void
1261794277daSAlan Somers usage(char **argv)
1262794277daSAlan Somers {
126312db3c91SKonstantin Belousov 	printf("%s [-h] [-i] [-r REPETITIONS] [-v]\n", argv[0]);
1264794277daSAlan Somers }
1265f6bde1fdSPoul-Henning Kamp 
1266f6bde1fdSPoul-Henning Kamp int
1267794277daSAlan Somers main(int argc, char **argv)
1268f6bde1fdSPoul-Henning Kamp {
1269f6bde1fdSPoul-Henning Kamp 	struct unrhdr *uh;
1270794277daSAlan Somers 	char *a;
1271794277daSAlan Somers 	long count = 10000;	/* Number of unrs to test */
127237f32e53SAlan Somers 	long reps = 1, m;
1273794277daSAlan Somers 	int ch;
1274cd565040SConrad Meyer 	u_int i;
127512db3c91SKonstantin Belousov 	bool testing_iter;
1276794277daSAlan Somers 
1277794277daSAlan Somers 	verbose = false;
127812db3c91SKonstantin Belousov 	testing_iter = false;
1279794277daSAlan Somers 
128012db3c91SKonstantin Belousov 	while ((ch = getopt(argc, argv, "hir:v")) != -1) {
1281794277daSAlan Somers 		switch (ch) {
128212db3c91SKonstantin Belousov 		case 'i':
128312db3c91SKonstantin Belousov 			testing_iter = true;
128412db3c91SKonstantin Belousov 			break;
1285794277daSAlan Somers 		case 'r':
1286794277daSAlan Somers 			errno = 0;
1287794277daSAlan Somers 			reps = strtol(optarg, NULL, 0);
1288794277daSAlan Somers 			if (errno == ERANGE || errno == EINVAL) {
1289794277daSAlan Somers 				usage(argv);
1290794277daSAlan Somers 				exit(2);
1291794277daSAlan Somers 			}
1292794277daSAlan Somers 
1293794277daSAlan Somers 			break;
1294794277daSAlan Somers 		case 'v':
1295794277daSAlan Somers 			verbose = true;
1296794277daSAlan Somers 			break;
1297794277daSAlan Somers 		case 'h':
1298794277daSAlan Somers 		default:
1299794277daSAlan Somers 			usage(argv);
1300794277daSAlan Somers 			exit(2);
1301794277daSAlan Somers 		}
1302794277daSAlan Somers 	}
1303f6bde1fdSPoul-Henning Kamp 
1304d9a54d5cSPoul-Henning Kamp 	setbuf(stdout, NULL);
130512db3c91SKonstantin Belousov 
130612db3c91SKonstantin Belousov 	if (testing_iter)
130712db3c91SKonstantin Belousov 		test_iter();
130812db3c91SKonstantin Belousov 
1309794277daSAlan Somers 	uh = new_unrhdr(0, count - 1, NULL);
1310d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1311f6bde1fdSPoul-Henning Kamp 
1312794277daSAlan Somers 	a = calloc(count, sizeof(char));
1313794277daSAlan Somers 	if (a == NULL)
1314794277daSAlan Somers 		err(1, "calloc failed");
1315f6bde1fdSPoul-Henning Kamp 
1316794277daSAlan Somers 	printf("sizeof(struct unr) %zu\n", sizeof(struct unr));
1317794277daSAlan Somers 	printf("sizeof(struct unrb) %zu\n", sizeof(struct unrb));
1318794277daSAlan Somers 	printf("sizeof(struct unrhdr) %zu\n", sizeof(struct unrhdr));
1319b4f69365SEd Maste 	printf("NBITS %lu\n", (unsigned long)NBITS);
1320794277daSAlan Somers 	for (m = 0; m < count * reps; m++) {
1321cd565040SConrad Meyer 		i = arc4random_uniform(count);
1322d9a54d5cSPoul-Henning Kamp #if 0
1323d9a54d5cSPoul-Henning Kamp 		if (a[i] && (j & 1))
1324d9a54d5cSPoul-Henning Kamp 			continue;
1325d9a54d5cSPoul-Henning Kamp #endif
1326cd565040SConrad Meyer 		if ((arc4random() & 1) != 0)
132713c02cbbSJaakko Heinonen 			test_alloc_unr(uh, i, a);
132813c02cbbSJaakko Heinonen 		else
132913c02cbbSJaakko Heinonen 			test_alloc_unr_specific(uh, i, a);
133013c02cbbSJaakko Heinonen 
1331794277daSAlan Somers 		if (verbose)
1332f6bde1fdSPoul-Henning Kamp 			print_unrhdr(uh);
1333f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
1334f6bde1fdSPoul-Henning Kamp 	}
133537f32e53SAlan Somers 	for (i = 0; i < (u_int)count; i++) {
1336d9a54d5cSPoul-Henning Kamp 		if (a[i]) {
1337794277daSAlan Somers 			if (verbose) {
1338d9a54d5cSPoul-Henning Kamp 				printf("C %u\n", i);
1339e4fea39eSPoul-Henning Kamp 				print_unrhdr(uh);
1340d9a54d5cSPoul-Henning Kamp 			}
1341794277daSAlan Somers 			free_unr(uh, i);
1342794277daSAlan Somers 		}
1343d9a54d5cSPoul-Henning Kamp 	}
1344d9a54d5cSPoul-Henning Kamp 	print_unrhdr(uh);
1345e4fea39eSPoul-Henning Kamp 	delete_unrhdr(uh);
1346794277daSAlan Somers 	free(a);
1347f6bde1fdSPoul-Henning Kamp 	return (0);
1348f6bde1fdSPoul-Henning Kamp }
1349f6bde1fdSPoul-Henning Kamp #endif
1350