xref: /freebsd/sys/kern/subr_unit.c (revision f6bde1fd05ca4c7befccba2b56d004be1ffc768b)
1f6bde1fdSPoul-Henning Kamp /*-
2f6bde1fdSPoul-Henning Kamp  * Copyright (c) 2004 Poul-Henning Kamp
3f6bde1fdSPoul-Henning Kamp  * All rights reserved.
4f6bde1fdSPoul-Henning Kamp  *
5f6bde1fdSPoul-Henning Kamp  * Redistribution and use in source and binary forms, with or without
6f6bde1fdSPoul-Henning Kamp  * modification, are permitted provided that the following conditions
7f6bde1fdSPoul-Henning Kamp  * are met:
8f6bde1fdSPoul-Henning Kamp  * 1. Redistributions of source code must retain the above copyright
9f6bde1fdSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer.
10f6bde1fdSPoul-Henning Kamp  * 2. Redistributions in binary form must reproduce the above copyright
11f6bde1fdSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer in the
12f6bde1fdSPoul-Henning Kamp  *    documentation and/or other materials provided with the distribution.
13f6bde1fdSPoul-Henning Kamp  *
14f6bde1fdSPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15f6bde1fdSPoul-Henning Kamp  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16f6bde1fdSPoul-Henning Kamp  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17f6bde1fdSPoul-Henning Kamp  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18f6bde1fdSPoul-Henning Kamp  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19f6bde1fdSPoul-Henning Kamp  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20f6bde1fdSPoul-Henning Kamp  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21f6bde1fdSPoul-Henning Kamp  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22f6bde1fdSPoul-Henning Kamp  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23f6bde1fdSPoul-Henning Kamp  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24f6bde1fdSPoul-Henning Kamp  * SUCH DAMAGE.
25f6bde1fdSPoul-Henning Kamp  *
26f6bde1fdSPoul-Henning Kamp  * $FreeBSD$
27f6bde1fdSPoul-Henning Kamp  *
28f6bde1fdSPoul-Henning Kamp  * Unit number allocation functions.
29f6bde1fdSPoul-Henning Kamp  *
30f6bde1fdSPoul-Henning Kamp  * These functions implement a mixed run-length/bitmap management of unit
31f6bde1fdSPoul-Henning Kamp  * number spaces.
32f6bde1fdSPoul-Henning Kamp  *
33f6bde1fdSPoul-Henning Kamp  * Allocation is always lowest free number first.
34f6bde1fdSPoul-Henning Kamp  *
35f6bde1fdSPoul-Henning Kamp  * Worst case memory usage (disregarding boundary effects in the low end)
36f6bde1fdSPoul-Henning Kamp  * is two bits for each slot in the unit number space.  (For a full
37f6bde1fdSPoul-Henning Kamp  * [0 ... UINT_MAX] space that is still a lot of course.)
38f6bde1fdSPoul-Henning Kamp  *
39f6bde1fdSPoul-Henning Kamp  * The typical case, where no unit numbers are freed, is managed in a
40f6bde1fdSPoul-Henning Kamp  * constant sized memory footprint of:
41f6bde1fdSPoul-Henning Kamp  *   sizeof(struct unrhdr) + 2 * sizeof (struct unr) == 56 bytes on i386
42f6bde1fdSPoul-Henning Kamp  *
43f6bde1fdSPoul-Henning Kamp  * The caller must provide locking.
44f6bde1fdSPoul-Henning Kamp  *
45f6bde1fdSPoul-Henning Kamp  * A userland test program is included.
46f6bde1fdSPoul-Henning Kamp  *
47f6bde1fdSPoul-Henning Kamp  */
48f6bde1fdSPoul-Henning Kamp 
49f6bde1fdSPoul-Henning Kamp #include <sys/types.h>
50f6bde1fdSPoul-Henning Kamp #include <sys/queue.h>
51f6bde1fdSPoul-Henning Kamp #include <sys/bitstring.h>
52f6bde1fdSPoul-Henning Kamp 
53f6bde1fdSPoul-Henning Kamp #ifdef _KERNEL
54f6bde1fdSPoul-Henning Kamp 
55f6bde1fdSPoul-Henning Kamp #include <sys/param.h>
56f6bde1fdSPoul-Henning Kamp #include <sys/malloc.h>
57f6bde1fdSPoul-Henning Kamp #include <sys/kernel.h>
58f6bde1fdSPoul-Henning Kamp #include <sys/systm.h>
59f6bde1fdSPoul-Henning Kamp 
60f6bde1fdSPoul-Henning Kamp /*
61f6bde1fdSPoul-Henning Kamp  * In theory it would be smarter to allocate the individual blocks
62f6bde1fdSPoul-Henning Kamp  * with the zone allocator, but at this time the expectation is that
63f6bde1fdSPoul-Henning Kamp  * there will typically not even be enough allocations to fill a single
64f6bde1fdSPoul-Henning Kamp  * page, so we stick with malloc for now.
65f6bde1fdSPoul-Henning Kamp  */
66f6bde1fdSPoul-Henning Kamp static MALLOC_DEFINE(M_UNIT, "Unitno", "Unit number allocation");
67f6bde1fdSPoul-Henning Kamp 
68f6bde1fdSPoul-Henning Kamp #define Malloc(foo) malloc(foo, M_UNIT, M_WAITOK | M_ZERO)
69f6bde1fdSPoul-Henning Kamp #define Free(foo) free(foo, M_UNIT)
70f6bde1fdSPoul-Henning Kamp 
71f6bde1fdSPoul-Henning Kamp #else /* ...USERLAND */
72f6bde1fdSPoul-Henning Kamp 
73f6bde1fdSPoul-Henning Kamp #include <stdio.h>
74f6bde1fdSPoul-Henning Kamp #include <stdlib.h>
75f6bde1fdSPoul-Henning Kamp 
76f6bde1fdSPoul-Henning Kamp #define KASSERT(cond, arg) \
77f6bde1fdSPoul-Henning Kamp 	do { \
78f6bde1fdSPoul-Henning Kamp 		if (!(cond)) { \
79f6bde1fdSPoul-Henning Kamp 			printf arg; \
80f6bde1fdSPoul-Henning Kamp 			exit (1); \
81f6bde1fdSPoul-Henning Kamp 		} \
82f6bde1fdSPoul-Henning Kamp 	} while (0)
83f6bde1fdSPoul-Henning Kamp 
84f6bde1fdSPoul-Henning Kamp #define Malloc(foo) calloc(foo, 1)
85f6bde1fdSPoul-Henning Kamp #define Free(foo) free(foo)
86f6bde1fdSPoul-Henning Kamp 
87f6bde1fdSPoul-Henning Kamp #endif
88f6bde1fdSPoul-Henning Kamp 
89f6bde1fdSPoul-Henning Kamp /*
90f6bde1fdSPoul-Henning Kamp  * This is our basic building block.
91f6bde1fdSPoul-Henning Kamp  *
92f6bde1fdSPoul-Henning Kamp  * It can be used in three different ways depending on the value of the ptr
93f6bde1fdSPoul-Henning Kamp  * element:
94f6bde1fdSPoul-Henning Kamp  *     If ptr is NULL, it represents a run of free items.
95f6bde1fdSPoul-Henning Kamp  *     If ptr points to the unrhdr it represents a run of allocated items.
96f6bde1fdSPoul-Henning Kamp  *     Otherwise it points to an bitstring of allocated items.
97f6bde1fdSPoul-Henning Kamp  *
98f6bde1fdSPoul-Henning Kamp  * For runs the len field is the length of the run.
99f6bde1fdSPoul-Henning Kamp  * For bitmaps the len field represents the number of allocated items.
100f6bde1fdSPoul-Henning Kamp  *
101f6bde1fdSPoul-Henning Kamp  * The bitmap is the same size as struct unr to optimize memory management.
102f6bde1fdSPoul-Henning Kamp  */
103f6bde1fdSPoul-Henning Kamp struct unr {
104f6bde1fdSPoul-Henning Kamp 	TAILQ_ENTRY(unr)	list;
105f6bde1fdSPoul-Henning Kamp 	u_int			len;
106f6bde1fdSPoul-Henning Kamp 	void			*ptr;
107f6bde1fdSPoul-Henning Kamp };
108f6bde1fdSPoul-Henning Kamp 
109f6bde1fdSPoul-Henning Kamp /* Number of bits in the bitmap */
110f6bde1fdSPoul-Henning Kamp #define NBITS	(sizeof(struct unr) * 8)
111f6bde1fdSPoul-Henning Kamp 
112f6bde1fdSPoul-Henning Kamp /* Header element for a unr number space. */
113f6bde1fdSPoul-Henning Kamp 
114f6bde1fdSPoul-Henning Kamp struct unrhdr {
115f6bde1fdSPoul-Henning Kamp 	TAILQ_HEAD(unrhd,unr)	head;
116f6bde1fdSPoul-Henning Kamp 	u_int			low;	/* Lowest item */
117f6bde1fdSPoul-Henning Kamp 	u_int			high;	/* Highest item */
118f6bde1fdSPoul-Henning Kamp 	u_int			busy;	/* Count of allocated items */
119f6bde1fdSPoul-Henning Kamp 	u_int			alloc;	/* Count of memory allocations */
120f6bde1fdSPoul-Henning Kamp };
121f6bde1fdSPoul-Henning Kamp 
122f6bde1fdSPoul-Henning Kamp 
123f6bde1fdSPoul-Henning Kamp #if defined(DIAGNOSTIC) || !defined(_KERNEL)
124f6bde1fdSPoul-Henning Kamp /*
125f6bde1fdSPoul-Henning Kamp  * Consistency check function.
126f6bde1fdSPoul-Henning Kamp  *
127f6bde1fdSPoul-Henning Kamp  * Checks the internal consistency as well as we can.
128f6bde1fdSPoul-Henning Kamp  *
129f6bde1fdSPoul-Henning Kamp  * Called at all boundaries of this API.
130f6bde1fdSPoul-Henning Kamp  */
131f6bde1fdSPoul-Henning Kamp static void
132f6bde1fdSPoul-Henning Kamp check_unrhdr(struct unrhdr *uh, int line)
133f6bde1fdSPoul-Henning Kamp {
134f6bde1fdSPoul-Henning Kamp 	struct unr *up;
135f6bde1fdSPoul-Henning Kamp 	u_int x, y, z, w;
136f6bde1fdSPoul-Henning Kamp 
137f6bde1fdSPoul-Henning Kamp 	y = 0;
138f6bde1fdSPoul-Henning Kamp 	z = 0;
139f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
140f6bde1fdSPoul-Henning Kamp 		z++;
141f6bde1fdSPoul-Henning Kamp 		if (up->ptr != uh && up->ptr != NULL) {
142f6bde1fdSPoul-Henning Kamp 			z++;
143f6bde1fdSPoul-Henning Kamp 			w = 0;
144f6bde1fdSPoul-Henning Kamp 			for (x = 0; x < NBITS; x++)
145f6bde1fdSPoul-Henning Kamp 				if (bit_test((bitstr_t *)up->ptr, x))
146f6bde1fdSPoul-Henning Kamp 					w++;
147f6bde1fdSPoul-Henning Kamp 			KASSERT (w == up->len,
148f6bde1fdSPoul-Henning Kamp 			    ("UNR inconsistency: bits %u found %u\n",
149f6bde1fdSPoul-Henning Kamp 			    up->len, w));
150f6bde1fdSPoul-Henning Kamp 		}
151f6bde1fdSPoul-Henning Kamp 		if (up->ptr != NULL)
152f6bde1fdSPoul-Henning Kamp 			y += up->len;
153f6bde1fdSPoul-Henning Kamp 	}
154f6bde1fdSPoul-Henning Kamp 	KASSERT (y == uh->busy,
155f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: items %u found %u (line %d)\n",
156f6bde1fdSPoul-Henning Kamp 	    uh->busy, y, line));
157f6bde1fdSPoul-Henning Kamp 	KASSERT (z == uh->alloc,
158f6bde1fdSPoul-Henning Kamp 	    ("UNR inconsistency: chunks %u found %u (line %d)\n",
159f6bde1fdSPoul-Henning Kamp 	    uh->alloc, z, line));
160f6bde1fdSPoul-Henning Kamp }
161f6bde1fdSPoul-Henning Kamp 
162f6bde1fdSPoul-Henning Kamp #else
163f6bde1fdSPoul-Henning Kamp 
164f6bde1fdSPoul-Henning Kamp static __inline void
165f6bde1fdSPoul-Henning Kamp check_unrhdr(struct unhdr *uh, int line)
166f6bde1fdSPoul-Henning Kamp {
167f6bde1fdSPoul-Henning Kamp 
168f6bde1fdSPoul-Henning Kamp }
169f6bde1fdSPoul-Henning Kamp 
170f6bde1fdSPoul-Henning Kamp #endif
171f6bde1fdSPoul-Henning Kamp 
172f6bde1fdSPoul-Henning Kamp 
173f6bde1fdSPoul-Henning Kamp /*
174f6bde1fdSPoul-Henning Kamp  * Userland memory management.  Just use calloc and keep track of how
175f6bde1fdSPoul-Henning Kamp  * many elements we have allocated for check_unrhdr().
176f6bde1fdSPoul-Henning Kamp  */
177f6bde1fdSPoul-Henning Kamp 
178f6bde1fdSPoul-Henning Kamp static __inline void *
179f6bde1fdSPoul-Henning Kamp new_unr(struct unrhdr *uh)
180f6bde1fdSPoul-Henning Kamp {
181f6bde1fdSPoul-Henning Kamp 	uh->alloc++;
182f6bde1fdSPoul-Henning Kamp 	return (Malloc(sizeof (struct unr)));
183f6bde1fdSPoul-Henning Kamp 
184f6bde1fdSPoul-Henning Kamp }
185f6bde1fdSPoul-Henning Kamp 
186f6bde1fdSPoul-Henning Kamp static __inline void
187f6bde1fdSPoul-Henning Kamp delete_unr(struct unrhdr *uh, void *ptr)
188f6bde1fdSPoul-Henning Kamp {
189f6bde1fdSPoul-Henning Kamp 	uh->alloc--;
190f6bde1fdSPoul-Henning Kamp 	Free(ptr);
191f6bde1fdSPoul-Henning Kamp }
192f6bde1fdSPoul-Henning Kamp 
193f6bde1fdSPoul-Henning Kamp /*
194f6bde1fdSPoul-Henning Kamp  * Allocate a new unrheader set.
195f6bde1fdSPoul-Henning Kamp  *
196f6bde1fdSPoul-Henning Kamp  * Highest and lowest valid values given as paramters.
197f6bde1fdSPoul-Henning Kamp  */
198f6bde1fdSPoul-Henning Kamp 
199f6bde1fdSPoul-Henning Kamp struct unrhdr *
200f6bde1fdSPoul-Henning Kamp new_unrhdr(u_int low, u_int high)
201f6bde1fdSPoul-Henning Kamp {
202f6bde1fdSPoul-Henning Kamp 	struct unrhdr *uh;
203f6bde1fdSPoul-Henning Kamp 	struct unr *up;
204f6bde1fdSPoul-Henning Kamp 
205f6bde1fdSPoul-Henning Kamp 	KASSERT(low <= high,
206f6bde1fdSPoul-Henning Kamp 	    ("UNR: use error: new_unrhdr(%u, %u)", low, high));
207f6bde1fdSPoul-Henning Kamp 	uh = Malloc(sizeof *uh);
208f6bde1fdSPoul-Henning Kamp 	TAILQ_INIT(&uh->head);
209f6bde1fdSPoul-Henning Kamp 	uh->low = low;
210f6bde1fdSPoul-Henning Kamp 	uh->high = high;
211f6bde1fdSPoul-Henning Kamp 	up = new_unr(uh);
212f6bde1fdSPoul-Henning Kamp 	up->len = 1 + (high - low);
213f6bde1fdSPoul-Henning Kamp 	up->ptr = NULL;
214f6bde1fdSPoul-Henning Kamp 	TAILQ_INSERT_HEAD(&uh->head, up, list);
215f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
216f6bde1fdSPoul-Henning Kamp 	return (uh);
217f6bde1fdSPoul-Henning Kamp }
218f6bde1fdSPoul-Henning Kamp 
219f6bde1fdSPoul-Henning Kamp /*
220f6bde1fdSPoul-Henning Kamp  * See if a given unr should be collapsed with a neighbor
221f6bde1fdSPoul-Henning Kamp  */
222f6bde1fdSPoul-Henning Kamp static void
223f6bde1fdSPoul-Henning Kamp collapse_unr(struct unrhdr *uh, struct unr *up)
224f6bde1fdSPoul-Henning Kamp {
225f6bde1fdSPoul-Henning Kamp 	struct unr *upp;
226f6bde1fdSPoul-Henning Kamp 
227f6bde1fdSPoul-Henning Kamp 	upp = TAILQ_PREV(up, unrhd, list);
228f6bde1fdSPoul-Henning Kamp 	if (upp != NULL && up->ptr == upp->ptr) {
229f6bde1fdSPoul-Henning Kamp 		up->len += upp->len;
230f6bde1fdSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
231f6bde1fdSPoul-Henning Kamp 		delete_unr(uh, upp);
232f6bde1fdSPoul-Henning Kamp 	}
233f6bde1fdSPoul-Henning Kamp 	upp = TAILQ_NEXT(up, list);
234f6bde1fdSPoul-Henning Kamp 	if (upp != NULL && up->ptr == upp->ptr) {
235f6bde1fdSPoul-Henning Kamp 		up->len += upp->len;
236f6bde1fdSPoul-Henning Kamp 		TAILQ_REMOVE(&uh->head, upp, list);
237f6bde1fdSPoul-Henning Kamp 		delete_unr(uh, upp);
238f6bde1fdSPoul-Henning Kamp 	}
239f6bde1fdSPoul-Henning Kamp }
240f6bde1fdSPoul-Henning Kamp 
241f6bde1fdSPoul-Henning Kamp /*
242f6bde1fdSPoul-Henning Kamp  * Allocate a free unr.
243f6bde1fdSPoul-Henning Kamp  */
244f6bde1fdSPoul-Henning Kamp u_int
245f6bde1fdSPoul-Henning Kamp alloc_unr(struct unrhdr *uh)
246f6bde1fdSPoul-Henning Kamp {
247f6bde1fdSPoul-Henning Kamp 	struct unr *up, *upp;
248f6bde1fdSPoul-Henning Kamp 	u_int x;
249f6bde1fdSPoul-Henning Kamp 	int y;
250f6bde1fdSPoul-Henning Kamp 
251f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
252f6bde1fdSPoul-Henning Kamp 	x = uh->low;
253f6bde1fdSPoul-Henning Kamp 	/*
254f6bde1fdSPoul-Henning Kamp 	 * We can always allocate from one of the first two unrs on the list.
255f6bde1fdSPoul-Henning Kamp 	 * The first one is likely an allocated run, but the second has to
256f6bde1fdSPoul-Henning Kamp 	 * be a free run or a bitmap.
257f6bde1fdSPoul-Henning Kamp 	 */
258f6bde1fdSPoul-Henning Kamp 	up = TAILQ_FIRST(&uh->head);
259f6bde1fdSPoul-Henning Kamp 	KASSERT(up != NULL, ("UNR empty list"));
260f6bde1fdSPoul-Henning Kamp 	if (up->ptr == uh) {
261f6bde1fdSPoul-Henning Kamp 		x += up->len;
262f6bde1fdSPoul-Henning Kamp 		up = TAILQ_NEXT(up, list);
263f6bde1fdSPoul-Henning Kamp 	}
264f6bde1fdSPoul-Henning Kamp 	KASSERT(up != NULL, ("UNR Ran out of numbers")); /* XXX */
265f6bde1fdSPoul-Henning Kamp 	KASSERT(up->ptr != uh, ("UNR second element allocated"));
266f6bde1fdSPoul-Henning Kamp 
267f6bde1fdSPoul-Henning Kamp 	if (up->ptr != NULL) {
268f6bde1fdSPoul-Henning Kamp 		/* Bitmap unr */
269f6bde1fdSPoul-Henning Kamp 		KASSERT(up->len < NBITS, ("UNR bitmap confusion"));
270f6bde1fdSPoul-Henning Kamp 		bit_ffc((bitstr_t *)up->ptr, NBITS, &y);
271f6bde1fdSPoul-Henning Kamp 		KASSERT(y != -1, ("UNR corruption: No clear bit in bitmap."));
272f6bde1fdSPoul-Henning Kamp 		bit_set((bitstr_t *)up->ptr, y);
273f6bde1fdSPoul-Henning Kamp 		up->len++;
274f6bde1fdSPoul-Henning Kamp 		uh->busy++;
275f6bde1fdSPoul-Henning Kamp 		if (up->len == NBITS) {
276f6bde1fdSPoul-Henning Kamp 			/* The unr is all allocated, drop bitmap */
277f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
278f6bde1fdSPoul-Henning Kamp 			up->ptr = uh;
279f6bde1fdSPoul-Henning Kamp 			collapse_unr(uh, up);
280f6bde1fdSPoul-Henning Kamp 		}
281f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
282f6bde1fdSPoul-Henning Kamp 		return (x + y);
283f6bde1fdSPoul-Henning Kamp 	}
284f6bde1fdSPoul-Henning Kamp 
285f6bde1fdSPoul-Henning Kamp 	if (up->len == 1) {
286f6bde1fdSPoul-Henning Kamp 		/* Run of one free item, grab it */
287f6bde1fdSPoul-Henning Kamp 		up->ptr = uh;
288f6bde1fdSPoul-Henning Kamp 		uh->busy++;
289f6bde1fdSPoul-Henning Kamp 		collapse_unr(uh, up);
290f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
291f6bde1fdSPoul-Henning Kamp 		return (x);
292f6bde1fdSPoul-Henning Kamp 	}
293f6bde1fdSPoul-Henning Kamp 
294f6bde1fdSPoul-Henning Kamp 	/*
295f6bde1fdSPoul-Henning Kamp 	 * Slice first item into an preceeding allocated run, even if we
296f6bde1fdSPoul-Henning Kamp 	 * have to create it.  Because allocation is always lowest free
297f6bde1fdSPoul-Henning Kamp 	 * number first, we know the preceeding element (if any) to be
298f6bde1fdSPoul-Henning Kamp 	 * an allocated run.
299f6bde1fdSPoul-Henning Kamp 	 */
300f6bde1fdSPoul-Henning Kamp 	upp = TAILQ_PREV(up, unrhd, list);
301f6bde1fdSPoul-Henning Kamp 	if (upp == NULL) {
302f6bde1fdSPoul-Henning Kamp 		upp = new_unr(uh);
303f6bde1fdSPoul-Henning Kamp 		upp->len = 0;
304f6bde1fdSPoul-Henning Kamp 		upp->ptr = uh;
305f6bde1fdSPoul-Henning Kamp 		TAILQ_INSERT_BEFORE(up, upp, list);
306f6bde1fdSPoul-Henning Kamp 	}
307f6bde1fdSPoul-Henning Kamp 	KASSERT(upp->ptr == uh, ("UNR list corruption"));
308f6bde1fdSPoul-Henning Kamp 	upp->len++;
309f6bde1fdSPoul-Henning Kamp 	up->len--;
310f6bde1fdSPoul-Henning Kamp 	uh->busy++;
311f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
312f6bde1fdSPoul-Henning Kamp 	return (x);
313f6bde1fdSPoul-Henning Kamp }
314f6bde1fdSPoul-Henning Kamp 
315f6bde1fdSPoul-Henning Kamp /*
316f6bde1fdSPoul-Henning Kamp  * Free a unr.
317f6bde1fdSPoul-Henning Kamp  *
318f6bde1fdSPoul-Henning Kamp  * If we can save unrs by using a bitmap, do so.
319f6bde1fdSPoul-Henning Kamp  */
320f6bde1fdSPoul-Henning Kamp void
321f6bde1fdSPoul-Henning Kamp free_unr(struct unrhdr *uh, u_int item)
322f6bde1fdSPoul-Henning Kamp {
323f6bde1fdSPoul-Henning Kamp 	struct unr *up, *upp, *upn, *ul;
324f6bde1fdSPoul-Henning Kamp 	u_int x, l, xl, n, pl;
325f6bde1fdSPoul-Henning Kamp 
326f6bde1fdSPoul-Henning Kamp 	KASSERT(item >= uh->low && item <= uh->high,
327f6bde1fdSPoul-Henning Kamp 	    ("UNR: free_unr(%u) out of range [%u...%u]",
328f6bde1fdSPoul-Henning Kamp 	     item, uh->low, uh->high));
329f6bde1fdSPoul-Henning Kamp 	check_unrhdr(uh, __LINE__);
330f6bde1fdSPoul-Henning Kamp 	item -= uh->low;
331f6bde1fdSPoul-Henning Kamp 	xl = x = 0;
332f6bde1fdSPoul-Henning Kamp 	/* Find the start of the potential bitmap */
333f6bde1fdSPoul-Henning Kamp 	l = item - item % NBITS;
334f6bde1fdSPoul-Henning Kamp 	ul = 0;
335f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
336f6bde1fdSPoul-Henning Kamp 
337f6bde1fdSPoul-Henning Kamp 		/* Keep track of which unr we'll split if we do */
338f6bde1fdSPoul-Henning Kamp 		if (x <= l) {
339f6bde1fdSPoul-Henning Kamp 			ul = up;
340f6bde1fdSPoul-Henning Kamp 			xl = x;
341f6bde1fdSPoul-Henning Kamp 		}
342f6bde1fdSPoul-Henning Kamp 
343f6bde1fdSPoul-Henning Kamp 		/* Handle bitmap items */
344f6bde1fdSPoul-Henning Kamp 		if (up->ptr != NULL && up->ptr != uh) {
345f6bde1fdSPoul-Henning Kamp 			if (x + NBITS <= item) { /* not yet */
346f6bde1fdSPoul-Henning Kamp 				x += NBITS;
347f6bde1fdSPoul-Henning Kamp 				continue;
348f6bde1fdSPoul-Henning Kamp 			}
349f6bde1fdSPoul-Henning Kamp 			KASSERT(bit_test((bitstr_t *)up->ptr, item - x) != 0,
350f6bde1fdSPoul-Henning Kamp 			    ("UNR: Freeing free item %d (%d) (bitmap)\n",
351f6bde1fdSPoul-Henning Kamp 			     item, item - x));
352f6bde1fdSPoul-Henning Kamp 			bit_clear((bitstr_t *)up->ptr, item - x);
353f6bde1fdSPoul-Henning Kamp 			uh->busy--;
354f6bde1fdSPoul-Henning Kamp 			up->len--;
355f6bde1fdSPoul-Henning Kamp 			/*
356f6bde1fdSPoul-Henning Kamp 			 * XXX: up->len == 1 could possibly be collapsed to
357f6bde1fdSPoul-Henning Kamp 			 * XXX: neighboring runs.
358f6bde1fdSPoul-Henning Kamp 			 */
359f6bde1fdSPoul-Henning Kamp 			if (up->len > 0)
360f6bde1fdSPoul-Henning Kamp 				return;
361f6bde1fdSPoul-Henning Kamp 			/* We have freed all items in bitmap, drop it */
362f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, up->ptr);
363f6bde1fdSPoul-Henning Kamp 			up->ptr = NULL;
364f6bde1fdSPoul-Henning Kamp 			up->len = NBITS;
365f6bde1fdSPoul-Henning Kamp 			collapse_unr(uh, up);
366f6bde1fdSPoul-Henning Kamp 			check_unrhdr(uh, __LINE__);
367f6bde1fdSPoul-Henning Kamp 			return;
368f6bde1fdSPoul-Henning Kamp 		}
369f6bde1fdSPoul-Henning Kamp 
370f6bde1fdSPoul-Henning Kamp 		/* Run length unr's */
371f6bde1fdSPoul-Henning Kamp 
372f6bde1fdSPoul-Henning Kamp 		if (x + up->len <= item) {	/* not yet */
373f6bde1fdSPoul-Henning Kamp 			x += up->len;
374f6bde1fdSPoul-Henning Kamp 			continue;
375f6bde1fdSPoul-Henning Kamp 		}
376f6bde1fdSPoul-Henning Kamp 
377f6bde1fdSPoul-Henning Kamp 		/* We now have our run length unr */
378f6bde1fdSPoul-Henning Kamp 		KASSERT(up->ptr == uh,
379f6bde1fdSPoul-Henning Kamp 		    ("UNR Freeing free item %d (run))\n", item));
380f6bde1fdSPoul-Henning Kamp 
381f6bde1fdSPoul-Henning Kamp 		/* Just this one left, reap it */
382f6bde1fdSPoul-Henning Kamp 		if (up->len == 1) {
383f6bde1fdSPoul-Henning Kamp 			up->ptr = NULL;
384f6bde1fdSPoul-Henning Kamp 			uh->busy--;
385f6bde1fdSPoul-Henning Kamp 			collapse_unr(uh, up);
386f6bde1fdSPoul-Henning Kamp 			check_unrhdr(uh, __LINE__);
387f6bde1fdSPoul-Henning Kamp 			return;
388f6bde1fdSPoul-Henning Kamp 		}
389f6bde1fdSPoul-Henning Kamp 
390f6bde1fdSPoul-Henning Kamp 		/* Check if we can shift the item to the previous run */
391f6bde1fdSPoul-Henning Kamp 		upp = TAILQ_PREV(up, unrhd, list);
392f6bde1fdSPoul-Henning Kamp 		if (item == x && upp != NULL && upp->ptr == NULL) {
393f6bde1fdSPoul-Henning Kamp 			upp->len++;
394f6bde1fdSPoul-Henning Kamp 			up->len--;
395f6bde1fdSPoul-Henning Kamp 			uh->busy--;
396f6bde1fdSPoul-Henning Kamp 			check_unrhdr(uh, __LINE__);
397f6bde1fdSPoul-Henning Kamp 			return;
398f6bde1fdSPoul-Henning Kamp 		}
399f6bde1fdSPoul-Henning Kamp 
400f6bde1fdSPoul-Henning Kamp 		/* Check if we can shift the item to the next run */
401f6bde1fdSPoul-Henning Kamp 		upn = TAILQ_NEXT(up, list);
402f6bde1fdSPoul-Henning Kamp 		if (item == x + up->len - 1 &&
403f6bde1fdSPoul-Henning Kamp 		    upn != NULL && upn->ptr == NULL) {
404f6bde1fdSPoul-Henning Kamp 			upn->len++;
405f6bde1fdSPoul-Henning Kamp 			up->len--;
406f6bde1fdSPoul-Henning Kamp 			uh->busy--;
407f6bde1fdSPoul-Henning Kamp 			check_unrhdr(uh, __LINE__);
408f6bde1fdSPoul-Henning Kamp 			return;
409f6bde1fdSPoul-Henning Kamp 		}
410f6bde1fdSPoul-Henning Kamp 
411f6bde1fdSPoul-Henning Kamp 		/* Split off the tail end, if any. */
412f6bde1fdSPoul-Henning Kamp 		pl = up->len - (1 + (item - x));
413f6bde1fdSPoul-Henning Kamp 		if (pl > 0) {
414f6bde1fdSPoul-Henning Kamp 			upp = new_unr(uh);
415f6bde1fdSPoul-Henning Kamp 			upp->ptr = uh;
416f6bde1fdSPoul-Henning Kamp 			upp->len = pl;
417f6bde1fdSPoul-Henning Kamp 			TAILQ_INSERT_AFTER(&uh->head, up, upp, list);
418f6bde1fdSPoul-Henning Kamp 		}
419f6bde1fdSPoul-Henning Kamp 
420f6bde1fdSPoul-Henning Kamp 		if (item == x) {
421f6bde1fdSPoul-Henning Kamp 			/* We are done splitting */
422f6bde1fdSPoul-Henning Kamp 			up->len = 1;
423f6bde1fdSPoul-Henning Kamp 			up->ptr = NULL;
424f6bde1fdSPoul-Henning Kamp 		} else {
425f6bde1fdSPoul-Henning Kamp 			/* The freed item */
426f6bde1fdSPoul-Henning Kamp 			upp = new_unr(uh);
427f6bde1fdSPoul-Henning Kamp 			upp->len = 1;
428f6bde1fdSPoul-Henning Kamp 			upp->ptr = NULL;
429f6bde1fdSPoul-Henning Kamp 			TAILQ_INSERT_AFTER(&uh->head, up, upp, list);
430f6bde1fdSPoul-Henning Kamp 			/* Adjust current unr */
431f6bde1fdSPoul-Henning Kamp 			up->len = item - x;
432f6bde1fdSPoul-Henning Kamp 		}
433f6bde1fdSPoul-Henning Kamp 
434f6bde1fdSPoul-Henning Kamp 		uh->busy--;
435f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
436f6bde1fdSPoul-Henning Kamp 
437f6bde1fdSPoul-Henning Kamp 		/* Our ul marker element may have shifted one later */
438f6bde1fdSPoul-Henning Kamp 		if (ul->len + xl <= l) {
439f6bde1fdSPoul-Henning Kamp 			xl += ul->len;
440f6bde1fdSPoul-Henning Kamp 			ul = TAILQ_NEXT(ul, list);
441f6bde1fdSPoul-Henning Kamp 		}
442f6bde1fdSPoul-Henning Kamp 		KASSERT(ul != NULL, ("UNR lost bitmap pointer"));
443f6bde1fdSPoul-Henning Kamp 
444f6bde1fdSPoul-Henning Kamp 		/* Count unrs entirely inside potential bitmap */
445f6bde1fdSPoul-Henning Kamp 		n = 0;
446f6bde1fdSPoul-Henning Kamp 		pl = xl;
447f6bde1fdSPoul-Henning Kamp 		item = l + NBITS;
448f6bde1fdSPoul-Henning Kamp 		for (up = ul;
449f6bde1fdSPoul-Henning Kamp 		     up != NULL && pl + up->len <= item;
450f6bde1fdSPoul-Henning Kamp 		     up = TAILQ_NEXT(up, list)) {
451f6bde1fdSPoul-Henning Kamp 			if (pl >= l)
452f6bde1fdSPoul-Henning Kamp 				n++;
453f6bde1fdSPoul-Henning Kamp 			pl += up->len;
454f6bde1fdSPoul-Henning Kamp 		}
455f6bde1fdSPoul-Henning Kamp 
456f6bde1fdSPoul-Henning Kamp 		/* If less than three, a bitmap does not pay off */
457f6bde1fdSPoul-Henning Kamp 		if (n < 3)
458f6bde1fdSPoul-Henning Kamp 			return;
459f6bde1fdSPoul-Henning Kamp 
460f6bde1fdSPoul-Henning Kamp 		/* Allocate bitmap */
461f6bde1fdSPoul-Henning Kamp 		upp = new_unr(uh);
462f6bde1fdSPoul-Henning Kamp 		upp->ptr = new_unr(uh);
463f6bde1fdSPoul-Henning Kamp 
464f6bde1fdSPoul-Henning Kamp 		/* Insert bitmap after ul element */
465f6bde1fdSPoul-Henning Kamp 		TAILQ_INSERT_AFTER(&uh->head, ul, upp, list);
466f6bde1fdSPoul-Henning Kamp 
467f6bde1fdSPoul-Henning Kamp 		/* Slice off the tail from the ul element */
468f6bde1fdSPoul-Henning Kamp 		pl = ul->len - (l - xl);
469f6bde1fdSPoul-Henning Kamp 		if (ul->ptr != NULL) {
470f6bde1fdSPoul-Henning Kamp 			bit_nset(upp->ptr, 0, pl - 1);
471f6bde1fdSPoul-Henning Kamp 			upp->len = pl;
472f6bde1fdSPoul-Henning Kamp 		}
473f6bde1fdSPoul-Henning Kamp 		ul->len -= pl;
474f6bde1fdSPoul-Henning Kamp 
475f6bde1fdSPoul-Henning Kamp 		/* Ditch ul if it got reduced to zero size */
476f6bde1fdSPoul-Henning Kamp 		if (ul->len == 0) {
477f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, ul, list);
478f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, ul);
479f6bde1fdSPoul-Henning Kamp 		}
480f6bde1fdSPoul-Henning Kamp 
481f6bde1fdSPoul-Henning Kamp 		/* Soak up run length unrs until we have absorbed NBITS */
482f6bde1fdSPoul-Henning Kamp 		while (pl != NBITS) {
483f6bde1fdSPoul-Henning Kamp 
484f6bde1fdSPoul-Henning Kamp 			/* Grab first one in line */
485f6bde1fdSPoul-Henning Kamp 			upn = TAILQ_NEXT(upp, list);
486f6bde1fdSPoul-Henning Kamp 
487f6bde1fdSPoul-Henning Kamp 			/* We may not have a multiple of NBITS totally */
488f6bde1fdSPoul-Henning Kamp 			if (upn == NULL)
489f6bde1fdSPoul-Henning Kamp 				break;
490f6bde1fdSPoul-Henning Kamp 
491f6bde1fdSPoul-Henning Kamp 			/* Run may extend past our new bitmap */
492f6bde1fdSPoul-Henning Kamp 			n = NBITS - pl;
493f6bde1fdSPoul-Henning Kamp 			if (n > upn->len)
494f6bde1fdSPoul-Henning Kamp 				n = upn->len;
495f6bde1fdSPoul-Henning Kamp 
496f6bde1fdSPoul-Henning Kamp 			if (upn->ptr != NULL) {
497f6bde1fdSPoul-Henning Kamp 				bit_nset(upp->ptr, pl, pl + n - 1);
498f6bde1fdSPoul-Henning Kamp 				upp->len += n;
499f6bde1fdSPoul-Henning Kamp 			}
500f6bde1fdSPoul-Henning Kamp 			pl += n;
501f6bde1fdSPoul-Henning Kamp 
502f6bde1fdSPoul-Henning Kamp 			if (n != upn->len) {
503f6bde1fdSPoul-Henning Kamp 				/* We did not absorb the entire run */
504f6bde1fdSPoul-Henning Kamp 				upn->len -= n;
505f6bde1fdSPoul-Henning Kamp 				break;
506f6bde1fdSPoul-Henning Kamp 			}
507f6bde1fdSPoul-Henning Kamp 			TAILQ_REMOVE(&uh->head, upn, list);
508f6bde1fdSPoul-Henning Kamp 			delete_unr(uh, upn);
509f6bde1fdSPoul-Henning Kamp 		}
510f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
511f6bde1fdSPoul-Henning Kamp 		return;
512f6bde1fdSPoul-Henning Kamp 	}
513f6bde1fdSPoul-Henning Kamp 	KASSERT(0 != 1, ("UNR: Fell off the end in free_unr()"));
514f6bde1fdSPoul-Henning Kamp }
515f6bde1fdSPoul-Henning Kamp 
516f6bde1fdSPoul-Henning Kamp #ifndef _KERNEL	/* USERLAND test driver */
517f6bde1fdSPoul-Henning Kamp 
518f6bde1fdSPoul-Henning Kamp /*
519f6bde1fdSPoul-Henning Kamp  * Simple stochastic test driver for the above functions
520f6bde1fdSPoul-Henning Kamp  */
521f6bde1fdSPoul-Henning Kamp 
522f6bde1fdSPoul-Henning Kamp static void
523f6bde1fdSPoul-Henning Kamp print_unr(struct unrhdr *uh, struct unr *up)
524f6bde1fdSPoul-Henning Kamp {
525f6bde1fdSPoul-Henning Kamp 	u_int x;
526f6bde1fdSPoul-Henning Kamp 
527f6bde1fdSPoul-Henning Kamp 	printf("  %p len = %5u ", up, up->len);
528f6bde1fdSPoul-Henning Kamp 	if (up->ptr == NULL)
529f6bde1fdSPoul-Henning Kamp 		printf("free\n");
530f6bde1fdSPoul-Henning Kamp 	else if (up->ptr == uh)
531f6bde1fdSPoul-Henning Kamp 		printf("alloc\n");
532f6bde1fdSPoul-Henning Kamp 	else {
533f6bde1fdSPoul-Henning Kamp 		printf(" [");
534f6bde1fdSPoul-Henning Kamp 		for (x = 0; x < NBITS; x++) {
535f6bde1fdSPoul-Henning Kamp 			if (bit_test((bitstr_t *)up->ptr, x))
536f6bde1fdSPoul-Henning Kamp 				putchar('#');
537f6bde1fdSPoul-Henning Kamp 			else
538f6bde1fdSPoul-Henning Kamp 				putchar(' ');
539f6bde1fdSPoul-Henning Kamp 		}
540f6bde1fdSPoul-Henning Kamp 		printf("]\n");
541f6bde1fdSPoul-Henning Kamp 	}
542f6bde1fdSPoul-Henning Kamp }
543f6bde1fdSPoul-Henning Kamp 
544f6bde1fdSPoul-Henning Kamp static void
545f6bde1fdSPoul-Henning Kamp print_unrhdr(struct unrhdr *uh)
546f6bde1fdSPoul-Henning Kamp {
547f6bde1fdSPoul-Henning Kamp 	struct unr *up;
548f6bde1fdSPoul-Henning Kamp 	u_int x;
549f6bde1fdSPoul-Henning Kamp 
550f6bde1fdSPoul-Henning Kamp 	printf("%p low = %u high = %u busy %u\n",
551f6bde1fdSPoul-Henning Kamp 	    uh, uh->low, uh->high, uh->busy);
552f6bde1fdSPoul-Henning Kamp 	x = uh->low;
553f6bde1fdSPoul-Henning Kamp 	TAILQ_FOREACH(up, &uh->head, list) {
554f6bde1fdSPoul-Henning Kamp 		printf("  from = %5u", x);
555f6bde1fdSPoul-Henning Kamp 		print_unr(uh, up);
556f6bde1fdSPoul-Henning Kamp 		if (up->ptr == NULL || up->ptr == uh)
557f6bde1fdSPoul-Henning Kamp 			x += up->len;
558f6bde1fdSPoul-Henning Kamp 		else
559f6bde1fdSPoul-Henning Kamp 			x += NBITS;
560f6bde1fdSPoul-Henning Kamp 	}
561f6bde1fdSPoul-Henning Kamp }
562f6bde1fdSPoul-Henning Kamp 
563f6bde1fdSPoul-Henning Kamp /* Number of unrs to test */
564f6bde1fdSPoul-Henning Kamp #define NN	10000
565f6bde1fdSPoul-Henning Kamp 
566f6bde1fdSPoul-Henning Kamp int
567f6bde1fdSPoul-Henning Kamp main(int argc __unused, const char **argv __unused)
568f6bde1fdSPoul-Henning Kamp {
569f6bde1fdSPoul-Henning Kamp 	struct unrhdr *uh;
570f6bde1fdSPoul-Henning Kamp 	int i, x;
571f6bde1fdSPoul-Henning Kamp 	char a[NN];
572f6bde1fdSPoul-Henning Kamp 
573f6bde1fdSPoul-Henning Kamp 	uh = new_unrhdr(0, NN - 1);
574f6bde1fdSPoul-Henning Kamp 
575f6bde1fdSPoul-Henning Kamp 	memset(a, 0, sizeof a);
576f6bde1fdSPoul-Henning Kamp 
577f6bde1fdSPoul-Henning Kamp 	fprintf(stderr, "sizeof(struct unr) %d\n", sizeof (struct unr));
578f6bde1fdSPoul-Henning Kamp 	fprintf(stderr, "sizeof(struct unrhdr) %d\n", sizeof (struct unrhdr));
579f6bde1fdSPoul-Henning Kamp 	x = 1;
580f6bde1fdSPoul-Henning Kamp 	for (;;) {
581f6bde1fdSPoul-Henning Kamp 		i = random() % NN;
582f6bde1fdSPoul-Henning Kamp 		if (a[i]) {
583f6bde1fdSPoul-Henning Kamp 			printf("F %u\n", i);
584f6bde1fdSPoul-Henning Kamp 			free_unr(uh, i);
585f6bde1fdSPoul-Henning Kamp 			a[i] = 0;
586f6bde1fdSPoul-Henning Kamp 		} else {
587f6bde1fdSPoul-Henning Kamp 			i = alloc_unr(uh);
588f6bde1fdSPoul-Henning Kamp 			a[i] = 1;
589f6bde1fdSPoul-Henning Kamp 			printf("A %u\n", i);
590f6bde1fdSPoul-Henning Kamp 		}
591f6bde1fdSPoul-Henning Kamp 		if (1)	/* XXX: change this for detailed debug printout */
592f6bde1fdSPoul-Henning Kamp 			print_unrhdr(uh);
593f6bde1fdSPoul-Henning Kamp 		check_unrhdr(uh, __LINE__);
594f6bde1fdSPoul-Henning Kamp 	}
595f6bde1fdSPoul-Henning Kamp 	return (0);
596f6bde1fdSPoul-Henning Kamp }
597f6bde1fdSPoul-Henning Kamp #endif
598