4 * Bitmap allocator/deallocator, using a radix tree with hinting.
5 * Unlimited-size allocations, power-of-2 only, power-of-2 aligned results
6 * only. This module has been separated from the generic kernel module and
7 * written specifically for embedding in HAMMER storage structures.
9 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
11 * This code is derived from software contributed to The DragonFly Project
12 * by Matthew Dillon <dillon@backplane.com>
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
24 * 3. Neither the name of The DragonFly Project nor the names of its
25 * contributors may be used to endorse or promote products derived
26 * from this software without specific, prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
33 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
34 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
35 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
36 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 * $DragonFly: src/sys/vfs/hammer/Attic/hammer_alist.c,v 1.4 2007/11/01 20:53:05 dillon Exp $
44 * This module implements a generic allocator through the use of a hinted
45 * radix tree. All allocations must be in powers of 2 and will return
46 * similarly aligned results. The radix tree typically recurses within
47 * a memory buffer and then continues its recursion by chaining to other
48 * memory buffers, creating a seemless whole capable of managing any amount
51 * The radix tree is layed out recursively using a linear array. Each meta
52 * node is immediately followed (layed out sequentially in memory) by
53 * HAMMER_ALIST_META_RADIX lower level nodes. This is a recursive structure
54 * but one that can be easily scanned through a very simple 'skip'
57 * The radix tree supports an early-termination optimization which
58 * effectively allows us to efficiently mix large and small allocations
59 * with a single abstraction. The address space can be partitioned
60 * arbitrarily without adding much in the way of additional meta-storage
63 * The radix tree supports allocator layering. By supplying a base_radix > 1
64 * the allocator will issue callbacks to recurse into lower layers once
65 * higher layers have been exhausted. Allocations in multiples of base_radix
66 * will be entirely retained in the higher level allocator and never recurse.
68 * This code can be compiled stand-alone for debugging.
73 #include <sys/param.h>
74 #include <sys/systm.h>
76 #include <sys/kernel.h>
77 #include <sys/malloc.h>
79 #include <vm/vm_object.h>
80 #include <vm/vm_kern.h>
81 #include <vm/vm_extern.h>
82 #include <vm/vm_page.h>
84 #include "hammer_alist.h"
85 #include "hammer_disk.h"
89 #ifndef ALIST_NO_DEBUG
93 #include <sys/types.h>
100 #define kmalloc(a,b,c) malloc(a)
101 #define kfree(a,b) free(a)
102 #define kprintf printf
103 #define KKASSERT(exp) assert(exp)
106 #include "hammer_alist.h"
108 void panic(const char *ctl, ...);
113 * static support functions
116 static int32_t hammer_alst_leaf_alloc_fwd(hammer_almeta_t scan,
117 int32_t blk, int count, int32_t atblk);
118 static int32_t hammer_alst_meta_alloc_fwd(hammer_alist_t live,
119 hammer_almeta_t scan,
120 int32_t blk, int32_t count,
121 int32_t radix, int skip, int32_t atblk);
122 static int32_t hammer_alst_leaf_alloc_rev(hammer_almeta_t scan,
123 int32_t blk, int count, int32_t atblk);
124 static int32_t hammer_alst_meta_alloc_rev(hammer_alist_t live,
125 hammer_almeta_t scan,
126 int32_t blk, int32_t count,
127 int32_t radix, int skip, int32_t atblk);
128 static void hammer_alst_leaf_free(hammer_almeta_t scan,
129 int32_t relblk, int count);
130 static void hammer_alst_meta_free(hammer_alist_t live, hammer_almeta_t scan,
131 int32_t freeBlk, int32_t count,
132 int32_t radix, int skip, int32_t blk);
133 static int32_t hammer_alst_radix_init(hammer_almeta_t scan,
134 int32_t radix, int skip, int32_t count);
136 static void hammer_alst_radix_print(hammer_alist_t live,
137 hammer_almeta_t scan, int32_t blk,
138 int32_t radix, int skip, int tab);
142 * Initialize an a-list config structure for use. The config structure
143 * describes the basic structure of an a-list's topology and may be
144 * shared by any number of a-lists which have the same topology.
146 * blocks is the total number of blocks, that is the number of blocks
147 * handled at this layer multiplied by the base radix.
149 * When base_radix != 1 the A-list has only meta elements and does not have
150 * any leaves, in order to be able to track partial allocations.
153 hammer_alist_template(hammer_alist_config_t bl, int32_t blocks,
154 int32_t base_radix, int32_t maxmeta)
160 * Calculate radix and skip field used for scanning. The leaf nodes
161 * in our tree are either BMAP or META nodes depending on whether
162 * we chain to a lower level allocation layer or not.
165 radix = HAMMER_ALIST_BMAP_RADIX;
167 radix = HAMMER_ALIST_META_RADIX;
170 while (radix < blocks / base_radix) {
171 radix *= HAMMER_ALIST_META_RADIX;
172 skip = skip * HAMMER_ALIST_META_RADIX + 1;
176 * Increase the radix based on the number of blocks a lower level
177 * allocator is able to handle at the 'base' of our allocator.
178 * If base_radix != 1 the caller will have to initialize the callback
179 * fields to implement the lower level allocator.
183 bzero(bl, sizeof(*bl));
185 bl->bl_blocks = blocks;
186 bl->bl_base_radix = base_radix;
187 bl->bl_radix = radix;
189 bl->bl_rootblks = hammer_alst_radix_init(NULL, bl->bl_radix,
190 bl->bl_skip, blocks);
191 ++bl->bl_rootblks; /* one more for freeblks header */
194 KKASSERT(maxmeta == 0 || bl->bl_rootblks <= maxmeta);
196 #if defined(ALIST_DEBUG)
198 "PRIMARY ALIST LAYER manages %d blocks"
199 ", requiring %dK (%d bytes) of ram\n",
200 bl->bl_blocks / bl->bl_base_radix,
201 (bl->bl_rootblks * sizeof(struct hammer_almeta) + 1023) / 1024,
202 (bl->bl_rootblks * sizeof(struct hammer_almeta))
204 kprintf("ALIST raw radix tree contains %d records\n", bl->bl_rootblks);
209 hammer_alist_init(hammer_alist_t live)
211 hammer_alist_config_t bl = live->config;
213 live->meta->bm_bighint = 0;
214 live->meta->bm_alist_freeblks = 0;
215 hammer_alst_radix_init(live->meta + 1, bl->bl_radix,
216 bl->bl_skip, bl->bl_blocks);
219 #if !defined(_KERNEL) && defined(ALIST_DEBUG)
222 * hammer_alist_create() (userland only)
224 * create a alist capable of handling up to the specified number of
225 * blocks. blocks must be greater then 0
227 * The smallest alist consists of a single leaf node capable of
228 * managing HAMMER_ALIST_BMAP_RADIX blocks, or (if base_radix != 1)
229 * a single meta node capable of managing HAMMER_ALIST_META_RADIX
230 * blocks which recurses into other storage layers for a total
231 * handling capability of (HAMMER_ALIST_META_RADIX * base_radix) blocks.
233 * Larger a-list's increase their capability exponentially by
234 * HAMMER_ALIST_META_RADIX.
236 * The block count is the total number of blocks inclusive of any
237 * layering. blocks can be less then base_radix and will result in
238 * a radix tree with a single leaf node which then chains down.
242 hammer_alist_create(int32_t blocks, int32_t base_radix,
243 struct malloc_type *mtype)
246 hammer_alist_config_t bl;
249 live = kmalloc(sizeof(*live), mtype, M_WAITOK);
250 live->config = bl = kmalloc(sizeof(*bl), mtype, M_WAITOK);
251 hammer_alist_template(bl, blocks, base_radix, 0);
253 metasize = sizeof(*live->meta) * bl->bl_rootblks;
254 live->meta = kmalloc(metasize, mtype, M_WAITOK);
255 bzero(live->meta, metasize);
257 #if defined(ALIST_DEBUG)
259 "ALIST representing %d blocks (%d MB of swap)"
260 ", requiring %dK (%d bytes) of ram\n",
262 bl->bl_blocks * 4 / 1024,
263 (bl->bl_rootblks * sizeof(*live->meta) + 1023) / 1024,
264 (bl->bl_rootblks * sizeof(*live->meta))
266 if (base_radix != 1) {
267 kprintf("ALIST recurses when it reaches a base_radix of %d\n",
270 kprintf("ALIST raw radix tree contains %d records\n", bl->bl_rootblks);
272 hammer_alist_init(live);
277 hammer_alist_destroy(hammer_alist_t live, struct malloc_type *mtype)
279 kfree(live->config, mtype);
280 kfree(live->meta, mtype);
289 * hammer_alist_alloc()
291 * Reserve space in the block bitmap. Return the base of a contiguous
292 * region or HAMMER_ALIST_BLOCK_NONE if space could not be allocated.
296 hammer_alist_alloc(hammer_alist_t live, int32_t count)
298 int32_t blk = HAMMER_ALIST_BLOCK_NONE;
299 hammer_alist_config_t bl = live->config;
301 KKASSERT((count | (count - 1)) == (count << 1) - 1);
303 if (bl && count <= bl->bl_radix) {
305 * When skip is 1 we are at a leaf. If we are the terminal
306 * allocator we use our native leaf functions and radix will
307 * be HAMMER_ALIST_BMAP_RADIX. Otherwise we are a meta node
308 * which will chain to another allocator.
310 if (bl->bl_skip == 1 && bl->bl_terminal) {
312 KKASSERT(bl->bl_radix == HAMMER_ALIST_BMAP_RADIX);
314 blk = hammer_alst_leaf_alloc_fwd(
315 live->meta + 1, 0, count, 0);
317 blk = hammer_alst_meta_alloc_fwd(
318 live, live->meta + 1,
319 0, count, bl->bl_radix, bl->bl_skip, 0);
321 if (blk != HAMMER_ALIST_BLOCK_NONE)
322 live->meta->bm_alist_freeblks -= count;
328 hammer_alist_alloc_fwd(hammer_alist_t live, int32_t count, int32_t atblk)
330 int32_t blk = HAMMER_ALIST_BLOCK_NONE;
331 hammer_alist_config_t bl = live->config;
333 KKASSERT((count | (count - 1)) == (count << 1) - 1);
335 if (bl && count <= bl->bl_radix) {
337 * When skip is 1 we are at a leaf. If we are the terminal
338 * allocator we use our native leaf functions and radix will
339 * be HAMMER_ALIST_BMAP_RADIX. Otherwise we are a meta node
340 * which will chain to another allocator.
342 if (bl->bl_skip == 1 && bl->bl_terminal) {
344 KKASSERT(bl->bl_radix == HAMMER_ALIST_BMAP_RADIX);
346 blk = hammer_alst_leaf_alloc_fwd(
347 live->meta + 1, 0, count, atblk);
349 blk = hammer_alst_meta_alloc_fwd(
350 live, live->meta + 1,
351 0, count, bl->bl_radix, bl->bl_skip, atblk);
353 if (blk != HAMMER_ALIST_BLOCK_NONE)
354 live->meta->bm_alist_freeblks -= count;
360 hammer_alist_alloc_rev(hammer_alist_t live, int32_t count, int32_t atblk)
362 hammer_alist_config_t bl = live->config;
363 int32_t blk = HAMMER_ALIST_BLOCK_NONE;
365 KKASSERT((count | (count - 1)) == (count << 1) - 1);
367 if (bl && count < bl->bl_radix) {
368 if (bl->bl_skip == 1 && bl->bl_terminal) {
370 KKASSERT(bl->bl_radix == HAMMER_ALIST_BMAP_RADIX);
372 blk = hammer_alst_leaf_alloc_rev(
373 live->meta + 1, 0, count, atblk);
375 blk = hammer_alst_meta_alloc_rev(
376 live, live->meta + 1,
377 0, count, bl->bl_radix, bl->bl_skip, atblk);
379 if (blk != HAMMER_ALIST_BLOCK_NONE)
380 live->meta->bm_alist_freeblks -= count;
388 * Free up space in the block bitmap. Return the base of a contiguous
389 * region. Panic if an inconsistancy is found.
391 * Unlike allocations, there are no alignment requirements for blkno or
392 * count when freeing blocks.
396 hammer_alist_free(hammer_alist_t live, int32_t blkno, int32_t count)
398 hammer_alist_config_t bl = live->config;
400 KKASSERT(blkno + count <= bl->bl_blocks);
401 if (bl->bl_skip == 1 && bl->bl_terminal) {
403 KKASSERT(bl->bl_radix == HAMMER_ALIST_BMAP_RADIX);
405 hammer_alst_leaf_free(live->meta + 1, blkno, count);
407 hammer_alst_meta_free(live, live->meta + 1,
409 bl->bl_radix, bl->bl_skip, 0);
411 live->meta->bm_alist_freeblks += count;
415 hammer_alist_isfull(hammer_alist_t live)
417 return(live->meta->bm_alist_freeblks == 0);
421 hammer_alist_isempty(hammer_alist_t live)
423 return(live->meta->bm_alist_freeblks == live->config->bl_radix);
429 * alist_print() - dump radix tree
433 hammer_alist_print(hammer_alist_t live, int tab)
435 hammer_alist_config_t bl = live->config;
437 kprintf("%*.*sALIST (%d free blocks) {\n",
438 tab, tab, "", live->meta->bm_alist_freeblks);
439 hammer_alst_radix_print(live, live->meta + 1, 0,
440 bl->bl_radix, bl->bl_skip, tab + 4);
441 kprintf("%*.*s}\n", tab, tab, "");
446 /************************************************************************
447 * ALLOCATION SUPPORT FUNCTIONS *
448 ************************************************************************
450 * These support functions do all the actual work. They may seem
451 * rather longish, but that's because I've commented them up. The
452 * actual code is straight forward.
457 * hammer_alist_leaf_alloc_fwd()
459 * Allocate at a leaf in the radix tree (a bitmap).
461 * This is the core of the allocator and is optimized for the 1 block
462 * and the HAMMER_ALIST_BMAP_RADIX block allocation cases. Other cases
463 * are somewhat slower. The 1 block allocation case is log2 and extremely
468 hammer_alst_leaf_alloc_fwd(hammer_almeta_t scan, int32_t blk,
469 int count, int32_t atblk)
471 u_int32_t orig = scan->bm_bitmap;
472 int32_t saveblk = blk;
475 * Optimize bitmap all-allocated case. Also, count = 1
476 * case assumes at least 1 bit is free in the bitmap, so
477 * we have to take care of this case here.
480 scan->bm_bighint = 0;
481 return(HAMMER_ALIST_BLOCK_NONE);
486 * Optimized code to allocate one bit out of the bitmap
488 * mask iterates e.g. 00001111 00000011 00000001
490 * mask starts at 00001111
494 int j = HAMMER_ALIST_BMAP_RADIX/2;
497 mask = (u_int32_t)-1 >> (HAMMER_ALIST_BMAP_RADIX/2);
500 if ((orig & mask) == 0) {
507 scan->bm_bitmap &= ~(1 << r);
513 * non-optimized code to allocate N bits out of the bitmap.
514 * The more bits, the faster the code runs. It will run
515 * the slowest allocating 2 bits, but since there aren't any
516 * memory ops in the core loop (or shouldn't be, anyway),
517 * you probably won't notice the difference.
519 * Similar to the blist case, the alist code also requires
520 * allocations to be power-of-2 sized and aligned to the
521 * size of the allocation, which simplifies the algorithm.
525 int n = HAMMER_ALIST_BMAP_RADIX - count;
528 mask = (u_int32_t)-1 >> n;
530 for (j = 0; j <= n; j += count) {
531 if ((orig & mask) == mask && blk >= atblk) {
532 scan->bm_bitmap &= ~mask;
535 mask = mask << count;
541 * We couldn't allocate count in this subtree, update bighint if
542 * atblk didn't interfere with the hinting mechanism.
544 if (saveblk >= atblk)
545 scan->bm_bighint = count - 1;
546 return(HAMMER_ALIST_BLOCK_NONE);
550 * This version allocates blocks in the reverse direction.
553 hammer_alst_leaf_alloc_rev(hammer_almeta_t scan, int32_t blk,
554 int count, int32_t atblk)
556 u_int32_t orig = scan->bm_bitmap;
560 * Optimize bitmap all-allocated case. Also, count = 1
561 * case assumes at least 1 bit is free in the bitmap, so
562 * we have to take care of this case here.
565 scan->bm_bighint = 0;
566 return(HAMMER_ALIST_BLOCK_NONE);
571 * Optimized code to allocate one bit out of the bitmap
575 int j = HAMMER_ALIST_BMAP_RADIX/2;
576 int r = HAMMER_ALIST_BMAP_RADIX - 1;
578 mask = ~((u_int32_t)-1 >> (HAMMER_ALIST_BMAP_RADIX/2));
581 if ((orig & mask) == 0) {
588 scan->bm_bitmap &= ~(1 << r);
594 * non-optimized code to allocate N bits out of the bitmap.
595 * The more bits, the faster the code runs. It will run
596 * the slowest allocating 2 bits, but since there aren't any
597 * memory ops in the core loop (or shouldn't be, anyway),
598 * you probably won't notice the difference.
600 * Similar to the blist case, the alist code also requires
601 * allocations to be power-of-2 sized and aligned to the
602 * size of the allocation, which simplifies the algorithm.
604 * initial mask if count == 2: 1100....0000
608 int n = HAMMER_ALIST_BMAP_RADIX - count;
611 mask = ((u_int32_t)-1 >> n) << n;
615 for (j = n; j >= 0; j -= count) {
616 if ((orig & mask) == mask && blk <= atblk) {
617 scan->bm_bitmap &= ~mask;
620 mask = mask >> count;
626 * We couldn't allocate count in this subtree, update bighint if
627 * atblk didn't interfere with it.
629 if (saveblk <= atblk)
630 scan->bm_bighint = count - 1;
631 return(HAMMER_ALIST_BLOCK_NONE);
635 * hammer_alist_meta_alloc_fwd()
637 * Allocate at a meta in the radix tree.
639 * Attempt to allocate at a meta node. If we can't, we update
640 * bighint and return a failure. Updating bighint optimize future
641 * calls that hit this node. We have to check for our collapse cases
642 * and we have a few optimizations strewn in as well.
645 hammer_alst_meta_alloc_fwd(hammer_alist_t live, hammer_almeta_t scan,
646 int32_t blk, int32_t count,
647 int32_t radix, int skip, int32_t atblk
649 hammer_alist_config_t bl;
657 * ALL-ALLOCATED special case
659 if (scan->bm_bitmap == 0) {
660 scan->bm_bighint = 0;
661 return(HAMMER_ALIST_BLOCK_NONE);
664 radix /= HAMMER_ALIST_META_RADIX;
668 * Radix now represents each bitmap entry for this meta node. If
669 * the number of blocks being allocated can be fully represented,
670 * we allocate directly out of this meta node.
672 * Meta node bitmaps use 2 bits per block.
675 * 01 PARTIALLY-FREE/PARTIALLY-ALLOCATED
679 if (count >= radix) {
680 int n = count / radix * 2; /* number of bits */
684 mask = (u_int32_t)-1 >> (HAMMER_ALIST_BMAP_RADIX - n);
686 for (j = 0; j < HAMMER_ALIST_META_RADIX; j += nd2) {
687 if ((scan->bm_bitmap & mask) == mask && blk >= atblk) {
688 scan->bm_bitmap &= ~mask;
694 if (scan->bm_bighint >= count && saveblk >= atblk)
695 scan->bm_bighint = count >> 1;
696 return(HAMMER_ALIST_BLOCK_NONE);
700 * If the count is too big we couldn't allocate anything from a
701 * recursion even if the sub-tree were entirely free.
708 * If not we have to recurse.
715 * If skip is 1 we are a meta leaf node, which means there
716 * is another allocation layer we have to dive down into.
718 for (i = 0; i < HAMMER_ALIST_META_RADIX; ++i) {
720 * If the next layer is completely free then call
721 * its init function to initialize it, then free
722 * all of its blocks before trying to allocate from
725 * bl_radix_init returns an error code or 0 on
728 if ((scan->bm_bitmap & mask) == mask &&
729 blk + radix > atblk) {
730 if (bl->bl_radix_init(live->info, blk, radix) == 0) {
732 scan->bm_bitmap &= ~mask;
733 scan->bm_bitmap |= pmask;
734 bl->bl_radix_free(live->info, blk,
741 * If there may be some free blocks try to allocate
742 * out of the layer. If the layer indicates that
743 * it is completely full then clear both bits to
744 * propogate the condition.
746 if ((scan->bm_bitmap & mask) == pmask &&
747 blk + radix > atblk) {
751 r = bl->bl_radix_alloc_fwd(live->info, blk,
754 if (r != HAMMER_ALIST_BLOCK_NONE) {
756 scan->bm_bitmap &= ~mask;
757 bl->bl_radix_destroy(live->info, blk, radix);
768 * Otherwise there are sub-records in the current a-list
769 * layer. We can also peek into the sub-layers to get
770 * more accurate size hints.
772 next_skip = (skip - 1) / HAMMER_ALIST_META_RADIX;
773 for (i = 1; i <= skip; i += next_skip) {
774 if (scan[i].bm_bighint == (int32_t)-1) {
782 * Initialize bitmap if allocating from the all-free
785 if ((scan->bm_bitmap & mask) == mask) {
786 scan[i].bm_bitmap = (u_int32_t)-1;
787 scan[i].bm_bighint = radix;
790 if (count <= scan[i].bm_bighint &&
791 blk + radix > atblk) {
793 * count fits in object, recurse into the
794 * next layer. If the next_skip is 1 it
795 * will be either a normal leaf or a meta
800 if (next_skip == 1 && bl->bl_terminal) {
801 r = hammer_alst_leaf_alloc_fwd(
802 &scan[i], blk, count, atblk);
804 r = hammer_alst_meta_alloc_fwd(
807 radix, next_skip, atblk);
809 if (r != HAMMER_ALIST_BLOCK_NONE) {
810 if (scan[i].bm_bitmap == 0) {
811 scan->bm_bitmap &= ~mask;
813 scan->bm_bitmap &= ~mask;
814 scan->bm_bitmap |= pmask;
827 * We couldn't allocate count in this subtree, update bighint.
829 if (scan->bm_bighint >= count && saveblk >= atblk)
830 scan->bm_bighint = count >> 1;
831 return(HAMMER_ALIST_BLOCK_NONE);
835 * This version allocates blocks in the reverse direction.
838 hammer_alst_meta_alloc_rev(hammer_alist_t live, hammer_almeta_t scan,
839 int32_t blk, int32_t count,
840 int32_t radix, int skip, int32_t atblk
842 hammer_alist_config_t bl;
851 * ALL-ALLOCATED special case
853 if (scan->bm_bitmap == 0) {
854 scan->bm_bighint = 0;
855 return(HAMMER_ALIST_BLOCK_NONE);
858 radix /= HAMMER_ALIST_META_RADIX;
862 * Radix now represents each bitmap entry for this meta node. If
863 * the number of blocks being allocated can be fully represented,
864 * we allocate directly out of this meta node.
866 * Meta node bitmaps use 2 bits per block.
869 * 01 PARTIALLY-FREE/PARTIALLY-ALLOCATED
873 if (count >= radix) {
874 int n = count / radix * 2; /* number of bits */
875 int nd2 = n / 2; /* number of radi */
879 * Initial mask if e.g. n == 2: 1100....0000
881 mask = (u_int32_t)-1 >> (HAMMER_ALIST_BMAP_RADIX - n) <<
882 (HAMMER_ALIST_BMAP_RADIX - n);
883 blk += (HAMMER_ALIST_META_RADIX - nd2) * radix;
885 for (j = HAMMER_ALIST_META_RADIX - nd2; j >= 0; j -= nd2) {
886 if ((scan->bm_bitmap & mask) == mask && blk <= atblk) {
887 scan->bm_bitmap &= ~mask;
893 if (scan->bm_bighint >= count && saveblk <= atblk)
894 scan->bm_bighint = count >> 1;
895 return(HAMMER_ALIST_BLOCK_NONE);
899 * If the count is too big we couldn't allocate anything from a
900 * recursion even if the sub-tree were entirely free.
903 saveblk = atblk; /* make it work for the conditional */
904 goto failed; /* at the failed label */
909 * We need the recurse but we are at a meta node leaf, which
910 * means there is another layer under us.
914 blk += radix * HAMMER_ALIST_META_RADIX - radix;
917 for (i = 0; i < HAMMER_ALIST_META_RADIX; ++i) {
919 * If the next layer is completely free then call
920 * its init function to initialize it and mark it
923 if ((scan->bm_bitmap & mask) == mask && blk <= atblk) {
924 if (bl->bl_radix_init(live->info, blk, radix) == 0) {
926 scan->bm_bitmap &= ~mask;
927 scan->bm_bitmap |= pmask;
928 bl->bl_radix_free(live->info, blk,
935 * If there may be some free blocks try to allocate
936 * out of the layer. If the layer indicates that
937 * it is completely full then clear both bits to
938 * propogate the condition.
940 if ((scan->bm_bitmap & mask) == pmask && blk <= atblk) {
944 r = bl->bl_radix_alloc_rev(live->info, blk,
947 if (r != HAMMER_ALIST_BLOCK_NONE) {
949 scan->bm_bitmap &= ~mask;
950 bl->bl_radix_destroy(live->info, blk, radix);
961 * Since we are going in the reverse direction we need an
962 * extra loop to determine if there is a terminator, then
965 * This is a little weird but we do not want to overflow the
966 * mask/pmask in the loop.
968 next_skip = (skip - 1) / HAMMER_ALIST_META_RADIX;
970 for (i = 1; i < skip; i += next_skip) {
971 if (scan[i].bm_bighint == (int32_t)-1) {
980 mask = 0x00000003 << j;
981 pmask = 0x00000001 << j;
987 * Initialize the bitmap in the child if allocating
988 * from the all-free case.
990 if ((scan->bm_bitmap & mask) == mask) {
991 scan[i].bm_bitmap = (u_int32_t)-1;
992 scan[i].bm_bighint = radix;
996 * Handle various count cases. Bighint may be too
997 * large but is never too small.
999 if (count <= scan[i].bm_bighint && blk <= atblk) {
1001 * count fits in object
1004 if (next_skip == 1 && bl->bl_terminal) {
1005 r = hammer_alst_leaf_alloc_rev(
1006 &scan[i], blk, count, atblk);
1008 r = hammer_alst_meta_alloc_rev(
1011 radix, next_skip, atblk);
1013 if (r != HAMMER_ALIST_BLOCK_NONE) {
1014 if (scan[i].bm_bitmap == 0) {
1015 scan->bm_bitmap &= ~mask;
1017 scan->bm_bitmap &= ~mask;
1018 scan->bm_bitmap |= pmask;
1032 * We couldn't allocate count in this subtree, update bighint.
1033 * Since we are restricted to powers of 2, the next highest count
1034 * we might be able to allocate is (count >> 1).
1036 if (scan->bm_bighint >= count && saveblk <= atblk)
1037 scan->bm_bighint = count >> 1;
1038 return(HAMMER_ALIST_BLOCK_NONE);
1044 * Free allocated blocks from leaf bitmap. The allocation code is
1045 * restricted to powers of 2, the freeing code is not.
1048 hammer_alst_leaf_free(hammer_almeta_t scan, int32_t blk, int count) {
1050 * free some data in this bitmap
1053 * 0000111111111110000
1057 int n = blk & (HAMMER_ALIST_BMAP_RADIX - 1);
1060 mask = ((u_int32_t)-1 << n) &
1061 ((u_int32_t)-1 >> (HAMMER_ALIST_BMAP_RADIX - count - n));
1063 if (scan->bm_bitmap & mask)
1064 panic("hammer_alst_radix_free: freeing free block");
1065 scan->bm_bitmap |= mask;
1068 * We could probably do a better job here. We are required to make
1069 * bighint at least as large as the biggest contiguous block of
1070 * data. If we just shoehorn it, a little extra overhead will
1071 * be incured on the next allocation (but only that one typically).
1073 scan->bm_bighint = HAMMER_ALIST_BMAP_RADIX;
1079 * Free allocated blocks from radix tree meta info.
1081 * This support routine frees a range of blocks from the bitmap.
1082 * The range must be entirely enclosed by this radix node. If a
1083 * meta node, we break the range down recursively to free blocks
1084 * in subnodes (which means that this code can free an arbitrary
1085 * range whereas the allocation code cannot allocate an arbitrary
1090 hammer_alst_meta_free(hammer_alist_t live, hammer_almeta_t scan,
1091 int32_t freeBlk, int32_t count,
1092 int32_t radix, int skip, int32_t blk
1094 hammer_alist_config_t bl;
1101 * Break the free down into its components. Because it is so easy
1102 * to implement, frees are not limited to power-of-2 sizes.
1104 * Each block in a meta-node bitmap takes two bits.
1106 radix /= HAMMER_ALIST_META_RADIX;
1109 i = (freeBlk - blk) / radix;
1111 mask = 0x00000003 << (i * 2);
1112 pmask = 0x00000001 << (i * 2);
1116 * Our meta node is a leaf node, which means it must recurse
1117 * into another allocator.
1119 while (i < HAMMER_ALIST_META_RADIX && blk < freeBlk + count) {
1122 v = blk + radix - freeBlk;
1126 if (scan->bm_bighint == (int32_t)-1)
1127 panic("hammer_alst_meta_free: freeing unexpected range");
1129 if (freeBlk == blk && count >= radix) {
1131 * When the sub-tree becomes entirely free
1132 * we have to destroy it if it was previously
1133 * partially allocated. If it was previously
1134 * fully allocated it has already been
1135 * destroyed (or never inited in the first
1140 if ((scan->bm_bitmap & mask) != 0) {
1141 bl->bl_radix_free(live->info, blk, radix,
1142 freeBlk - blk, v, &empty);
1143 bl->bl_radix_destroy(live->info, blk, radix);
1145 scan->bm_bitmap |= mask;
1146 scan->bm_bighint = radix * HAMMER_ALIST_META_RADIX;
1147 /* XXX bighint not being set properly */
1150 * Recursion case. If freeing from an
1151 * all-allocated state init the sub-tree
1156 if ((scan->bm_bitmap & mask) == 0)
1157 bl->bl_radix_init(live->info, blk, radix);
1158 bl->bl_radix_free(live->info, blk, radix,
1159 freeBlk - blk, v, &empty);
1161 scan->bm_bitmap |= mask;
1162 scan->bm_bighint = radix * HAMMER_ALIST_META_RADIX;
1163 bl->bl_radix_destroy(live->info, blk, radix);
1164 /* XXX bighint not being set properly */
1166 scan->bm_bitmap |= pmask;
1167 if (scan->bm_bighint < radix / 2)
1168 scan->bm_bighint = radix / 2;
1169 /* XXX bighint not being set properly */
1180 next_skip = (skip - 1) / HAMMER_ALIST_META_RADIX;
1181 i = 1 + i * next_skip;
1183 while (i <= skip && blk < freeBlk + count) {
1186 v = blk + radix - freeBlk;
1190 if (scan->bm_bighint == (int32_t)-1)
1191 panic("hammer_alst_meta_free: freeing unexpected range");
1193 if (freeBlk == blk && count >= radix) {
1195 * All-free case, no need to update sub-tree
1197 scan->bm_bitmap |= mask;
1198 scan->bm_bighint = radix * HAMMER_ALIST_META_RADIX;
1199 /* XXX bighint not being set properly */
1204 if (next_skip == 1 && bl->bl_terminal) {
1205 hammer_alst_leaf_free(&scan[i], freeBlk, v);
1207 hammer_alst_meta_free(live, &scan[i],
1212 if (scan[i].bm_bitmap == (u_int32_t)-1)
1213 scan->bm_bitmap |= mask;
1215 scan->bm_bitmap |= pmask;
1216 if (scan->bm_bighint < scan[i].bm_bighint)
1217 scan->bm_bighint = scan[i].bm_bighint;
1230 * hammer_alst_radix_init()
1232 * Initialize our meta structures and bitmaps and calculate the exact
1233 * number of meta-nodes required to manage 'count' blocks.
1235 * The required space may be truncated due to early termination records.
1238 hammer_alst_radix_init(hammer_almeta_t scan, int32_t radix,
1239 int skip, int32_t count)
1243 int32_t memindex = 1;
1248 * Basic initialization of the almeta for meta or leaf nodes
1251 scan->bm_bighint = 0;
1252 scan->bm_bitmap = 0;
1256 * We are at a leaf, we only eat one meta element.
1262 * Meta node. If allocating the entire object we can special
1263 * case it. However, we need to figure out how much memory
1264 * is required to manage 'count' blocks, so we continue on anyway.
1266 radix /= HAMMER_ALIST_META_RADIX;
1267 next_skip = (skip - 1) / HAMMER_ALIST_META_RADIX;
1271 for (i = 1; i <= skip; i += next_skip) {
1273 * We eat up to this record
1277 if (count >= radix) {
1279 * Allocate the entire object
1281 memindex += hammer_alst_radix_init(
1282 ((scan) ? &scan[i] : NULL),
1288 /* already marked as wholely allocated */
1289 } else if (count > 0) {
1291 * Allocate a partial object
1293 memindex += hammer_alst_radix_init(
1294 ((scan) ? &scan[i] : NULL),
1302 * Mark as partially allocated
1305 scan->bm_bitmap |= pmask;
1308 * Add terminator and break out. The terminal
1309 * eats the meta node at scan[i].
1313 scan[i].bm_bighint = (int32_t)-1;
1314 /* already marked as wholely allocated */
1326 hammer_alst_radix_print(hammer_alist_t live, hammer_almeta_t scan,
1327 int32_t blk, int32_t radix, int skip, int tab)
1334 if (skip == 1 && live->config->bl_terminal) {
1336 "%*.*s(%04x,%d): bitmap %08x big=%d\n",
1345 if (scan->bm_bitmap == 0) {
1347 "%*.*s(%04x,%d) ALL ALLOCATED\n",
1354 if (scan->bm_bitmap == (u_int32_t)-1) {
1356 "%*.*s(%04x,%d) ALL FREE\n",
1365 "%*.*s(%04x,%d): %s (%d) bitmap=%08x big=%d {\n",
1368 (skip == 1 ? "LAYER" : "subtree"),
1374 radix /= HAMMER_ALIST_META_RADIX;
1379 for (i = 0; i < HAMMER_ALIST_META_RADIX; ++i) {
1380 if ((scan->bm_bitmap & mask) == mask) {
1382 "%*.*s(%04x,%d): ALL FREE\n",
1386 } else if ((scan->bm_bitmap & mask) == 0) {
1388 "%*.*s(%04x,%d): ALL ALLOCATED\n",
1393 live->config->bl_radix_print(
1394 live->info, blk, radix, tab);
1400 next_skip = ((u_int)skip / HAMMER_ALIST_META_RADIX);
1402 for (i = 1; i <= skip; i += next_skip) {
1403 if (scan[i].bm_bighint == (int32_t)-1) {
1405 "%*.*s(%04x,%d): Terminator\n",
1412 if ((scan->bm_bitmap & mask) == mask) {
1414 "%*.*s(%04x,%d): ALL FREE\n",
1418 } else if ((scan->bm_bitmap & mask) == 0) {
1420 "%*.*s(%04x,%d): ALL ALLOCATED\n",
1425 hammer_alst_radix_print(
1450 static struct hammer_alist_live **layers; /* initialized by main */
1451 static int32_t layer_radix = -1;
1455 debug_radix_init(void *info, int32_t blk, int32_t radix)
1457 hammer_alist_t layer;
1458 int layer_no = blk / layer_radix;
1460 printf("lower layer: init (%04x,%d)\n", blk, radix);
1461 KKASSERT(layers[layer_no] == NULL);
1462 layer = layers[layer_no] = hammer_alist_create(radix, 1, NULL);
1468 debug_radix_destroy(void *info, int32_t blk, int32_t radix)
1470 hammer_alist_t layer;
1471 int layer_no = blk / layer_radix;
1473 printf("lower layer: destroy (%04x,%d)\n", blk, radix);
1474 layer = layers[layer_no];
1475 KKASSERT(layer != NULL);
1476 hammer_alist_destroy(layer, NULL);
1477 layers[layer_no] = NULL;
1484 debug_radix_alloc_fwd(void *info, int32_t blk, int32_t radix,
1485 int32_t count, int32_t atblk, int32_t *fullp)
1487 hammer_alist_t layer = layers[blk / layer_radix];
1490 r = hammer_alist_alloc_fwd(layer, count, atblk - blk);
1491 *fullp = (layer->meta->bm_alist_freeblks == 0);
1492 if (r != HAMMER_ALIST_BLOCK_NONE)
1499 debug_radix_alloc_rev(void *info, int32_t blk, int32_t radix,
1500 int32_t count, int32_t atblk, int32_t *fullp)
1502 hammer_alist_t layer = layers[blk / layer_radix];
1505 r = hammer_alist_alloc_rev(layer, count, atblk - blk);
1506 *fullp = (layer->meta->bm_alist_freeblks == 0);
1507 if (r != HAMMER_ALIST_BLOCK_NONE)
1514 debug_radix_free(void *info, int32_t blk, int32_t radix,
1515 int32_t base_blk, int32_t count, int32_t *emptyp)
1517 int layer_no = blk / layer_radix;
1518 hammer_alist_t layer = layers[layer_no];
1520 if (layer == NULL) {
1521 layer = hammer_alist_create(layer_radix, 1, NULL);
1522 layers[layer_no] = layer;
1524 hammer_alist_free(layer, base_blk, count);
1525 *emptyp = (layer->meta->bm_alist_freeblks == layer_radix);
1530 debug_radix_print(void *info, int32_t blk, int32_t radix, int tab)
1532 hammer_alist_t layer = layers[blk / layer_radix];
1534 hammer_alist_print(layer, tab);
1538 main(int ac, char **av)
1542 hammer_alist_t live;
1543 hammer_almeta_t meta = NULL;
1545 for (i = 1; i < ac; ++i) {
1546 const char *ptr = av[i];
1549 size = strtol(ptr, NULL, 0);
1550 else if (layer_radix == -1)
1551 layer_radix = strtol(ptr, NULL, 0);
1557 fprintf(stderr, "Bad option: %s\n", ptr - 2);
1562 if (layer_radix == -1)
1563 layer_radix = 1; /* no second storage layer */
1564 if ((size ^ (size - 1)) != (size << 1) - 1) {
1565 fprintf(stderr, "size must be a power of 2\n");
1568 if ((layer_radix ^ (layer_radix - 1)) != (layer_radix << 1) - 1) {
1569 fprintf(stderr, "the second layer radix must be a power of 2\n");
1573 live = hammer_alist_create(size, layer_radix, NULL);
1574 layers = calloc(size, sizeof(hammer_alist_t));
1576 printf("A-LIST TEST %d blocks, first-layer radix %d, "
1577 "second-layer radix %d\n",
1578 size, live->config->bl_radix / layer_radix, layer_radix);
1580 live->config->bl_radix_init = debug_radix_init;
1581 live->config->bl_radix_destroy = debug_radix_destroy;
1582 live->config->bl_radix_alloc_fwd = debug_radix_alloc_fwd;
1583 live->config->bl_radix_alloc_rev = debug_radix_alloc_rev;
1584 live->config->bl_radix_free = debug_radix_free;
1585 live->config->bl_radix_print = debug_radix_print;
1587 hammer_alist_free(live, 0, size);
1597 live->meta->bm_alist_freeblks, size);
1599 if (fgets(buf, sizeof(buf), stdin) == NULL)
1603 hammer_alist_print(live, 0);
1606 if (sscanf(buf + 1, "%d %d", &count, &atblk) >= 1) {
1607 blk = hammer_alist_alloc_fwd(live, count, atblk);
1608 kprintf(" R=%04x\n", blk);
1614 atblk = HAMMER_ALIST_BLOCK_MAX;
1615 if (sscanf(buf + 1, "%d %d", &count, &atblk) >= 1) {
1616 blk = hammer_alist_alloc_rev(live, count, atblk);
1617 kprintf(" R=%04x\n", blk);
1623 if (sscanf(buf + 1, "%x %d", &da, &count) == 2) {
1624 hammer_alist_free(live, da, count);
1634 "r %d -allocate reverse\n"
1648 panic(const char *ctl, ...)
1652 __va_start(va, ctl);
1653 vfprintf(stderr, ctl, va);
1654 fprintf(stderr, "\n");