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 *
_Malloc(size_t foo,int line)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
mtx_lock(struct mtx * mp)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
mtx_unlock(struct mtx * mp)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
mtx_assert(struct mtx * mp,int flag)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
is_bitmap(struct unrhdr * uh,struct unr * up)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
ub_empty(struct unrb * ub,int len)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
ub_full(struct unrb * ub,int len)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 *
create_iter_unr(struct unrhdr * uh)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
next_iter_unrl(struct unrhdr * uh,struct unrhdr_iter * iter)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
next_iter_unr(void * handle)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
free_iter_unr(void * handle)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)
343*04f683b2SKonstantin Belousov #ifndef __diagused
344*04f683b2SKonstantin Belousov #define __diagused
345*04f683b2SKonstantin Belousov #endif
346*04f683b2SKonstantin Belousov
347f6bde1fdSPoul-Henning Kamp /*
348f6bde1fdSPoul-Henning Kamp * Consistency check function.
349f6bde1fdSPoul-Henning Kamp *
350f6bde1fdSPoul-Henning Kamp * Checks the internal consistency as well as we can.
351f6bde1fdSPoul-Henning Kamp *
352f6bde1fdSPoul-Henning Kamp * Called at all boundaries of this API.
353f6bde1fdSPoul-Henning Kamp */
354f6bde1fdSPoul-Henning Kamp static void
check_unrhdr(struct unrhdr * uh,int line)355f6bde1fdSPoul-Henning Kamp check_unrhdr(struct unrhdr *uh, int line)
356f6bde1fdSPoul-Henning Kamp {
357f6bde1fdSPoul-Henning Kamp struct unr *up;
358d9a54d5cSPoul-Henning Kamp struct unrb *ub;
3591b82e02fSAlan Somers int w;
3601384a0b9SKonstantin Belousov u_int y __diagused, z __diagused;
361f6bde1fdSPoul-Henning Kamp
362d9a54d5cSPoul-Henning Kamp y = uh->first;
363f6bde1fdSPoul-Henning Kamp z = 0;
364f6bde1fdSPoul-Henning Kamp TAILQ_FOREACH(up, &uh->head, list) {
365f6bde1fdSPoul-Henning Kamp z++;
36636b1f8a8SKonstantin Belousov if (is_bitmap(uh, up)) {
367d9a54d5cSPoul-Henning Kamp ub = up->ptr;
368d9a54d5cSPoul-Henning Kamp KASSERT (up->len <= NBITS,
3698907f744SAlan Somers ("UNR inconsistency: len %u max %zd (line %d)\n",
370d9a54d5cSPoul-Henning Kamp up->len, NBITS, line));
371f6bde1fdSPoul-Henning Kamp z++;
372f6bde1fdSPoul-Henning Kamp w = 0;
3731b82e02fSAlan Somers bit_count(ub->map, 0, up->len, &w);
374d9a54d5cSPoul-Henning Kamp y += w;
375d9a54d5cSPoul-Henning Kamp } else if (up->ptr != NULL)
376f6bde1fdSPoul-Henning Kamp y += up->len;
377f6bde1fdSPoul-Henning Kamp }
378f6bde1fdSPoul-Henning Kamp KASSERT (y == uh->busy,
379f6bde1fdSPoul-Henning Kamp ("UNR inconsistency: items %u found %u (line %d)\n",
380f6bde1fdSPoul-Henning Kamp uh->busy, y, line));
381f6bde1fdSPoul-Henning Kamp KASSERT (z == uh->alloc,
382f6bde1fdSPoul-Henning Kamp ("UNR inconsistency: chunks %u found %u (line %d)\n",
383f6bde1fdSPoul-Henning Kamp uh->alloc, z, line));
384f6bde1fdSPoul-Henning Kamp }
385f6bde1fdSPoul-Henning Kamp
386f6bde1fdSPoul-Henning Kamp #else
387f6bde1fdSPoul-Henning Kamp
388f6bde1fdSPoul-Henning Kamp static __inline void
check_unrhdr(struct unrhdr * uh __unused,int line __unused)3898907f744SAlan Somers check_unrhdr(struct unrhdr *uh __unused, int line __unused)
390f6bde1fdSPoul-Henning Kamp {
391f6bde1fdSPoul-Henning Kamp
392f6bde1fdSPoul-Henning Kamp }
393f6bde1fdSPoul-Henning Kamp
394f6bde1fdSPoul-Henning Kamp #endif
395f6bde1fdSPoul-Henning Kamp
396f6bde1fdSPoul-Henning Kamp /*
397f6bde1fdSPoul-Henning Kamp * Userland memory management. Just use calloc and keep track of how
398f6bde1fdSPoul-Henning Kamp * many elements we have allocated for check_unrhdr().
399f6bde1fdSPoul-Henning Kamp */
400f6bde1fdSPoul-Henning Kamp
401f6bde1fdSPoul-Henning Kamp static __inline void *
new_unr(struct unrhdr * uh,void ** p1,void ** p2)402d9a54d5cSPoul-Henning Kamp new_unr(struct unrhdr *uh, void **p1, void **p2)
403f6bde1fdSPoul-Henning Kamp {
404d9a54d5cSPoul-Henning Kamp void *p;
405f6bde1fdSPoul-Henning Kamp
406d9a54d5cSPoul-Henning Kamp uh->alloc++;
407d9a54d5cSPoul-Henning Kamp KASSERT(*p1 != NULL || *p2 != NULL, ("Out of cached memory"));
408d9a54d5cSPoul-Henning Kamp if (*p1 != NULL) {
409d9a54d5cSPoul-Henning Kamp p = *p1;
410d9a54d5cSPoul-Henning Kamp *p1 = NULL;
411d9a54d5cSPoul-Henning Kamp return (p);
412d9a54d5cSPoul-Henning Kamp } else {
413d9a54d5cSPoul-Henning Kamp p = *p2;
414d9a54d5cSPoul-Henning Kamp *p2 = NULL;
415d9a54d5cSPoul-Henning Kamp return (p);
416d9a54d5cSPoul-Henning Kamp }
417f6bde1fdSPoul-Henning Kamp }
418f6bde1fdSPoul-Henning Kamp
419f6bde1fdSPoul-Henning Kamp static __inline void
delete_unr(struct unrhdr * uh,void * ptr)420f6bde1fdSPoul-Henning Kamp delete_unr(struct unrhdr *uh, void *ptr)
421f6bde1fdSPoul-Henning Kamp {
42209828ba9SKonstantin Belousov struct unr *up;
423d9a54d5cSPoul-Henning Kamp
424f6bde1fdSPoul-Henning Kamp uh->alloc--;
42509828ba9SKonstantin Belousov up = ptr;
42609828ba9SKonstantin Belousov TAILQ_INSERT_TAIL(&uh->ppfree, up, list);
42709828ba9SKonstantin Belousov }
42809828ba9SKonstantin Belousov
42909828ba9SKonstantin Belousov void
clean_unrhdrl(struct unrhdr * uh)43009828ba9SKonstantin Belousov clean_unrhdrl(struct unrhdr *uh)
43109828ba9SKonstantin Belousov {
43209828ba9SKonstantin Belousov struct unr *up;
43309828ba9SKonstantin Belousov
434e59b940dSKonstantin Belousov if (uh->mtx != NULL)
43509828ba9SKonstantin Belousov mtx_assert(uh->mtx, MA_OWNED);
43609828ba9SKonstantin Belousov while ((up = TAILQ_FIRST(&uh->ppfree)) != NULL) {
43709828ba9SKonstantin Belousov TAILQ_REMOVE(&uh->ppfree, up, list);
438e59b940dSKonstantin Belousov if (uh->mtx != NULL)
43909828ba9SKonstantin Belousov mtx_unlock(uh->mtx);
44009828ba9SKonstantin Belousov Free(up);
441e59b940dSKonstantin Belousov if (uh->mtx != NULL)
44209828ba9SKonstantin Belousov mtx_lock(uh->mtx);
44309828ba9SKonstantin Belousov }
44409828ba9SKonstantin Belousov
44509828ba9SKonstantin Belousov }
44609828ba9SKonstantin Belousov
44709828ba9SKonstantin Belousov void
clean_unrhdr(struct unrhdr * uh)44809828ba9SKonstantin Belousov clean_unrhdr(struct unrhdr *uh)
44909828ba9SKonstantin Belousov {
45009828ba9SKonstantin Belousov
451e59b940dSKonstantin Belousov if (uh->mtx != NULL)
45209828ba9SKonstantin Belousov mtx_lock(uh->mtx);
45309828ba9SKonstantin Belousov clean_unrhdrl(uh);
454e59b940dSKonstantin Belousov if (uh->mtx != NULL)
45509828ba9SKonstantin Belousov mtx_unlock(uh->mtx);
456f6bde1fdSPoul-Henning Kamp }
457f6bde1fdSPoul-Henning Kamp
458dc872d46SKonstantin Belousov void
init_unrhdr(struct unrhdr * uh,int low,int high,struct mtx * mutex)459dc872d46SKonstantin Belousov init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex)
460f6bde1fdSPoul-Henning Kamp {
461f6bde1fdSPoul-Henning Kamp
462831aa555SJaakko Heinonen KASSERT(low >= 0 && low <= high,
463501812f2SJaakko Heinonen ("UNR: use error: new_unrhdr(%d, %d)", low, high));
464e59b940dSKonstantin Belousov if (mutex == UNR_NO_MTX)
465e59b940dSKonstantin Belousov uh->mtx = NULL;
466e59b940dSKonstantin Belousov else if (mutex != NULL)
467d9a54d5cSPoul-Henning Kamp uh->mtx = mutex;
468d9a54d5cSPoul-Henning Kamp else
469d9a54d5cSPoul-Henning Kamp uh->mtx = &unitmtx;
470f6bde1fdSPoul-Henning Kamp TAILQ_INIT(&uh->head);
47109828ba9SKonstantin Belousov TAILQ_INIT(&uh->ppfree);
472f6bde1fdSPoul-Henning Kamp uh->low = low;
473f6bde1fdSPoul-Henning Kamp uh->high = high;
474d9a54d5cSPoul-Henning Kamp uh->first = 0;
475d9a54d5cSPoul-Henning Kamp uh->last = 1 + (high - low);
476c4be460eSKonstantin Belousov uh->busy = 0;
477c4be460eSKonstantin Belousov uh->alloc = 0;
478f6bde1fdSPoul-Henning Kamp check_unrhdr(uh, __LINE__);
479dc872d46SKonstantin Belousov }
480dc872d46SKonstantin Belousov
481dc872d46SKonstantin Belousov /*
482dc872d46SKonstantin Belousov * Allocate a new unrheader set.
483dc872d46SKonstantin Belousov *
484dc872d46SKonstantin Belousov * Highest and lowest valid values given as parameters.
485dc872d46SKonstantin Belousov */
486dc872d46SKonstantin Belousov
487dc872d46SKonstantin Belousov struct unrhdr *
new_unrhdr(int low,int high,struct mtx * mutex)488dc872d46SKonstantin Belousov new_unrhdr(int low, int high, struct mtx *mutex)
489dc872d46SKonstantin Belousov {
490dc872d46SKonstantin Belousov struct unrhdr *uh;
491dc872d46SKonstantin Belousov
492dc872d46SKonstantin Belousov uh = Malloc(sizeof *uh);
493dc872d46SKonstantin Belousov init_unrhdr(uh, low, high, mutex);
494f6bde1fdSPoul-Henning Kamp return (uh);
495f6bde1fdSPoul-Henning Kamp }
496f6bde1fdSPoul-Henning Kamp
497e4fea39eSPoul-Henning Kamp void
delete_unrhdr(struct unrhdr * uh)498e4fea39eSPoul-Henning Kamp delete_unrhdr(struct unrhdr *uh)
499e4fea39eSPoul-Henning Kamp {
500e4fea39eSPoul-Henning Kamp
501d9a54d5cSPoul-Henning Kamp check_unrhdr(uh, __LINE__);
502e4fea39eSPoul-Henning Kamp KASSERT(uh->busy == 0, ("unrhdr has %u allocations", uh->busy));
503e4fea39eSPoul-Henning Kamp KASSERT(uh->alloc == 0, ("UNR memory leak in delete_unrhdr"));
50409828ba9SKonstantin Belousov KASSERT(TAILQ_FIRST(&uh->ppfree) == NULL,
50509828ba9SKonstantin Belousov ("unrhdr has postponed item for free"));
506e4fea39eSPoul-Henning Kamp Free(uh);
507e4fea39eSPoul-Henning Kamp }
508e4fea39eSPoul-Henning Kamp
509333dcaa4SMatt Joras void
clear_unrhdr(struct unrhdr * uh)510333dcaa4SMatt Joras clear_unrhdr(struct unrhdr *uh)
511333dcaa4SMatt Joras {
512333dcaa4SMatt Joras struct unr *up, *uq;
513333dcaa4SMatt Joras
514333dcaa4SMatt Joras KASSERT(TAILQ_EMPTY(&uh->ppfree),
515333dcaa4SMatt Joras ("unrhdr has postponed item for free"));
5160d8e0405SMatt Joras TAILQ_FOREACH_SAFE(up, &uh->head, list, uq) {
517333dcaa4SMatt Joras if (up->ptr != uh) {
518333dcaa4SMatt Joras Free(up->ptr);
519333dcaa4SMatt Joras }
520333dcaa4SMatt Joras Free(up);
521333dcaa4SMatt Joras }
522333dcaa4SMatt Joras uh->busy = 0;
523333dcaa4SMatt Joras uh->alloc = 0;
5240d8e0405SMatt Joras init_unrhdr(uh, uh->low, uh->high, uh->mtx);
5250d8e0405SMatt Joras
5260d8e0405SMatt Joras check_unrhdr(uh, __LINE__);
527333dcaa4SMatt Joras }
528333dcaa4SMatt Joras
529f6bde1fdSPoul-Henning Kamp /*
530d9a54d5cSPoul-Henning Kamp * Look for sequence of items which can be combined into a bitmap, if
531d9a54d5cSPoul-Henning Kamp * multiple are present, take the one which saves most memory.
532d9a54d5cSPoul-Henning Kamp *
533d9a54d5cSPoul-Henning Kamp * Return (1) if a sequence was found to indicate that another call
534d9a54d5cSPoul-Henning Kamp * might be able to do more. Return (0) if we found no suitable sequence.
535d9a54d5cSPoul-Henning Kamp *
536d9a54d5cSPoul-Henning Kamp * NB: called from alloc_unr(), no new memory allocation allowed.
537d9a54d5cSPoul-Henning Kamp */
538d9a54d5cSPoul-Henning Kamp static int
optimize_unr(struct unrhdr * uh)539d9a54d5cSPoul-Henning Kamp optimize_unr(struct unrhdr *uh)
540d9a54d5cSPoul-Henning Kamp {
541d9a54d5cSPoul-Henning Kamp struct unr *up, *uf, *us;
542d9a54d5cSPoul-Henning Kamp struct unrb *ub, *ubf;
543d9a54d5cSPoul-Henning Kamp u_int a, l, ba;
544d9a54d5cSPoul-Henning Kamp
545d9a54d5cSPoul-Henning Kamp /*
546d9a54d5cSPoul-Henning Kamp * Look for the run of items (if any) which when collapsed into
547d9a54d5cSPoul-Henning Kamp * a bitmap would save most memory.
548d9a54d5cSPoul-Henning Kamp */
549d9a54d5cSPoul-Henning Kamp us = NULL;
550d9a54d5cSPoul-Henning Kamp ba = 0;
551d9a54d5cSPoul-Henning Kamp TAILQ_FOREACH(uf, &uh->head, list) {
552d9a54d5cSPoul-Henning Kamp if (uf->len >= NBITS)
553d9a54d5cSPoul-Henning Kamp continue;
554d9a54d5cSPoul-Henning Kamp a = 1;
555d9a54d5cSPoul-Henning Kamp if (is_bitmap(uh, uf))
556d9a54d5cSPoul-Henning Kamp a++;
557d9a54d5cSPoul-Henning Kamp l = uf->len;
558d9a54d5cSPoul-Henning Kamp up = uf;
559d9a54d5cSPoul-Henning Kamp while (1) {
560d9a54d5cSPoul-Henning Kamp up = TAILQ_NEXT(up, list);
561d9a54d5cSPoul-Henning Kamp if (up == NULL)
562d9a54d5cSPoul-Henning Kamp break;
563d9a54d5cSPoul-Henning Kamp if ((up->len + l) > NBITS)
564d9a54d5cSPoul-Henning Kamp break;
565d9a54d5cSPoul-Henning Kamp a++;
566d9a54d5cSPoul-Henning Kamp if (is_bitmap(uh, up))
567d9a54d5cSPoul-Henning Kamp a++;
568d9a54d5cSPoul-Henning Kamp l += up->len;
569d9a54d5cSPoul-Henning Kamp }
570d9a54d5cSPoul-Henning Kamp if (a > ba) {
571d9a54d5cSPoul-Henning Kamp ba = a;
572d9a54d5cSPoul-Henning Kamp us = uf;
573d9a54d5cSPoul-Henning Kamp }
574d9a54d5cSPoul-Henning Kamp }
575d9a54d5cSPoul-Henning Kamp if (ba < 3)
576d9a54d5cSPoul-Henning Kamp return (0);
577d9a54d5cSPoul-Henning Kamp
578d9a54d5cSPoul-Henning Kamp /*
579d9a54d5cSPoul-Henning Kamp * If the first element is not a bitmap, make it one.
580d9a54d5cSPoul-Henning Kamp * Trying to do so without allocating more memory complicates things
581d9a54d5cSPoul-Henning Kamp * a bit
582d9a54d5cSPoul-Henning Kamp */
583d9a54d5cSPoul-Henning Kamp if (!is_bitmap(uh, us)) {
584d9a54d5cSPoul-Henning Kamp uf = TAILQ_NEXT(us, list);
585d9a54d5cSPoul-Henning Kamp TAILQ_REMOVE(&uh->head, us, list);
586d9a54d5cSPoul-Henning Kamp a = us->len;
587d9a54d5cSPoul-Henning Kamp l = us->ptr == uh ? 1 : 0;
588d9a54d5cSPoul-Henning Kamp ub = (void *)us;
5898907f744SAlan Somers bit_nclear(ub->map, 0, NBITS - 1);
5908907f744SAlan Somers if (l)
591d9a54d5cSPoul-Henning Kamp bit_nset(ub->map, 0, a);
592d9a54d5cSPoul-Henning Kamp if (!is_bitmap(uh, uf)) {
5938907f744SAlan Somers if (uf->ptr == NULL)
594d9a54d5cSPoul-Henning Kamp bit_nclear(ub->map, a, a + uf->len - 1);
5958907f744SAlan Somers else
596d9a54d5cSPoul-Henning Kamp bit_nset(ub->map, a, a + uf->len - 1);
597d9a54d5cSPoul-Henning Kamp uf->ptr = ub;
598d9a54d5cSPoul-Henning Kamp uf->len += a;
599d9a54d5cSPoul-Henning Kamp us = uf;
600d9a54d5cSPoul-Henning Kamp } else {
601d9a54d5cSPoul-Henning Kamp ubf = uf->ptr;
602d9a54d5cSPoul-Henning Kamp for (l = 0; l < uf->len; l++, a++) {
6038907f744SAlan Somers if (bit_test(ubf->map, l))
604d9a54d5cSPoul-Henning Kamp bit_set(ub->map, a);
6058907f744SAlan Somers else
606d9a54d5cSPoul-Henning Kamp bit_clear(ub->map, a);
607d9a54d5cSPoul-Henning Kamp }
608d9a54d5cSPoul-Henning Kamp uf->len = a;
609d9a54d5cSPoul-Henning Kamp delete_unr(uh, uf->ptr);
610d9a54d5cSPoul-Henning Kamp uf->ptr = ub;
611d9a54d5cSPoul-Henning Kamp us = uf;
612d9a54d5cSPoul-Henning Kamp }
613d9a54d5cSPoul-Henning Kamp }
614d9a54d5cSPoul-Henning Kamp ub = us->ptr;
615d9a54d5cSPoul-Henning Kamp while (1) {
616d9a54d5cSPoul-Henning Kamp uf = TAILQ_NEXT(us, list);
617d9a54d5cSPoul-Henning Kamp if (uf == NULL)
618d9a54d5cSPoul-Henning Kamp return (1);
619d9a54d5cSPoul-Henning Kamp if (uf->len + us->len > NBITS)
620d9a54d5cSPoul-Henning Kamp return (1);
621d9a54d5cSPoul-Henning Kamp if (uf->ptr == NULL) {
622d9a54d5cSPoul-Henning Kamp bit_nclear(ub->map, us->len, us->len + uf->len - 1);
623d9a54d5cSPoul-Henning Kamp us->len += uf->len;
624d9a54d5cSPoul-Henning Kamp TAILQ_REMOVE(&uh->head, uf, list);
625d9a54d5cSPoul-Henning Kamp delete_unr(uh, uf);
626d9a54d5cSPoul-Henning Kamp } else if (uf->ptr == uh) {
627d9a54d5cSPoul-Henning Kamp bit_nset(ub->map, us->len, us->len + uf->len - 1);
628d9a54d5cSPoul-Henning Kamp us->len += uf->len;
629d9a54d5cSPoul-Henning Kamp TAILQ_REMOVE(&uh->head, uf, list);
630d9a54d5cSPoul-Henning Kamp delete_unr(uh, uf);
631d9a54d5cSPoul-Henning Kamp } else {
632d9a54d5cSPoul-Henning Kamp ubf = uf->ptr;
633d9a54d5cSPoul-Henning Kamp for (l = 0; l < uf->len; l++, us->len++) {
6348907f744SAlan Somers if (bit_test(ubf->map, l))
635d9a54d5cSPoul-Henning Kamp bit_set(ub->map, us->len);
6368907f744SAlan Somers else
637d9a54d5cSPoul-Henning Kamp bit_clear(ub->map, us->len);
638d9a54d5cSPoul-Henning Kamp }
639d9a54d5cSPoul-Henning Kamp TAILQ_REMOVE(&uh->head, uf, list);
640d9a54d5cSPoul-Henning Kamp delete_unr(uh, ubf);
641d9a54d5cSPoul-Henning Kamp delete_unr(uh, uf);
642d9a54d5cSPoul-Henning Kamp }
643d9a54d5cSPoul-Henning Kamp }
644d9a54d5cSPoul-Henning Kamp }
645d9a54d5cSPoul-Henning Kamp
646d9a54d5cSPoul-Henning Kamp /*
647d9a54d5cSPoul-Henning Kamp * See if a given unr should be collapsed with a neighbor.
648d9a54d5cSPoul-Henning Kamp *
649d9a54d5cSPoul-Henning Kamp * NB: called from alloc_unr(), no new memory allocation allowed.
650f6bde1fdSPoul-Henning Kamp */
651f6bde1fdSPoul-Henning Kamp static void
collapse_unr(struct unrhdr * uh,struct unr * up)652f6bde1fdSPoul-Henning Kamp collapse_unr(struct unrhdr *uh, struct unr *up)
653f6bde1fdSPoul-Henning Kamp {
654f6bde1fdSPoul-Henning Kamp struct unr *upp;
655d9a54d5cSPoul-Henning Kamp struct unrb *ub;
656f6bde1fdSPoul-Henning Kamp
657d9a54d5cSPoul-Henning Kamp /* If bitmap is all set or clear, change it to runlength */
658d9a54d5cSPoul-Henning Kamp if (is_bitmap(uh, up)) {
659d9a54d5cSPoul-Henning Kamp ub = up->ptr;
6608907f744SAlan Somers if (ub_full(ub, up->len)) {
661d9a54d5cSPoul-Henning Kamp delete_unr(uh, up->ptr);
662d9a54d5cSPoul-Henning Kamp up->ptr = uh;
6638907f744SAlan Somers } else if (ub_empty(ub, up->len)) {
664d9a54d5cSPoul-Henning Kamp delete_unr(uh, up->ptr);
665d9a54d5cSPoul-Henning Kamp up->ptr = NULL;
666d9a54d5cSPoul-Henning Kamp }
667d9a54d5cSPoul-Henning Kamp }
668d9a54d5cSPoul-Henning Kamp
669d9a54d5cSPoul-Henning Kamp /* If nothing left in runlength, delete it */
670d9a54d5cSPoul-Henning Kamp if (up->len == 0) {
671d9a54d5cSPoul-Henning Kamp upp = TAILQ_PREV(up, unrhd, list);
672d9a54d5cSPoul-Henning Kamp if (upp == NULL)
673d9a54d5cSPoul-Henning Kamp upp = TAILQ_NEXT(up, list);
674d9a54d5cSPoul-Henning Kamp TAILQ_REMOVE(&uh->head, up, list);
675d9a54d5cSPoul-Henning Kamp delete_unr(uh, up);
676d9a54d5cSPoul-Henning Kamp up = upp;
677d9a54d5cSPoul-Henning Kamp }
678d9a54d5cSPoul-Henning Kamp
679d9a54d5cSPoul-Henning Kamp /* If we have "hot-spot" still, merge with neighbor if possible */
680d9a54d5cSPoul-Henning Kamp if (up != NULL) {
681f6bde1fdSPoul-Henning Kamp upp = TAILQ_PREV(up, unrhd, list);
682f6bde1fdSPoul-Henning Kamp if (upp != NULL && up->ptr == upp->ptr) {
683f6bde1fdSPoul-Henning Kamp up->len += upp->len;
684f6bde1fdSPoul-Henning Kamp TAILQ_REMOVE(&uh->head, upp, list);
685f6bde1fdSPoul-Henning Kamp delete_unr(uh, upp);
686f6bde1fdSPoul-Henning Kamp }
687f6bde1fdSPoul-Henning Kamp upp = TAILQ_NEXT(up, list);
688f6bde1fdSPoul-Henning Kamp if (upp != NULL && up->ptr == upp->ptr) {
689f6bde1fdSPoul-Henning Kamp up->len += upp->len;
690f6bde1fdSPoul-Henning Kamp TAILQ_REMOVE(&uh->head, upp, list);
691f6bde1fdSPoul-Henning Kamp delete_unr(uh, upp);
692f6bde1fdSPoul-Henning Kamp }
693f6bde1fdSPoul-Henning Kamp }
694f6bde1fdSPoul-Henning Kamp
695d9a54d5cSPoul-Henning Kamp /* Merge into ->first if possible */
696d9a54d5cSPoul-Henning Kamp upp = TAILQ_FIRST(&uh->head);
697d9a54d5cSPoul-Henning Kamp if (upp != NULL && upp->ptr == uh) {
698d9a54d5cSPoul-Henning Kamp uh->first += upp->len;
699d9a54d5cSPoul-Henning Kamp TAILQ_REMOVE(&uh->head, upp, list);
700d9a54d5cSPoul-Henning Kamp delete_unr(uh, upp);
701d9a54d5cSPoul-Henning Kamp if (up == upp)
702d9a54d5cSPoul-Henning Kamp up = NULL;
703d9a54d5cSPoul-Henning Kamp }
704d9a54d5cSPoul-Henning Kamp
705d9a54d5cSPoul-Henning Kamp /* Merge into ->last if possible */
706d9a54d5cSPoul-Henning Kamp upp = TAILQ_LAST(&uh->head, unrhd);
707d9a54d5cSPoul-Henning Kamp if (upp != NULL && upp->ptr == NULL) {
708d9a54d5cSPoul-Henning Kamp uh->last += upp->len;
709d9a54d5cSPoul-Henning Kamp TAILQ_REMOVE(&uh->head, upp, list);
710d9a54d5cSPoul-Henning Kamp delete_unr(uh, upp);
711d9a54d5cSPoul-Henning Kamp if (up == upp)
712d9a54d5cSPoul-Henning Kamp up = NULL;
713d9a54d5cSPoul-Henning Kamp }
714d9a54d5cSPoul-Henning Kamp
715d9a54d5cSPoul-Henning Kamp /* Try to make bitmaps */
716d9a54d5cSPoul-Henning Kamp while (optimize_unr(uh))
717d9a54d5cSPoul-Henning Kamp continue;
718d9a54d5cSPoul-Henning Kamp }
719d9a54d5cSPoul-Henning Kamp
720f6bde1fdSPoul-Henning Kamp /*
721f6bde1fdSPoul-Henning Kamp * Allocate a free unr.
722f6bde1fdSPoul-Henning Kamp */
723d9a54d5cSPoul-Henning Kamp int
alloc_unrl(struct unrhdr * uh)724d9a54d5cSPoul-Henning Kamp alloc_unrl(struct unrhdr *uh)
725f6bde1fdSPoul-Henning Kamp {
726d9a54d5cSPoul-Henning Kamp struct unr *up;
727d9a54d5cSPoul-Henning Kamp struct unrb *ub;
728f6bde1fdSPoul-Henning Kamp u_int x;
729f6bde1fdSPoul-Henning Kamp int y;
730f6bde1fdSPoul-Henning Kamp
731e59b940dSKonstantin Belousov if (uh->mtx != NULL)
732d9a54d5cSPoul-Henning Kamp mtx_assert(uh->mtx, MA_OWNED);
733f6bde1fdSPoul-Henning Kamp check_unrhdr(uh, __LINE__);
734d9a54d5cSPoul-Henning Kamp x = uh->low + uh->first;
735d9a54d5cSPoul-Henning Kamp
736f6bde1fdSPoul-Henning Kamp up = TAILQ_FIRST(&uh->head);
737f6bde1fdSPoul-Henning Kamp
738d9a54d5cSPoul-Henning Kamp /*
739d9a54d5cSPoul-Henning Kamp * If we have an ideal split, just adjust the first+last
740d9a54d5cSPoul-Henning Kamp */
741d9a54d5cSPoul-Henning Kamp if (up == NULL && uh->last > 0) {
742d9a54d5cSPoul-Henning Kamp uh->first++;
743d9a54d5cSPoul-Henning Kamp uh->last--;
744f6bde1fdSPoul-Henning Kamp uh->busy++;
745f6bde1fdSPoul-Henning Kamp return (x);
746f6bde1fdSPoul-Henning Kamp }
747f6bde1fdSPoul-Henning Kamp
748f6bde1fdSPoul-Henning Kamp /*
749d9a54d5cSPoul-Henning Kamp * We can always allocate from the first list element, so if we have
750d9a54d5cSPoul-Henning Kamp * nothing on the list, we must have run out of unit numbers.
751f6bde1fdSPoul-Henning Kamp */
75293f6c81eSPoul-Henning Kamp if (up == NULL)
753d9a54d5cSPoul-Henning Kamp return (-1);
754d9a54d5cSPoul-Henning Kamp
755d9a54d5cSPoul-Henning Kamp KASSERT(up->ptr != uh, ("UNR first element is allocated"));
756d9a54d5cSPoul-Henning Kamp
757d9a54d5cSPoul-Henning Kamp if (up->ptr == NULL) { /* free run */
758d9a54d5cSPoul-Henning Kamp uh->first++;
759f6bde1fdSPoul-Henning Kamp up->len--;
760d9a54d5cSPoul-Henning Kamp } else { /* bitmap */
761d9a54d5cSPoul-Henning Kamp ub = up->ptr;
762d9a54d5cSPoul-Henning Kamp bit_ffc(ub->map, up->len, &y);
763d9a54d5cSPoul-Henning Kamp KASSERT(y != -1, ("UNR corruption: No clear bit in bitmap."));
764d9a54d5cSPoul-Henning Kamp bit_set(ub->map, y);
765d9a54d5cSPoul-Henning Kamp x += y;
766d9a54d5cSPoul-Henning Kamp }
767f6bde1fdSPoul-Henning Kamp uh->busy++;
768d9a54d5cSPoul-Henning Kamp collapse_unr(uh, up);
769f6bde1fdSPoul-Henning Kamp return (x);
770f6bde1fdSPoul-Henning Kamp }
771f6bde1fdSPoul-Henning Kamp
772d9a54d5cSPoul-Henning Kamp int
alloc_unr(struct unrhdr * uh)773d9a54d5cSPoul-Henning Kamp alloc_unr(struct unrhdr *uh)
774d9a54d5cSPoul-Henning Kamp {
775d9a54d5cSPoul-Henning Kamp int i;
776d9a54d5cSPoul-Henning Kamp
777e59b940dSKonstantin Belousov if (uh->mtx != NULL)
778d9a54d5cSPoul-Henning Kamp mtx_lock(uh->mtx);
779d9a54d5cSPoul-Henning Kamp i = alloc_unrl(uh);
78009828ba9SKonstantin Belousov clean_unrhdrl(uh);
781e59b940dSKonstantin Belousov if (uh->mtx != NULL)
782d9a54d5cSPoul-Henning Kamp mtx_unlock(uh->mtx);
783d9a54d5cSPoul-Henning Kamp return (i);
784d9a54d5cSPoul-Henning Kamp }
785d9a54d5cSPoul-Henning Kamp
78613c02cbbSJaakko Heinonen static int
alloc_unr_specificl(struct unrhdr * uh,u_int item,void ** p1,void ** p2)78713c02cbbSJaakko Heinonen alloc_unr_specificl(struct unrhdr *uh, u_int item, void **p1, void **p2)
78813c02cbbSJaakko Heinonen {
78913c02cbbSJaakko Heinonen struct unr *up, *upn;
79013c02cbbSJaakko Heinonen struct unrb *ub;
79113c02cbbSJaakko Heinonen u_int i, last, tl;
79213c02cbbSJaakko Heinonen
793e59b940dSKonstantin Belousov if (uh->mtx != NULL)
79413c02cbbSJaakko Heinonen mtx_assert(uh->mtx, MA_OWNED);
79513c02cbbSJaakko Heinonen
79613c02cbbSJaakko Heinonen if (item < uh->low + uh->first || item > uh->high)
79713c02cbbSJaakko Heinonen return (-1);
79813c02cbbSJaakko Heinonen
79913c02cbbSJaakko Heinonen up = TAILQ_FIRST(&uh->head);
80013c02cbbSJaakko Heinonen /* Ideal split. */
80113c02cbbSJaakko Heinonen if (up == NULL && item - uh->low == uh->first) {
80213c02cbbSJaakko Heinonen uh->first++;
80313c02cbbSJaakko Heinonen uh->last--;
80413c02cbbSJaakko Heinonen uh->busy++;
80513c02cbbSJaakko Heinonen check_unrhdr(uh, __LINE__);
80613c02cbbSJaakko Heinonen return (item);
80713c02cbbSJaakko Heinonen }
80813c02cbbSJaakko Heinonen
80913c02cbbSJaakko Heinonen i = item - uh->low - uh->first;
81013c02cbbSJaakko Heinonen
81113c02cbbSJaakko Heinonen if (up == NULL) {
81213c02cbbSJaakko Heinonen up = new_unr(uh, p1, p2);
81313c02cbbSJaakko Heinonen up->ptr = NULL;
81413c02cbbSJaakko Heinonen up->len = i;
81513c02cbbSJaakko Heinonen TAILQ_INSERT_TAIL(&uh->head, up, list);
81613c02cbbSJaakko Heinonen up = new_unr(uh, p1, p2);
81713c02cbbSJaakko Heinonen up->ptr = uh;
81813c02cbbSJaakko Heinonen up->len = 1;
81913c02cbbSJaakko Heinonen TAILQ_INSERT_TAIL(&uh->head, up, list);
82013c02cbbSJaakko Heinonen uh->last = uh->high - uh->low - i;
82113c02cbbSJaakko Heinonen uh->busy++;
82213c02cbbSJaakko Heinonen check_unrhdr(uh, __LINE__);
82313c02cbbSJaakko Heinonen return (item);
82413c02cbbSJaakko Heinonen } else {
82513c02cbbSJaakko Heinonen /* Find the item which contains the unit we want to allocate. */
82613c02cbbSJaakko Heinonen TAILQ_FOREACH(up, &uh->head, list) {
82713c02cbbSJaakko Heinonen if (up->len > i)
82813c02cbbSJaakko Heinonen break;
82913c02cbbSJaakko Heinonen i -= up->len;
83013c02cbbSJaakko Heinonen }
83113c02cbbSJaakko Heinonen }
83213c02cbbSJaakko Heinonen
83313c02cbbSJaakko Heinonen if (up == NULL) {
83413c02cbbSJaakko Heinonen if (i > 0) {
83513c02cbbSJaakko Heinonen up = new_unr(uh, p1, p2);
83613c02cbbSJaakko Heinonen up->ptr = NULL;
83713c02cbbSJaakko Heinonen up->len = i;
83813c02cbbSJaakko Heinonen TAILQ_INSERT_TAIL(&uh->head, up, list);
83913c02cbbSJaakko Heinonen }
84013c02cbbSJaakko Heinonen up = new_unr(uh, p1, p2);
84113c02cbbSJaakko Heinonen up->ptr = uh;
84213c02cbbSJaakko Heinonen up->len = 1;
84313c02cbbSJaakko Heinonen TAILQ_INSERT_TAIL(&uh->head, up, list);
84413c02cbbSJaakko Heinonen goto done;
84513c02cbbSJaakko Heinonen }
84613c02cbbSJaakko Heinonen
84713c02cbbSJaakko Heinonen if (is_bitmap(uh, up)) {
84813c02cbbSJaakko Heinonen ub = up->ptr;
84913c02cbbSJaakko Heinonen if (bit_test(ub->map, i) == 0) {
85013c02cbbSJaakko Heinonen bit_set(ub->map, i);
85113c02cbbSJaakko Heinonen goto done;
85213c02cbbSJaakko Heinonen } else
85313c02cbbSJaakko Heinonen return (-1);
85413c02cbbSJaakko Heinonen } else if (up->ptr == uh)
85513c02cbbSJaakko Heinonen return (-1);
85613c02cbbSJaakko Heinonen
85713c02cbbSJaakko Heinonen KASSERT(up->ptr == NULL,
85813c02cbbSJaakko Heinonen ("alloc_unr_specificl: up->ptr != NULL (up=%p)", up));
85913c02cbbSJaakko Heinonen
86013c02cbbSJaakko Heinonen /* Split off the tail end, if any. */
86113c02cbbSJaakko Heinonen tl = up->len - (1 + i);
86213c02cbbSJaakko Heinonen if (tl > 0) {
86313c02cbbSJaakko Heinonen upn = new_unr(uh, p1, p2);
86413c02cbbSJaakko Heinonen upn->ptr = NULL;
86513c02cbbSJaakko Heinonen upn->len = tl;
86613c02cbbSJaakko Heinonen TAILQ_INSERT_AFTER(&uh->head, up, upn, list);
86713c02cbbSJaakko Heinonen }
86813c02cbbSJaakko Heinonen
86913c02cbbSJaakko Heinonen /* Split off head end, if any */
87013c02cbbSJaakko Heinonen if (i > 0) {
87113c02cbbSJaakko Heinonen upn = new_unr(uh, p1, p2);
87213c02cbbSJaakko Heinonen upn->len = i;
87313c02cbbSJaakko Heinonen upn->ptr = NULL;
87413c02cbbSJaakko Heinonen TAILQ_INSERT_BEFORE(up, upn, list);
87513c02cbbSJaakko Heinonen }
87613c02cbbSJaakko Heinonen up->len = 1;
87713c02cbbSJaakko Heinonen up->ptr = uh;
87813c02cbbSJaakko Heinonen
87913c02cbbSJaakko Heinonen done:
88013c02cbbSJaakko Heinonen last = uh->high - uh->low - (item - uh->low);
88113c02cbbSJaakko Heinonen if (uh->last > last)
88213c02cbbSJaakko Heinonen uh->last = last;
88313c02cbbSJaakko Heinonen uh->busy++;
88413c02cbbSJaakko Heinonen collapse_unr(uh, up);
88513c02cbbSJaakko Heinonen check_unrhdr(uh, __LINE__);
88613c02cbbSJaakko Heinonen return (item);
88713c02cbbSJaakko Heinonen }
88813c02cbbSJaakko Heinonen
88913c02cbbSJaakko Heinonen int
alloc_unr_specific(struct unrhdr * uh,u_int item)89013c02cbbSJaakko Heinonen alloc_unr_specific(struct unrhdr *uh, u_int item)
89113c02cbbSJaakko Heinonen {
89213c02cbbSJaakko Heinonen void *p1, *p2;
89313c02cbbSJaakko Heinonen int i;
89413c02cbbSJaakko Heinonen
89513c02cbbSJaakko Heinonen WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "alloc_unr_specific");
89613c02cbbSJaakko Heinonen
89713c02cbbSJaakko Heinonen p1 = Malloc(sizeof(struct unr));
89813c02cbbSJaakko Heinonen p2 = Malloc(sizeof(struct unr));
89913c02cbbSJaakko Heinonen
900e59b940dSKonstantin Belousov if (uh->mtx != NULL)
90113c02cbbSJaakko Heinonen mtx_lock(uh->mtx);
90213c02cbbSJaakko Heinonen i = alloc_unr_specificl(uh, item, &p1, &p2);
903e59b940dSKonstantin Belousov if (uh->mtx != NULL)
90413c02cbbSJaakko Heinonen mtx_unlock(uh->mtx);
90513c02cbbSJaakko Heinonen
90613c02cbbSJaakko Heinonen if (p1 != NULL)
90713c02cbbSJaakko Heinonen Free(p1);
90813c02cbbSJaakko Heinonen if (p2 != NULL)
90913c02cbbSJaakko Heinonen Free(p2);
91013c02cbbSJaakko Heinonen
91113c02cbbSJaakko Heinonen return (i);
91213c02cbbSJaakko Heinonen }
91313c02cbbSJaakko Heinonen
914f6bde1fdSPoul-Henning Kamp /*
915f6bde1fdSPoul-Henning Kamp * Free a unr.
916f6bde1fdSPoul-Henning Kamp *
917f6bde1fdSPoul-Henning Kamp * If we can save unrs by using a bitmap, do so.
918f6bde1fdSPoul-Henning Kamp */
919d9a54d5cSPoul-Henning Kamp static void
free_unrl(struct unrhdr * uh,u_int item,void ** p1,void ** p2)920d9a54d5cSPoul-Henning Kamp free_unrl(struct unrhdr *uh, u_int item, void **p1, void **p2)
921f6bde1fdSPoul-Henning Kamp {
922d9a54d5cSPoul-Henning Kamp struct unr *up, *upp, *upn;
923d9a54d5cSPoul-Henning Kamp struct unrb *ub;
924d9a54d5cSPoul-Henning Kamp u_int pl;
925f6bde1fdSPoul-Henning Kamp
926f6bde1fdSPoul-Henning Kamp KASSERT(item >= uh->low && item <= uh->high,
927f6bde1fdSPoul-Henning Kamp ("UNR: free_unr(%u) out of range [%u...%u]",
928f6bde1fdSPoul-Henning Kamp item, uh->low, uh->high));
929f6bde1fdSPoul-Henning Kamp check_unrhdr(uh, __LINE__);
930f6bde1fdSPoul-Henning Kamp item -= uh->low;
931d9a54d5cSPoul-Henning Kamp upp = TAILQ_FIRST(&uh->head);
932f6bde1fdSPoul-Henning Kamp /*
933d9a54d5cSPoul-Henning Kamp * Freeing in the ideal split case
934f6bde1fdSPoul-Henning Kamp */
935d9a54d5cSPoul-Henning Kamp if (item + 1 == uh->first && upp == NULL) {
936d9a54d5cSPoul-Henning Kamp uh->last++;
937d9a54d5cSPoul-Henning Kamp uh->first--;
938d9a54d5cSPoul-Henning Kamp uh->busy--;
939f6bde1fdSPoul-Henning Kamp check_unrhdr(uh, __LINE__);
940f6bde1fdSPoul-Henning Kamp return;
941f6bde1fdSPoul-Henning Kamp }
942d9a54d5cSPoul-Henning Kamp /*
943d9a54d5cSPoul-Henning Kamp * Freeing in the ->first section. Create a run starting at the
944d9a54d5cSPoul-Henning Kamp * freed item. The code below will subdivide it.
945d9a54d5cSPoul-Henning Kamp */
946d9a54d5cSPoul-Henning Kamp if (item < uh->first) {
947d9a54d5cSPoul-Henning Kamp up = new_unr(uh, p1, p2);
948d9a54d5cSPoul-Henning Kamp up->ptr = uh;
949d9a54d5cSPoul-Henning Kamp up->len = uh->first - item;
950d9a54d5cSPoul-Henning Kamp TAILQ_INSERT_HEAD(&uh->head, up, list);
951d9a54d5cSPoul-Henning Kamp uh->first -= up->len;
952f6bde1fdSPoul-Henning Kamp }
953f6bde1fdSPoul-Henning Kamp
954d9a54d5cSPoul-Henning Kamp item -= uh->first;
955d9a54d5cSPoul-Henning Kamp
956d9a54d5cSPoul-Henning Kamp /* Find the item which contains the unit we want to free */
957d9a54d5cSPoul-Henning Kamp TAILQ_FOREACH(up, &uh->head, list) {
958d9a54d5cSPoul-Henning Kamp if (up->len > item)
959d9a54d5cSPoul-Henning Kamp break;
960d9a54d5cSPoul-Henning Kamp item -= up->len;
961d9a54d5cSPoul-Henning Kamp }
962d9a54d5cSPoul-Henning Kamp
963d9a54d5cSPoul-Henning Kamp /* Handle bitmap items */
964d9a54d5cSPoul-Henning Kamp if (is_bitmap(uh, up)) {
965d9a54d5cSPoul-Henning Kamp ub = up->ptr;
966d9a54d5cSPoul-Henning Kamp
967d9a54d5cSPoul-Henning Kamp KASSERT(bit_test(ub->map, item) != 0,
968d9a54d5cSPoul-Henning Kamp ("UNR: Freeing free item %d (bitmap)\n", item));
969d9a54d5cSPoul-Henning Kamp bit_clear(ub->map, item);
970d9a54d5cSPoul-Henning Kamp uh->busy--;
971d9a54d5cSPoul-Henning Kamp collapse_unr(uh, up);
972d9a54d5cSPoul-Henning Kamp return;
973d9a54d5cSPoul-Henning Kamp }
974d9a54d5cSPoul-Henning Kamp
975d9a54d5cSPoul-Henning Kamp KASSERT(up->ptr == uh, ("UNR Freeing free item %d (run))\n", item));
976f6bde1fdSPoul-Henning Kamp
977f6bde1fdSPoul-Henning Kamp /* Just this one left, reap it */
978f6bde1fdSPoul-Henning Kamp if (up->len == 1) {
979f6bde1fdSPoul-Henning Kamp up->ptr = NULL;
980f6bde1fdSPoul-Henning Kamp uh->busy--;
981f6bde1fdSPoul-Henning Kamp collapse_unr(uh, up);
982f6bde1fdSPoul-Henning Kamp return;
983f6bde1fdSPoul-Henning Kamp }
984f6bde1fdSPoul-Henning Kamp
985d9a54d5cSPoul-Henning Kamp /* Check if we can shift the item into the previous 'free' run */
986f6bde1fdSPoul-Henning Kamp upp = TAILQ_PREV(up, unrhd, list);
987d9a54d5cSPoul-Henning Kamp if (item == 0 && upp != NULL && upp->ptr == NULL) {
988f6bde1fdSPoul-Henning Kamp upp->len++;
989f6bde1fdSPoul-Henning Kamp up->len--;
990f6bde1fdSPoul-Henning Kamp uh->busy--;
991d9a54d5cSPoul-Henning Kamp collapse_unr(uh, up);
992f6bde1fdSPoul-Henning Kamp return;
993f6bde1fdSPoul-Henning Kamp }
994f6bde1fdSPoul-Henning Kamp
995d9a54d5cSPoul-Henning Kamp /* Check if we can shift the item to the next 'free' run */
996f6bde1fdSPoul-Henning Kamp upn = TAILQ_NEXT(up, list);
997d9a54d5cSPoul-Henning Kamp if (item == up->len - 1 && upn != NULL && upn->ptr == NULL) {
998f6bde1fdSPoul-Henning Kamp upn->len++;
999f6bde1fdSPoul-Henning Kamp up->len--;
1000f6bde1fdSPoul-Henning Kamp uh->busy--;
1001d9a54d5cSPoul-Henning Kamp collapse_unr(uh, up);
1002f6bde1fdSPoul-Henning Kamp return;
1003f6bde1fdSPoul-Henning Kamp }
1004f6bde1fdSPoul-Henning Kamp
1005f6bde1fdSPoul-Henning Kamp /* Split off the tail end, if any. */
1006d9a54d5cSPoul-Henning Kamp pl = up->len - (1 + item);
1007f6bde1fdSPoul-Henning Kamp if (pl > 0) {
1008d9a54d5cSPoul-Henning Kamp upp = new_unr(uh, p1, p2);
1009f6bde1fdSPoul-Henning Kamp upp->ptr = uh;
1010f6bde1fdSPoul-Henning Kamp upp->len = pl;
1011f6bde1fdSPoul-Henning Kamp TAILQ_INSERT_AFTER(&uh->head, up, upp, list);
1012f6bde1fdSPoul-Henning Kamp }
1013f6bde1fdSPoul-Henning Kamp
1014d9a54d5cSPoul-Henning Kamp /* Split off head end, if any */
1015d9a54d5cSPoul-Henning Kamp if (item > 0) {
1016d9a54d5cSPoul-Henning Kamp upp = new_unr(uh, p1, p2);
1017d9a54d5cSPoul-Henning Kamp upp->len = item;
1018d9a54d5cSPoul-Henning Kamp upp->ptr = uh;
1019d9a54d5cSPoul-Henning Kamp TAILQ_INSERT_BEFORE(up, upp, list);
1020d9a54d5cSPoul-Henning Kamp }
1021f6bde1fdSPoul-Henning Kamp up->len = 1;
1022f6bde1fdSPoul-Henning Kamp up->ptr = NULL;
1023f6bde1fdSPoul-Henning Kamp uh->busy--;
1024d9a54d5cSPoul-Henning Kamp collapse_unr(uh, up);
1025f6bde1fdSPoul-Henning Kamp }
1026f6bde1fdSPoul-Henning Kamp
1027d9a54d5cSPoul-Henning Kamp void
free_unr(struct unrhdr * uh,u_int item)1028d9a54d5cSPoul-Henning Kamp free_unr(struct unrhdr *uh, u_int item)
1029d9a54d5cSPoul-Henning Kamp {
1030d9a54d5cSPoul-Henning Kamp void *p1, *p2;
1031f6bde1fdSPoul-Henning Kamp
10327550e3eaSKonstantin Belousov WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "free_unr");
1033d9a54d5cSPoul-Henning Kamp p1 = Malloc(sizeof(struct unr));
1034d9a54d5cSPoul-Henning Kamp p2 = Malloc(sizeof(struct unr));
1035e59b940dSKonstantin Belousov if (uh->mtx != NULL)
1036d9a54d5cSPoul-Henning Kamp mtx_lock(uh->mtx);
1037d9a54d5cSPoul-Henning Kamp free_unrl(uh, item, &p1, &p2);
103809828ba9SKonstantin Belousov clean_unrhdrl(uh);
1039e59b940dSKonstantin Belousov if (uh->mtx != NULL)
1040d9a54d5cSPoul-Henning Kamp mtx_unlock(uh->mtx);
1041d9a54d5cSPoul-Henning Kamp if (p1 != NULL)
1042d9a54d5cSPoul-Henning Kamp Free(p1);
1043d9a54d5cSPoul-Henning Kamp if (p2 != NULL)
1044d9a54d5cSPoul-Henning Kamp Free(p2);
1045f6bde1fdSPoul-Henning Kamp }
1046f6bde1fdSPoul-Henning Kamp
1047f386b277SKonstantin Belousov #ifdef _KERNEL
1048f386b277SKonstantin Belousov #include "opt_ddb.h"
1049f386b277SKonstantin Belousov #ifdef DDB
1050f386b277SKonstantin Belousov #include <ddb/ddb.h>
1051f386b277SKonstantin Belousov #endif
1052f386b277SKonstantin Belousov #endif
105393f6c81eSPoul-Henning Kamp
1054f386b277SKonstantin Belousov #if (defined(_KERNEL) && defined(DDB)) || !defined(_KERNEL)
1055f6bde1fdSPoul-Henning Kamp
1056f386b277SKonstantin Belousov #if !defined(_KERNEL)
1057f386b277SKonstantin Belousov #define db_printf printf
1058f386b277SKonstantin Belousov #endif
1059794277daSAlan Somers
1060f6bde1fdSPoul-Henning Kamp static void
print_unr(struct unrhdr * uh,struct unr * up)1061f6bde1fdSPoul-Henning Kamp print_unr(struct unrhdr *uh, struct unr *up)
1062f6bde1fdSPoul-Henning Kamp {
1063f6bde1fdSPoul-Henning Kamp u_int x;
1064d9a54d5cSPoul-Henning Kamp struct unrb *ub;
1065f6bde1fdSPoul-Henning Kamp
1066f386b277SKonstantin Belousov db_printf(" %p len = %5u ", up, up->len);
1067f6bde1fdSPoul-Henning Kamp if (up->ptr == NULL)
1068f386b277SKonstantin Belousov db_printf("free\n");
1069f6bde1fdSPoul-Henning Kamp else if (up->ptr == uh)
1070f386b277SKonstantin Belousov db_printf("alloc\n");
1071f6bde1fdSPoul-Henning Kamp else {
1072d9a54d5cSPoul-Henning Kamp ub = up->ptr;
1073f386b277SKonstantin Belousov db_printf("bitmap [");
1074d9a54d5cSPoul-Henning Kamp for (x = 0; x < up->len; x++) {
1075d9a54d5cSPoul-Henning Kamp if (bit_test(ub->map, x))
1076f386b277SKonstantin Belousov db_printf("#");
1077f6bde1fdSPoul-Henning Kamp else
1078f386b277SKonstantin Belousov db_printf(" ");
1079f6bde1fdSPoul-Henning Kamp }
1080f386b277SKonstantin Belousov db_printf("]\n");
1081f6bde1fdSPoul-Henning Kamp }
1082f6bde1fdSPoul-Henning Kamp }
1083f6bde1fdSPoul-Henning Kamp
1084f6bde1fdSPoul-Henning Kamp static void
print_unrhdr(struct unrhdr * uh)1085f6bde1fdSPoul-Henning Kamp print_unrhdr(struct unrhdr *uh)
1086f6bde1fdSPoul-Henning Kamp {
1087f6bde1fdSPoul-Henning Kamp struct unr *up;
1088f6bde1fdSPoul-Henning Kamp u_int x;
1089f6bde1fdSPoul-Henning Kamp
1090f386b277SKonstantin Belousov db_printf(
1091d9a54d5cSPoul-Henning Kamp "%p low = %u high = %u first = %u last = %u busy %u chunks = %u\n",
1092d9a54d5cSPoul-Henning Kamp uh, uh->low, uh->high, uh->first, uh->last, uh->busy, uh->alloc);
1093d9a54d5cSPoul-Henning Kamp x = uh->low + uh->first;
1094f6bde1fdSPoul-Henning Kamp TAILQ_FOREACH(up, &uh->head, list) {
1095f386b277SKonstantin Belousov db_printf(" from = %5u", x);
1096f6bde1fdSPoul-Henning Kamp print_unr(uh, up);
1097f6bde1fdSPoul-Henning Kamp if (up->ptr == NULL || up->ptr == uh)
1098f6bde1fdSPoul-Henning Kamp x += up->len;
1099f6bde1fdSPoul-Henning Kamp else
1100f6bde1fdSPoul-Henning Kamp x += NBITS;
1101f6bde1fdSPoul-Henning Kamp }
1102f6bde1fdSPoul-Henning Kamp }
1103f6bde1fdSPoul-Henning Kamp
1104f386b277SKonstantin Belousov #endif
1105f386b277SKonstantin Belousov
1106f386b277SKonstantin Belousov #if defined(_KERNEL) && defined(DDB)
DB_SHOW_COMMAND(unrhdr,unrhdr_print_unrhdr)1107f386b277SKonstantin Belousov DB_SHOW_COMMAND(unrhdr, unrhdr_print_unrhdr)
1108f386b277SKonstantin Belousov {
1109f386b277SKonstantin Belousov if (!have_addr) {
1110f386b277SKonstantin Belousov db_printf("show unrhdr addr\n");
1111f386b277SKonstantin Belousov return;
1112f386b277SKonstantin Belousov }
1113f386b277SKonstantin Belousov
1114f386b277SKonstantin Belousov print_unrhdr((struct unrhdr *)addr);
1115f386b277SKonstantin Belousov }
1116c4cc0cabSKonstantin Belousov
1117c4cc0cabSKonstantin Belousov static void
print_unrhdr_iter(struct unrhdr_iter * iter)1118c4cc0cabSKonstantin Belousov print_unrhdr_iter(struct unrhdr_iter *iter)
1119c4cc0cabSKonstantin Belousov {
1120c4cc0cabSKonstantin Belousov db_printf("iter %p unrhdr %p ipos %d upos %p ufi %d\n",
1121c4cc0cabSKonstantin Belousov iter, iter->uh, iter->ipos, iter->upos, iter->upos_first_item);
1122c4cc0cabSKonstantin Belousov }
1123c4cc0cabSKonstantin Belousov
DB_SHOW_COMMAND(unrhdr_iter,unrhdr_print_iter)1124c4cc0cabSKonstantin Belousov DB_SHOW_COMMAND(unrhdr_iter, unrhdr_print_iter)
1125c4cc0cabSKonstantin Belousov {
1126c4cc0cabSKonstantin Belousov if (!have_addr) {
1127c4cc0cabSKonstantin Belousov db_printf("show unrhdr_iter addr\n");
1128c4cc0cabSKonstantin Belousov return;
1129c4cc0cabSKonstantin Belousov }
1130c4cc0cabSKonstantin Belousov
1131c4cc0cabSKonstantin Belousov print_unrhdr_iter((struct unrhdr_iter *)addr);
1132c4cc0cabSKonstantin Belousov }
1133f386b277SKonstantin Belousov #endif
1134f386b277SKonstantin Belousov
1135f386b277SKonstantin Belousov #ifndef _KERNEL /* USERLAND test driver */
1136f386b277SKonstantin Belousov
1137f386b277SKonstantin Belousov /*
1138f386b277SKonstantin Belousov * Simple stochastic test driver for the above functions. The code resides
1139f386b277SKonstantin Belousov * here so that it can access static functions and structures.
1140f386b277SKonstantin Belousov */
1141f386b277SKonstantin Belousov
1142f386b277SKonstantin Belousov static bool verbose;
1143f386b277SKonstantin Belousov #define VPRINTF(...) {if (verbose) printf(__VA_ARGS__);}
1144f386b277SKonstantin Belousov
114513c02cbbSJaakko Heinonen static void
test_alloc_unr(struct unrhdr * uh,u_int i,char a[])114613c02cbbSJaakko Heinonen test_alloc_unr(struct unrhdr *uh, u_int i, char a[])
114713c02cbbSJaakko Heinonen {
114813c02cbbSJaakko Heinonen int j;
114913c02cbbSJaakko Heinonen
115013c02cbbSJaakko Heinonen if (a[i]) {
1151794277daSAlan Somers VPRINTF("F %u\n", i);
115213c02cbbSJaakko Heinonen free_unr(uh, i);
115313c02cbbSJaakko Heinonen a[i] = 0;
115413c02cbbSJaakko Heinonen } else {
115513c02cbbSJaakko Heinonen no_alloc = 1;
115613c02cbbSJaakko Heinonen j = alloc_unr(uh);
115713c02cbbSJaakko Heinonen if (j != -1) {
115813c02cbbSJaakko Heinonen a[j] = 1;
1159794277daSAlan Somers VPRINTF("A %d\n", j);
116013c02cbbSJaakko Heinonen }
116113c02cbbSJaakko Heinonen no_alloc = 0;
116213c02cbbSJaakko Heinonen }
116313c02cbbSJaakko Heinonen }
116413c02cbbSJaakko Heinonen
116513c02cbbSJaakko Heinonen static void
test_alloc_unr_specific(struct unrhdr * uh,u_int i,char a[])116613c02cbbSJaakko Heinonen test_alloc_unr_specific(struct unrhdr *uh, u_int i, char a[])
116713c02cbbSJaakko Heinonen {
116813c02cbbSJaakko Heinonen int j;
116913c02cbbSJaakko Heinonen
117013c02cbbSJaakko Heinonen j = alloc_unr_specific(uh, i);
117113c02cbbSJaakko Heinonen if (j == -1) {
1172794277daSAlan Somers VPRINTF("F %u\n", i);
117313c02cbbSJaakko Heinonen a[i] = 0;
117413c02cbbSJaakko Heinonen free_unr(uh, i);
117513c02cbbSJaakko Heinonen } else {
117613c02cbbSJaakko Heinonen a[i] = 1;
1177794277daSAlan Somers VPRINTF("A %d\n", j);
117813c02cbbSJaakko Heinonen }
117913c02cbbSJaakko Heinonen }
118013c02cbbSJaakko Heinonen
118112db3c91SKonstantin Belousov #define TBASE 7
118212db3c91SKonstantin Belousov #define XSIZE 10
118312db3c91SKonstantin Belousov #define ISIZE 1000
118412db3c91SKonstantin Belousov
118512db3c91SKonstantin Belousov static int
test_iter_compar(const void * a,const void * b)118612db3c91SKonstantin Belousov test_iter_compar(const void *a, const void *b)
118712db3c91SKonstantin Belousov {
118812db3c91SKonstantin Belousov return (*(const int *)a - *(const int *)b);
118912db3c91SKonstantin Belousov }
119012db3c91SKonstantin Belousov
119112db3c91SKonstantin Belousov static void
test_iter_fill(int * vals,struct unrhdr * uh,int i,int v,int * res)119212db3c91SKonstantin Belousov test_iter_fill(int *vals, struct unrhdr *uh, int i, int v, int *res)
119312db3c91SKonstantin Belousov {
119412db3c91SKonstantin Belousov int x;
119512db3c91SKonstantin Belousov
119612db3c91SKonstantin Belousov vals[i] = v;
119712db3c91SKonstantin Belousov x = alloc_unr_specific(uh, v);
119812db3c91SKonstantin Belousov if (x != v) {
119912db3c91SKonstantin Belousov VPRINTF("alloc_unr_specific failed %d %d\n", x, v);
120012db3c91SKonstantin Belousov *res = 1;
120112db3c91SKonstantin Belousov }
120212db3c91SKonstantin Belousov }
120312db3c91SKonstantin Belousov
1204794277daSAlan Somers static void
test_iter(void)1205a014e0a3SKonstantin Belousov test_iter(void)
1206a014e0a3SKonstantin Belousov {
120712db3c91SKonstantin Belousov struct unrhdr *uh;
120812db3c91SKonstantin Belousov void *ihandle;
120912db3c91SKonstantin Belousov int vals[ISIZE];
121012db3c91SKonstantin Belousov int i, j, v, x, res;
121112db3c91SKonstantin Belousov
121212db3c91SKonstantin Belousov res = 0;
121312db3c91SKonstantin Belousov uh = new_unrhdr(TBASE, INT_MAX, NULL);
121412db3c91SKonstantin Belousov for (i = 0; i < XSIZE; i++) {
121512db3c91SKonstantin Belousov vals[i] = i + TBASE;
121612db3c91SKonstantin Belousov x = alloc_unr_specific(uh, i + TBASE);
121712db3c91SKonstantin Belousov if (x != i + TBASE) {
121812db3c91SKonstantin Belousov VPRINTF("alloc_unr_specific failed %d %d\n", x,
121912db3c91SKonstantin Belousov i + TBASE);
122012db3c91SKonstantin Belousov res = 1;
122112db3c91SKonstantin Belousov }
122212db3c91SKonstantin Belousov }
122312db3c91SKonstantin Belousov for (; i < ISIZE; i++) {
122412db3c91SKonstantin Belousov for (;;) {
122512db3c91SKonstantin Belousov again:
122612db3c91SKonstantin Belousov v = arc4random_uniform(INT_MAX);
122712db3c91SKonstantin Belousov if (v < TBASE)
122812db3c91SKonstantin Belousov goto again;
122912db3c91SKonstantin Belousov for (j = 0; j < i; j++) {
123012db3c91SKonstantin Belousov if (v == vals[j] || v + 1 == vals[j])
123112db3c91SKonstantin Belousov goto again;
123212db3c91SKonstantin Belousov }
123312db3c91SKonstantin Belousov break;
123412db3c91SKonstantin Belousov }
123512db3c91SKonstantin Belousov test_iter_fill(vals, uh, i, v, &res);
123612db3c91SKonstantin Belousov i++, v++;
123712db3c91SKonstantin Belousov if (i < ISIZE)
123812db3c91SKonstantin Belousov test_iter_fill(vals, uh, i, v, &res);
123912db3c91SKonstantin Belousov }
124012db3c91SKonstantin Belousov qsort(vals, ISIZE, sizeof(vals[0]), test_iter_compar);
124112db3c91SKonstantin Belousov
124212db3c91SKonstantin Belousov ihandle = create_iter_unr(uh);
124312db3c91SKonstantin Belousov i = 0;
124412db3c91SKonstantin Belousov while ((v = next_iter_unr(ihandle)) != -1) {
124512db3c91SKonstantin Belousov if (vals[i] != v) {
124612db3c91SKonstantin Belousov VPRINTF("iter %d: iter %d != val %d\n", i, v, vals[i]);
124712db3c91SKonstantin Belousov if (res == 0) {
124812db3c91SKonstantin Belousov if (verbose)
124912db3c91SKonstantin Belousov print_unrhdr(uh);
125012db3c91SKonstantin Belousov res = 1;
125112db3c91SKonstantin Belousov }
125212db3c91SKonstantin Belousov } else {
125312db3c91SKonstantin Belousov VPRINTF("iter %d: val %d\n", i, v);
125412db3c91SKonstantin Belousov }
125512db3c91SKonstantin Belousov i++;
125612db3c91SKonstantin Belousov }
125712db3c91SKonstantin Belousov free_iter_unr(ihandle);
125812db3c91SKonstantin Belousov clean_unrhdr(uh);
125912db3c91SKonstantin Belousov clear_unrhdr(uh);
126012db3c91SKonstantin Belousov delete_unrhdr(uh);
126112db3c91SKonstantin Belousov exit(res);
1262a014e0a3SKonstantin Belousov }
1263a014e0a3SKonstantin Belousov
1264a014e0a3SKonstantin Belousov static void
usage(char ** argv)1265794277daSAlan Somers usage(char **argv)
1266794277daSAlan Somers {
126712db3c91SKonstantin Belousov printf("%s [-h] [-i] [-r REPETITIONS] [-v]\n", argv[0]);
1268794277daSAlan Somers }
1269f6bde1fdSPoul-Henning Kamp
1270f6bde1fdSPoul-Henning Kamp int
main(int argc,char ** argv)1271794277daSAlan Somers main(int argc, char **argv)
1272f6bde1fdSPoul-Henning Kamp {
1273f6bde1fdSPoul-Henning Kamp struct unrhdr *uh;
1274794277daSAlan Somers char *a;
1275794277daSAlan Somers long count = 10000; /* Number of unrs to test */
127637f32e53SAlan Somers long reps = 1, m;
1277794277daSAlan Somers int ch;
1278cd565040SConrad Meyer u_int i;
127912db3c91SKonstantin Belousov bool testing_iter;
1280794277daSAlan Somers
1281794277daSAlan Somers verbose = false;
128212db3c91SKonstantin Belousov testing_iter = false;
1283794277daSAlan Somers
128412db3c91SKonstantin Belousov while ((ch = getopt(argc, argv, "hir:v")) != -1) {
1285794277daSAlan Somers switch (ch) {
128612db3c91SKonstantin Belousov case 'i':
128712db3c91SKonstantin Belousov testing_iter = true;
128812db3c91SKonstantin Belousov break;
1289794277daSAlan Somers case 'r':
1290794277daSAlan Somers errno = 0;
1291794277daSAlan Somers reps = strtol(optarg, NULL, 0);
1292794277daSAlan Somers if (errno == ERANGE || errno == EINVAL) {
1293794277daSAlan Somers usage(argv);
1294794277daSAlan Somers exit(2);
1295794277daSAlan Somers }
1296794277daSAlan Somers
1297794277daSAlan Somers break;
1298794277daSAlan Somers case 'v':
1299794277daSAlan Somers verbose = true;
1300794277daSAlan Somers break;
1301794277daSAlan Somers case 'h':
1302794277daSAlan Somers default:
1303794277daSAlan Somers usage(argv);
1304794277daSAlan Somers exit(2);
1305794277daSAlan Somers }
1306794277daSAlan Somers }
1307f6bde1fdSPoul-Henning Kamp
1308d9a54d5cSPoul-Henning Kamp setbuf(stdout, NULL);
130912db3c91SKonstantin Belousov
131012db3c91SKonstantin Belousov if (testing_iter)
131112db3c91SKonstantin Belousov test_iter();
131212db3c91SKonstantin Belousov
1313794277daSAlan Somers uh = new_unrhdr(0, count - 1, NULL);
1314d9a54d5cSPoul-Henning Kamp print_unrhdr(uh);
1315f6bde1fdSPoul-Henning Kamp
1316794277daSAlan Somers a = calloc(count, sizeof(char));
1317794277daSAlan Somers if (a == NULL)
1318794277daSAlan Somers err(1, "calloc failed");
1319f6bde1fdSPoul-Henning Kamp
1320794277daSAlan Somers printf("sizeof(struct unr) %zu\n", sizeof(struct unr));
1321794277daSAlan Somers printf("sizeof(struct unrb) %zu\n", sizeof(struct unrb));
1322794277daSAlan Somers printf("sizeof(struct unrhdr) %zu\n", sizeof(struct unrhdr));
1323b4f69365SEd Maste printf("NBITS %lu\n", (unsigned long)NBITS);
1324794277daSAlan Somers for (m = 0; m < count * reps; m++) {
1325cd565040SConrad Meyer i = arc4random_uniform(count);
1326d9a54d5cSPoul-Henning Kamp #if 0
1327d9a54d5cSPoul-Henning Kamp if (a[i] && (j & 1))
1328d9a54d5cSPoul-Henning Kamp continue;
1329d9a54d5cSPoul-Henning Kamp #endif
1330cd565040SConrad Meyer if ((arc4random() & 1) != 0)
133113c02cbbSJaakko Heinonen test_alloc_unr(uh, i, a);
133213c02cbbSJaakko Heinonen else
133313c02cbbSJaakko Heinonen test_alloc_unr_specific(uh, i, a);
133413c02cbbSJaakko Heinonen
1335794277daSAlan Somers if (verbose)
1336f6bde1fdSPoul-Henning Kamp print_unrhdr(uh);
1337f6bde1fdSPoul-Henning Kamp check_unrhdr(uh, __LINE__);
1338f6bde1fdSPoul-Henning Kamp }
133937f32e53SAlan Somers for (i = 0; i < (u_int)count; i++) {
1340d9a54d5cSPoul-Henning Kamp if (a[i]) {
1341794277daSAlan Somers if (verbose) {
1342d9a54d5cSPoul-Henning Kamp printf("C %u\n", i);
1343e4fea39eSPoul-Henning Kamp print_unrhdr(uh);
1344d9a54d5cSPoul-Henning Kamp }
1345794277daSAlan Somers free_unr(uh, i);
1346794277daSAlan Somers }
1347d9a54d5cSPoul-Henning Kamp }
1348d9a54d5cSPoul-Henning Kamp print_unrhdr(uh);
1349e4fea39eSPoul-Henning Kamp delete_unrhdr(uh);
1350794277daSAlan Somers free(a);
1351f6bde1fdSPoul-Henning Kamp return (0);
1352f6bde1fdSPoul-Henning Kamp }
1353f6bde1fdSPoul-Henning Kamp #endif
1354