nrelease - fix/improve livecd
[dragonfly.git] / sys / kern / subr_unit.c
1 /*-
2  * Copyright (c) 2004 Poul-Henning Kamp
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/kern/subr_unit.c,v 1.7 2005/03/14 06:51:29 phk Exp $
27  *
28  *
29  * Unit number allocation functions.
30  *
31  * These functions implement a mixed run-length/bitmap management of unit
32  * number spaces in a very memory efficient manner.
33  *
34  * Allocation policy is always lowest free number first.
35  *
36  * A return value of -1 signals that no more unit numbers are available.
37  *
38  * There is no cost associated with the range of unitnumbers, so unless
39  * the resource really is finite, specify INT_MAX to new_unrhdr() and
40  * forget about checking the return value.
41  *
42  * If a mutex is not provided when the unit number space is created, a
43  * default global mutex is used.  The advantage to passing a mutex in, is
44  * that the the alloc_unrl() function can be called with the mutex already
45  * held (it will not be released by alloc_unrl()).
46  *
47  * The allocation function alloc_unr{l}() never sleeps (but it may block on
48  * the mutex of course).
49  *
50  * Freeing a unit number may require allocating memory, and can therefore
51  * sleep so the free_unr() function does not come in a pre-locked variant.
52  *
53  * A userland test program is included.
54  *
55  * Memory usage is a very complex function of the the exact allocation
56  * pattern, but always very compact:
57  *    * For the very typical case where a single unbroken run of unit
58  *      numbers are allocated 44 bytes are used on x86.
59  *    * For a unit number space of 1000 units and the random pattern
60  *      in the usermode test program included, the worst case usage
61  *      was 252 bytes on x86 for 500 allocated and 500 free units.
62  *    * For a unit number space of 10000 units and the random pattern
63  *      in the usermode test program included, the worst case usage
64  *      was 798 bytes on x86 for 5000 allocated and 5000 free units.
65  *    * The worst case is where every other unit number is allocated and
66  *      the the rest are free.  In that case 44 + N/4 bytes are used where
67  *      N is the number of the highest unit allocated.
68  */
69
70 #include <sys/types.h>
71 #include <sys/queue.h>
72 #include <sys/bitstring.h>
73
74 #ifdef _KERNEL
75
76 #include <sys/param.h>
77 #include <sys/malloc.h>
78 #include <sys/kernel.h>
79 #include <sys/systm.h>
80 #include <sys/limits.h>
81 #include <sys/lock.h>
82 #include <sys/proc.h>
83
84 /*
85  * In theory it would be smarter to allocate the individual blocks
86  * with the zone allocator, but at this time the expectation is that
87  * there will typically not even be enough allocations to fill a single
88  * page, so we stick with malloc for now.
89  */
90 static MALLOC_DEFINE(M_UNIT, "Unitno", "Unit number allocation");
91
92 #define Malloc(foo) kmalloc(foo, M_UNIT, M_WAITOK | M_ZERO)
93 #define Free(foo) kfree(foo, M_UNIT)
94
95 static struct lock unit_lock;
96
97 LOCK_SYSINIT(unit, &unit_lock, "unit# allocation", LK_CANRECURSE);
98
99 #else /* ...USERLAND */
100
101 /* No unit allocation on DragonFly's userland */
102
103 #endif /* USERLAND */
104
105 /*
106  * This is our basic building block.
107  *
108  * It can be used in three different ways depending on the value of the ptr
109  * element:
110  *     If ptr is NULL, it represents a run of free items.
111  *     If ptr points to the unrhdr it represents a run of allocated items.
112  *     Otherwise it points to an bitstring of allocated items.
113  *
114  * For runs the len field is the length of the run.
115  * For bitmaps the len field represents the number of allocated items.
116  *
117  * The bitmap is the same size as struct unr to optimize memory management.
118  */
119 struct unr {
120         TAILQ_ENTRY(unr)        list;
121         u_int                   len;
122         void                    *ptr;
123 };
124
125 struct unrb {
126         u_char                  busy;
127         bitstr_t                map[sizeof(struct unr) - 1];
128 };
129
130 CTASSERT(sizeof(struct unr) == sizeof(struct unrb));
131
132 /* Number of bits in the bitmap */
133 #define NBITS   ((int)sizeof(((struct unrb *)NULL)->map) * 8)
134
135 /* Header element for a unr number space. */
136
137 struct unrhdr {
138         TAILQ_HEAD(unrhd,unr)   head;
139         u_int                   low;    /* Lowest item */
140         u_int                   high;   /* Highest item */
141         u_int                   busy;   /* Count of allocated items */
142         u_int                   alloc;  /* Count of memory allocations */
143         u_int                   first;  /* items in allocated from start */
144         u_int                   last;   /* items free at end */
145         struct lock             *lock;
146 };
147
148
149 #if defined(DIAGNOSTIC) || !defined(_KERNEL)
150 /*
151  * Consistency check function.
152  *
153  * Checks the internal consistency as well as we can.
154  *
155  * Called at all boundaries of this API.
156  */
157 static void
158 check_unrhdr(struct unrhdr *uh, int line)
159 {
160         struct unr *up;
161         struct unrb *ub;
162         u_int x, y, z, w;
163
164         y = uh->first;
165         z = 0;
166         TAILQ_FOREACH(up, &uh->head, list) {
167                 z++;
168                 if (up->ptr != uh && up->ptr != NULL) {
169                         ub = up->ptr;
170                         KASSERT (up->len <= NBITS,
171                             ("UNR inconsistency: len %u max %d (line %d)\n",
172                             up->len, NBITS, line));
173                         z++;
174                         w = 0;
175                         for (x = 0; x < up->len; x++)
176                                 if (bit_test(ub->map, x))
177                                         w++;
178                         KASSERT (w == ub->busy,
179                             ("UNR inconsistency: busy %u found %u (line %d)\n",
180                             ub->busy, w, line));
181                         y += w;
182                 } else if (up->ptr != NULL)
183                         y += up->len;
184         }
185         KASSERT (y == uh->busy,
186             ("UNR inconsistency: items %u found %u (line %d)\n",
187             uh->busy, y, line));
188         KASSERT (z == uh->alloc,
189             ("UNR inconsistency: chunks %u found %u (line %d)\n",
190             uh->alloc, z, line));
191 }
192
193 #else
194
195 static __inline void
196 check_unrhdr(struct unrhdr *uh, int line)
197 {
198
199 }
200
201 #endif
202
203
204 /*
205  * Userland memory management.  Just use calloc and keep track of how
206  * many elements we have allocated for check_unrhdr().
207  */
208
209 static __inline void *
210 new_unr(struct unrhdr *uh, void **p1, void **p2)
211 {
212         void *p;
213
214         uh->alloc++;
215         KASSERT(*p1 != NULL || *p2 != NULL, ("Out of cached memory"));
216         if (*p1 != NULL) {
217                 p = *p1;
218                 *p1 = NULL;
219                 return (p);
220         } else {
221                 p = *p2;
222                 *p2 = NULL;
223                 return (p);
224         }
225 }
226
227 static __inline void
228 delete_unr(struct unrhdr *uh, void *ptr)
229 {
230
231         uh->alloc--;
232         Free(ptr);
233 }
234
235 /*
236  * Allocate a new unrheader set.
237  *
238  * Highest and lowest valid values given as paramters.
239  */
240
241 struct unrhdr *
242 new_unrhdr(int low, int high, struct lock *lock)
243 {
244         struct unrhdr *uh;
245
246         KASSERT(low <= high,
247             ("UNR: use error: new_unrhdr(%u, %u)", low, high));
248         uh = Malloc(sizeof *uh);
249         if (lock != NULL)
250                 uh->lock = lock;
251         else
252                 uh->lock = &unit_lock;
253         TAILQ_INIT(&uh->head);
254         uh->low = low;
255         uh->high = high;
256         uh->first = 0;
257         uh->last = 1 + (high - low);
258         check_unrhdr(uh, __LINE__);
259         return (uh);
260 }
261
262 void
263 delete_unrhdr(struct unrhdr *uh)
264 {
265
266         check_unrhdr(uh, __LINE__);
267         KASSERT(uh->busy == 0, ("unrhdr has %u allocations", uh->busy));
268         KASSERT(uh->alloc == 0, ("UNR memory leak in delete_unrhdr"));
269         Free(uh);
270 }
271
272 static __inline int
273 is_bitmap(struct unrhdr *uh, struct unr *up)
274 {
275         return (up->ptr != uh && up->ptr != NULL);
276 }
277
278 /*
279  * Look for sequence of items which can be combined into a bitmap, if
280  * multiple are present, take the one which saves most memory.
281  *
282  * Return (1) if a sequence was found to indicate that another call
283  * might be able to do more.  Return (0) if we found no suitable sequence.
284  *
285  * NB: called from alloc_unr(), no new memory allocation allowed.
286  */
287 static int
288 optimize_unr(struct unrhdr *uh)
289 {
290         struct unr *up, *uf, *us;
291         struct unrb *ub, *ubf;
292         u_int a, l, ba;
293
294         /*
295          * Look for the run of items (if any) which when collapsed into
296          * a bitmap would save most memory.
297          */
298         us = NULL;
299         ba = 0;
300         TAILQ_FOREACH(uf, &uh->head, list) {
301                 if (uf->len >= NBITS)
302                         continue;
303                 a = 1;
304                 if (is_bitmap(uh, uf))
305                         a++;
306                 l = uf->len;
307                 up = uf;
308                 while (1) {
309                         up = TAILQ_NEXT(up, list);
310                         if (up == NULL)
311                                 break;
312                         if ((up->len + l) > NBITS)
313                                 break;
314                         a++;
315                         if (is_bitmap(uh, up))
316                                 a++;
317                         l += up->len;
318                 }
319                 if (a > ba) {
320                         ba = a;
321                         us = uf;
322                 }
323         }
324         if (ba < 3)
325                 return (0);
326
327         /*
328          * If the first element is not a bitmap, make it one.
329          * Trying to do so without allocating more memory complicates things
330          * a bit
331          */
332         if (!is_bitmap(uh, us)) {
333                 uf = TAILQ_NEXT(us, list);
334                 TAILQ_REMOVE(&uh->head, us, list);
335                 a = us->len;
336                 l = us->ptr == uh ? 1 : 0;
337                 ub = (void *)us;
338                 ub->busy = 0;
339                 if (l) {
340                         bit_nset(ub->map, 0, a);
341                         ub->busy += a;
342                 } else {
343                         bit_nclear(ub->map, 0, a);
344                 }
345                 if (!is_bitmap(uh, uf)) {
346                         if (uf->ptr == NULL) {
347                                 bit_nclear(ub->map, a, a + uf->len - 1);
348                         } else {
349                                 bit_nset(ub->map, a, a + uf->len - 1);
350                                 ub->busy += uf->len;
351                         }
352                         uf->ptr = ub;
353                         uf->len += a;
354                         us = uf;
355                 } else {
356                         ubf = uf->ptr;
357                         for (l = 0; l < uf->len; l++, a++) {
358                                 if (bit_test(ubf->map, l)) {
359                                         bit_set(ub->map, a);
360                                         ub->busy++;
361                                 } else {
362                                         bit_clear(ub->map, a);
363                                 }
364                         }
365                         uf->len = a;
366                         delete_unr(uh, uf->ptr);
367                         uf->ptr = ub;
368                         us = uf;
369                 }
370         }
371         ub = us->ptr;
372         while (1) {
373                 uf = TAILQ_NEXT(us, list);
374                 if (uf == NULL)
375                         return (1);
376                 if (uf->len + us->len > NBITS)
377                         return (1);
378                 if (uf->ptr == NULL) {
379                         bit_nclear(ub->map, us->len, us->len + uf->len - 1);
380                         us->len += uf->len;
381                         TAILQ_REMOVE(&uh->head, uf, list);
382                         delete_unr(uh, uf);
383                 } else if (uf->ptr == uh) {
384                         bit_nset(ub->map, us->len, us->len + uf->len - 1);
385                         ub->busy += uf->len;
386                         us->len += uf->len;
387                         TAILQ_REMOVE(&uh->head, uf, list);
388                         delete_unr(uh, uf);
389                 } else {
390                         ubf = uf->ptr;
391                         for (l = 0; l < uf->len; l++, us->len++) {
392                                 if (bit_test(ubf->map, l)) {
393                                         bit_set(ub->map, us->len);
394                                         ub->busy++;
395                                 } else {
396                                         bit_clear(ub->map, us->len);
397                                 }
398                         }
399                         TAILQ_REMOVE(&uh->head, uf, list);
400                         delete_unr(uh, ubf);
401                         delete_unr(uh, uf);
402                 }
403         }
404 }
405
406 /*
407  * See if a given unr should be collapsed with a neighbor.
408  *
409  * NB: called from alloc_unr(), no new memory allocation allowed.
410  */
411 static void
412 collapse_unr(struct unrhdr *uh, struct unr *up)
413 {
414         struct unr *upp;
415         struct unrb *ub;
416
417         /* If bitmap is all set or clear, change it to runlength */
418         if (is_bitmap(uh, up)) {
419                 ub = up->ptr;
420                 if (ub->busy == up->len) {
421                         delete_unr(uh, up->ptr);
422                         up->ptr = uh;
423                 } else if (ub->busy == 0) {
424                         delete_unr(uh, up->ptr);
425                         up->ptr = NULL;
426                 }
427         }
428
429         /* If nothing left in runlength, delete it */
430         if (up->len == 0) {
431                 upp = TAILQ_PREV(up, unrhd, list);
432                 if (upp == NULL)
433                         upp = TAILQ_NEXT(up, list);
434                 TAILQ_REMOVE(&uh->head, up, list);
435                 delete_unr(uh, up);
436                 up = upp;
437         }
438
439         /* If we have "hot-spot" still, merge with neighbor if possible */
440         if (up != NULL) {
441                 upp = TAILQ_PREV(up, unrhd, list);
442                 if (upp != NULL && up->ptr == upp->ptr) {
443                         up->len += upp->len;
444                         TAILQ_REMOVE(&uh->head, upp, list);
445                         delete_unr(uh, upp);
446                         }
447                 upp = TAILQ_NEXT(up, list);
448                 if (upp != NULL && up->ptr == upp->ptr) {
449                         up->len += upp->len;
450                         TAILQ_REMOVE(&uh->head, upp, list);
451                         delete_unr(uh, upp);
452                 }
453         }
454
455         /* Merge into ->first if possible */
456         upp = TAILQ_FIRST(&uh->head);
457         if (upp != NULL && upp->ptr == uh) {
458                 uh->first += upp->len;
459                 TAILQ_REMOVE(&uh->head, upp, list);
460                 delete_unr(uh, upp);
461                 if (up == upp)
462                         up = NULL;
463         }
464
465         /* Merge into ->last if possible */
466         upp = TAILQ_LAST(&uh->head, unrhd);
467         if (upp != NULL && upp->ptr == NULL) {
468                 uh->last += upp->len;
469                 TAILQ_REMOVE(&uh->head, upp, list);
470                 delete_unr(uh, upp);
471                 if (up == upp)
472                         up = NULL;
473         }
474
475         /* Try to make bitmaps */
476         while (optimize_unr(uh))
477                 continue;
478 }
479
480 /*
481  * Allocate a free unr.
482  */
483 int
484 alloc_unrl(struct unrhdr *uh)
485 {
486         struct unr *up;
487         struct unrb *ub;
488         u_int x;
489         int y;
490         struct lock *ml __debugvar = uh->lock;
491         struct thread *td __debugvar = curthread;
492
493         KKASSERT(lockstatus(ml, td) != 0);
494         check_unrhdr(uh, __LINE__);
495         x = uh->low + uh->first;
496
497         up = TAILQ_FIRST(&uh->head);
498
499         /*
500          * If we have an ideal split, just adjust the first+last
501          */
502         if (up == NULL && uh->last > 0) {
503                 uh->first++;
504                 uh->last--;
505                 uh->busy++;
506                 return (x);
507         }
508
509         /*
510          * We can always allocate from the first list element, so if we have
511          * nothing on the list, we must have run out of unit numbers.
512          */
513         if (up == NULL)
514                 return (-1);
515
516         KASSERT(up->ptr != uh, ("UNR first element is allocated"));
517
518         if (up->ptr == NULL) {  /* free run */
519                 uh->first++;
520                 up->len--;
521         } else {                /* bitmap */
522                 ub = up->ptr;
523                 KASSERT(ub->busy < up->len, ("UNR bitmap confusion"));
524                 bit_ffc(ub->map, up->len, &y);
525                 KASSERT(y != -1, ("UNR corruption: No clear bit in bitmap."));
526                 bit_set(ub->map, y);
527                 ub->busy++;
528                 x += y;
529         }
530         uh->busy++;
531         collapse_unr(uh, up);
532         return (x);
533 }
534
535 int
536 alloc_unr(struct unrhdr *uh)
537 {
538         int i;
539
540         lockmgr(uh->lock, LK_EXCLUSIVE);
541         i = alloc_unrl(uh);
542         lockmgr(uh->lock, LK_RELEASE);
543         return (i);
544 }
545
546 /*
547  * Free a unr.
548  *
549  * If we can save unrs by using a bitmap, do so.
550  */
551 static void
552 free_unrl(struct unrhdr *uh, u_int item, void **p1, void **p2)
553 {
554         struct unr *up, *upp, *upn;
555         struct unrb *ub;
556         u_int pl;
557
558         KASSERT(item >= uh->low && item <= uh->high,
559             ("UNR: free_unr(%u) out of range [%u...%u]",
560              item, uh->low, uh->high));
561         check_unrhdr(uh, __LINE__);
562         item -= uh->low;
563         upp = TAILQ_FIRST(&uh->head);
564         /*
565          * Freeing in the ideal split case
566          */
567         if (item + 1 == uh->first && upp == NULL) {
568                 uh->last++;
569                 uh->first--;
570                 uh->busy--;
571                 check_unrhdr(uh, __LINE__);
572                 return;
573         }
574         /*
575          * Freeing in the ->first section.  Create a run starting at the
576          * freed item.  The code below will subdivide it.
577          */
578         if (item < uh->first) {
579                 up = new_unr(uh, p1, p2);
580                 up->ptr = uh;
581                 up->len = uh->first - item;
582                 TAILQ_INSERT_HEAD(&uh->head, up, list);
583                 uh->first -= up->len;
584         }
585
586         item -= uh->first;
587
588         /* Find the item which contains the unit we want to free */
589         TAILQ_FOREACH(up, &uh->head, list) {
590                 if (up->len > item)
591                         break;
592                 item -= up->len;
593         }
594
595         /* Handle bitmap items */
596         if (is_bitmap(uh, up)) {
597                 ub = up->ptr;
598                 
599                 KASSERT(bit_test(ub->map, item) != 0,
600                     ("UNR: Freeing free item %d (bitmap)\n", item));
601                 bit_clear(ub->map, item);
602                 uh->busy--;
603                 ub->busy--;
604                 collapse_unr(uh, up);
605                 return;
606         }
607
608         KASSERT(up->ptr == uh, ("UNR Freeing free item %d (run))\n", item));
609
610         /* Just this one left, reap it */
611         if (up->len == 1) {
612                 up->ptr = NULL;
613                 uh->busy--;
614                 collapse_unr(uh, up);
615                 return;
616         }
617
618         /* Check if we can shift the item into the previous 'free' run */
619         upp = TAILQ_PREV(up, unrhd, list);
620         if (item == 0 && upp != NULL && upp->ptr == NULL) {
621                 upp->len++;
622                 up->len--;
623                 uh->busy--;
624                 collapse_unr(uh, up);
625                 return;
626         }
627
628         /* Check if we can shift the item to the next 'free' run */
629         upn = TAILQ_NEXT(up, list);
630         if (item == up->len - 1 && upn != NULL && upn->ptr == NULL) {
631                 upn->len++;
632                 up->len--;
633                 uh->busy--;
634                 collapse_unr(uh, up);
635                 return;
636         }
637
638         /* Split off the tail end, if any. */
639         pl = up->len - (1 + item);
640         if (pl > 0) {
641                 upp = new_unr(uh, p1, p2);
642                 upp->ptr = uh;
643                 upp->len = pl;
644                 TAILQ_INSERT_AFTER(&uh->head, up, upp, list);
645         }
646
647         /* Split off head end, if any */
648         if (item > 0) {
649                 upp = new_unr(uh, p1, p2);
650                 upp->len = item;
651                 upp->ptr = uh;
652                 TAILQ_INSERT_BEFORE(up, upp, list);
653         }
654         up->len = 1;
655         up->ptr = NULL;
656         uh->busy--;
657         collapse_unr(uh, up);
658 }
659
660 void
661 free_unr(struct unrhdr *uh, u_int item)
662 {
663         void *p1, *p2;
664
665         p1 = Malloc(sizeof(struct unr));
666         p2 = Malloc(sizeof(struct unr));
667         lockmgr(uh->lock, LK_EXCLUSIVE);
668         free_unrl(uh, item, &p1, &p2);
669         lockmgr(uh->lock, LK_RELEASE);
670         if (p1 != NULL)
671                 Free(p1);
672         if (p2 != NULL)
673                 Free(p2);
674 }
675
676 #ifndef _KERNEL /* USERLAND test driver */
677
678 /*
679  * Simple stochastic test driver for the above functions
680  */
681
682 static void
683 print_unr(struct unrhdr *uh, struct unr *up)
684 {
685         u_int x;
686         struct unrb *ub;
687
688         printf("  %p len = %5u ", up, up->len);
689         if (up->ptr == NULL)
690                 printf("free\n");
691         else if (up->ptr == uh)
692                 printf("alloc\n");
693         else {
694                 ub = up->ptr;
695                 printf("bitmap(%d) [", ub->busy);
696                 for (x = 0; x < up->len; x++) {
697                         if (bit_test(ub->map, x))
698                                 printf("#");
699                         else
700                                 printf(" ");
701                 }
702                 printf("]\n");
703         }
704 }
705
706 static void
707 print_unrhdr(struct unrhdr *uh)
708 {
709         struct unr *up;
710         u_int x;
711
712         printf(
713             "%p low = %u high = %u first = %u last = %u busy %u chunks = %u\n",
714             uh, uh->low, uh->high, uh->first, uh->last, uh->busy, uh->alloc);
715         x = uh->low + uh->first;
716         TAILQ_FOREACH(up, &uh->head, list) {
717                 printf("  from = %5u", x);
718                 print_unr(uh, up);
719                 if (up->ptr == NULL || up->ptr == uh)
720                         x += up->len;
721                 else
722                         x += NBITS;
723         }
724 }
725
726 /* Number of unrs to test */
727 #define NN      10000
728
729 int
730 main(int argc __unused, const char **argv __unused)
731 {
732         struct unrhdr *uh;
733         u_int i, x, m, j;
734         char a[NN];
735
736         setbuf(stdout, NULL);
737         uh = new_unrhdr(0, NN - 1, NULL);
738         print_unrhdr(uh);
739
740         memset(a, 0, sizeof a);
741
742         fprintf(stderr, "sizeof(struct unr) %d\n", sizeof (struct unr));
743         fprintf(stderr, "sizeof(struct unrb) %d\n", sizeof (struct unrb));
744         fprintf(stderr, "sizeof(struct unrhdr) %d\n", sizeof (struct unrhdr));
745         fprintf(stderr, "NBITS %d\n", NBITS);
746         x = 1;
747         for (m = 0; m < NN * 100; m++) {
748                 j = random();
749                 i = (j >> 1) % NN;
750 #if 0
751                 if (a[i] && (j & 1))
752                         continue;
753 #endif
754                 if (a[i]) {
755                         printf("F %u\n", i);
756                         free_unr(uh, i);
757                         a[i] = 0;
758                 } else {
759                         no_alloc = 1;
760                         i = alloc_unr(uh);
761                         if (i != -1) {
762                                 a[i] = 1;
763                                 printf("A %u\n", i);
764                         }
765                         no_alloc = 0;
766                 }
767                 if (1)  /* XXX: change this for detailed debug printout */
768                         print_unrhdr(uh);
769                 check_unrhdr(uh, __LINE__);
770         }
771         for (i = 0; i < NN; i++) {
772                 if (a[i]) {
773                         printf("C %u\n", i);
774                         free_unr(uh, i);
775                         print_unrhdr(uh);
776                 }
777         }
778         print_unrhdr(uh);
779         delete_unrhdr(uh);
780         return (0);
781 }
782 #endif