106b4bf3eSWarner Losh /*- 206b4bf3eSWarner Losh * Copyright (c) 1998 Matthew Dillon. All Rights Reserved. 306b4bf3eSWarner Losh * Redistribution and use in source and binary forms, with or without 406b4bf3eSWarner Losh * modification, are permitted provided that the following conditions 506b4bf3eSWarner Losh * are met: 606b4bf3eSWarner Losh * 1. Redistributions of source code must retain the above copyright 706b4bf3eSWarner Losh * notice, this list of conditions and the following disclaimer. 806b4bf3eSWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 906b4bf3eSWarner Losh * notice, this list of conditions and the following disclaimer in the 1006b4bf3eSWarner Losh * documentation and/or other materials provided with the distribution. 1169a28758SEd Maste * 3. Neither the name of the University nor the names of its contributors 1206b4bf3eSWarner Losh * may be used to endorse or promote products derived from this software 1306b4bf3eSWarner Losh * without specific prior written permission. 1406b4bf3eSWarner Losh * 1506b4bf3eSWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 1606b4bf3eSWarner Losh * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1706b4bf3eSWarner Losh * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1806b4bf3eSWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 1906b4bf3eSWarner Losh * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2006b4bf3eSWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 2106b4bf3eSWarner Losh * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2206b4bf3eSWarner Losh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 2306b4bf3eSWarner Losh * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 2406b4bf3eSWarner Losh * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 2506b4bf3eSWarner Losh * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2606b4bf3eSWarner Losh */ 277090df5aSMatthew Dillon /* 287090df5aSMatthew Dillon * BLIST.C - Bitmap allocator/deallocator, using a radix tree with hinting 297090df5aSMatthew Dillon * 307090df5aSMatthew Dillon * This module implements a general bitmap allocator/deallocator. The 317090df5aSMatthew Dillon * allocator eats around 2 bits per 'block'. The module does not 32f565f395SEitan Adler * try to interpret the meaning of a 'block' other than to return 337090df5aSMatthew Dillon * SWAPBLK_NONE on an allocation failure. 347090df5aSMatthew Dillon * 35ec371b57SAlan Cox * A radix tree controls access to pieces of the bitmap, and includes 36ec371b57SAlan Cox * auxiliary information at each interior node about the availabilty of 37ec371b57SAlan Cox * contiguous free blocks in the subtree rooted at that node. Two radix 38ec371b57SAlan Cox * constants are involved: one for the size of the bitmaps contained in the 39ec371b57SAlan Cox * leaf nodes (BLIST_BMAP_RADIX), and one for the number of descendents of 40ec371b57SAlan Cox * each of the meta (interior) nodes (BLIST_META_RADIX). Each subtree is 41ec371b57SAlan Cox * associated with a range of blocks. The root of any subtree stores a 42ec371b57SAlan Cox * hint field that defines an upper bound on the size of the largest 43ec371b57SAlan Cox * allocation that can begin in the associated block range. A hint is an 44ec371b57SAlan Cox * upper bound on a potential allocation, but not necessarily a tight upper 45ec371b57SAlan Cox * bound. 467090df5aSMatthew Dillon * 477090df5aSMatthew Dillon * The radix tree also implements two collapsed states for meta nodes: 487090df5aSMatthew Dillon * the ALL-ALLOCATED state and the ALL-FREE state. If a meta node is 497090df5aSMatthew Dillon * in either of these two states, all information contained underneath 507090df5aSMatthew Dillon * the node is considered stale. These states are used to optimize 517090df5aSMatthew Dillon * allocation and freeing operations. 527090df5aSMatthew Dillon * 537090df5aSMatthew Dillon * The hinting greatly increases code efficiency for allocations while 547090df5aSMatthew Dillon * the general radix structure optimizes both allocations and frees. The 557090df5aSMatthew Dillon * radix tree should be able to operate well no matter how much 567090df5aSMatthew Dillon * fragmentation there is and no matter how large a bitmap is used. 577090df5aSMatthew Dillon * 5851dc4feaSSergey Kandaurov * The blist code wires all necessary memory at creation time. Neither 5951dc4feaSSergey Kandaurov * allocations nor frees require interaction with the memory subsystem. 6051dc4feaSSergey Kandaurov * The non-blocking features of the blist code are used in the swap code 6151dc4feaSSergey Kandaurov * (vm/swap_pager.c). 627090df5aSMatthew Dillon * 63e3043798SPedro F. Giffuni * LAYOUT: The radix tree is laid out recursively using a 64e3043798SPedro F. Giffuni * linear array. Each meta node is immediately followed (laid out 657090df5aSMatthew Dillon * sequentially in memory) by BLIST_META_RADIX lower level nodes. This 667090df5aSMatthew Dillon * is a recursive structure but one that can be easily scanned through 677090df5aSMatthew Dillon * a very simple 'skip' calculation. In order to support large radixes, 687090df5aSMatthew Dillon * portions of the tree may reside outside our memory allocation. We 697090df5aSMatthew Dillon * handle this with an early-termination optimization (when bighint is 707090df5aSMatthew Dillon * set to -1) on the scan. The memory allocation is only large enough 717090df5aSMatthew Dillon * to cover the number of blocks requested at creation time even if it 727090df5aSMatthew Dillon * must be encompassed in larger root-node radix. 737090df5aSMatthew Dillon * 74f565f395SEitan Adler * NOTE: the allocator cannot currently allocate more than 757090df5aSMatthew Dillon * BLIST_BMAP_RADIX blocks per call. It will panic with 'allocation too 767090df5aSMatthew Dillon * large' if you try. This is an area that could use improvement. The 777090df5aSMatthew Dillon * radix is large enough that this restriction does not effect the swap 78b411efa4SAlan Cox * system, though. Currently only the allocation code is affected by 797090df5aSMatthew Dillon * this algorithmic unfeature. The freeing code can handle arbitrary 807090df5aSMatthew Dillon * ranges. 817090df5aSMatthew Dillon * 827090df5aSMatthew Dillon * This code can be compiled stand-alone for debugging. 837090df5aSMatthew Dillon */ 847090df5aSMatthew Dillon 85677b542eSDavid E. O'Brien #include <sys/cdefs.h> 86677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 87677b542eSDavid E. O'Brien 88c4473420SPeter Wemm #ifdef _KERNEL 897090df5aSMatthew Dillon 907090df5aSMatthew Dillon #include <sys/param.h> 917090df5aSMatthew Dillon #include <sys/systm.h> 927090df5aSMatthew Dillon #include <sys/lock.h> 937090df5aSMatthew Dillon #include <sys/kernel.h> 947090df5aSMatthew Dillon #include <sys/blist.h> 957090df5aSMatthew Dillon #include <sys/malloc.h> 96d027ed2eSAlan Cox #include <sys/sbuf.h> 970cddd8f0SMatthew Dillon #include <sys/proc.h> 9823955314SAlfred Perlstein #include <sys/mutex.h> 997090df5aSMatthew Dillon 1007090df5aSMatthew Dillon #else 1017090df5aSMatthew Dillon 1027090df5aSMatthew Dillon #ifndef BLIST_NO_DEBUG 1037090df5aSMatthew Dillon #define BLIST_DEBUG 1047090df5aSMatthew Dillon #endif 1057090df5aSMatthew Dillon 1067090df5aSMatthew Dillon #include <sys/types.h> 107d90bf7d8SAlan Cox #include <sys/malloc.h> 108d027ed2eSAlan Cox #include <sys/sbuf.h> 1097090df5aSMatthew Dillon #include <stdio.h> 1107090df5aSMatthew Dillon #include <string.h> 1118eefcd40SAlan Cox #include <stddef.h> 1127090df5aSMatthew Dillon #include <stdlib.h> 1137090df5aSMatthew Dillon #include <stdarg.h> 114d3783724SAlan Cox #include <stdbool.h> 1157090df5aSMatthew Dillon 1164be4fd5dSAlan Cox #define bitcount64(x) __bitcount64((uint64_t)(x)) 11792da00bbSMatthew Dillon #define malloc(a,b,c) calloc(a, 1) 1187090df5aSMatthew Dillon #define free(a,b) free(a) 119ec371b57SAlan Cox static __inline int imax(int a, int b) { return (a > b ? a : b); } 1207090df5aSMatthew Dillon 1217090df5aSMatthew Dillon #include <sys/blist.h> 1227090df5aSMatthew Dillon 1237090df5aSMatthew Dillon void panic(const char *ctl, ...); 1247090df5aSMatthew Dillon 1257090df5aSMatthew Dillon #endif 1267090df5aSMatthew Dillon 1277090df5aSMatthew Dillon /* 1287090df5aSMatthew Dillon * static support functions 1297090df5aSMatthew Dillon */ 130ec371b57SAlan Cox static daddr_t blst_leaf_alloc(blmeta_t *scan, daddr_t blk, int count); 131bee93d3cSAlan Cox static daddr_t blst_meta_alloc(blmeta_t *scan, daddr_t cursor, daddr_t count, 132bee93d3cSAlan Cox u_daddr_t radix); 1337090df5aSMatthew Dillon static void blst_leaf_free(blmeta_t *scan, daddr_t relblk, int count); 1347090df5aSMatthew Dillon static void blst_meta_free(blmeta_t *scan, daddr_t freeBlk, daddr_t count, 135bee93d3cSAlan Cox u_daddr_t radix); 1367090df5aSMatthew Dillon static void blst_copy(blmeta_t *scan, daddr_t blk, daddr_t radix, 1372ac0c7c3SAlan Cox blist_t dest, daddr_t count); 138a7249a6cSAlan Cox static daddr_t blst_leaf_fill(blmeta_t *scan, daddr_t blk, int count); 139a7249a6cSAlan Cox static daddr_t blst_meta_fill(blmeta_t *scan, daddr_t allocBlk, daddr_t count, 140bee93d3cSAlan Cox u_daddr_t radix); 141c4473420SPeter Wemm #ifndef _KERNEL 142d3783724SAlan Cox static void blst_radix_print(blmeta_t *scan, daddr_t blk, daddr_t radix, 1432ac0c7c3SAlan Cox int tab); 1447090df5aSMatthew Dillon #endif 1457090df5aSMatthew Dillon 146c4473420SPeter Wemm #ifdef _KERNEL 1477090df5aSMatthew Dillon static MALLOC_DEFINE(M_SWAP, "SWAP", "Swap space"); 1487090df5aSMatthew Dillon #endif 1497090df5aSMatthew Dillon 150ec371b57SAlan Cox _Static_assert(BLIST_BMAP_RADIX % BLIST_META_RADIX == 0, 151ec371b57SAlan Cox "radix divisibility error"); 152ec371b57SAlan Cox #define BLIST_BMAP_MASK (BLIST_BMAP_RADIX - 1) 153d027ed2eSAlan Cox #define BLIST_META_MASK (BLIST_META_RADIX - 1) 154ba98e6a2SAlan Cox 1557090df5aSMatthew Dillon /* 1562ac0c7c3SAlan Cox * For a subtree that can represent the state of up to 'radix' blocks, the 1572ac0c7c3SAlan Cox * number of leaf nodes of the subtree is L=radix/BLIST_BMAP_RADIX. If 'm' 1582ac0c7c3SAlan Cox * is short for BLIST_META_RADIX, then for a tree of height h with L=m**h 1592ac0c7c3SAlan Cox * leaf nodes, the total number of tree nodes is 1 + m + m**2 + ... + m**h, 1602ac0c7c3SAlan Cox * or, equivalently, (m**(h+1)-1)/(m-1). This quantity is called 'skip' 1612ac0c7c3SAlan Cox * in the 'meta' functions that process subtrees. Since integer division 1622ac0c7c3SAlan Cox * discards remainders, we can express this computation as 1632ac0c7c3SAlan Cox * skip = (m * m**h) / (m - 1) 164ba98e6a2SAlan Cox * skip = (m * (radix / BLIST_BMAP_RADIX)) / (m - 1) 165ba98e6a2SAlan Cox * and since m divides BLIST_BMAP_RADIX, we can simplify further to 166ba98e6a2SAlan Cox * skip = (radix / (BLIST_BMAP_RADIX / m)) / (m - 1) 167ba98e6a2SAlan Cox * skip = radix / ((BLIST_BMAP_RADIX / m) * (m - 1)) 168ba98e6a2SAlan Cox * so that simple integer division by a constant can safely be used for the 169ba98e6a2SAlan Cox * calculation. 1702ac0c7c3SAlan Cox */ 1712ac0c7c3SAlan Cox static inline daddr_t 1722ac0c7c3SAlan Cox radix_to_skip(daddr_t radix) 1732ac0c7c3SAlan Cox { 1742ac0c7c3SAlan Cox 1752ac0c7c3SAlan Cox return (radix / 176d027ed2eSAlan Cox ((BLIST_BMAP_RADIX / BLIST_META_RADIX) * BLIST_META_MASK)); 177d027ed2eSAlan Cox } 178d027ed2eSAlan Cox 179d027ed2eSAlan Cox /* 180d027ed2eSAlan Cox * Use binary search, or a faster method, to find the 1 bit in a u_daddr_t. 181d027ed2eSAlan Cox * Assumes that the argument has only one bit set. 182d027ed2eSAlan Cox */ 183d027ed2eSAlan Cox static inline int 184d027ed2eSAlan Cox bitpos(u_daddr_t mask) 185d027ed2eSAlan Cox { 186d027ed2eSAlan Cox int hi, lo, mid; 187d027ed2eSAlan Cox 188d027ed2eSAlan Cox switch (sizeof(mask)) { 189d027ed2eSAlan Cox #ifdef HAVE_INLINE_FFSLL 190d027ed2eSAlan Cox case sizeof(long long): 191d027ed2eSAlan Cox return (ffsll(mask) - 1); 192d027ed2eSAlan Cox #endif 193d027ed2eSAlan Cox default: 194d027ed2eSAlan Cox lo = 0; 195d027ed2eSAlan Cox hi = BLIST_BMAP_RADIX; 196d027ed2eSAlan Cox while (lo + 1 < hi) { 197d027ed2eSAlan Cox mid = (lo + hi) >> 1; 198d027ed2eSAlan Cox if ((mask >> mid) != 0) 199d027ed2eSAlan Cox lo = mid; 200d027ed2eSAlan Cox else 201d027ed2eSAlan Cox hi = mid; 202d027ed2eSAlan Cox } 203d027ed2eSAlan Cox return (lo); 204d027ed2eSAlan Cox } 2052ac0c7c3SAlan Cox } 2062ac0c7c3SAlan Cox 2072ac0c7c3SAlan Cox /* 2087090df5aSMatthew Dillon * blist_create() - create a blist capable of handling up to the specified 2097090df5aSMatthew Dillon * number of blocks 2107090df5aSMatthew Dillon * 211f565f395SEitan Adler * blocks - must be greater than 0 212c8c7ad92SKip Macy * flags - malloc flags 2137090df5aSMatthew Dillon * 2147090df5aSMatthew Dillon * The smallest blist consists of a single leaf node capable of 2157090df5aSMatthew Dillon * managing BLIST_BMAP_RADIX blocks. 2167090df5aSMatthew Dillon */ 2177090df5aSMatthew Dillon blist_t 218c8c7ad92SKip Macy blist_create(daddr_t blocks, int flags) 2197090df5aSMatthew Dillon { 2207090df5aSMatthew Dillon blist_t bl; 2218eefcd40SAlan Cox daddr_t i, last_block; 2228eefcd40SAlan Cox u_daddr_t nodes, radix, skip; 2238eefcd40SAlan Cox int digit; 2247090df5aSMatthew Dillon 2257090df5aSMatthew Dillon /* 2268eefcd40SAlan Cox * Calculate the radix and node count used for scanning. Find the last 2278eefcd40SAlan Cox * block that is followed by a terminator. 2287090df5aSMatthew Dillon */ 2298eefcd40SAlan Cox last_block = blocks - 1; 2307090df5aSMatthew Dillon radix = BLIST_BMAP_RADIX; 2317090df5aSMatthew Dillon while (radix < blocks) { 2328eefcd40SAlan Cox if (((last_block / radix + 1) & BLIST_META_MASK) != 0) 2338eefcd40SAlan Cox /* 2348eefcd40SAlan Cox * A terminator will be added. Update last_block to the 2358eefcd40SAlan Cox * position just before that terminator. 2368eefcd40SAlan Cox */ 2378eefcd40SAlan Cox last_block |= radix - 1; 23857e6d29bSMatthew Dillon radix *= BLIST_META_RADIX; 2397090df5aSMatthew Dillon } 2407090df5aSMatthew Dillon 2418eefcd40SAlan Cox /* 2428eefcd40SAlan Cox * Count the meta-nodes in the expanded tree, including the final 2438eefcd40SAlan Cox * terminator, from the bottom level up to the root. 2448eefcd40SAlan Cox */ 2458eefcd40SAlan Cox nodes = (last_block >= blocks) ? 2 : 1; 2468eefcd40SAlan Cox last_block /= BLIST_BMAP_RADIX; 2478eefcd40SAlan Cox while (last_block > 0) { 2488eefcd40SAlan Cox nodes += last_block + 1; 2498eefcd40SAlan Cox last_block /= BLIST_META_RADIX; 2508eefcd40SAlan Cox } 251*03ca2137SAlan Cox bl = malloc(offsetof(struct blist, bl_root[nodes]), M_SWAP, flags | 252*03ca2137SAlan Cox M_ZERO); 253015d7db6SAlan Cox if (bl == NULL) 254015d7db6SAlan Cox return (NULL); 2557090df5aSMatthew Dillon 2567090df5aSMatthew Dillon bl->bl_blocks = blocks; 2577090df5aSMatthew Dillon bl->bl_radix = radix; 258d4e3484bSAlan Cox bl->bl_cursor = 0; 2598eefcd40SAlan Cox 2608eefcd40SAlan Cox /* 2618eefcd40SAlan Cox * Initialize the empty tree by filling in root values, then initialize 2628eefcd40SAlan Cox * just the terminators in the rest of the tree. 2638eefcd40SAlan Cox */ 2648eefcd40SAlan Cox bl->bl_root[0].bm_bighint = 0; 2658eefcd40SAlan Cox if (radix == BLIST_BMAP_RADIX) 2668eefcd40SAlan Cox bl->bl_root[0].u.bmu_bitmap = 0; 2678eefcd40SAlan Cox else 2688eefcd40SAlan Cox bl->bl_root[0].u.bmu_avail = 0; 2698eefcd40SAlan Cox last_block = blocks - 1; 2708eefcd40SAlan Cox i = 0; 2718eefcd40SAlan Cox while (radix > BLIST_BMAP_RADIX) { 2728eefcd40SAlan Cox radix /= BLIST_META_RADIX; 2738eefcd40SAlan Cox skip = radix_to_skip(radix); 2748eefcd40SAlan Cox digit = last_block / radix; 2758eefcd40SAlan Cox i += 1 + digit * skip; 2768eefcd40SAlan Cox if (digit != BLIST_META_MASK) { 2778eefcd40SAlan Cox /* 2788eefcd40SAlan Cox * Add a terminator. 2798eefcd40SAlan Cox */ 2808eefcd40SAlan Cox bl->bl_root[i + skip].bm_bighint = (daddr_t)-1; 2818eefcd40SAlan Cox bl->bl_root[i + skip].u.bmu_bitmap = 0; 282015d7db6SAlan Cox } 2838eefcd40SAlan Cox last_block %= radix; 2848eefcd40SAlan Cox } 2857090df5aSMatthew Dillon 2867090df5aSMatthew Dillon #if defined(BLIST_DEBUG) 2877090df5aSMatthew Dillon printf( 28892da00bbSMatthew Dillon "BLIST representing %lld blocks (%lld MB of swap)" 28992da00bbSMatthew Dillon ", requiring %lldK of ram\n", 29092da00bbSMatthew Dillon (long long)bl->bl_blocks, 29192da00bbSMatthew Dillon (long long)bl->bl_blocks * 4 / 1024, 292015d7db6SAlan Cox (long long)(nodes * sizeof(blmeta_t) + 1023) / 1024 2937090df5aSMatthew Dillon ); 29492da00bbSMatthew Dillon printf("BLIST raw radix tree contains %lld records\n", 295015d7db6SAlan Cox (long long)nodes); 2967090df5aSMatthew Dillon #endif 2977090df5aSMatthew Dillon 2987090df5aSMatthew Dillon return (bl); 2997090df5aSMatthew Dillon } 3007090df5aSMatthew Dillon 3017090df5aSMatthew Dillon void 3027090df5aSMatthew Dillon blist_destroy(blist_t bl) 3037090df5aSMatthew Dillon { 3048eefcd40SAlan Cox 3057090df5aSMatthew Dillon free(bl, M_SWAP); 3067090df5aSMatthew Dillon } 3077090df5aSMatthew Dillon 3087090df5aSMatthew Dillon /* 3097090df5aSMatthew Dillon * blist_alloc() - reserve space in the block bitmap. Return the base 3107090df5aSMatthew Dillon * of a contiguous region or SWAPBLK_NONE if space could 3117090df5aSMatthew Dillon * not be allocated. 3127090df5aSMatthew Dillon */ 3137090df5aSMatthew Dillon daddr_t 3147090df5aSMatthew Dillon blist_alloc(blist_t bl, daddr_t count) 3157090df5aSMatthew Dillon { 3164be4fd5dSAlan Cox daddr_t blk; 3177090df5aSMatthew Dillon 318d4e3484bSAlan Cox /* 319d4e3484bSAlan Cox * This loop iterates at most twice. An allocation failure in the 320d4e3484bSAlan Cox * first iteration leads to a second iteration only if the cursor was 321d4e3484bSAlan Cox * non-zero. When the cursor is zero, an allocation failure will 322d4e3484bSAlan Cox * reduce the hint, stopping further iterations. 323d4e3484bSAlan Cox */ 324d4e3484bSAlan Cox while (count <= bl->bl_root->bm_bighint) { 325bee93d3cSAlan Cox blk = blst_meta_alloc(bl->bl_root, bl->bl_cursor, count, 326bee93d3cSAlan Cox bl->bl_radix); 327d4e3484bSAlan Cox if (blk != SWAPBLK_NONE) { 328d4e3484bSAlan Cox bl->bl_cursor = blk + count; 329a0ae476bSAlan Cox if (bl->bl_cursor == bl->bl_blocks) 330a0ae476bSAlan Cox bl->bl_cursor = 0; 3317090df5aSMatthew Dillon return (blk); 332d4e3484bSAlan Cox } else if (bl->bl_cursor != 0) 333d4e3484bSAlan Cox bl->bl_cursor = 0; 3347090df5aSMatthew Dillon } 3354be4fd5dSAlan Cox return (SWAPBLK_NONE); 3364be4fd5dSAlan Cox } 3374be4fd5dSAlan Cox 3384be4fd5dSAlan Cox /* 3394be4fd5dSAlan Cox * blist_avail() - return the number of free blocks. 3404be4fd5dSAlan Cox */ 3414be4fd5dSAlan Cox daddr_t 3424be4fd5dSAlan Cox blist_avail(blist_t bl) 3434be4fd5dSAlan Cox { 3444be4fd5dSAlan Cox 3454be4fd5dSAlan Cox if (bl->bl_radix == BLIST_BMAP_RADIX) 3464be4fd5dSAlan Cox return (bitcount64(bl->bl_root->u.bmu_bitmap)); 3474be4fd5dSAlan Cox else 3484be4fd5dSAlan Cox return (bl->bl_root->u.bmu_avail); 3494be4fd5dSAlan Cox } 3507090df5aSMatthew Dillon 3517090df5aSMatthew Dillon /* 3527090df5aSMatthew Dillon * blist_free() - free up space in the block bitmap. Return the base 3537090df5aSMatthew Dillon * of a contiguous region. Panic if an inconsistancy is 3547090df5aSMatthew Dillon * found. 3557090df5aSMatthew Dillon */ 3567090df5aSMatthew Dillon void 3577090df5aSMatthew Dillon blist_free(blist_t bl, daddr_t blkno, daddr_t count) 3587090df5aSMatthew Dillon { 359a396c83aSAlan Cox 360bee93d3cSAlan Cox blst_meta_free(bl->bl_root, blkno, count, bl->bl_radix); 3617090df5aSMatthew Dillon } 3627090df5aSMatthew Dillon 3637090df5aSMatthew Dillon /* 36492da00bbSMatthew Dillon * blist_fill() - mark a region in the block bitmap as off-limits 36592da00bbSMatthew Dillon * to the allocator (i.e. allocate it), ignoring any 36692da00bbSMatthew Dillon * existing allocations. Return the number of blocks 36792da00bbSMatthew Dillon * actually filled that were free before the call. 36892da00bbSMatthew Dillon */ 369a7249a6cSAlan Cox daddr_t 37092da00bbSMatthew Dillon blist_fill(blist_t bl, daddr_t blkno, daddr_t count) 37192da00bbSMatthew Dillon { 37292da00bbSMatthew Dillon 373bee93d3cSAlan Cox return (blst_meta_fill(bl->bl_root, blkno, count, bl->bl_radix)); 37492da00bbSMatthew Dillon } 37592da00bbSMatthew Dillon 37692da00bbSMatthew Dillon /* 3777090df5aSMatthew Dillon * blist_resize() - resize an existing radix tree to handle the 3787090df5aSMatthew Dillon * specified number of blocks. This will reallocate 3797090df5aSMatthew Dillon * the tree and transfer the previous bitmap to the new 3807090df5aSMatthew Dillon * one. When extending the tree you can specify whether 3817090df5aSMatthew Dillon * the new blocks are to left allocated or freed. 3827090df5aSMatthew Dillon */ 3837090df5aSMatthew Dillon void 384c8c7ad92SKip Macy blist_resize(blist_t *pbl, daddr_t count, int freenew, int flags) 3857090df5aSMatthew Dillon { 386c8c7ad92SKip Macy blist_t newbl = blist_create(count, flags); 3877090df5aSMatthew Dillon blist_t save = *pbl; 3887090df5aSMatthew Dillon 3897090df5aSMatthew Dillon *pbl = newbl; 3907090df5aSMatthew Dillon if (count > save->bl_blocks) 3917090df5aSMatthew Dillon count = save->bl_blocks; 3922ac0c7c3SAlan Cox blst_copy(save->bl_root, 0, save->bl_radix, newbl, count); 3937090df5aSMatthew Dillon 3947090df5aSMatthew Dillon /* 3957090df5aSMatthew Dillon * If resizing upwards, should we free the new space or not? 3967090df5aSMatthew Dillon */ 3977090df5aSMatthew Dillon if (freenew && count < newbl->bl_blocks) { 3987090df5aSMatthew Dillon blist_free(newbl, count, newbl->bl_blocks - count); 3997090df5aSMatthew Dillon } 4007090df5aSMatthew Dillon blist_destroy(save); 4017090df5aSMatthew Dillon } 4027090df5aSMatthew Dillon 4037090df5aSMatthew Dillon #ifdef BLIST_DEBUG 4047090df5aSMatthew Dillon 4057090df5aSMatthew Dillon /* 4067090df5aSMatthew Dillon * blist_print() - dump radix tree 4077090df5aSMatthew Dillon */ 4087090df5aSMatthew Dillon void 4097090df5aSMatthew Dillon blist_print(blist_t bl) 4107090df5aSMatthew Dillon { 4112ac0c7c3SAlan Cox printf("BLIST cursor = %08jx {\n", (uintmax_t)bl->bl_cursor); 4122ac0c7c3SAlan Cox blst_radix_print(bl->bl_root, 0, bl->bl_radix, 4); 4137090df5aSMatthew Dillon printf("}\n"); 4147090df5aSMatthew Dillon } 4157090df5aSMatthew Dillon 4167090df5aSMatthew Dillon #endif 4177090df5aSMatthew Dillon 418d027ed2eSAlan Cox static const u_daddr_t fib[] = { 419d027ed2eSAlan Cox 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 420d027ed2eSAlan Cox 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 421d027ed2eSAlan Cox 514229, 832040, 1346269, 2178309, 3524578, 422d027ed2eSAlan Cox }; 423d027ed2eSAlan Cox 424d027ed2eSAlan Cox /* 425d027ed2eSAlan Cox * Use 'gap' to describe a maximal range of unallocated blocks/bits. 426d027ed2eSAlan Cox */ 427d027ed2eSAlan Cox struct gap_stats { 428d027ed2eSAlan Cox daddr_t start; /* current gap start, or SWAPBLK_NONE */ 429d027ed2eSAlan Cox daddr_t num; /* number of gaps observed */ 430d027ed2eSAlan Cox daddr_t max; /* largest gap size */ 431d027ed2eSAlan Cox daddr_t avg; /* average gap size */ 432d027ed2eSAlan Cox daddr_t err; /* sum - num * avg */ 433d027ed2eSAlan Cox daddr_t histo[nitems(fib)]; /* # gaps in each size range */ 434d027ed2eSAlan Cox int max_bucket; /* last histo elt with nonzero val */ 435d027ed2eSAlan Cox }; 436d027ed2eSAlan Cox 437d027ed2eSAlan Cox /* 438d027ed2eSAlan Cox * gap_stats_counting() - is the state 'counting 1 bits'? 439d027ed2eSAlan Cox * or 'skipping 0 bits'? 440d027ed2eSAlan Cox */ 441d027ed2eSAlan Cox static inline bool 442d027ed2eSAlan Cox gap_stats_counting(const struct gap_stats *stats) 443d027ed2eSAlan Cox { 444d027ed2eSAlan Cox 445d027ed2eSAlan Cox return (stats->start != SWAPBLK_NONE); 446d027ed2eSAlan Cox } 447d027ed2eSAlan Cox 448d027ed2eSAlan Cox /* 449d027ed2eSAlan Cox * init_gap_stats() - initialize stats on gap sizes 450d027ed2eSAlan Cox */ 451d027ed2eSAlan Cox static inline void 452d027ed2eSAlan Cox init_gap_stats(struct gap_stats *stats) 453d027ed2eSAlan Cox { 454d027ed2eSAlan Cox 455d027ed2eSAlan Cox bzero(stats, sizeof(*stats)); 456d027ed2eSAlan Cox stats->start = SWAPBLK_NONE; 457d027ed2eSAlan Cox } 458d027ed2eSAlan Cox 459d027ed2eSAlan Cox /* 460d027ed2eSAlan Cox * update_gap_stats() - update stats on gap sizes 461d027ed2eSAlan Cox */ 462d027ed2eSAlan Cox static void 463d027ed2eSAlan Cox update_gap_stats(struct gap_stats *stats, daddr_t posn) 464d027ed2eSAlan Cox { 465d027ed2eSAlan Cox daddr_t size; 466d027ed2eSAlan Cox int hi, lo, mid; 467d027ed2eSAlan Cox 468d027ed2eSAlan Cox if (!gap_stats_counting(stats)) { 469d027ed2eSAlan Cox stats->start = posn; 470d027ed2eSAlan Cox return; 471d027ed2eSAlan Cox } 472d027ed2eSAlan Cox size = posn - stats->start; 473d027ed2eSAlan Cox stats->start = SWAPBLK_NONE; 474d027ed2eSAlan Cox if (size > stats->max) 475d027ed2eSAlan Cox stats->max = size; 476d027ed2eSAlan Cox 477d027ed2eSAlan Cox /* 478d027ed2eSAlan Cox * Find the fibonacci range that contains size, 479d027ed2eSAlan Cox * expecting to find it in an early range. 480d027ed2eSAlan Cox */ 481d027ed2eSAlan Cox lo = 0; 482d027ed2eSAlan Cox hi = 1; 483d027ed2eSAlan Cox while (hi < nitems(fib) && fib[hi] <= size) { 484d027ed2eSAlan Cox lo = hi; 485d027ed2eSAlan Cox hi *= 2; 486d027ed2eSAlan Cox } 487d027ed2eSAlan Cox if (hi >= nitems(fib)) 488d027ed2eSAlan Cox hi = nitems(fib); 489d027ed2eSAlan Cox while (lo + 1 != hi) { 490d027ed2eSAlan Cox mid = (lo + hi) >> 1; 491d027ed2eSAlan Cox if (fib[mid] <= size) 492d027ed2eSAlan Cox lo = mid; 493d027ed2eSAlan Cox else 494d027ed2eSAlan Cox hi = mid; 495d027ed2eSAlan Cox } 496d027ed2eSAlan Cox stats->histo[lo]++; 497d027ed2eSAlan Cox if (lo > stats->max_bucket) 498d027ed2eSAlan Cox stats->max_bucket = lo; 499d027ed2eSAlan Cox stats->err += size - stats->avg; 500d027ed2eSAlan Cox stats->num++; 501d027ed2eSAlan Cox stats->avg += stats->err / stats->num; 502d027ed2eSAlan Cox stats->err %= stats->num; 503d027ed2eSAlan Cox } 504d027ed2eSAlan Cox 505d027ed2eSAlan Cox /* 506d027ed2eSAlan Cox * dump_gap_stats() - print stats on gap sizes 507d027ed2eSAlan Cox */ 508d027ed2eSAlan Cox static inline void 509d027ed2eSAlan Cox dump_gap_stats(const struct gap_stats *stats, struct sbuf *s) 510d027ed2eSAlan Cox { 511d027ed2eSAlan Cox int i; 512d027ed2eSAlan Cox 513d027ed2eSAlan Cox sbuf_printf(s, "number of maximal free ranges: %jd\n", 514d027ed2eSAlan Cox (intmax_t)stats->num); 515d027ed2eSAlan Cox sbuf_printf(s, "largest free range: %jd\n", (intmax_t)stats->max); 516d027ed2eSAlan Cox sbuf_printf(s, "average maximal free range size: %jd\n", 517d027ed2eSAlan Cox (intmax_t)stats->avg); 518d027ed2eSAlan Cox sbuf_printf(s, "number of maximal free ranges of different sizes:\n"); 519d027ed2eSAlan Cox sbuf_printf(s, " count | size range\n"); 520d027ed2eSAlan Cox sbuf_printf(s, " ----- | ----------\n"); 521d027ed2eSAlan Cox for (i = 0; i < stats->max_bucket; i++) { 522d027ed2eSAlan Cox if (stats->histo[i] != 0) { 523d027ed2eSAlan Cox sbuf_printf(s, "%20jd | ", 524d027ed2eSAlan Cox (intmax_t)stats->histo[i]); 525d027ed2eSAlan Cox if (fib[i] != fib[i + 1] - 1) 526d027ed2eSAlan Cox sbuf_printf(s, "%jd to %jd\n", (intmax_t)fib[i], 527d027ed2eSAlan Cox (intmax_t)fib[i + 1] - 1); 528d027ed2eSAlan Cox else 529d027ed2eSAlan Cox sbuf_printf(s, "%jd\n", (intmax_t)fib[i]); 530d027ed2eSAlan Cox } 531d027ed2eSAlan Cox } 532d027ed2eSAlan Cox sbuf_printf(s, "%20jd | ", (intmax_t)stats->histo[i]); 533d027ed2eSAlan Cox if (stats->histo[i] > 1) 534d027ed2eSAlan Cox sbuf_printf(s, "%jd to %jd\n", (intmax_t)fib[i], 535d027ed2eSAlan Cox (intmax_t)stats->max); 536d027ed2eSAlan Cox else 537d027ed2eSAlan Cox sbuf_printf(s, "%jd\n", (intmax_t)stats->max); 538d027ed2eSAlan Cox } 539d027ed2eSAlan Cox 540d027ed2eSAlan Cox /* 541d027ed2eSAlan Cox * blist_stats() - dump radix tree stats 542d027ed2eSAlan Cox */ 543d027ed2eSAlan Cox void 544d027ed2eSAlan Cox blist_stats(blist_t bl, struct sbuf *s) 545d027ed2eSAlan Cox { 546d027ed2eSAlan Cox struct gap_stats gstats; 547d027ed2eSAlan Cox struct gap_stats *stats = &gstats; 548d027ed2eSAlan Cox daddr_t i, nodes, radix; 549d027ed2eSAlan Cox u_daddr_t bit, diff, mask; 550d027ed2eSAlan Cox 551d027ed2eSAlan Cox init_gap_stats(stats); 552d027ed2eSAlan Cox nodes = 0; 553d027ed2eSAlan Cox i = bl->bl_radix; 554d027ed2eSAlan Cox while (i < bl->bl_radix + bl->bl_blocks) { 555d027ed2eSAlan Cox /* 556d027ed2eSAlan Cox * Find max size subtree starting at i. 557d027ed2eSAlan Cox */ 558d027ed2eSAlan Cox radix = BLIST_BMAP_RADIX; 559d027ed2eSAlan Cox while (((i / radix) & BLIST_META_MASK) == 0) 560d027ed2eSAlan Cox radix *= BLIST_META_RADIX; 561d027ed2eSAlan Cox 562d027ed2eSAlan Cox /* 563d027ed2eSAlan Cox * Check for skippable subtrees starting at i. 564d027ed2eSAlan Cox */ 565d027ed2eSAlan Cox while (radix > BLIST_BMAP_RADIX) { 566d027ed2eSAlan Cox if (bl->bl_root[nodes].u.bmu_avail == 0) { 567d027ed2eSAlan Cox if (gap_stats_counting(stats)) 568d027ed2eSAlan Cox update_gap_stats(stats, i); 569d027ed2eSAlan Cox break; 570d027ed2eSAlan Cox } 571d027ed2eSAlan Cox if (bl->bl_root[nodes].u.bmu_avail == radix) { 572d027ed2eSAlan Cox if (!gap_stats_counting(stats)) 573d027ed2eSAlan Cox update_gap_stats(stats, i); 574d027ed2eSAlan Cox break; 575d027ed2eSAlan Cox } 576d027ed2eSAlan Cox 577d027ed2eSAlan Cox /* 578d027ed2eSAlan Cox * Skip subtree root. 579d027ed2eSAlan Cox */ 580d027ed2eSAlan Cox nodes++; 581d027ed2eSAlan Cox radix /= BLIST_META_RADIX; 582d027ed2eSAlan Cox } 583d027ed2eSAlan Cox if (radix == BLIST_BMAP_RADIX) { 584d027ed2eSAlan Cox /* 585d027ed2eSAlan Cox * Scan leaf. 586d027ed2eSAlan Cox */ 587d027ed2eSAlan Cox mask = bl->bl_root[nodes].u.bmu_bitmap; 588d027ed2eSAlan Cox diff = mask ^ (mask << 1); 589d027ed2eSAlan Cox if (gap_stats_counting(stats)) 590d027ed2eSAlan Cox diff ^= 1; 591d027ed2eSAlan Cox while (diff != 0) { 592d027ed2eSAlan Cox bit = diff & -diff; 593d027ed2eSAlan Cox update_gap_stats(stats, i + bitpos(bit)); 594d027ed2eSAlan Cox diff ^= bit; 595d027ed2eSAlan Cox } 596d027ed2eSAlan Cox } 597d027ed2eSAlan Cox nodes += radix_to_skip(radix); 598d027ed2eSAlan Cox i += radix; 599d027ed2eSAlan Cox } 600d027ed2eSAlan Cox update_gap_stats(stats, i); 601d027ed2eSAlan Cox dump_gap_stats(stats, s); 602d027ed2eSAlan Cox } 603d027ed2eSAlan Cox 6047090df5aSMatthew Dillon /************************************************************************ 6057090df5aSMatthew Dillon * ALLOCATION SUPPORT FUNCTIONS * 6067090df5aSMatthew Dillon ************************************************************************ 6077090df5aSMatthew Dillon * 6087090df5aSMatthew Dillon * These support functions do all the actual work. They may seem 6097090df5aSMatthew Dillon * rather longish, but that's because I've commented them up. The 6107090df5aSMatthew Dillon * actual code is straight forward. 6117090df5aSMatthew Dillon * 6127090df5aSMatthew Dillon */ 6137090df5aSMatthew Dillon 6147090df5aSMatthew Dillon /* 6157090df5aSMatthew Dillon * blist_leaf_alloc() - allocate at a leaf in the radix tree (a bitmap). 6167090df5aSMatthew Dillon * 61754541421SAlan Cox * This is the core of the allocator and is optimized for the 61854541421SAlan Cox * BLIST_BMAP_RADIX block allocation case. Otherwise, execution 619d027ed2eSAlan Cox * time is proportional to log2(count) + bitpos time. 6207090df5aSMatthew Dillon */ 6217090df5aSMatthew Dillon static daddr_t 622ec371b57SAlan Cox blst_leaf_alloc(blmeta_t *scan, daddr_t blk, int count) 62354541421SAlan Cox { 62454541421SAlan Cox u_daddr_t mask; 625ec371b57SAlan Cox int count1, hi, lo, num_shifts, range1, range_ext; 6267090df5aSMatthew Dillon 62754541421SAlan Cox range1 = 0; 62854541421SAlan Cox count1 = count - 1; 62954541421SAlan Cox num_shifts = fls(count1); 63054541421SAlan Cox mask = scan->u.bmu_bitmap; 631ec371b57SAlan Cox while ((-mask & ~mask) != 0 && num_shifts > 0) { 63254541421SAlan Cox /* 63354541421SAlan Cox * If bit i is set in mask, then bits in [i, i+range1] are set 63454541421SAlan Cox * in scan->u.bmu_bitmap. The value of range1 is equal to 63554541421SAlan Cox * count1 >> num_shifts. Grow range and reduce num_shifts to 0, 63654541421SAlan Cox * while preserving these invariants. The updates to mask leave 63754541421SAlan Cox * fewer bits set, but each bit that remains set represents a 63854541421SAlan Cox * longer string of consecutive bits set in scan->u.bmu_bitmap. 639ec371b57SAlan Cox * If more updates to mask cannot clear more bits, because mask 640ec371b57SAlan Cox * is partitioned with all 0 bits preceding all 1 bits, the loop 641ec371b57SAlan Cox * terminates immediately. 64254541421SAlan Cox */ 64354541421SAlan Cox num_shifts--; 64454541421SAlan Cox range_ext = range1 + ((count1 >> num_shifts) & 1); 645ec371b57SAlan Cox /* 646ec371b57SAlan Cox * mask is a signed quantity for the shift because when it is 647ec371b57SAlan Cox * shifted right, the sign bit should copied; when the last 648ec371b57SAlan Cox * block of the leaf is free, pretend, for a while, that all the 649ec371b57SAlan Cox * blocks that follow it are also free. 650ec371b57SAlan Cox */ 651ec371b57SAlan Cox mask &= (daddr_t)mask >> range_ext; 65254541421SAlan Cox range1 += range_ext; 65354541421SAlan Cox } 65454541421SAlan Cox if (mask == 0) { 65554541421SAlan Cox /* 65654541421SAlan Cox * Update bighint. There is no allocation bigger than range1 657ec371b57SAlan Cox * starting in this leaf. 65854541421SAlan Cox */ 65954541421SAlan Cox scan->bm_bighint = range1; 6607090df5aSMatthew Dillon return (SWAPBLK_NONE); 6617090df5aSMatthew Dillon } 66254541421SAlan Cox 663ec371b57SAlan Cox /* Discard any candidates that appear before blk. */ 664ec371b57SAlan Cox mask &= (u_daddr_t)-1 << (blk & BLIST_BMAP_MASK); 66554541421SAlan Cox if (mask == 0) 66654541421SAlan Cox return (SWAPBLK_NONE); 6677090df5aSMatthew Dillon 6687090df5aSMatthew Dillon /* 66954541421SAlan Cox * The least significant set bit in mask marks the start of the first 67054541421SAlan Cox * available range of sufficient size. Clear all the bits but that one, 671d027ed2eSAlan Cox * and then find its position. 6727090df5aSMatthew Dillon */ 67354541421SAlan Cox mask &= -mask; 674d027ed2eSAlan Cox lo = bitpos(mask); 6757090df5aSMatthew Dillon 676ec371b57SAlan Cox hi = lo + count; 677ec371b57SAlan Cox if (hi > BLIST_BMAP_RADIX) { 67854541421SAlan Cox /* 679ec371b57SAlan Cox * An allocation within this leaf is impossible, so a successful 680ec371b57SAlan Cox * allocation depends on the next leaf providing some of the blocks. 68154541421SAlan Cox */ 682ec371b57SAlan Cox if (((blk / BLIST_BMAP_RADIX + 1) & BLIST_META_MASK) == 0) { 683ec371b57SAlan Cox /* 684ec371b57SAlan Cox * The next leaf has a different meta-node parent, so it 685ec371b57SAlan Cox * is not necessarily initialized. Update bighint, 686ec371b57SAlan Cox * comparing the range found at the end of mask to the 687ec371b57SAlan Cox * largest earlier range that could have been made to 688ec371b57SAlan Cox * vanish in the initial processing of mask. 689ec371b57SAlan Cox */ 690ec371b57SAlan Cox scan->bm_bighint = imax(BLIST_BMAP_RADIX - lo, range1); 691ec371b57SAlan Cox return (SWAPBLK_NONE); 692ec371b57SAlan Cox } 693ec371b57SAlan Cox hi -= BLIST_BMAP_RADIX; 694ec371b57SAlan Cox if (((scan[1].u.bmu_bitmap + 1) & ~((u_daddr_t)-1 << hi)) != 0) { 695ec371b57SAlan Cox /* 696ec371b57SAlan Cox * The next leaf doesn't have enough free blocks at the 697ec371b57SAlan Cox * beginning to complete the spanning allocation. The 698ec371b57SAlan Cox * hint cannot be updated, because the same allocation 699ec371b57SAlan Cox * request could be satisfied later, by this leaf, if 700ec371b57SAlan Cox * the state of the next leaf changes, and without any 701ec371b57SAlan Cox * changes to this leaf. 702ec371b57SAlan Cox */ 703ec371b57SAlan Cox return (SWAPBLK_NONE); 704ec371b57SAlan Cox } 705ec371b57SAlan Cox /* Clear the first 'hi' bits in the next leaf, allocating them. */ 706ec371b57SAlan Cox scan[1].u.bmu_bitmap &= (u_daddr_t)-1 << hi; 707ec371b57SAlan Cox hi = BLIST_BMAP_RADIX; 708ec371b57SAlan Cox } 709ec371b57SAlan Cox 710ec371b57SAlan Cox /* Set the bits of mask at position 'lo' and higher. */ 711ec371b57SAlan Cox mask = -mask; 712ec371b57SAlan Cox if (hi == BLIST_BMAP_RADIX) { 713ec371b57SAlan Cox /* 714ec371b57SAlan Cox * Update bighint. There is no allocation bigger than range1 715ec371b57SAlan Cox * available in this leaf after this allocation completes. 716ec371b57SAlan Cox */ 717ec371b57SAlan Cox scan->bm_bighint = range1; 718ec371b57SAlan Cox } else { 719ec371b57SAlan Cox /* Clear the bits of mask at position 'hi' and higher. */ 720ec371b57SAlan Cox mask &= (u_daddr_t)-1 >> (BLIST_BMAP_RADIX - hi); 721ec371b57SAlan Cox /* If this allocation uses all the bits, clear the hint. */ 722ec371b57SAlan Cox if (mask == scan->u.bmu_bitmap) 723ec371b57SAlan Cox scan->bm_bighint = 0; 724ec371b57SAlan Cox } 725ec371b57SAlan Cox /* Clear the allocated bits from this leaf. */ 7267090df5aSMatthew Dillon scan->u.bmu_bitmap &= ~mask; 727ec371b57SAlan Cox return ((blk & ~BLIST_BMAP_MASK) + lo); 7287090df5aSMatthew Dillon } 7297090df5aSMatthew Dillon 7307090df5aSMatthew Dillon /* 7317090df5aSMatthew Dillon * blist_meta_alloc() - allocate at a meta in the radix tree. 7327090df5aSMatthew Dillon * 7337090df5aSMatthew Dillon * Attempt to allocate at a meta node. If we can't, we update 7347090df5aSMatthew Dillon * bighint and return a failure. Updating bighint optimize future 7357090df5aSMatthew Dillon * calls that hit this node. We have to check for our collapse cases 7367090df5aSMatthew Dillon * and we have a few optimizations strewn in as well. 7377090df5aSMatthew Dillon */ 7387090df5aSMatthew Dillon static daddr_t 739bee93d3cSAlan Cox blst_meta_alloc(blmeta_t *scan, daddr_t cursor, daddr_t count, u_daddr_t radix) 740d4e3484bSAlan Cox { 741bee93d3cSAlan Cox daddr_t blk, i, next_skip, r, skip; 742d4e3484bSAlan Cox int child; 743d4e3484bSAlan Cox bool scan_from_start; 7447090df5aSMatthew Dillon 745a396c83aSAlan Cox if (radix == BLIST_BMAP_RADIX) 746ec371b57SAlan Cox return (blst_leaf_alloc(scan, cursor, count)); 7474be4fd5dSAlan Cox if (scan->u.bmu_avail < count) { 7487090df5aSMatthew Dillon /* 7494be4fd5dSAlan Cox * The meta node's hint must be too large if the allocation 7504be4fd5dSAlan Cox * exceeds the number of free blocks. Reduce the hint, and 7514be4fd5dSAlan Cox * return failure. 7527090df5aSMatthew Dillon */ 7534be4fd5dSAlan Cox scan->bm_bighint = scan->u.bmu_avail; 7547090df5aSMatthew Dillon return (SWAPBLK_NONE); 7557090df5aSMatthew Dillon } 756ec371b57SAlan Cox blk = cursor & -radix; 7572ac0c7c3SAlan Cox skip = radix_to_skip(radix); 758d4e3484bSAlan Cox next_skip = skip / BLIST_META_RADIX; 7597090df5aSMatthew Dillon 7604be4fd5dSAlan Cox /* 7614be4fd5dSAlan Cox * An ALL-FREE meta node requires special handling before allocating 7624be4fd5dSAlan Cox * any of its blocks. 7634be4fd5dSAlan Cox */ 7647090df5aSMatthew Dillon if (scan->u.bmu_avail == radix) { 76557e6d29bSMatthew Dillon radix /= BLIST_META_RADIX; 7667090df5aSMatthew Dillon 7677090df5aSMatthew Dillon /* 7684be4fd5dSAlan Cox * Reinitialize each of the meta node's children. An ALL-FREE 7694be4fd5dSAlan Cox * meta node cannot have a terminator in any subtree. 7707090df5aSMatthew Dillon */ 7712ac0c7c3SAlan Cox for (i = 1; i < skip; i += next_skip) { 772d4e3484bSAlan Cox if (next_skip == 1) 7737090df5aSMatthew Dillon scan[i].u.bmu_bitmap = (u_daddr_t)-1; 774d4e3484bSAlan Cox else 7757090df5aSMatthew Dillon scan[i].u.bmu_avail = radix; 776d4e3484bSAlan Cox scan[i].bm_bighint = radix; 7777090df5aSMatthew Dillon } 7787090df5aSMatthew Dillon } else { 77957e6d29bSMatthew Dillon radix /= BLIST_META_RADIX; 7807090df5aSMatthew Dillon } 7817090df5aSMatthew Dillon 7824be4fd5dSAlan Cox if (count > radix) { 7834be4fd5dSAlan Cox /* 7844be4fd5dSAlan Cox * The allocation exceeds the number of blocks that are 7854be4fd5dSAlan Cox * managed by a subtree of this meta node. 7864be4fd5dSAlan Cox */ 7874be4fd5dSAlan Cox panic("allocation too large"); 7884be4fd5dSAlan Cox } 789d4e3484bSAlan Cox scan_from_start = cursor == blk; 790d4e3484bSAlan Cox child = (cursor - blk) / radix; 791d4e3484bSAlan Cox blk += child * radix; 7922ac0c7c3SAlan Cox for (i = 1 + child * next_skip; i < skip; i += next_skip) { 7937090df5aSMatthew Dillon if (count <= scan[i].bm_bighint) { 7947090df5aSMatthew Dillon /* 795ec371b57SAlan Cox * The allocation might fit beginning in the i'th subtree. 7967090df5aSMatthew Dillon */ 797bee93d3cSAlan Cox r = blst_meta_alloc(&scan[i], 798bee93d3cSAlan Cox cursor > blk ? cursor : blk, count, radix); 7997090df5aSMatthew Dillon if (r != SWAPBLK_NONE) { 8007090df5aSMatthew Dillon scan->u.bmu_avail -= count; 8017090df5aSMatthew Dillon return (r); 8027090df5aSMatthew Dillon } 8037090df5aSMatthew Dillon } else if (scan[i].bm_bighint == (daddr_t)-1) { 8047090df5aSMatthew Dillon /* 8057090df5aSMatthew Dillon * Terminator 8067090df5aSMatthew Dillon */ 8077090df5aSMatthew Dillon break; 8087090df5aSMatthew Dillon } 8097090df5aSMatthew Dillon blk += radix; 8107090df5aSMatthew Dillon } 8117090df5aSMatthew Dillon 8127090df5aSMatthew Dillon /* 8137090df5aSMatthew Dillon * We couldn't allocate count in this subtree, update bighint. 8147090df5aSMatthew Dillon */ 815d4e3484bSAlan Cox if (scan_from_start && scan->bm_bighint >= count) 8167090df5aSMatthew Dillon scan->bm_bighint = count - 1; 817d4e3484bSAlan Cox 8187090df5aSMatthew Dillon return (SWAPBLK_NONE); 8197090df5aSMatthew Dillon } 8207090df5aSMatthew Dillon 8217090df5aSMatthew Dillon /* 8227090df5aSMatthew Dillon * BLST_LEAF_FREE() - free allocated block from leaf bitmap 8237090df5aSMatthew Dillon * 8247090df5aSMatthew Dillon */ 8257090df5aSMatthew Dillon static void 826b411efa4SAlan Cox blst_leaf_free(blmeta_t *scan, daddr_t blk, int count) 827b411efa4SAlan Cox { 828ec371b57SAlan Cox u_daddr_t mask; 829ec371b57SAlan Cox int n; 830ec371b57SAlan Cox 8317090df5aSMatthew Dillon /* 8327090df5aSMatthew Dillon * free some data in this bitmap 833ec371b57SAlan Cox * mask=0000111111111110000 8347090df5aSMatthew Dillon * \_________/\__/ 835ec371b57SAlan Cox * count n 8367090df5aSMatthew Dillon */ 837ec371b57SAlan Cox n = blk & BLIST_BMAP_MASK; 8387090df5aSMatthew Dillon mask = ((u_daddr_t)-1 << n) & 8397090df5aSMatthew Dillon ((u_daddr_t)-1 >> (BLIST_BMAP_RADIX - count - n)); 8407090df5aSMatthew Dillon if (scan->u.bmu_bitmap & mask) 841ec371b57SAlan Cox panic("freeing free block"); 8427090df5aSMatthew Dillon scan->u.bmu_bitmap |= mask; 8437090df5aSMatthew Dillon 8447090df5aSMatthew Dillon /* 8457090df5aSMatthew Dillon * We could probably do a better job here. We are required to make 8467090df5aSMatthew Dillon * bighint at least as large as the biggest contiguous block of 8477090df5aSMatthew Dillon * data. If we just shoehorn it, a little extra overhead will 8487090df5aSMatthew Dillon * be incured on the next allocation (but only that one typically). 8497090df5aSMatthew Dillon */ 8507090df5aSMatthew Dillon scan->bm_bighint = BLIST_BMAP_RADIX; 8517090df5aSMatthew Dillon } 8527090df5aSMatthew Dillon 8537090df5aSMatthew Dillon /* 8547090df5aSMatthew Dillon * BLST_META_FREE() - free allocated blocks from radix tree meta info 8557090df5aSMatthew Dillon * 8567090df5aSMatthew Dillon * This support routine frees a range of blocks from the bitmap. 8577090df5aSMatthew Dillon * The range must be entirely enclosed by this radix node. If a 8587090df5aSMatthew Dillon * meta node, we break the range down recursively to free blocks 8597090df5aSMatthew Dillon * in subnodes (which means that this code can free an arbitrary 8607090df5aSMatthew Dillon * range whereas the allocation code cannot allocate an arbitrary 8617090df5aSMatthew Dillon * range). 8627090df5aSMatthew Dillon */ 8637090df5aSMatthew Dillon static void 864bee93d3cSAlan Cox blst_meta_free(blmeta_t *scan, daddr_t freeBlk, daddr_t count, u_daddr_t radix) 865d3783724SAlan Cox { 866bee93d3cSAlan Cox daddr_t blk, i, next_skip, skip, v; 867d3783724SAlan Cox int child; 8687090df5aSMatthew Dillon 869a396c83aSAlan Cox if (scan->bm_bighint == (daddr_t)-1) 870a396c83aSAlan Cox panic("freeing invalid range"); 871a396c83aSAlan Cox if (radix == BLIST_BMAP_RADIX) 872a396c83aSAlan Cox return (blst_leaf_free(scan, freeBlk, count)); 8732ac0c7c3SAlan Cox skip = radix_to_skip(radix); 874d3783724SAlan Cox next_skip = skip / BLIST_META_RADIX; 8757090df5aSMatthew Dillon 8767090df5aSMatthew Dillon if (scan->u.bmu_avail == 0) { 8777090df5aSMatthew Dillon /* 8787090df5aSMatthew Dillon * ALL-ALLOCATED special case, with possible 8797090df5aSMatthew Dillon * shortcut to ALL-FREE special case. 8807090df5aSMatthew Dillon */ 8817090df5aSMatthew Dillon scan->u.bmu_avail = count; 8827090df5aSMatthew Dillon scan->bm_bighint = count; 8837090df5aSMatthew Dillon 8847090df5aSMatthew Dillon if (count != radix) { 8852ac0c7c3SAlan Cox for (i = 1; i < skip; i += next_skip) { 8867090df5aSMatthew Dillon if (scan[i].bm_bighint == (daddr_t)-1) 8877090df5aSMatthew Dillon break; 8887090df5aSMatthew Dillon scan[i].bm_bighint = 0; 8897090df5aSMatthew Dillon if (next_skip == 1) { 8907090df5aSMatthew Dillon scan[i].u.bmu_bitmap = 0; 8917090df5aSMatthew Dillon } else { 8927090df5aSMatthew Dillon scan[i].u.bmu_avail = 0; 8937090df5aSMatthew Dillon } 8947090df5aSMatthew Dillon } 8957090df5aSMatthew Dillon /* fall through */ 8967090df5aSMatthew Dillon } 8977090df5aSMatthew Dillon } else { 8987090df5aSMatthew Dillon scan->u.bmu_avail += count; 8997090df5aSMatthew Dillon /* scan->bm_bighint = radix; */ 9007090df5aSMatthew Dillon } 9017090df5aSMatthew Dillon 9027090df5aSMatthew Dillon /* 9037090df5aSMatthew Dillon * ALL-FREE special case. 9047090df5aSMatthew Dillon */ 9057090df5aSMatthew Dillon 9067090df5aSMatthew Dillon if (scan->u.bmu_avail == radix) 9077090df5aSMatthew Dillon return; 9087090df5aSMatthew Dillon if (scan->u.bmu_avail > radix) 909bdc9a8d0SJohn Baldwin panic("blst_meta_free: freeing already free blocks (%lld) %lld/%lld", 910bdc9a8d0SJohn Baldwin (long long)count, (long long)scan->u.bmu_avail, 911bdc9a8d0SJohn Baldwin (long long)radix); 9127090df5aSMatthew Dillon 9137090df5aSMatthew Dillon /* 9147090df5aSMatthew Dillon * Break the free down into its components 9157090df5aSMatthew Dillon */ 9167090df5aSMatthew Dillon 917bee93d3cSAlan Cox blk = freeBlk & -radix; 91857e6d29bSMatthew Dillon radix /= BLIST_META_RADIX; 9197090df5aSMatthew Dillon 920d3783724SAlan Cox child = (freeBlk - blk) / radix; 921d3783724SAlan Cox blk += child * radix; 922d3783724SAlan Cox i = 1 + child * next_skip; 9232ac0c7c3SAlan Cox while (i < skip && blk < freeBlk + count) { 9247090df5aSMatthew Dillon v = blk + radix - freeBlk; 9257090df5aSMatthew Dillon if (v > count) 9267090df5aSMatthew Dillon v = count; 927bee93d3cSAlan Cox blst_meta_free(&scan[i], freeBlk, v, radix); 9287090df5aSMatthew Dillon if (scan->bm_bighint < scan[i].bm_bighint) 9297090df5aSMatthew Dillon scan->bm_bighint = scan[i].bm_bighint; 9307090df5aSMatthew Dillon count -= v; 9317090df5aSMatthew Dillon freeBlk += v; 9327090df5aSMatthew Dillon blk += radix; 9337090df5aSMatthew Dillon i += next_skip; 9347090df5aSMatthew Dillon } 9357090df5aSMatthew Dillon } 9367090df5aSMatthew Dillon 9377090df5aSMatthew Dillon /* 9387090df5aSMatthew Dillon * BLIST_RADIX_COPY() - copy one radix tree to another 9397090df5aSMatthew Dillon * 9407090df5aSMatthew Dillon * Locates free space in the source tree and frees it in the destination 9417090df5aSMatthew Dillon * tree. The space may not already be free in the destination. 9427090df5aSMatthew Dillon */ 943b411efa4SAlan Cox static void 9442ac0c7c3SAlan Cox blst_copy(blmeta_t *scan, daddr_t blk, daddr_t radix, blist_t dest, 9452ac0c7c3SAlan Cox daddr_t count) 946b411efa4SAlan Cox { 9472ac0c7c3SAlan Cox daddr_t i, next_skip, skip; 9487090df5aSMatthew Dillon 9497090df5aSMatthew Dillon /* 9507090df5aSMatthew Dillon * Leaf node 9517090df5aSMatthew Dillon */ 9527090df5aSMatthew Dillon 9537090df5aSMatthew Dillon if (radix == BLIST_BMAP_RADIX) { 9547090df5aSMatthew Dillon u_daddr_t v = scan->u.bmu_bitmap; 9557090df5aSMatthew Dillon 9567090df5aSMatthew Dillon if (v == (u_daddr_t)-1) { 9577090df5aSMatthew Dillon blist_free(dest, blk, count); 9587090df5aSMatthew Dillon } else if (v != 0) { 9597090df5aSMatthew Dillon int i; 9607090df5aSMatthew Dillon 9617090df5aSMatthew Dillon for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) { 962064650c1SAlan Cox if (v & ((u_daddr_t)1 << i)) 9637090df5aSMatthew Dillon blist_free(dest, blk + i, 1); 9647090df5aSMatthew Dillon } 9657090df5aSMatthew Dillon } 9667090df5aSMatthew Dillon return; 9677090df5aSMatthew Dillon } 9687090df5aSMatthew Dillon 9697090df5aSMatthew Dillon /* 9707090df5aSMatthew Dillon * Meta node 9717090df5aSMatthew Dillon */ 9727090df5aSMatthew Dillon 9737090df5aSMatthew Dillon if (scan->u.bmu_avail == 0) { 9747090df5aSMatthew Dillon /* 9757090df5aSMatthew Dillon * Source all allocated, leave dest allocated 9767090df5aSMatthew Dillon */ 9777090df5aSMatthew Dillon return; 9787090df5aSMatthew Dillon } 9797090df5aSMatthew Dillon if (scan->u.bmu_avail == radix) { 9807090df5aSMatthew Dillon /* 9817090df5aSMatthew Dillon * Source all free, free entire dest 9827090df5aSMatthew Dillon */ 9837090df5aSMatthew Dillon if (count < radix) 9847090df5aSMatthew Dillon blist_free(dest, blk, count); 9857090df5aSMatthew Dillon else 9867090df5aSMatthew Dillon blist_free(dest, blk, radix); 9877090df5aSMatthew Dillon return; 9887090df5aSMatthew Dillon } 9897090df5aSMatthew Dillon 9907090df5aSMatthew Dillon 9912ac0c7c3SAlan Cox skip = radix_to_skip(radix); 992d3783724SAlan Cox next_skip = skip / BLIST_META_RADIX; 9932ac0c7c3SAlan Cox radix /= BLIST_META_RADIX; 9947090df5aSMatthew Dillon 9952ac0c7c3SAlan Cox for (i = 1; count && i < skip; i += next_skip) { 9967090df5aSMatthew Dillon if (scan[i].bm_bighint == (daddr_t)-1) 9977090df5aSMatthew Dillon break; 9987090df5aSMatthew Dillon 9997090df5aSMatthew Dillon if (count >= radix) { 10002ac0c7c3SAlan Cox blst_copy(&scan[i], blk, radix, dest, radix); 10017090df5aSMatthew Dillon count -= radix; 10027090df5aSMatthew Dillon } else { 10037090df5aSMatthew Dillon if (count) { 10042ac0c7c3SAlan Cox blst_copy(&scan[i], blk, radix, dest, count); 10057090df5aSMatthew Dillon } 10067090df5aSMatthew Dillon count = 0; 10077090df5aSMatthew Dillon } 10087090df5aSMatthew Dillon blk += radix; 10097090df5aSMatthew Dillon } 10107090df5aSMatthew Dillon } 10117090df5aSMatthew Dillon 10127090df5aSMatthew Dillon /* 101392da00bbSMatthew Dillon * BLST_LEAF_FILL() - allocate specific blocks in leaf bitmap 101492da00bbSMatthew Dillon * 101592da00bbSMatthew Dillon * This routine allocates all blocks in the specified range 101692da00bbSMatthew Dillon * regardless of any existing allocations in that range. Returns 101792da00bbSMatthew Dillon * the number of blocks allocated by the call. 101892da00bbSMatthew Dillon */ 1019a7249a6cSAlan Cox static daddr_t 102092da00bbSMatthew Dillon blst_leaf_fill(blmeta_t *scan, daddr_t blk, int count) 102192da00bbSMatthew Dillon { 1022a7249a6cSAlan Cox daddr_t nblks; 10234be4fd5dSAlan Cox u_daddr_t mask; 1024ec371b57SAlan Cox int n; 102592da00bbSMatthew Dillon 1026ec371b57SAlan Cox n = blk & BLIST_BMAP_MASK; 102792da00bbSMatthew Dillon mask = ((u_daddr_t)-1 << n) & 102892da00bbSMatthew Dillon ((u_daddr_t)-1 >> (BLIST_BMAP_RADIX - count - n)); 102992da00bbSMatthew Dillon 10304be4fd5dSAlan Cox /* Count the number of blocks that we are allocating. */ 10314be4fd5dSAlan Cox nblks = bitcount64(scan->u.bmu_bitmap & mask); 103292da00bbSMatthew Dillon 103392da00bbSMatthew Dillon scan->u.bmu_bitmap &= ~mask; 10344be4fd5dSAlan Cox return (nblks); 103592da00bbSMatthew Dillon } 103692da00bbSMatthew Dillon 103792da00bbSMatthew Dillon /* 103892da00bbSMatthew Dillon * BLIST_META_FILL() - allocate specific blocks at a meta node 103992da00bbSMatthew Dillon * 104092da00bbSMatthew Dillon * This routine allocates the specified range of blocks, 104192da00bbSMatthew Dillon * regardless of any existing allocations in the range. The 104292da00bbSMatthew Dillon * range must be within the extent of this node. Returns the 104392da00bbSMatthew Dillon * number of blocks allocated by the call. 104492da00bbSMatthew Dillon */ 1045a7249a6cSAlan Cox static daddr_t 1046bee93d3cSAlan Cox blst_meta_fill(blmeta_t *scan, daddr_t allocBlk, daddr_t count, u_daddr_t radix) 1047d3783724SAlan Cox { 1048bee93d3cSAlan Cox daddr_t blk, i, nblks, next_skip, skip, v; 1049d3783724SAlan Cox int child; 105092da00bbSMatthew Dillon 1051a396c83aSAlan Cox if (scan->bm_bighint == (daddr_t)-1) 1052a396c83aSAlan Cox panic("filling invalid range"); 10534be4fd5dSAlan Cox if (count > radix) { 10544be4fd5dSAlan Cox /* 10554be4fd5dSAlan Cox * The allocation exceeds the number of blocks that are 1056a396c83aSAlan Cox * managed by this node. 10574be4fd5dSAlan Cox */ 1058a396c83aSAlan Cox panic("fill too large"); 10594be4fd5dSAlan Cox } 1060a396c83aSAlan Cox if (radix == BLIST_BMAP_RADIX) 1061a396c83aSAlan Cox return (blst_leaf_fill(scan, allocBlk, count)); 106292da00bbSMatthew Dillon if (count == radix || scan->u.bmu_avail == 0) { 106392da00bbSMatthew Dillon /* 106492da00bbSMatthew Dillon * ALL-ALLOCATED special case 106592da00bbSMatthew Dillon */ 106692da00bbSMatthew Dillon nblks = scan->u.bmu_avail; 106792da00bbSMatthew Dillon scan->u.bmu_avail = 0; 106886dd278fSAlan Cox scan->bm_bighint = 0; 1069a396c83aSAlan Cox return (nblks); 107092da00bbSMatthew Dillon } 10712ac0c7c3SAlan Cox skip = radix_to_skip(radix); 1072d3783724SAlan Cox next_skip = skip / BLIST_META_RADIX; 1073bee93d3cSAlan Cox blk = allocBlk & -radix; 107492da00bbSMatthew Dillon 10754be4fd5dSAlan Cox /* 10764be4fd5dSAlan Cox * An ALL-FREE meta node requires special handling before allocating 10774be4fd5dSAlan Cox * any of its blocks. 10784be4fd5dSAlan Cox */ 107992da00bbSMatthew Dillon if (scan->u.bmu_avail == radix) { 108057e6d29bSMatthew Dillon radix /= BLIST_META_RADIX; 108192da00bbSMatthew Dillon 108292da00bbSMatthew Dillon /* 10834be4fd5dSAlan Cox * Reinitialize each of the meta node's children. An ALL-FREE 10844be4fd5dSAlan Cox * meta node cannot have a terminator in any subtree. 108592da00bbSMatthew Dillon */ 10862ac0c7c3SAlan Cox for (i = 1; i < skip; i += next_skip) { 1087a396c83aSAlan Cox if (next_skip == 1) 108892da00bbSMatthew Dillon scan[i].u.bmu_bitmap = (u_daddr_t)-1; 1089a396c83aSAlan Cox else 109092da00bbSMatthew Dillon scan[i].u.bmu_avail = radix; 1091a396c83aSAlan Cox scan[i].bm_bighint = radix; 109292da00bbSMatthew Dillon } 109392da00bbSMatthew Dillon } else { 109457e6d29bSMatthew Dillon radix /= BLIST_META_RADIX; 109592da00bbSMatthew Dillon } 109692da00bbSMatthew Dillon 1097d3783724SAlan Cox nblks = 0; 1098d3783724SAlan Cox child = (allocBlk - blk) / radix; 1099d3783724SAlan Cox blk += child * radix; 1100d3783724SAlan Cox i = 1 + child * next_skip; 11012ac0c7c3SAlan Cox while (i < skip && blk < allocBlk + count) { 110292da00bbSMatthew Dillon v = blk + radix - allocBlk; 110392da00bbSMatthew Dillon if (v > count) 110492da00bbSMatthew Dillon v = count; 1105bee93d3cSAlan Cox nblks += blst_meta_fill(&scan[i], allocBlk, v, radix); 110692da00bbSMatthew Dillon count -= v; 110792da00bbSMatthew Dillon allocBlk += v; 110892da00bbSMatthew Dillon blk += radix; 110992da00bbSMatthew Dillon i += next_skip; 111092da00bbSMatthew Dillon } 111192da00bbSMatthew Dillon scan->u.bmu_avail -= nblks; 1112a396c83aSAlan Cox return (nblks); 111392da00bbSMatthew Dillon } 111492da00bbSMatthew Dillon 11157090df5aSMatthew Dillon #ifdef BLIST_DEBUG 11167090df5aSMatthew Dillon 11177090df5aSMatthew Dillon static void 11182ac0c7c3SAlan Cox blst_radix_print(blmeta_t *scan, daddr_t blk, daddr_t radix, int tab) 11197090df5aSMatthew Dillon { 11202ac0c7c3SAlan Cox daddr_t i, next_skip, skip; 11217090df5aSMatthew Dillon 11227090df5aSMatthew Dillon if (radix == BLIST_BMAP_RADIX) { 11237090df5aSMatthew Dillon printf( 1124d90bf7d8SAlan Cox "%*.*s(%08llx,%lld): bitmap %016llx big=%lld\n", 11257090df5aSMatthew Dillon tab, tab, "", 112692da00bbSMatthew Dillon (long long)blk, (long long)radix, 112792da00bbSMatthew Dillon (long long)scan->u.bmu_bitmap, 112892da00bbSMatthew Dillon (long long)scan->bm_bighint 11297090df5aSMatthew Dillon ); 11307090df5aSMatthew Dillon return; 11317090df5aSMatthew Dillon } 11327090df5aSMatthew Dillon 11337090df5aSMatthew Dillon if (scan->u.bmu_avail == 0) { 11347090df5aSMatthew Dillon printf( 113592da00bbSMatthew Dillon "%*.*s(%08llx,%lld) ALL ALLOCATED\n", 11367090df5aSMatthew Dillon tab, tab, "", 113792da00bbSMatthew Dillon (long long)blk, 113892da00bbSMatthew Dillon (long long)radix 11397090df5aSMatthew Dillon ); 11407090df5aSMatthew Dillon return; 11417090df5aSMatthew Dillon } 11427090df5aSMatthew Dillon if (scan->u.bmu_avail == radix) { 11437090df5aSMatthew Dillon printf( 114492da00bbSMatthew Dillon "%*.*s(%08llx,%lld) ALL FREE\n", 11457090df5aSMatthew Dillon tab, tab, "", 114692da00bbSMatthew Dillon (long long)blk, 114792da00bbSMatthew Dillon (long long)radix 11487090df5aSMatthew Dillon ); 11497090df5aSMatthew Dillon return; 11507090df5aSMatthew Dillon } 11517090df5aSMatthew Dillon 11527090df5aSMatthew Dillon printf( 115392da00bbSMatthew Dillon "%*.*s(%08llx,%lld): subtree (%lld/%lld) big=%lld {\n", 11547090df5aSMatthew Dillon tab, tab, "", 115592da00bbSMatthew Dillon (long long)blk, (long long)radix, 115692da00bbSMatthew Dillon (long long)scan->u.bmu_avail, 115792da00bbSMatthew Dillon (long long)radix, 115892da00bbSMatthew Dillon (long long)scan->bm_bighint 11597090df5aSMatthew Dillon ); 11607090df5aSMatthew Dillon 11612ac0c7c3SAlan Cox skip = radix_to_skip(radix); 1162d3783724SAlan Cox next_skip = skip / BLIST_META_RADIX; 11632ac0c7c3SAlan Cox radix /= BLIST_META_RADIX; 11647090df5aSMatthew Dillon tab += 4; 11657090df5aSMatthew Dillon 11662ac0c7c3SAlan Cox for (i = 1; i < skip; i += next_skip) { 11677090df5aSMatthew Dillon if (scan[i].bm_bighint == (daddr_t)-1) { 11687090df5aSMatthew Dillon printf( 116992da00bbSMatthew Dillon "%*.*s(%08llx,%lld): Terminator\n", 11707090df5aSMatthew Dillon tab, tab, "", 117192da00bbSMatthew Dillon (long long)blk, (long long)radix 11727090df5aSMatthew Dillon ); 11737090df5aSMatthew Dillon break; 11747090df5aSMatthew Dillon } 11752ac0c7c3SAlan Cox blst_radix_print(&scan[i], blk, radix, tab); 11767090df5aSMatthew Dillon blk += radix; 11777090df5aSMatthew Dillon } 11787090df5aSMatthew Dillon tab -= 4; 11797090df5aSMatthew Dillon 11807090df5aSMatthew Dillon printf( 11817090df5aSMatthew Dillon "%*.*s}\n", 11827090df5aSMatthew Dillon tab, tab, "" 11837090df5aSMatthew Dillon ); 11847090df5aSMatthew Dillon } 11857090df5aSMatthew Dillon 11867090df5aSMatthew Dillon #endif 11877090df5aSMatthew Dillon 11887090df5aSMatthew Dillon #ifdef BLIST_DEBUG 11897090df5aSMatthew Dillon 11907090df5aSMatthew Dillon int 11917090df5aSMatthew Dillon main(int ac, char **av) 11927090df5aSMatthew Dillon { 11937090df5aSMatthew Dillon int size = 1024; 11947090df5aSMatthew Dillon int i; 11957090df5aSMatthew Dillon blist_t bl; 1196d027ed2eSAlan Cox struct sbuf *s; 11977090df5aSMatthew Dillon 11987090df5aSMatthew Dillon for (i = 1; i < ac; ++i) { 11997090df5aSMatthew Dillon const char *ptr = av[i]; 12007090df5aSMatthew Dillon if (*ptr != '-') { 12017090df5aSMatthew Dillon size = strtol(ptr, NULL, 0); 12027090df5aSMatthew Dillon continue; 12037090df5aSMatthew Dillon } 12047090df5aSMatthew Dillon ptr += 2; 12057090df5aSMatthew Dillon fprintf(stderr, "Bad option: %s\n", ptr - 2); 12067090df5aSMatthew Dillon exit(1); 12077090df5aSMatthew Dillon } 1208c8c7ad92SKip Macy bl = blist_create(size, M_WAITOK); 12097090df5aSMatthew Dillon blist_free(bl, 0, size); 12107090df5aSMatthew Dillon 12117090df5aSMatthew Dillon for (;;) { 12127090df5aSMatthew Dillon char buf[1024]; 1213d90bf7d8SAlan Cox long long da = 0; 1214d90bf7d8SAlan Cox long long count = 0; 12157090df5aSMatthew Dillon 12164be4fd5dSAlan Cox printf("%lld/%lld/%lld> ", (long long)blist_avail(bl), 121792da00bbSMatthew Dillon (long long)size, (long long)bl->bl_radix); 12187090df5aSMatthew Dillon fflush(stdout); 12197090df5aSMatthew Dillon if (fgets(buf, sizeof(buf), stdin) == NULL) 12207090df5aSMatthew Dillon break; 12217090df5aSMatthew Dillon switch(buf[0]) { 12227090df5aSMatthew Dillon case 'r': 122392da00bbSMatthew Dillon if (sscanf(buf + 1, "%lld", &count) == 1) { 1224d90bf7d8SAlan Cox blist_resize(&bl, count, 1, M_WAITOK); 12257090df5aSMatthew Dillon } else { 12267090df5aSMatthew Dillon printf("?\n"); 12277090df5aSMatthew Dillon } 12287090df5aSMatthew Dillon case 'p': 12297090df5aSMatthew Dillon blist_print(bl); 12307090df5aSMatthew Dillon break; 1231d027ed2eSAlan Cox case 's': 1232d027ed2eSAlan Cox s = sbuf_new_auto(); 1233d027ed2eSAlan Cox blist_stats(bl, s); 1234d027ed2eSAlan Cox sbuf_finish(s); 1235d027ed2eSAlan Cox printf("%s", sbuf_data(s)); 1236d027ed2eSAlan Cox sbuf_delete(s); 1237d027ed2eSAlan Cox break; 12387090df5aSMatthew Dillon case 'a': 123992da00bbSMatthew Dillon if (sscanf(buf + 1, "%lld", &count) == 1) { 12407090df5aSMatthew Dillon daddr_t blk = blist_alloc(bl, count); 124192da00bbSMatthew Dillon printf(" R=%08llx\n", (long long)blk); 12427090df5aSMatthew Dillon } else { 12437090df5aSMatthew Dillon printf("?\n"); 12447090df5aSMatthew Dillon } 12457090df5aSMatthew Dillon break; 12467090df5aSMatthew Dillon case 'f': 1247d90bf7d8SAlan Cox if (sscanf(buf + 1, "%llx %lld", &da, &count) == 2) { 12487090df5aSMatthew Dillon blist_free(bl, da, count); 12497090df5aSMatthew Dillon } else { 12507090df5aSMatthew Dillon printf("?\n"); 12517090df5aSMatthew Dillon } 12527090df5aSMatthew Dillon break; 125392da00bbSMatthew Dillon case 'l': 1254d90bf7d8SAlan Cox if (sscanf(buf + 1, "%llx %lld", &da, &count) == 2) { 1255a7249a6cSAlan Cox printf(" n=%jd\n", 1256a7249a6cSAlan Cox (intmax_t)blist_fill(bl, da, count)); 125792da00bbSMatthew Dillon } else { 125892da00bbSMatthew Dillon printf("?\n"); 125992da00bbSMatthew Dillon } 126092da00bbSMatthew Dillon break; 12617090df5aSMatthew Dillon case '?': 12627090df5aSMatthew Dillon case 'h': 12637090df5aSMatthew Dillon puts( 12647090df5aSMatthew Dillon "p -print\n" 1265d027ed2eSAlan Cox "s -stats\n" 12667090df5aSMatthew Dillon "a %d -allocate\n" 12677090df5aSMatthew Dillon "f %x %d -free\n" 126892da00bbSMatthew Dillon "l %x %d -fill\n" 12697090df5aSMatthew Dillon "r %d -resize\n" 12707090df5aSMatthew Dillon "h/? -help" 12717090df5aSMatthew Dillon ); 12727090df5aSMatthew Dillon break; 12737090df5aSMatthew Dillon default: 12747090df5aSMatthew Dillon printf("?\n"); 12757090df5aSMatthew Dillon break; 12767090df5aSMatthew Dillon } 12777090df5aSMatthew Dillon } 12787090df5aSMatthew Dillon return(0); 12797090df5aSMatthew Dillon } 12807090df5aSMatthew Dillon 12817090df5aSMatthew Dillon void 12827090df5aSMatthew Dillon panic(const char *ctl, ...) 12837090df5aSMatthew Dillon { 12847090df5aSMatthew Dillon va_list va; 12857090df5aSMatthew Dillon 12867090df5aSMatthew Dillon va_start(va, ctl); 12877090df5aSMatthew Dillon vfprintf(stderr, ctl, va); 12887090df5aSMatthew Dillon fprintf(stderr, "\n"); 12897090df5aSMatthew Dillon va_end(va); 12907090df5aSMatthew Dillon exit(1); 12917090df5aSMatthew Dillon } 12927090df5aSMatthew Dillon 12937090df5aSMatthew Dillon #endif 1294