1*76748ca4Srillig /* $NetBSD: subr_blist.c,v 1.16 2024/09/08 17:28:36 rillig Exp $ */ 22c8d11baSyamt 3186b3b10Syamt /*- 4186b3b10Syamt * Copyright (c) 1998 Matthew Dillon. All Rights Reserved. 5186b3b10Syamt * Redistribution and use in source and binary forms, with or without 6186b3b10Syamt * modification, are permitted provided that the following conditions 7186b3b10Syamt * are met: 8186b3b10Syamt * 1. Redistributions of source code must retain the above copyright 9186b3b10Syamt * notice, this list of conditions and the following disclaimer. 10186b3b10Syamt * 2. Redistributions in binary form must reproduce the above copyright 11186b3b10Syamt * notice, this list of conditions and the following disclaimer in the 12186b3b10Syamt * documentation and/or other materials provided with the distribution. 13186b3b10Syamt * 4. Neither the name of the University nor the names of its contributors 14186b3b10Syamt * may be used to endorse or promote products derived from this software 15186b3b10Syamt * without specific prior written permission. 16186b3b10Syamt * 17186b3b10Syamt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 18186b3b10Syamt * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19186b3b10Syamt * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20186b3b10Syamt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 21186b3b10Syamt * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22186b3b10Syamt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 23186b3b10Syamt * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24186b3b10Syamt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25186b3b10Syamt * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26186b3b10Syamt * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27186b3b10Syamt * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28186b3b10Syamt */ 29186b3b10Syamt /* 30186b3b10Syamt * BLIST.C - Bitmap allocator/deallocator, using a radix tree with hinting 31186b3b10Syamt * 32186b3b10Syamt * This module implements a general bitmap allocator/deallocator. The 33186b3b10Syamt * allocator eats around 2 bits per 'block'. The module does not 34d860f590Swiz * try to interpret the meaning of a 'block' other than to return 35a1e1c4e8Syamt * BLIST_NONE on an allocation failure. 36186b3b10Syamt * 37186b3b10Syamt * A radix tree is used to maintain the bitmap. Two radix constants are 38186b3b10Syamt * involved: One for the bitmaps contained in the leaf nodes (typically 39186b3b10Syamt * 32), and one for the meta nodes (typically 16). Both meta and leaf 40186b3b10Syamt * nodes have a hint field. This field gives us a hint as to the largest 41186b3b10Syamt * free contiguous range of blocks under the node. It may contain a 42186b3b10Syamt * value that is too high, but will never contain a value that is too 43186b3b10Syamt * low. When the radix tree is searched, allocation failures in subtrees 44186b3b10Syamt * update the hint. 45186b3b10Syamt * 46186b3b10Syamt * The radix tree also implements two collapsed states for meta nodes: 47186b3b10Syamt * the ALL-ALLOCATED state and the ALL-FREE state. If a meta node is 48186b3b10Syamt * in either of these two states, all information contained underneath 49186b3b10Syamt * the node is considered stale. These states are used to optimize 50186b3b10Syamt * allocation and freeing operations. 51186b3b10Syamt * 52186b3b10Syamt * The hinting greatly increases code efficiency for allocations while 53186b3b10Syamt * the general radix structure optimizes both allocations and frees. The 54186b3b10Syamt * radix tree should be able to operate well no matter how much 55186b3b10Syamt * fragmentation there is and no matter how large a bitmap is used. 56186b3b10Syamt * 57186b3b10Syamt * Unlike the rlist code, the blist code wires all necessary memory at 58186b3b10Syamt * creation time. Neither allocations nor frees require interaction with 59186b3b10Syamt * the memory subsystem. In contrast, the rlist code may allocate memory 60186b3b10Syamt * on an rlist_free() call. The non-blocking features of the blist code 61186b3b10Syamt * are used to great advantage in the swap code (vm/nswap_pager.c). The 62d860f590Swiz * rlist code uses a little less overall memory than the blist code (but 63186b3b10Syamt * due to swap interleaving not all that much less), but the blist code 64186b3b10Syamt * scales much, much better. 65186b3b10Syamt * 665fb5f516Sandvar * LAYOUT: The radix tree is laid out recursively using a 675fb5f516Sandvar * linear array. Each meta node is immediately followed (laid out 68186b3b10Syamt * sequentially in memory) by BLIST_META_RADIX lower level nodes. This 69186b3b10Syamt * is a recursive structure but one that can be easily scanned through 70186b3b10Syamt * a very simple 'skip' calculation. In order to support large radixes, 71186b3b10Syamt * portions of the tree may reside outside our memory allocation. We 72186b3b10Syamt * handle this with an early-termination optimization (when bighint is 73186b3b10Syamt * set to -1) on the scan. The memory allocation is only large enough 74186b3b10Syamt * to cover the number of blocks requested at creation time even if it 75186b3b10Syamt * must be encompassed in larger root-node radix. 76186b3b10Syamt * 77d860f590Swiz * NOTE: the allocator cannot currently allocate more than 78186b3b10Syamt * BLIST_BMAP_RADIX blocks per call. It will panic with 'allocation too 79186b3b10Syamt * large' if you try. This is an area that could use improvement. The 80*76748ca4Srillig * radix is large enough that this restriction does not affect the swap 81186b3b10Syamt * system, though. Currently only the allocation code is effected by 82186b3b10Syamt * this algorithmic unfeature. The freeing code can handle arbitrary 83186b3b10Syamt * ranges. 84186b3b10Syamt * 85186b3b10Syamt * This code can be compiled stand-alone for debugging. 86186b3b10Syamt */ 87186b3b10Syamt 88186b3b10Syamt #include <sys/cdefs.h> 89*76748ca4Srillig __KERNEL_RCSID(0, "$NetBSD: subr_blist.c,v 1.16 2024/09/08 17:28:36 rillig Exp $"); 902c8d11baSyamt #if 0 91186b3b10Syamt __FBSDID("$FreeBSD: src/sys/kern/subr_blist.c,v 1.17 2004/06/04 04:03:25 alc Exp $"); 922c8d11baSyamt #endif 93186b3b10Syamt 94186b3b10Syamt #ifdef _KERNEL 95186b3b10Syamt 96186b3b10Syamt #include <sys/param.h> 97186b3b10Syamt #include <sys/systm.h> 98186b3b10Syamt #include <sys/blist.h> 99bd5b92d6Srmind #include <sys/kmem.h> 100186b3b10Syamt 101186b3b10Syamt #else 102186b3b10Syamt 103186b3b10Syamt #ifndef BLIST_NO_DEBUG 104186b3b10Syamt #define BLIST_DEBUG 105186b3b10Syamt #endif 106186b3b10Syamt 107186b3b10Syamt #include <sys/types.h> 108186b3b10Syamt #include <stdio.h> 109186b3b10Syamt #include <string.h> 110186b3b10Syamt #include <stdlib.h> 111186b3b10Syamt #include <stdarg.h> 112a1e1c4e8Syamt #include <inttypes.h> 113186b3b10Syamt 114bd5b92d6Srmind #define KM_SLEEP 1 1156914423cSzafer #define kmem_zalloc(a,b) calloc(1, (a)) 1166914423cSzafer #define kmem_alloc(a,b) malloc(a) 117bd5b92d6Srmind #define kmem_free(a,b) free(a) 118186b3b10Syamt 119a1e1c4e8Syamt #include "../sys/blist.h" 120186b3b10Syamt 121a67c3c89Schristos void panic(const char *ctl, ...) __printflike(1, 2); 122186b3b10Syamt 123186b3b10Syamt #endif 124186b3b10Syamt 125186b3b10Syamt /* 126a1bde394Syamt * blmeta and bl_bitmap_t MUST be a power of 2 in size. 127a1bde394Syamt */ 128a1bde394Syamt 129a1bde394Syamt typedef struct blmeta { 130a1bde394Syamt union { 131dad03fdaSyamt blist_blkno_t bmu_avail; /* space available under us */ 132dad03fdaSyamt blist_bitmap_t bmu_bitmap; /* bitmap if we are a leaf */ 133a1bde394Syamt } u; 134dad03fdaSyamt blist_blkno_t bm_bighint; /* biggest contiguous block hint*/ 135a1bde394Syamt } blmeta_t; 136a1bde394Syamt 137a1bde394Syamt struct blist { 138dad03fdaSyamt blist_blkno_t bl_blocks; /* area of coverage */ 139dad03fdaSyamt blist_blkno_t bl_radix; /* coverage radix */ 140dad03fdaSyamt blist_blkno_t bl_skip; /* starting skip */ 141dad03fdaSyamt blist_blkno_t bl_free; /* number of free blocks */ 142a1bde394Syamt blmeta_t *bl_root; /* root of radix tree */ 143dad03fdaSyamt blist_blkno_t bl_rootblks; /* blks allocated for tree */ 144a1bde394Syamt }; 145a1bde394Syamt 146a1bde394Syamt #define BLIST_META_RADIX 16 147a1bde394Syamt 148a1bde394Syamt /* 149186b3b10Syamt * static support functions 150186b3b10Syamt */ 151186b3b10Syamt 152dad03fdaSyamt static blist_blkno_t blst_leaf_alloc(blmeta_t *scan, blist_blkno_t blk, 153dad03fdaSyamt int count); 154dad03fdaSyamt static blist_blkno_t blst_meta_alloc(blmeta_t *scan, blist_blkno_t blk, 155dad03fdaSyamt blist_blkno_t count, blist_blkno_t radix, blist_blkno_t skip); 156dad03fdaSyamt static void blst_leaf_free(blmeta_t *scan, blist_blkno_t relblk, int count); 157dad03fdaSyamt static void blst_meta_free(blmeta_t *scan, blist_blkno_t freeBlk, 158dad03fdaSyamt blist_blkno_t count, blist_blkno_t radix, blist_blkno_t skip, 159dad03fdaSyamt blist_blkno_t blk); 160dad03fdaSyamt static void blst_copy(blmeta_t *scan, blist_blkno_t blk, blist_blkno_t radix, 161dad03fdaSyamt blist_blkno_t skip, blist_t dest, blist_blkno_t count); 162dad03fdaSyamt static int blst_leaf_fill(blmeta_t *scan, blist_blkno_t blk, int count); 163dad03fdaSyamt static blist_blkno_t blst_meta_fill(blmeta_t *scan, blist_blkno_t allocBlk, 164dad03fdaSyamt blist_blkno_t count, blist_blkno_t radix, blist_blkno_t skip, 165dad03fdaSyamt blist_blkno_t blk); 166dad03fdaSyamt static blist_blkno_t blst_radix_init(blmeta_t *scan, blist_blkno_t radix, 167dad03fdaSyamt blist_blkno_t skip, blist_blkno_t count); 168186b3b10Syamt #ifndef _KERNEL 169dad03fdaSyamt static void blst_radix_print(blmeta_t *scan, blist_blkno_t blk, 170dad03fdaSyamt blist_blkno_t radix, blist_blkno_t skip, int tab); 171186b3b10Syamt #endif 172186b3b10Syamt 173186b3b10Syamt /* 174186b3b10Syamt * blist_create() - create a blist capable of handling up to the specified 175186b3b10Syamt * number of blocks 176186b3b10Syamt * 177d860f590Swiz * blocks must be greater than 0 178186b3b10Syamt * 179186b3b10Syamt * The smallest blist consists of a single leaf node capable of 180186b3b10Syamt * managing BLIST_BMAP_RADIX blocks. 181186b3b10Syamt */ 182186b3b10Syamt 183186b3b10Syamt blist_t 184dad03fdaSyamt blist_create(blist_blkno_t blocks) 185186b3b10Syamt { 186186b3b10Syamt blist_t bl; 187dad03fdaSyamt blist_blkno_t radix; 188dad03fdaSyamt blist_blkno_t skip = 0; 189186b3b10Syamt 190186b3b10Syamt /* 191186b3b10Syamt * Calculate radix and skip field used for scanning. 192dad03fdaSyamt * 193dad03fdaSyamt * XXX check overflow 194186b3b10Syamt */ 195186b3b10Syamt radix = BLIST_BMAP_RADIX; 196186b3b10Syamt 197186b3b10Syamt while (radix < blocks) { 198186b3b10Syamt radix *= BLIST_META_RADIX; 199186b3b10Syamt skip = (skip + 1) * BLIST_META_RADIX; 200186b3b10Syamt } 201186b3b10Syamt 202bd5b92d6Srmind bl = kmem_zalloc(sizeof(struct blist), KM_SLEEP); 203186b3b10Syamt 204186b3b10Syamt bl->bl_blocks = blocks; 205186b3b10Syamt bl->bl_radix = radix; 206186b3b10Syamt bl->bl_skip = skip; 207186b3b10Syamt bl->bl_rootblks = 1 + 208186b3b10Syamt blst_radix_init(NULL, bl->bl_radix, bl->bl_skip, blocks); 209bd5b92d6Srmind bl->bl_root = kmem_alloc(sizeof(blmeta_t) * bl->bl_rootblks, KM_SLEEP); 210186b3b10Syamt 211186b3b10Syamt #if defined(BLIST_DEBUG) 212186b3b10Syamt printf( 213a1e1c4e8Syamt "BLIST representing %" PRIu64 " blocks (%" PRIu64 " MB of swap)" 214a1e1c4e8Syamt ", requiring %" PRIu64 "K of ram\n", 215dad03fdaSyamt (uint64_t)bl->bl_blocks, 216dad03fdaSyamt (uint64_t)bl->bl_blocks * 4 / 1024, 217dad03fdaSyamt ((uint64_t)bl->bl_rootblks * sizeof(blmeta_t) + 1023) / 1024 218186b3b10Syamt ); 219a1e1c4e8Syamt printf("BLIST raw radix tree contains %" PRIu64 " records\n", 220dad03fdaSyamt (uint64_t)bl->bl_rootblks); 221186b3b10Syamt #endif 222186b3b10Syamt blst_radix_init(bl->bl_root, bl->bl_radix, bl->bl_skip, blocks); 223186b3b10Syamt 224186b3b10Syamt return(bl); 225186b3b10Syamt } 226186b3b10Syamt 227186b3b10Syamt void 228186b3b10Syamt blist_destroy(blist_t bl) 229186b3b10Syamt { 230bd5b92d6Srmind 231bd5b92d6Srmind kmem_free(bl->bl_root, sizeof(blmeta_t) * bl->bl_rootblks); 232bd5b92d6Srmind kmem_free(bl, sizeof(struct blist)); 233186b3b10Syamt } 234186b3b10Syamt 235186b3b10Syamt /* 236186b3b10Syamt * blist_alloc() - reserve space in the block bitmap. Return the base 237a1e1c4e8Syamt * of a contiguous region or BLIST_NONE if space could 238186b3b10Syamt * not be allocated. 239186b3b10Syamt */ 240186b3b10Syamt 241dad03fdaSyamt blist_blkno_t 242dad03fdaSyamt blist_alloc(blist_t bl, blist_blkno_t count) 243186b3b10Syamt { 244dad03fdaSyamt blist_blkno_t blk = BLIST_NONE; 245186b3b10Syamt 246186b3b10Syamt if (bl) { 247186b3b10Syamt if (bl->bl_radix == BLIST_BMAP_RADIX) 248186b3b10Syamt blk = blst_leaf_alloc(bl->bl_root, 0, count); 249186b3b10Syamt else 250186b3b10Syamt blk = blst_meta_alloc(bl->bl_root, 0, count, bl->bl_radix, bl->bl_skip); 251a1e1c4e8Syamt if (blk != BLIST_NONE) 252186b3b10Syamt bl->bl_free -= count; 253186b3b10Syamt } 254186b3b10Syamt return(blk); 255186b3b10Syamt } 256186b3b10Syamt 257186b3b10Syamt /* 258186b3b10Syamt * blist_free() - free up space in the block bitmap. Return the base 259ff23aff6Sandvar * of a contiguous region. Panic if an inconsistency is 260186b3b10Syamt * found. 261186b3b10Syamt */ 262186b3b10Syamt 263186b3b10Syamt void 264dad03fdaSyamt blist_free(blist_t bl, blist_blkno_t blkno, blist_blkno_t count) 265186b3b10Syamt { 266186b3b10Syamt if (bl) { 267186b3b10Syamt if (bl->bl_radix == BLIST_BMAP_RADIX) 268186b3b10Syamt blst_leaf_free(bl->bl_root, blkno, count); 269186b3b10Syamt else 270186b3b10Syamt blst_meta_free(bl->bl_root, blkno, count, bl->bl_radix, bl->bl_skip, 0); 271186b3b10Syamt bl->bl_free += count; 272186b3b10Syamt } 273186b3b10Syamt } 274186b3b10Syamt 275186b3b10Syamt /* 276186b3b10Syamt * blist_fill() - mark a region in the block bitmap as off-limits 277186b3b10Syamt * to the allocator (i.e. allocate it), ignoring any 278186b3b10Syamt * existing allocations. Return the number of blocks 279186b3b10Syamt * actually filled that were free before the call. 280186b3b10Syamt */ 281186b3b10Syamt 282dad03fdaSyamt blist_blkno_t 283dad03fdaSyamt blist_fill(blist_t bl, blist_blkno_t blkno, blist_blkno_t count) 284186b3b10Syamt { 285dad03fdaSyamt blist_blkno_t filled; 286186b3b10Syamt 287186b3b10Syamt if (bl) { 288186b3b10Syamt if (bl->bl_radix == BLIST_BMAP_RADIX) 289186b3b10Syamt filled = blst_leaf_fill(bl->bl_root, blkno, count); 290186b3b10Syamt else 291186b3b10Syamt filled = blst_meta_fill(bl->bl_root, blkno, count, 292186b3b10Syamt bl->bl_radix, bl->bl_skip, 0); 293186b3b10Syamt bl->bl_free -= filled; 294186b3b10Syamt return filled; 295186b3b10Syamt } else 296186b3b10Syamt return 0; 297186b3b10Syamt } 298186b3b10Syamt 299186b3b10Syamt /* 300186b3b10Syamt * blist_resize() - resize an existing radix tree to handle the 301186b3b10Syamt * specified number of blocks. This will reallocate 302186b3b10Syamt * the tree and transfer the previous bitmap to the new 303186b3b10Syamt * one. When extending the tree you can specify whether 304186b3b10Syamt * the new blocks are to left allocated or freed. 305186b3b10Syamt */ 306186b3b10Syamt 307186b3b10Syamt void 308dad03fdaSyamt blist_resize(blist_t *pbl, blist_blkno_t count, int freenew) 309186b3b10Syamt { 310186b3b10Syamt blist_t newbl = blist_create(count); 311186b3b10Syamt blist_t save = *pbl; 312186b3b10Syamt 313186b3b10Syamt *pbl = newbl; 314186b3b10Syamt if (count > save->bl_blocks) 315186b3b10Syamt count = save->bl_blocks; 316186b3b10Syamt blst_copy(save->bl_root, 0, save->bl_radix, save->bl_skip, newbl, count); 317186b3b10Syamt 318186b3b10Syamt /* 319186b3b10Syamt * If resizing upwards, should we free the new space or not? 320186b3b10Syamt */ 321186b3b10Syamt if (freenew && count < newbl->bl_blocks) { 322186b3b10Syamt blist_free(newbl, count, newbl->bl_blocks - count); 323186b3b10Syamt } 324186b3b10Syamt blist_destroy(save); 325186b3b10Syamt } 326186b3b10Syamt 327186b3b10Syamt #ifdef BLIST_DEBUG 328186b3b10Syamt 329186b3b10Syamt /* 330186b3b10Syamt * blist_print() - dump radix tree 331186b3b10Syamt */ 332186b3b10Syamt 333186b3b10Syamt void 334186b3b10Syamt blist_print(blist_t bl) 335186b3b10Syamt { 336186b3b10Syamt printf("BLIST {\n"); 337186b3b10Syamt blst_radix_print(bl->bl_root, 0, bl->bl_radix, bl->bl_skip, 4); 338186b3b10Syamt printf("}\n"); 339186b3b10Syamt } 340186b3b10Syamt 341186b3b10Syamt #endif 342186b3b10Syamt 343186b3b10Syamt /************************************************************************ 344186b3b10Syamt * ALLOCATION SUPPORT FUNCTIONS * 345186b3b10Syamt ************************************************************************ 346186b3b10Syamt * 347186b3b10Syamt * These support functions do all the actual work. They may seem 348186b3b10Syamt * rather longish, but that's because I've commented them up. The 349186b3b10Syamt * actual code is straight forward. 350186b3b10Syamt * 351186b3b10Syamt */ 352186b3b10Syamt 353186b3b10Syamt /* 354186b3b10Syamt * blist_leaf_alloc() - allocate at a leaf in the radix tree (a bitmap). 355186b3b10Syamt * 356186b3b10Syamt * This is the core of the allocator and is optimized for the 1 block 357186b3b10Syamt * and the BLIST_BMAP_RADIX block allocation cases. Other cases are 358186b3b10Syamt * somewhat slower. The 1 block allocation case is log2 and extremely 359186b3b10Syamt * quick. 360186b3b10Syamt */ 361186b3b10Syamt 362dad03fdaSyamt static blist_blkno_t 363186b3b10Syamt blst_leaf_alloc( 364186b3b10Syamt blmeta_t *scan, 365dad03fdaSyamt blist_blkno_t blk, 366186b3b10Syamt int count 367186b3b10Syamt ) { 368dad03fdaSyamt blist_bitmap_t orig = scan->u.bmu_bitmap; 369186b3b10Syamt 370186b3b10Syamt if (orig == 0) { 371186b3b10Syamt /* 372186b3b10Syamt * Optimize bitmap all-allocated case. Also, count = 1 373186b3b10Syamt * case assumes at least 1 bit is free in the bitmap, so 374186b3b10Syamt * we have to take care of this case here. 375186b3b10Syamt */ 376186b3b10Syamt scan->bm_bighint = 0; 377a1e1c4e8Syamt return(BLIST_NONE); 378186b3b10Syamt } 379186b3b10Syamt if (count == 1) { 380186b3b10Syamt /* 381186b3b10Syamt * Optimized code to allocate one bit out of the bitmap 382186b3b10Syamt */ 383dad03fdaSyamt blist_bitmap_t mask; 384186b3b10Syamt int j = BLIST_BMAP_RADIX/2; 385186b3b10Syamt int r = 0; 386186b3b10Syamt 387dad03fdaSyamt mask = (blist_bitmap_t)-1 >> (BLIST_BMAP_RADIX/2); 388186b3b10Syamt 389186b3b10Syamt while (j) { 390186b3b10Syamt if ((orig & mask) == 0) { 391186b3b10Syamt r += j; 392186b3b10Syamt orig >>= j; 393186b3b10Syamt } 394186b3b10Syamt j >>= 1; 395186b3b10Syamt mask >>= j; 396186b3b10Syamt } 397dad03fdaSyamt scan->u.bmu_bitmap &= ~((blist_bitmap_t)1 << r); 398186b3b10Syamt return(blk + r); 399186b3b10Syamt } 400186b3b10Syamt if (count <= BLIST_BMAP_RADIX) { 401186b3b10Syamt /* 402186b3b10Syamt * non-optimized code to allocate N bits out of the bitmap. 403186b3b10Syamt * The more bits, the faster the code runs. It will run 404186b3b10Syamt * the slowest allocating 2 bits, but since there aren't any 405186b3b10Syamt * memory ops in the core loop (or shouldn't be, anyway), 406186b3b10Syamt * you probably won't notice the difference. 407186b3b10Syamt */ 408186b3b10Syamt int j; 409186b3b10Syamt int n = BLIST_BMAP_RADIX - count; 410dad03fdaSyamt blist_bitmap_t mask; 411186b3b10Syamt 412dad03fdaSyamt mask = (blist_bitmap_t)-1 >> n; 413186b3b10Syamt 414186b3b10Syamt for (j = 0; j <= n; ++j) { 415186b3b10Syamt if ((orig & mask) == mask) { 416186b3b10Syamt scan->u.bmu_bitmap &= ~mask; 417186b3b10Syamt return(blk + j); 418186b3b10Syamt } 419186b3b10Syamt mask = (mask << 1); 420186b3b10Syamt } 421186b3b10Syamt } 422186b3b10Syamt /* 423186b3b10Syamt * We couldn't allocate count in this subtree, update bighint. 424186b3b10Syamt */ 425186b3b10Syamt scan->bm_bighint = count - 1; 426a1e1c4e8Syamt return(BLIST_NONE); 427186b3b10Syamt } 428186b3b10Syamt 429186b3b10Syamt /* 430186b3b10Syamt * blist_meta_alloc() - allocate at a meta in the radix tree. 431186b3b10Syamt * 432186b3b10Syamt * Attempt to allocate at a meta node. If we can't, we update 433186b3b10Syamt * bighint and return a failure. Updating bighint optimize future 434186b3b10Syamt * calls that hit this node. We have to check for our collapse cases 435186b3b10Syamt * and we have a few optimizations strewn in as well. 436186b3b10Syamt */ 437186b3b10Syamt 438dad03fdaSyamt static blist_blkno_t 439186b3b10Syamt blst_meta_alloc( 440186b3b10Syamt blmeta_t *scan, 441dad03fdaSyamt blist_blkno_t blk, 442dad03fdaSyamt blist_blkno_t count, 443dad03fdaSyamt blist_blkno_t radix, 444dad03fdaSyamt blist_blkno_t skip 445186b3b10Syamt ) { 446dad03fdaSyamt blist_blkno_t i; 447dad03fdaSyamt blist_blkno_t next_skip = (skip / BLIST_META_RADIX); 448186b3b10Syamt 449186b3b10Syamt if (scan->u.bmu_avail == 0) { 450186b3b10Syamt /* 451186b3b10Syamt * ALL-ALLOCATED special case 452186b3b10Syamt */ 453186b3b10Syamt scan->bm_bighint = count; 454a1e1c4e8Syamt return(BLIST_NONE); 455186b3b10Syamt } 456186b3b10Syamt 457186b3b10Syamt if (scan->u.bmu_avail == radix) { 458186b3b10Syamt radix /= BLIST_META_RADIX; 459186b3b10Syamt 460186b3b10Syamt /* 461186b3b10Syamt * ALL-FREE special case, initialize uninitialize 462186b3b10Syamt * sublevel. 463186b3b10Syamt */ 464186b3b10Syamt for (i = 1; i <= skip; i += next_skip) { 465dad03fdaSyamt if (scan[i].bm_bighint == (blist_blkno_t)-1) 466186b3b10Syamt break; 467186b3b10Syamt if (next_skip == 1) { 468dad03fdaSyamt scan[i].u.bmu_bitmap = (blist_bitmap_t)-1; 469186b3b10Syamt scan[i].bm_bighint = BLIST_BMAP_RADIX; 470186b3b10Syamt } else { 471186b3b10Syamt scan[i].bm_bighint = radix; 472186b3b10Syamt scan[i].u.bmu_avail = radix; 473186b3b10Syamt } 474186b3b10Syamt } 475186b3b10Syamt } else { 476186b3b10Syamt radix /= BLIST_META_RADIX; 477186b3b10Syamt } 478186b3b10Syamt 479186b3b10Syamt for (i = 1; i <= skip; i += next_skip) { 480dad03fdaSyamt if (scan[i].bm_bighint == (blist_blkno_t)-1) { 481a1e1c4e8Syamt /* 482a1e1c4e8Syamt * Terminator 483a1e1c4e8Syamt */ 484a1e1c4e8Syamt break; 485a1e1c4e8Syamt } else if (count <= scan[i].bm_bighint) { 486186b3b10Syamt /* 487186b3b10Syamt * count fits in object 488186b3b10Syamt */ 489dad03fdaSyamt blist_blkno_t r; 490186b3b10Syamt if (next_skip == 1) { 491186b3b10Syamt r = blst_leaf_alloc(&scan[i], blk, count); 492186b3b10Syamt } else { 493186b3b10Syamt r = blst_meta_alloc(&scan[i], blk, count, radix, next_skip - 1); 494186b3b10Syamt } 495a1e1c4e8Syamt if (r != BLIST_NONE) { 496186b3b10Syamt scan->u.bmu_avail -= count; 497186b3b10Syamt if (scan->bm_bighint > scan->u.bmu_avail) 498186b3b10Syamt scan->bm_bighint = scan->u.bmu_avail; 499186b3b10Syamt return(r); 500186b3b10Syamt } 501186b3b10Syamt } else if (count > radix) { 502186b3b10Syamt /* 503186b3b10Syamt * count does not fit in object even if it were 504186b3b10Syamt * complete free. 505186b3b10Syamt */ 506186b3b10Syamt panic("blist_meta_alloc: allocation too large"); 507186b3b10Syamt } 508186b3b10Syamt blk += radix; 509186b3b10Syamt } 510186b3b10Syamt 511186b3b10Syamt /* 512186b3b10Syamt * We couldn't allocate count in this subtree, update bighint. 513186b3b10Syamt */ 514186b3b10Syamt if (scan->bm_bighint >= count) 515186b3b10Syamt scan->bm_bighint = count - 1; 516a1e1c4e8Syamt return(BLIST_NONE); 517186b3b10Syamt } 518186b3b10Syamt 519186b3b10Syamt /* 520186b3b10Syamt * BLST_LEAF_FREE() - free allocated block from leaf bitmap 521186b3b10Syamt * 522186b3b10Syamt */ 523186b3b10Syamt 524186b3b10Syamt static void 525186b3b10Syamt blst_leaf_free( 526186b3b10Syamt blmeta_t *scan, 527dad03fdaSyamt blist_blkno_t blk, 528186b3b10Syamt int count 529186b3b10Syamt ) { 530186b3b10Syamt /* 531186b3b10Syamt * free some data in this bitmap 532186b3b10Syamt * 533186b3b10Syamt * e.g. 534186b3b10Syamt * 0000111111111110000 535186b3b10Syamt * \_________/\__/ 536186b3b10Syamt * v n 537186b3b10Syamt */ 538186b3b10Syamt int n = blk & (BLIST_BMAP_RADIX - 1); 539dad03fdaSyamt blist_bitmap_t mask; 540186b3b10Syamt 541dad03fdaSyamt mask = ((blist_bitmap_t)-1 << n) & 542dad03fdaSyamt ((blist_bitmap_t)-1 >> (BLIST_BMAP_RADIX - count - n)); 543186b3b10Syamt 544186b3b10Syamt if (scan->u.bmu_bitmap & mask) 545186b3b10Syamt panic("blst_radix_free: freeing free block"); 546186b3b10Syamt scan->u.bmu_bitmap |= mask; 547186b3b10Syamt 548186b3b10Syamt /* 549186b3b10Syamt * We could probably do a better job here. We are required to make 550186b3b10Syamt * bighint at least as large as the biggest contiguous block of 551186b3b10Syamt * data. If we just shoehorn it, a little extra overhead will 552186b3b10Syamt * be incured on the next allocation (but only that one typically). 553186b3b10Syamt */ 554186b3b10Syamt scan->bm_bighint = BLIST_BMAP_RADIX; 555186b3b10Syamt } 556186b3b10Syamt 557186b3b10Syamt /* 558186b3b10Syamt * BLST_META_FREE() - free allocated blocks from radix tree meta info 559186b3b10Syamt * 560186b3b10Syamt * This support routine frees a range of blocks from the bitmap. 561186b3b10Syamt * The range must be entirely enclosed by this radix node. If a 562186b3b10Syamt * meta node, we break the range down recursively to free blocks 563186b3b10Syamt * in subnodes (which means that this code can free an arbitrary 564186b3b10Syamt * range whereas the allocation code cannot allocate an arbitrary 565186b3b10Syamt * range). 566186b3b10Syamt */ 567186b3b10Syamt 568186b3b10Syamt static void 569186b3b10Syamt blst_meta_free( 570186b3b10Syamt blmeta_t *scan, 571dad03fdaSyamt blist_blkno_t freeBlk, 572dad03fdaSyamt blist_blkno_t count, 573dad03fdaSyamt blist_blkno_t radix, 574dad03fdaSyamt blist_blkno_t skip, 575dad03fdaSyamt blist_blkno_t blk 576186b3b10Syamt ) { 577dad03fdaSyamt blist_blkno_t i; 578dad03fdaSyamt blist_blkno_t next_skip = (skip / BLIST_META_RADIX); 579186b3b10Syamt 580186b3b10Syamt #if 0 581a1e1c4e8Syamt printf("FREE (%" PRIx64 ",%" PRIu64 582a1e1c4e8Syamt ") FROM (%" PRIx64 ",%" PRIu64 ")\n", 583dad03fdaSyamt (uint64_t)freeBlk, (uint64_t)count, 584dad03fdaSyamt (uint64_t)blk, (uint64_t)radix 585186b3b10Syamt ); 586186b3b10Syamt #endif 587186b3b10Syamt 588186b3b10Syamt if (scan->u.bmu_avail == 0) { 589186b3b10Syamt /* 590186b3b10Syamt * ALL-ALLOCATED special case, with possible 591186b3b10Syamt * shortcut to ALL-FREE special case. 592186b3b10Syamt */ 593186b3b10Syamt scan->u.bmu_avail = count; 594186b3b10Syamt scan->bm_bighint = count; 595186b3b10Syamt 596186b3b10Syamt if (count != radix) { 597186b3b10Syamt for (i = 1; i <= skip; i += next_skip) { 598dad03fdaSyamt if (scan[i].bm_bighint == (blist_blkno_t)-1) 599186b3b10Syamt break; 600186b3b10Syamt scan[i].bm_bighint = 0; 601186b3b10Syamt if (next_skip == 1) { 602186b3b10Syamt scan[i].u.bmu_bitmap = 0; 603186b3b10Syamt } else { 604186b3b10Syamt scan[i].u.bmu_avail = 0; 605186b3b10Syamt } 606186b3b10Syamt } 607186b3b10Syamt /* fall through */ 608186b3b10Syamt } 609186b3b10Syamt } else { 610186b3b10Syamt scan->u.bmu_avail += count; 611186b3b10Syamt /* scan->bm_bighint = radix; */ 612186b3b10Syamt } 613186b3b10Syamt 614186b3b10Syamt /* 615186b3b10Syamt * ALL-FREE special case. 616186b3b10Syamt */ 617186b3b10Syamt 618186b3b10Syamt if (scan->u.bmu_avail == radix) 619186b3b10Syamt return; 620186b3b10Syamt if (scan->u.bmu_avail > radix) 621a1e1c4e8Syamt panic("blst_meta_free: freeing already free blocks (%" 622a1e1c4e8Syamt PRIu64 ") %" PRIu64 "/%" PRIu64, 623dad03fdaSyamt (uint64_t)count, 624dad03fdaSyamt (uint64_t)scan->u.bmu_avail, 625dad03fdaSyamt (uint64_t)radix); 626186b3b10Syamt 627186b3b10Syamt /* 628186b3b10Syamt * Break the free down into its components 629186b3b10Syamt */ 630186b3b10Syamt 631186b3b10Syamt radix /= BLIST_META_RADIX; 632186b3b10Syamt 633186b3b10Syamt i = (freeBlk - blk) / radix; 634186b3b10Syamt blk += i * radix; 635186b3b10Syamt i = i * next_skip + 1; 636186b3b10Syamt 637186b3b10Syamt while (i <= skip && blk < freeBlk + count) { 638dad03fdaSyamt blist_blkno_t v; 639186b3b10Syamt 640186b3b10Syamt v = blk + radix - freeBlk; 641186b3b10Syamt if (v > count) 642186b3b10Syamt v = count; 643186b3b10Syamt 644dad03fdaSyamt if (scan->bm_bighint == (blist_blkno_t)-1) 645186b3b10Syamt panic("blst_meta_free: freeing unexpected range"); 646186b3b10Syamt 647186b3b10Syamt if (next_skip == 1) { 648186b3b10Syamt blst_leaf_free(&scan[i], freeBlk, v); 649186b3b10Syamt } else { 650186b3b10Syamt blst_meta_free(&scan[i], freeBlk, v, radix, next_skip - 1, blk); 651186b3b10Syamt } 652186b3b10Syamt if (scan->bm_bighint < scan[i].bm_bighint) 653186b3b10Syamt scan->bm_bighint = scan[i].bm_bighint; 654186b3b10Syamt count -= v; 655186b3b10Syamt freeBlk += v; 656186b3b10Syamt blk += radix; 657186b3b10Syamt i += next_skip; 658186b3b10Syamt } 659186b3b10Syamt } 660186b3b10Syamt 661186b3b10Syamt /* 662186b3b10Syamt * BLIST_RADIX_COPY() - copy one radix tree to another 663186b3b10Syamt * 664186b3b10Syamt * Locates free space in the source tree and frees it in the destination 665186b3b10Syamt * tree. The space may not already be free in the destination. 666186b3b10Syamt */ 667186b3b10Syamt 668186b3b10Syamt static void blst_copy( 669186b3b10Syamt blmeta_t *scan, 670dad03fdaSyamt blist_blkno_t blk, 671dad03fdaSyamt blist_blkno_t radix, 672dad03fdaSyamt blist_blkno_t skip, 673186b3b10Syamt blist_t dest, 674dad03fdaSyamt blist_blkno_t count 675186b3b10Syamt ) { 676dad03fdaSyamt blist_blkno_t next_skip; 677dad03fdaSyamt blist_blkno_t i; 678186b3b10Syamt 679186b3b10Syamt /* 680186b3b10Syamt * Leaf node 681186b3b10Syamt */ 682186b3b10Syamt 683186b3b10Syamt if (radix == BLIST_BMAP_RADIX) { 684dad03fdaSyamt blist_bitmap_t v = scan->u.bmu_bitmap; 685186b3b10Syamt 686dad03fdaSyamt if (v == (blist_bitmap_t)-1) { 687186b3b10Syamt blist_free(dest, blk, count); 688186b3b10Syamt } else if (v != 0) { 689efb69433Schristos int j; 690186b3b10Syamt 691efb69433Schristos for (j = 0; j < BLIST_BMAP_RADIX && j < count; ++j) { 692efb69433Schristos if (v & (1 << j)) 693efb69433Schristos blist_free(dest, blk + j, 1); 694186b3b10Syamt } 695186b3b10Syamt } 696186b3b10Syamt return; 697186b3b10Syamt } 698186b3b10Syamt 699186b3b10Syamt /* 700186b3b10Syamt * Meta node 701186b3b10Syamt */ 702186b3b10Syamt 703186b3b10Syamt if (scan->u.bmu_avail == 0) { 704186b3b10Syamt /* 705186b3b10Syamt * Source all allocated, leave dest allocated 706186b3b10Syamt */ 707186b3b10Syamt return; 708186b3b10Syamt } 709186b3b10Syamt if (scan->u.bmu_avail == radix) { 710186b3b10Syamt /* 711186b3b10Syamt * Source all free, free entire dest 712186b3b10Syamt */ 713186b3b10Syamt if (count < radix) 714186b3b10Syamt blist_free(dest, blk, count); 715186b3b10Syamt else 716186b3b10Syamt blist_free(dest, blk, radix); 717186b3b10Syamt return; 718186b3b10Syamt } 719186b3b10Syamt 720186b3b10Syamt 721186b3b10Syamt radix /= BLIST_META_RADIX; 722dad03fdaSyamt next_skip = (skip / BLIST_META_RADIX); 723186b3b10Syamt 724186b3b10Syamt for (i = 1; count && i <= skip; i += next_skip) { 725dad03fdaSyamt if (scan[i].bm_bighint == (blist_blkno_t)-1) 726186b3b10Syamt break; 727186b3b10Syamt 728186b3b10Syamt if (count >= radix) { 729186b3b10Syamt blst_copy( 730186b3b10Syamt &scan[i], 731186b3b10Syamt blk, 732186b3b10Syamt radix, 733186b3b10Syamt next_skip - 1, 734186b3b10Syamt dest, 735186b3b10Syamt radix 736186b3b10Syamt ); 737186b3b10Syamt count -= radix; 738186b3b10Syamt } else { 739186b3b10Syamt if (count) { 740186b3b10Syamt blst_copy( 741186b3b10Syamt &scan[i], 742186b3b10Syamt blk, 743186b3b10Syamt radix, 744186b3b10Syamt next_skip - 1, 745186b3b10Syamt dest, 746186b3b10Syamt count 747186b3b10Syamt ); 748186b3b10Syamt } 749186b3b10Syamt count = 0; 750186b3b10Syamt } 751186b3b10Syamt blk += radix; 752186b3b10Syamt } 753186b3b10Syamt } 754186b3b10Syamt 755186b3b10Syamt /* 756186b3b10Syamt * BLST_LEAF_FILL() - allocate specific blocks in leaf bitmap 757186b3b10Syamt * 758186b3b10Syamt * This routine allocates all blocks in the specified range 759186b3b10Syamt * regardless of any existing allocations in that range. Returns 760186b3b10Syamt * the number of blocks allocated by the call. 761186b3b10Syamt */ 762186b3b10Syamt 763186b3b10Syamt static int 764dad03fdaSyamt blst_leaf_fill(blmeta_t *scan, blist_blkno_t blk, int count) 765186b3b10Syamt { 766186b3b10Syamt int n = blk & (BLIST_BMAP_RADIX - 1); 767186b3b10Syamt int nblks; 768dad03fdaSyamt blist_bitmap_t mask, bitmap; 769186b3b10Syamt 770dad03fdaSyamt mask = ((blist_bitmap_t)-1 << n) & 771dad03fdaSyamt ((blist_bitmap_t)-1 >> (BLIST_BMAP_RADIX - count - n)); 772186b3b10Syamt 773186b3b10Syamt /* Count the number of blocks we're about to allocate */ 774186b3b10Syamt bitmap = scan->u.bmu_bitmap & mask; 775186b3b10Syamt for (nblks = 0; bitmap != 0; nblks++) 776186b3b10Syamt bitmap &= bitmap - 1; 777186b3b10Syamt 778186b3b10Syamt scan->u.bmu_bitmap &= ~mask; 779186b3b10Syamt return nblks; 780186b3b10Syamt } 781186b3b10Syamt 782186b3b10Syamt /* 783186b3b10Syamt * BLIST_META_FILL() - allocate specific blocks at a meta node 784186b3b10Syamt * 785186b3b10Syamt * This routine allocates the specified range of blocks, 786186b3b10Syamt * regardless of any existing allocations in the range. The 787186b3b10Syamt * range must be within the extent of this node. Returns the 788186b3b10Syamt * number of blocks allocated by the call. 789186b3b10Syamt */ 790dad03fdaSyamt static blist_blkno_t 791186b3b10Syamt blst_meta_fill( 792186b3b10Syamt blmeta_t *scan, 793dad03fdaSyamt blist_blkno_t allocBlk, 794dad03fdaSyamt blist_blkno_t count, 795dad03fdaSyamt blist_blkno_t radix, 796dad03fdaSyamt blist_blkno_t skip, 797dad03fdaSyamt blist_blkno_t blk 798186b3b10Syamt ) { 799dad03fdaSyamt blist_blkno_t i; 800dad03fdaSyamt blist_blkno_t next_skip = (skip / BLIST_META_RADIX); 801dad03fdaSyamt blist_blkno_t nblks = 0; 802186b3b10Syamt 803186b3b10Syamt if (count == radix || scan->u.bmu_avail == 0) { 804186b3b10Syamt /* 805186b3b10Syamt * ALL-ALLOCATED special case 806186b3b10Syamt */ 807186b3b10Syamt nblks = scan->u.bmu_avail; 808186b3b10Syamt scan->u.bmu_avail = 0; 809186b3b10Syamt scan->bm_bighint = count; 810186b3b10Syamt return nblks; 811186b3b10Syamt } 812186b3b10Syamt 8136f3f9a8fSyamt if (count > radix) 8146f3f9a8fSyamt panic("blist_meta_fill: allocation too large"); 8156f3f9a8fSyamt 816186b3b10Syamt if (scan->u.bmu_avail == radix) { 817186b3b10Syamt radix /= BLIST_META_RADIX; 818186b3b10Syamt 819186b3b10Syamt /* 820186b3b10Syamt * ALL-FREE special case, initialize sublevel 821186b3b10Syamt */ 822186b3b10Syamt for (i = 1; i <= skip; i += next_skip) { 823dad03fdaSyamt if (scan[i].bm_bighint == (blist_blkno_t)-1) 824186b3b10Syamt break; 825186b3b10Syamt if (next_skip == 1) { 826dad03fdaSyamt scan[i].u.bmu_bitmap = (blist_bitmap_t)-1; 827186b3b10Syamt scan[i].bm_bighint = BLIST_BMAP_RADIX; 828186b3b10Syamt } else { 829186b3b10Syamt scan[i].bm_bighint = radix; 830186b3b10Syamt scan[i].u.bmu_avail = radix; 831186b3b10Syamt } 832186b3b10Syamt } 833186b3b10Syamt } else { 834186b3b10Syamt radix /= BLIST_META_RADIX; 835186b3b10Syamt } 836186b3b10Syamt 837186b3b10Syamt i = (allocBlk - blk) / radix; 838186b3b10Syamt blk += i * radix; 839186b3b10Syamt i = i * next_skip + 1; 840186b3b10Syamt 841186b3b10Syamt while (i <= skip && blk < allocBlk + count) { 842dad03fdaSyamt blist_blkno_t v; 843186b3b10Syamt 844186b3b10Syamt v = blk + radix - allocBlk; 845186b3b10Syamt if (v > count) 846186b3b10Syamt v = count; 847186b3b10Syamt 848dad03fdaSyamt if (scan->bm_bighint == (blist_blkno_t)-1) 849186b3b10Syamt panic("blst_meta_fill: filling unexpected range"); 850186b3b10Syamt 851186b3b10Syamt if (next_skip == 1) { 852186b3b10Syamt nblks += blst_leaf_fill(&scan[i], allocBlk, v); 853186b3b10Syamt } else { 854186b3b10Syamt nblks += blst_meta_fill(&scan[i], allocBlk, v, 855186b3b10Syamt radix, next_skip - 1, blk); 856186b3b10Syamt } 857186b3b10Syamt count -= v; 858186b3b10Syamt allocBlk += v; 859186b3b10Syamt blk += radix; 860186b3b10Syamt i += next_skip; 861186b3b10Syamt } 862186b3b10Syamt scan->u.bmu_avail -= nblks; 863186b3b10Syamt return nblks; 864186b3b10Syamt } 865186b3b10Syamt 866186b3b10Syamt /* 867186b3b10Syamt * BLST_RADIX_INIT() - initialize radix tree 868186b3b10Syamt * 869186b3b10Syamt * Initialize our meta structures and bitmaps and calculate the exact 870186b3b10Syamt * amount of space required to manage 'count' blocks - this space may 871d860f590Swiz * be considerably less than the calculated radix due to the large 872186b3b10Syamt * RADIX values we use. 873186b3b10Syamt */ 874186b3b10Syamt 875dad03fdaSyamt static blist_blkno_t 876dad03fdaSyamt blst_radix_init(blmeta_t *scan, blist_blkno_t radix, blist_blkno_t skip, 877dad03fdaSyamt blist_blkno_t count) 878186b3b10Syamt { 879dad03fdaSyamt blist_blkno_t i; 880dad03fdaSyamt blist_blkno_t next_skip; 881dad03fdaSyamt blist_blkno_t memindex = 0; 882186b3b10Syamt 883186b3b10Syamt /* 884186b3b10Syamt * Leaf node 885186b3b10Syamt */ 886186b3b10Syamt 887186b3b10Syamt if (radix == BLIST_BMAP_RADIX) { 888186b3b10Syamt if (scan) { 889186b3b10Syamt scan->bm_bighint = 0; 890186b3b10Syamt scan->u.bmu_bitmap = 0; 891186b3b10Syamt } 892186b3b10Syamt return(memindex); 893186b3b10Syamt } 894186b3b10Syamt 895186b3b10Syamt /* 896186b3b10Syamt * Meta node. If allocating the entire object we can special 897186b3b10Syamt * case it. However, we need to figure out how much memory 898186b3b10Syamt * is required to manage 'count' blocks, so we continue on anyway. 899186b3b10Syamt */ 900186b3b10Syamt 901186b3b10Syamt if (scan) { 902186b3b10Syamt scan->bm_bighint = 0; 903186b3b10Syamt scan->u.bmu_avail = 0; 904186b3b10Syamt } 905186b3b10Syamt 906186b3b10Syamt radix /= BLIST_META_RADIX; 907dad03fdaSyamt next_skip = (skip / BLIST_META_RADIX); 908186b3b10Syamt 909186b3b10Syamt for (i = 1; i <= skip; i += next_skip) { 910186b3b10Syamt if (count >= radix) { 911186b3b10Syamt /* 912186b3b10Syamt * Allocate the entire object 913186b3b10Syamt */ 914186b3b10Syamt memindex = i + blst_radix_init( 915186b3b10Syamt ((scan) ? &scan[i] : NULL), 916186b3b10Syamt radix, 917186b3b10Syamt next_skip - 1, 918186b3b10Syamt radix 919186b3b10Syamt ); 920186b3b10Syamt count -= radix; 921186b3b10Syamt } else if (count > 0) { 922186b3b10Syamt /* 923186b3b10Syamt * Allocate a partial object 924186b3b10Syamt */ 925186b3b10Syamt memindex = i + blst_radix_init( 926186b3b10Syamt ((scan) ? &scan[i] : NULL), 927186b3b10Syamt radix, 928186b3b10Syamt next_skip - 1, 929186b3b10Syamt count 930186b3b10Syamt ); 931186b3b10Syamt count = 0; 932186b3b10Syamt } else { 933186b3b10Syamt /* 934186b3b10Syamt * Add terminator and break out 935186b3b10Syamt */ 936186b3b10Syamt if (scan) 937dad03fdaSyamt scan[i].bm_bighint = (blist_blkno_t)-1; 938186b3b10Syamt break; 939186b3b10Syamt } 940186b3b10Syamt } 941186b3b10Syamt if (memindex < i) 942186b3b10Syamt memindex = i; 943186b3b10Syamt return(memindex); 944186b3b10Syamt } 945186b3b10Syamt 946186b3b10Syamt #ifdef BLIST_DEBUG 947186b3b10Syamt 948186b3b10Syamt static void 949dad03fdaSyamt blst_radix_print(blmeta_t *scan, blist_blkno_t blk, blist_blkno_t radix, 950dad03fdaSyamt blist_blkno_t skip, int tab) 951186b3b10Syamt { 952dad03fdaSyamt blist_blkno_t i; 953dad03fdaSyamt blist_blkno_t next_skip; 954186b3b10Syamt int lastState = 0; 955186b3b10Syamt 956186b3b10Syamt if (radix == BLIST_BMAP_RADIX) { 957186b3b10Syamt printf( 958dad03fdaSyamt "%*.*s(%0*" PRIx64 ",%" PRIu64 959dad03fdaSyamt "): bitmap %0*" PRIx64 " big=%" PRIu64 "\n", 960186b3b10Syamt tab, tab, "", 961dad03fdaSyamt sizeof(blk) * 2, 962dad03fdaSyamt (uint64_t)blk, 963dad03fdaSyamt (uint64_t)radix, 964dad03fdaSyamt sizeof(scan->u.bmu_bitmap) * 2, 965dad03fdaSyamt (uint64_t)scan->u.bmu_bitmap, 966dad03fdaSyamt (uint64_t)scan->bm_bighint 967186b3b10Syamt ); 968186b3b10Syamt return; 969186b3b10Syamt } 970186b3b10Syamt 971186b3b10Syamt if (scan->u.bmu_avail == 0) { 972186b3b10Syamt printf( 973dad03fdaSyamt "%*.*s(%0*" PRIx64 ",%" PRIu64") ALL ALLOCATED\n", 974dad03fdaSyamt tab, tab, "", 975dad03fdaSyamt sizeof(blk) * 2, 976dad03fdaSyamt (uint64_t)blk, 977dad03fdaSyamt (uint64_t)radix 978186b3b10Syamt ); 979186b3b10Syamt return; 980186b3b10Syamt } 981186b3b10Syamt if (scan->u.bmu_avail == radix) { 982186b3b10Syamt printf( 983dad03fdaSyamt "%*.*s(%0*" PRIx64 ",%" PRIu64 ") ALL FREE\n", 984dad03fdaSyamt tab, tab, "", 985dad03fdaSyamt sizeof(blk) * 2, 986dad03fdaSyamt (uint64_t)blk, 987dad03fdaSyamt (uint64_t)radix 988186b3b10Syamt ); 989186b3b10Syamt return; 990186b3b10Syamt } 991186b3b10Syamt 992186b3b10Syamt printf( 993dad03fdaSyamt "%*.*s(%0*" PRIx64 ",%" PRIu64 "): subtree (%" PRIu64 "/%" 994a1e1c4e8Syamt PRIu64 ") big=%" PRIu64 " {\n", 995186b3b10Syamt tab, tab, "", 996dad03fdaSyamt sizeof(blk) * 2, 997dad03fdaSyamt (uint64_t)blk, 998dad03fdaSyamt (uint64_t)radix, 999dad03fdaSyamt (uint64_t)scan->u.bmu_avail, 1000dad03fdaSyamt (uint64_t)radix, 1001dad03fdaSyamt (uint64_t)scan->bm_bighint 1002186b3b10Syamt ); 1003186b3b10Syamt 1004186b3b10Syamt radix /= BLIST_META_RADIX; 1005dad03fdaSyamt next_skip = (skip / BLIST_META_RADIX); 1006186b3b10Syamt tab += 4; 1007186b3b10Syamt 1008186b3b10Syamt for (i = 1; i <= skip; i += next_skip) { 1009dad03fdaSyamt if (scan[i].bm_bighint == (blist_blkno_t)-1) { 1010186b3b10Syamt printf( 1011dad03fdaSyamt "%*.*s(%0*" PRIx64 ",%" PRIu64 "): Terminator\n", 1012186b3b10Syamt tab, tab, "", 1013dad03fdaSyamt sizeof(blk) * 2, 1014dad03fdaSyamt (uint64_t)blk, 1015dad03fdaSyamt (uint64_t)radix 1016186b3b10Syamt ); 1017186b3b10Syamt lastState = 0; 1018186b3b10Syamt break; 1019186b3b10Syamt } 1020186b3b10Syamt blst_radix_print( 1021186b3b10Syamt &scan[i], 1022186b3b10Syamt blk, 1023186b3b10Syamt radix, 1024186b3b10Syamt next_skip - 1, 1025186b3b10Syamt tab 1026186b3b10Syamt ); 1027186b3b10Syamt blk += radix; 1028186b3b10Syamt } 1029186b3b10Syamt tab -= 4; 1030186b3b10Syamt 1031186b3b10Syamt printf( 1032186b3b10Syamt "%*.*s}\n", 1033186b3b10Syamt tab, tab, "" 1034186b3b10Syamt ); 1035186b3b10Syamt } 1036186b3b10Syamt 1037186b3b10Syamt #endif 1038186b3b10Syamt 1039186b3b10Syamt #ifdef BLIST_DEBUG 1040186b3b10Syamt 1041186b3b10Syamt int 1042186b3b10Syamt main(int ac, char **av) 1043186b3b10Syamt { 1044dad03fdaSyamt blist_blkno_t size = 1024; 1045186b3b10Syamt int i; 1046186b3b10Syamt blist_t bl; 1047186b3b10Syamt 1048186b3b10Syamt for (i = 1; i < ac; ++i) { 1049186b3b10Syamt const char *ptr = av[i]; 1050186b3b10Syamt if (*ptr != '-') { 1051186b3b10Syamt size = strtol(ptr, NULL, 0); 1052186b3b10Syamt continue; 1053186b3b10Syamt } 1054186b3b10Syamt ptr += 2; 1055186b3b10Syamt fprintf(stderr, "Bad option: %s\n", ptr - 2); 1056186b3b10Syamt exit(1); 1057186b3b10Syamt } 1058186b3b10Syamt bl = blist_create(size); 1059186b3b10Syamt blist_free(bl, 0, size); 1060186b3b10Syamt 1061186b3b10Syamt for (;;) { 1062186b3b10Syamt char buf[1024]; 1063a1e1c4e8Syamt uint64_t da = 0; 1064a1e1c4e8Syamt uint64_t count = 0; 1065186b3b10Syamt 1066a1e1c4e8Syamt printf("%" PRIu64 "/%" PRIu64 "/%" PRIu64 "> ", 1067dad03fdaSyamt (uint64_t)bl->bl_free, 1068dad03fdaSyamt (uint64_t)size, 1069dad03fdaSyamt (uint64_t)bl->bl_radix); 1070186b3b10Syamt fflush(stdout); 1071186b3b10Syamt if (fgets(buf, sizeof(buf), stdin) == NULL) 1072186b3b10Syamt break; 1073186b3b10Syamt switch(buf[0]) { 1074186b3b10Syamt case 'r': 1075a1e1c4e8Syamt if (sscanf(buf + 1, "%" SCNu64, &count) == 1) { 1076186b3b10Syamt blist_resize(&bl, count, 1); 1077186b3b10Syamt } else { 1078186b3b10Syamt printf("?\n"); 1079186b3b10Syamt } 1080186b3b10Syamt case 'p': 1081186b3b10Syamt blist_print(bl); 1082186b3b10Syamt break; 1083186b3b10Syamt case 'a': 1084a1e1c4e8Syamt if (sscanf(buf + 1, "%" SCNu64, &count) == 1) { 1085dad03fdaSyamt blist_blkno_t blk = blist_alloc(bl, count); 1086dad03fdaSyamt printf(" R=%0*" PRIx64 "\n", 1087dad03fdaSyamt sizeof(blk) * 2, 1088dad03fdaSyamt (uint64_t)blk); 1089186b3b10Syamt } else { 1090186b3b10Syamt printf("?\n"); 1091186b3b10Syamt } 1092186b3b10Syamt break; 1093186b3b10Syamt case 'f': 1094a1e1c4e8Syamt if (sscanf(buf + 1, "%" SCNx64 " %" SCNu64, 1095a1e1c4e8Syamt &da, &count) == 2) { 1096186b3b10Syamt blist_free(bl, da, count); 1097186b3b10Syamt } else { 1098186b3b10Syamt printf("?\n"); 1099186b3b10Syamt } 1100186b3b10Syamt break; 1101186b3b10Syamt case 'l': 1102a1e1c4e8Syamt if (sscanf(buf + 1, "%" SCNx64 " %" SCNu64, 1103a1e1c4e8Syamt &da, &count) == 2) { 1104dad03fdaSyamt printf(" n=%" PRIu64 "\n", 1105dad03fdaSyamt (uint64_t)blist_fill(bl, da, count)); 1106186b3b10Syamt } else { 1107186b3b10Syamt printf("?\n"); 1108186b3b10Syamt } 1109186b3b10Syamt break; 1110186b3b10Syamt case '?': 1111186b3b10Syamt case 'h': 1112186b3b10Syamt puts( 1113186b3b10Syamt "p -print\n" 1114186b3b10Syamt "a %d -allocate\n" 1115186b3b10Syamt "f %x %d -free\n" 1116186b3b10Syamt "l %x %d -fill\n" 1117186b3b10Syamt "r %d -resize\n" 1118186b3b10Syamt "h/? -help" 1119186b3b10Syamt ); 1120186b3b10Syamt break; 1121186b3b10Syamt default: 1122186b3b10Syamt printf("?\n"); 1123186b3b10Syamt break; 1124186b3b10Syamt } 1125186b3b10Syamt } 1126186b3b10Syamt return(0); 1127186b3b10Syamt } 1128186b3b10Syamt 1129186b3b10Syamt void 1130186b3b10Syamt panic(const char *ctl, ...) 1131186b3b10Syamt { 1132186b3b10Syamt va_list va; 1133186b3b10Syamt 1134186b3b10Syamt va_start(va, ctl); 1135186b3b10Syamt vfprintf(stderr, ctl, va); 1136186b3b10Syamt fprintf(stderr, "\n"); 1137186b3b10Syamt va_end(va); 1138186b3b10Syamt exit(1); 1139186b3b10Syamt } 1140186b3b10Syamt 1141186b3b10Syamt #endif 1142186b3b10Syamt 1143