1774d251dSAttilio Rao /* 2774d251dSAttilio Rao * Copyright (c) 2013 EMC Corp. 3774d251dSAttilio Rao * Copyright (c) 2011 Jeffrey Roberson <jeff@freebsd.org> 4774d251dSAttilio Rao * Copyright (c) 2008 Mayur Shardul <mayur.shardul@gmail.com> 5774d251dSAttilio Rao * All rights reserved. 6774d251dSAttilio Rao * 7774d251dSAttilio Rao * Redistribution and use in source and binary forms, with or without 8774d251dSAttilio Rao * modification, are permitted provided that the following conditions 9774d251dSAttilio Rao * are met: 10774d251dSAttilio Rao * 1. Redistributions of source code must retain the above copyright 11774d251dSAttilio Rao * notice, this list of conditions and the following disclaimer. 12774d251dSAttilio Rao * 2. Redistributions in binary form must reproduce the above copyright 13774d251dSAttilio Rao * notice, this list of conditions and the following disclaimer in the 14774d251dSAttilio Rao * documentation and/or other materials provided with the distribution. 15774d251dSAttilio Rao * 16774d251dSAttilio Rao * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17774d251dSAttilio Rao * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18774d251dSAttilio Rao * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19774d251dSAttilio Rao * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20774d251dSAttilio Rao * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21774d251dSAttilio Rao * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22774d251dSAttilio Rao * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23774d251dSAttilio Rao * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24774d251dSAttilio Rao * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25774d251dSAttilio Rao * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26774d251dSAttilio Rao * SUCH DAMAGE. 27774d251dSAttilio Rao * 28774d251dSAttilio Rao */ 29774d251dSAttilio Rao 30774d251dSAttilio Rao /* 31774d251dSAttilio Rao * Path-compressed radix trie implementation. 32774d251dSAttilio Rao * The following code is not generalized into a general purpose library 33774d251dSAttilio Rao * because there are way too many parameters embedded that should really 34774d251dSAttilio Rao * be decided by the library consumers. At the same time, consumers 35774d251dSAttilio Rao * of this code must achieve highest possible performance. 36774d251dSAttilio Rao * 37774d251dSAttilio Rao * The implementation takes into account the following rationale: 38774d251dSAttilio Rao * - Size of the nodes should be as small as possible but still big enough 39774d251dSAttilio Rao * to avoid a large maximum depth for the trie. This is a balance 40774d251dSAttilio Rao * between the necessity to not wire too much physical memory for the nodes 41774d251dSAttilio Rao * and the necessity to avoid too much cache pollution during the trie 42774d251dSAttilio Rao * operations. 43774d251dSAttilio Rao * - There is not a huge bias toward the number of lookup operations over 44774d251dSAttilio Rao * the number of insert and remove operations. This basically implies 45774d251dSAttilio Rao * that optimizations supposedly helping one operation but hurting the 46774d251dSAttilio Rao * other might be carefully evaluated. 47774d251dSAttilio Rao * - On average not many nodes are expected to be fully populated, hence 48774d251dSAttilio Rao * level compression may just complicate things. 49774d251dSAttilio Rao */ 50774d251dSAttilio Rao 51774d251dSAttilio Rao #include <sys/cdefs.h> 52774d251dSAttilio Rao __FBSDID("$FreeBSD$"); 53774d251dSAttilio Rao 54774d251dSAttilio Rao #include "opt_ddb.h" 55774d251dSAttilio Rao 56774d251dSAttilio Rao #include <sys/param.h> 57774d251dSAttilio Rao #include <sys/systm.h> 58774d251dSAttilio Rao #include <sys/kernel.h> 59774d251dSAttilio Rao #include <sys/vmmeter.h> 60774d251dSAttilio Rao 61774d251dSAttilio Rao #include <vm/uma.h> 62774d251dSAttilio Rao #include <vm/vm.h> 63774d251dSAttilio Rao #include <vm/vm_param.h> 64774d251dSAttilio Rao #include <vm/vm_page.h> 65774d251dSAttilio Rao #include <vm/vm_radix.h> 66774d251dSAttilio Rao 67774d251dSAttilio Rao #ifdef DDB 68774d251dSAttilio Rao #include <ddb/ddb.h> 69774d251dSAttilio Rao #endif 70774d251dSAttilio Rao 71774d251dSAttilio Rao /* 72774d251dSAttilio Rao * These widths should allow the pointers to a node's children to fit within 73774d251dSAttilio Rao * a single cache line. The extra levels from a narrow width should not be 74774d251dSAttilio Rao * a problem thanks to path compression. 75774d251dSAttilio Rao */ 76774d251dSAttilio Rao #ifdef __LP64__ 77774d251dSAttilio Rao #define VM_RADIX_WIDTH 4 78774d251dSAttilio Rao #else 79774d251dSAttilio Rao #define VM_RADIX_WIDTH 3 80774d251dSAttilio Rao #endif 81774d251dSAttilio Rao 82774d251dSAttilio Rao #define VM_RADIX_COUNT (1 << VM_RADIX_WIDTH) 83774d251dSAttilio Rao #define VM_RADIX_MASK (VM_RADIX_COUNT - 1) 84774d251dSAttilio Rao #define VM_RADIX_LIMIT \ 85774d251dSAttilio Rao (howmany((sizeof(vm_pindex_t) * NBBY), VM_RADIX_WIDTH) - 1) 86774d251dSAttilio Rao 87774d251dSAttilio Rao /* Flag bits stored in node pointers. */ 88774d251dSAttilio Rao #define VM_RADIX_ISLEAF 0x1 89774d251dSAttilio Rao #define VM_RADIX_FLAGS 0x1 90774d251dSAttilio Rao #define VM_RADIX_PAD VM_RADIX_FLAGS 91774d251dSAttilio Rao 92774d251dSAttilio Rao /* Returns one unit associated with specified level. */ 93774d251dSAttilio Rao #define VM_RADIX_UNITLEVEL(lev) \ 94774d251dSAttilio Rao ((vm_pindex_t)1 << ((VM_RADIX_LIMIT - (lev)) * VM_RADIX_WIDTH)) 95774d251dSAttilio Rao 96774d251dSAttilio Rao struct vm_radix_node { 97774d251dSAttilio Rao vm_pindex_t rn_owner; /* Owner of record. */ 98774d251dSAttilio Rao uint16_t rn_count; /* Valid children. */ 99774d251dSAttilio Rao uint16_t rn_clev; /* Current level. */ 1002c899fedSAlan Cox void *rn_child[VM_RADIX_COUNT]; /* Child nodes. */ 101774d251dSAttilio Rao }; 102774d251dSAttilio Rao 103774d251dSAttilio Rao static uma_zone_t vm_radix_node_zone; 104774d251dSAttilio Rao 105774d251dSAttilio Rao /* 106774d251dSAttilio Rao * Allocate a radix node. Pre-allocation should ensure that the request 107774d251dSAttilio Rao * will always be satisfied. 108774d251dSAttilio Rao */ 109774d251dSAttilio Rao static __inline struct vm_radix_node * 110774d251dSAttilio Rao vm_radix_node_get(vm_pindex_t owner, uint16_t count, uint16_t clevel) 111774d251dSAttilio Rao { 112774d251dSAttilio Rao struct vm_radix_node *rnode; 113774d251dSAttilio Rao 114774d251dSAttilio Rao rnode = uma_zalloc(vm_radix_node_zone, M_NOWAIT); 115774d251dSAttilio Rao 116774d251dSAttilio Rao /* 117774d251dSAttilio Rao * The required number of nodes should already be pre-allocated 118774d251dSAttilio Rao * by vm_radix_prealloc(). However, UMA can hold a few nodes 119774d251dSAttilio Rao * in per-CPU buckets, which will not be accessible by the 120774d251dSAttilio Rao * current CPU. Thus, the allocation could return NULL when 121774d251dSAttilio Rao * the pre-allocated pool is close to exhaustion. Anyway, 122774d251dSAttilio Rao * in practice this should never occur because a new node 123774d251dSAttilio Rao * is not always required for insert. Thus, the pre-allocated 124774d251dSAttilio Rao * pool should have some extra pages that prevent this from 125774d251dSAttilio Rao * becoming a problem. 126774d251dSAttilio Rao */ 127774d251dSAttilio Rao if (rnode == NULL) 128774d251dSAttilio Rao panic("%s: uma_zalloc() returned NULL for a new node", 129774d251dSAttilio Rao __func__); 130774d251dSAttilio Rao rnode->rn_owner = owner; 131774d251dSAttilio Rao rnode->rn_count = count; 132774d251dSAttilio Rao rnode->rn_clev = clevel; 133774d251dSAttilio Rao return (rnode); 134774d251dSAttilio Rao } 135774d251dSAttilio Rao 136774d251dSAttilio Rao /* 137774d251dSAttilio Rao * Free radix node. 138774d251dSAttilio Rao */ 139774d251dSAttilio Rao static __inline void 140774d251dSAttilio Rao vm_radix_node_put(struct vm_radix_node *rnode) 141774d251dSAttilio Rao { 142774d251dSAttilio Rao 143774d251dSAttilio Rao uma_zfree(vm_radix_node_zone, rnode); 144774d251dSAttilio Rao } 145774d251dSAttilio Rao 146774d251dSAttilio Rao /* 147774d251dSAttilio Rao * Return the position in the array for a given level. 148774d251dSAttilio Rao */ 149774d251dSAttilio Rao static __inline int 150774d251dSAttilio Rao vm_radix_slot(vm_pindex_t index, uint16_t level) 151774d251dSAttilio Rao { 152774d251dSAttilio Rao 153774d251dSAttilio Rao return ((index >> ((VM_RADIX_LIMIT - level) * VM_RADIX_WIDTH)) & 154774d251dSAttilio Rao VM_RADIX_MASK); 155774d251dSAttilio Rao } 156774d251dSAttilio Rao 157774d251dSAttilio Rao /* Trims the key after the specified level. */ 158774d251dSAttilio Rao static __inline vm_pindex_t 159774d251dSAttilio Rao vm_radix_trimkey(vm_pindex_t index, uint16_t level) 160774d251dSAttilio Rao { 161774d251dSAttilio Rao vm_pindex_t ret; 162774d251dSAttilio Rao 163774d251dSAttilio Rao ret = index; 164774d251dSAttilio Rao if (level < VM_RADIX_LIMIT) { 165774d251dSAttilio Rao ret >>= (VM_RADIX_LIMIT - level) * VM_RADIX_WIDTH; 166774d251dSAttilio Rao ret <<= (VM_RADIX_LIMIT - level) * VM_RADIX_WIDTH; 167774d251dSAttilio Rao } 168774d251dSAttilio Rao return (ret); 169774d251dSAttilio Rao } 170774d251dSAttilio Rao 171774d251dSAttilio Rao /* 172774d251dSAttilio Rao * Get the root node for a radix tree. 173774d251dSAttilio Rao */ 174774d251dSAttilio Rao static __inline struct vm_radix_node * 175774d251dSAttilio Rao vm_radix_getroot(struct vm_radix *rtree) 176774d251dSAttilio Rao { 177774d251dSAttilio Rao 1782c899fedSAlan Cox return ((struct vm_radix_node *)rtree->rt_root); 179774d251dSAttilio Rao } 180774d251dSAttilio Rao 181774d251dSAttilio Rao /* 182774d251dSAttilio Rao * Set the root node for a radix tree. 183774d251dSAttilio Rao */ 184774d251dSAttilio Rao static __inline void 185774d251dSAttilio Rao vm_radix_setroot(struct vm_radix *rtree, struct vm_radix_node *rnode) 186774d251dSAttilio Rao { 187774d251dSAttilio Rao 188774d251dSAttilio Rao rtree->rt_root = (uintptr_t)rnode; 189774d251dSAttilio Rao } 190774d251dSAttilio Rao 191774d251dSAttilio Rao /* 1923fc10b73SAlan Cox * Returns TRUE if the specified radix node is a leaf and FALSE otherwise. 1933fc10b73SAlan Cox */ 1943fc10b73SAlan Cox static __inline boolean_t 1953fc10b73SAlan Cox vm_radix_isleaf(struct vm_radix_node *rnode) 1963fc10b73SAlan Cox { 1973fc10b73SAlan Cox 1983fc10b73SAlan Cox return (((uintptr_t)rnode & VM_RADIX_ISLEAF) != 0); 1993fc10b73SAlan Cox } 2003fc10b73SAlan Cox 2013fc10b73SAlan Cox /* 20296f1a842SAlan Cox * Returns the associated page extracted from rnode. 203774d251dSAttilio Rao */ 204774d251dSAttilio Rao static __inline vm_page_t 20596f1a842SAlan Cox vm_radix_topage(struct vm_radix_node *rnode) 206774d251dSAttilio Rao { 207774d251dSAttilio Rao 20896f1a842SAlan Cox return ((vm_page_t)((uintptr_t)rnode & ~VM_RADIX_FLAGS)); 209774d251dSAttilio Rao } 210774d251dSAttilio Rao 211774d251dSAttilio Rao /* 212774d251dSAttilio Rao * Adds the page as a child of the provided node. 213774d251dSAttilio Rao */ 214774d251dSAttilio Rao static __inline void 215774d251dSAttilio Rao vm_radix_addpage(struct vm_radix_node *rnode, vm_pindex_t index, uint16_t clev, 216774d251dSAttilio Rao vm_page_t page) 217774d251dSAttilio Rao { 218774d251dSAttilio Rao int slot; 219774d251dSAttilio Rao 220774d251dSAttilio Rao slot = vm_radix_slot(index, clev); 221774d251dSAttilio Rao rnode->rn_child[slot] = (void *)((uintptr_t)page | VM_RADIX_ISLEAF); 222774d251dSAttilio Rao } 223774d251dSAttilio Rao 224774d251dSAttilio Rao /* 225774d251dSAttilio Rao * Returns the slot where two keys differ. 226774d251dSAttilio Rao * It cannot accept 2 equal keys. 227774d251dSAttilio Rao */ 228774d251dSAttilio Rao static __inline uint16_t 229774d251dSAttilio Rao vm_radix_keydiff(vm_pindex_t index1, vm_pindex_t index2) 230774d251dSAttilio Rao { 231774d251dSAttilio Rao uint16_t clev; 232774d251dSAttilio Rao 233774d251dSAttilio Rao KASSERT(index1 != index2, ("%s: passing the same key value %jx", 234774d251dSAttilio Rao __func__, (uintmax_t)index1)); 235774d251dSAttilio Rao 236774d251dSAttilio Rao index1 ^= index2; 237774d251dSAttilio Rao for (clev = 0; clev <= VM_RADIX_LIMIT ; clev++) 238774d251dSAttilio Rao if (vm_radix_slot(index1, clev)) 239774d251dSAttilio Rao return (clev); 240774d251dSAttilio Rao panic("%s: cannot reach this point", __func__); 241774d251dSAttilio Rao return (0); 242774d251dSAttilio Rao } 243774d251dSAttilio Rao 244774d251dSAttilio Rao /* 245774d251dSAttilio Rao * Returns TRUE if it can be determined that key does not belong to the 246774d251dSAttilio Rao * specified rnode. Otherwise, returns FALSE. 247774d251dSAttilio Rao */ 248774d251dSAttilio Rao static __inline boolean_t 249774d251dSAttilio Rao vm_radix_keybarr(struct vm_radix_node *rnode, vm_pindex_t idx) 250774d251dSAttilio Rao { 251774d251dSAttilio Rao 252774d251dSAttilio Rao if (rnode->rn_clev > 0) { 253774d251dSAttilio Rao idx = vm_radix_trimkey(idx, rnode->rn_clev - 1); 254c1c82b36SAlan Cox return (idx != rnode->rn_owner); 255774d251dSAttilio Rao } 256774d251dSAttilio Rao return (FALSE); 257774d251dSAttilio Rao } 258774d251dSAttilio Rao 259774d251dSAttilio Rao /* 260774d251dSAttilio Rao * Adjusts the idx key to the first upper level available, based on a valid 261774d251dSAttilio Rao * initial level and map of available levels. 262774d251dSAttilio Rao * Returns a value bigger than 0 to signal that there are not valid levels 263774d251dSAttilio Rao * available. 264774d251dSAttilio Rao */ 265774d251dSAttilio Rao static __inline int 266774d251dSAttilio Rao vm_radix_addlev(vm_pindex_t *idx, boolean_t *levels, uint16_t ilev) 267774d251dSAttilio Rao { 268774d251dSAttilio Rao vm_pindex_t wrapidx; 269774d251dSAttilio Rao 270774d251dSAttilio Rao for (; levels[ilev] == FALSE || 271774d251dSAttilio Rao vm_radix_slot(*idx, ilev) == (VM_RADIX_COUNT - 1); ilev--) 272774d251dSAttilio Rao if (ilev == 0) 273774d251dSAttilio Rao return (1); 274774d251dSAttilio Rao wrapidx = *idx; 275774d251dSAttilio Rao *idx = vm_radix_trimkey(*idx, ilev); 276774d251dSAttilio Rao *idx += VM_RADIX_UNITLEVEL(ilev); 277774d251dSAttilio Rao return (*idx < wrapidx); 278774d251dSAttilio Rao } 279774d251dSAttilio Rao 280774d251dSAttilio Rao /* 281774d251dSAttilio Rao * Adjusts the idx key to the first lower level available, based on a valid 282774d251dSAttilio Rao * initial level and map of available levels. 283774d251dSAttilio Rao * Returns a value bigger than 0 to signal that there are not valid levels 284774d251dSAttilio Rao * available. 285774d251dSAttilio Rao */ 286774d251dSAttilio Rao static __inline int 287774d251dSAttilio Rao vm_radix_declev(vm_pindex_t *idx, boolean_t *levels, uint16_t ilev) 288774d251dSAttilio Rao { 289774d251dSAttilio Rao vm_pindex_t wrapidx; 290774d251dSAttilio Rao 291774d251dSAttilio Rao for (; levels[ilev] == FALSE || 292774d251dSAttilio Rao vm_radix_slot(*idx, ilev) == 0; ilev--) 293774d251dSAttilio Rao if (ilev == 0) 294774d251dSAttilio Rao return (1); 295774d251dSAttilio Rao wrapidx = *idx; 296774d251dSAttilio Rao *idx = vm_radix_trimkey(*idx, ilev); 297774d251dSAttilio Rao *idx |= VM_RADIX_UNITLEVEL(ilev) - 1; 298774d251dSAttilio Rao *idx -= VM_RADIX_UNITLEVEL(ilev); 299774d251dSAttilio Rao return (*idx > wrapidx); 300774d251dSAttilio Rao } 301774d251dSAttilio Rao 302774d251dSAttilio Rao /* 303774d251dSAttilio Rao * Internal helper for vm_radix_reclaim_allnodes(). 304774d251dSAttilio Rao * This function is recursive. 305774d251dSAttilio Rao */ 306774d251dSAttilio Rao static void 307774d251dSAttilio Rao vm_radix_reclaim_allnodes_int(struct vm_radix_node *rnode) 308774d251dSAttilio Rao { 309774d251dSAttilio Rao int slot; 310774d251dSAttilio Rao 311652615dcSAlan Cox KASSERT(rnode->rn_count <= VM_RADIX_COUNT, 312652615dcSAlan Cox ("vm_radix_reclaim_allnodes_int: bad count in rnode %p", rnode)); 313652615dcSAlan Cox for (slot = 0; rnode->rn_count != 0; slot++) { 314774d251dSAttilio Rao if (rnode->rn_child[slot] == NULL) 315774d251dSAttilio Rao continue; 3163fc10b73SAlan Cox if (!vm_radix_isleaf(rnode->rn_child[slot])) 317774d251dSAttilio Rao vm_radix_reclaim_allnodes_int(rnode->rn_child[slot]); 318774d251dSAttilio Rao rnode->rn_child[slot] = NULL; 319774d251dSAttilio Rao rnode->rn_count--; 320774d251dSAttilio Rao } 321774d251dSAttilio Rao vm_radix_node_put(rnode); 322774d251dSAttilio Rao } 323774d251dSAttilio Rao 324774d251dSAttilio Rao #ifdef INVARIANTS 325774d251dSAttilio Rao /* 326774d251dSAttilio Rao * Radix node zone destructor. 327774d251dSAttilio Rao */ 328774d251dSAttilio Rao static void 329774d251dSAttilio Rao vm_radix_node_zone_dtor(void *mem, int size __unused, void *arg __unused) 330774d251dSAttilio Rao { 331774d251dSAttilio Rao struct vm_radix_node *rnode; 332774d251dSAttilio Rao int slot; 333774d251dSAttilio Rao 334774d251dSAttilio Rao rnode = mem; 335774d251dSAttilio Rao KASSERT(rnode->rn_count == 0, 336774d251dSAttilio Rao ("vm_radix_node_put: rnode %p has %d children", rnode, 337774d251dSAttilio Rao rnode->rn_count)); 338774d251dSAttilio Rao for (slot = 0; slot < VM_RADIX_COUNT; slot++) 339774d251dSAttilio Rao KASSERT(rnode->rn_child[slot] == NULL, 340774d251dSAttilio Rao ("vm_radix_node_put: rnode %p has a child", rnode)); 341774d251dSAttilio Rao } 342774d251dSAttilio Rao #endif 343774d251dSAttilio Rao 344774d251dSAttilio Rao /* 345774d251dSAttilio Rao * Radix node zone initializer. 346774d251dSAttilio Rao */ 347774d251dSAttilio Rao static int 348774d251dSAttilio Rao vm_radix_node_zone_init(void *mem, int size __unused, int flags __unused) 349774d251dSAttilio Rao { 350774d251dSAttilio Rao struct vm_radix_node *rnode; 351774d251dSAttilio Rao 352774d251dSAttilio Rao rnode = mem; 353774d251dSAttilio Rao memset(rnode->rn_child, 0, sizeof(rnode->rn_child)); 354774d251dSAttilio Rao return (0); 355774d251dSAttilio Rao } 356774d251dSAttilio Rao 357774d251dSAttilio Rao /* 358774d251dSAttilio Rao * Pre-allocate intermediate nodes from the UMA slab zone. 359774d251dSAttilio Rao */ 360774d251dSAttilio Rao static void 361774d251dSAttilio Rao vm_radix_prealloc(void *arg __unused) 362774d251dSAttilio Rao { 363*880659feSAlan Cox int nodes; 364774d251dSAttilio Rao 365*880659feSAlan Cox /* 366*880659feSAlan Cox * Calculate the number of reserved nodes, discounting the pages that 367*880659feSAlan Cox * are needed to store them. 368*880659feSAlan Cox */ 369*880659feSAlan Cox nodes = ((vm_paddr_t)cnt.v_page_count * PAGE_SIZE) / (PAGE_SIZE + 370*880659feSAlan Cox sizeof(struct vm_radix_node)); 371*880659feSAlan Cox if (!uma_zone_reserve_kva(vm_radix_node_zone, nodes)) 372774d251dSAttilio Rao panic("%s: unable to create new zone", __func__); 373*880659feSAlan Cox uma_prealloc(vm_radix_node_zone, nodes); 374774d251dSAttilio Rao } 375774d251dSAttilio Rao SYSINIT(vm_radix_prealloc, SI_SUB_KMEM, SI_ORDER_SECOND, vm_radix_prealloc, 376774d251dSAttilio Rao NULL); 377774d251dSAttilio Rao 378774d251dSAttilio Rao /* 379774d251dSAttilio Rao * Initialize the UMA slab zone. 380774d251dSAttilio Rao * Until vm_radix_prealloc() is called, the zone will be served by the 381774d251dSAttilio Rao * UMA boot-time pre-allocated pool of pages. 382774d251dSAttilio Rao */ 383774d251dSAttilio Rao void 384774d251dSAttilio Rao vm_radix_init(void) 385774d251dSAttilio Rao { 386774d251dSAttilio Rao 387774d251dSAttilio Rao vm_radix_node_zone = uma_zcreate("RADIX NODE", 388774d251dSAttilio Rao sizeof(struct vm_radix_node), NULL, 389774d251dSAttilio Rao #ifdef INVARIANTS 390774d251dSAttilio Rao vm_radix_node_zone_dtor, 391774d251dSAttilio Rao #else 392774d251dSAttilio Rao NULL, 393774d251dSAttilio Rao #endif 394774d251dSAttilio Rao vm_radix_node_zone_init, NULL, VM_RADIX_PAD, UMA_ZONE_VM | 395774d251dSAttilio Rao UMA_ZONE_NOFREE); 396774d251dSAttilio Rao } 397774d251dSAttilio Rao 398774d251dSAttilio Rao /* 399774d251dSAttilio Rao * Inserts the key-value pair into the trie. 400774d251dSAttilio Rao * Panics if the key already exists. 401774d251dSAttilio Rao */ 402774d251dSAttilio Rao void 403774d251dSAttilio Rao vm_radix_insert(struct vm_radix *rtree, vm_page_t page) 404774d251dSAttilio Rao { 405774d251dSAttilio Rao vm_pindex_t index, newind; 406a08f2cf6SAlan Cox void **parentp; 407a08f2cf6SAlan Cox struct vm_radix_node *rnode, *tmp; 408774d251dSAttilio Rao vm_page_t m; 409774d251dSAttilio Rao int slot; 410774d251dSAttilio Rao uint16_t clev; 411774d251dSAttilio Rao 412774d251dSAttilio Rao index = page->pindex; 413774d251dSAttilio Rao 414774d251dSAttilio Rao /* 415774d251dSAttilio Rao * The owner of record for root is not really important because it 416774d251dSAttilio Rao * will never be used. 417774d251dSAttilio Rao */ 418774d251dSAttilio Rao rnode = vm_radix_getroot(rtree); 419774d251dSAttilio Rao if (rnode == NULL) { 420a08f2cf6SAlan Cox rtree->rt_root = (uintptr_t)page | VM_RADIX_ISLEAF; 421774d251dSAttilio Rao return; 422774d251dSAttilio Rao } 423a08f2cf6SAlan Cox parentp = (void **)&rtree->rt_root; 424a08f2cf6SAlan Cox for (;;) { 425a08f2cf6SAlan Cox if (vm_radix_isleaf(rnode)) { 426a08f2cf6SAlan Cox m = vm_radix_topage(rnode); 427774d251dSAttilio Rao if (m->pindex == index) 428774d251dSAttilio Rao panic("%s: key %jx is already present", 429774d251dSAttilio Rao __func__, (uintmax_t)index); 430774d251dSAttilio Rao clev = vm_radix_keydiff(m->pindex, index); 431774d251dSAttilio Rao tmp = vm_radix_node_get(vm_radix_trimkey(index, 432774d251dSAttilio Rao clev - 1), 2, clev); 433a08f2cf6SAlan Cox *parentp = tmp; 434774d251dSAttilio Rao vm_radix_addpage(tmp, index, clev, page); 435774d251dSAttilio Rao vm_radix_addpage(tmp, m->pindex, clev, m); 436774d251dSAttilio Rao return; 437a08f2cf6SAlan Cox } else if (vm_radix_keybarr(rnode, index)) 438a08f2cf6SAlan Cox break; 439a08f2cf6SAlan Cox slot = vm_radix_slot(index, rnode->rn_clev); 440774d251dSAttilio Rao if (rnode->rn_child[slot] == NULL) { 441774d251dSAttilio Rao rnode->rn_count++; 442774d251dSAttilio Rao vm_radix_addpage(rnode, index, rnode->rn_clev, page); 443774d251dSAttilio Rao return; 444774d251dSAttilio Rao } 445a08f2cf6SAlan Cox parentp = &rnode->rn_child[slot]; 446774d251dSAttilio Rao rnode = rnode->rn_child[slot]; 447a08f2cf6SAlan Cox } 448774d251dSAttilio Rao 449774d251dSAttilio Rao /* 450774d251dSAttilio Rao * A new node is needed because the right insertion level is reached. 451774d251dSAttilio Rao * Setup the new intermediate node and add the 2 children: the 452774d251dSAttilio Rao * new object and the older edge. 453774d251dSAttilio Rao */ 45472abda64SAlan Cox newind = rnode->rn_owner; 45572abda64SAlan Cox clev = vm_radix_keydiff(newind, index); 45672abda64SAlan Cox tmp = vm_radix_node_get(vm_radix_trimkey(index, clev - 1), 2, 457774d251dSAttilio Rao clev); 458a08f2cf6SAlan Cox *parentp = tmp; 45972abda64SAlan Cox vm_radix_addpage(tmp, index, clev, page); 460774d251dSAttilio Rao slot = vm_radix_slot(newind, clev); 46172abda64SAlan Cox tmp->rn_child[slot] = rnode; 462774d251dSAttilio Rao } 463774d251dSAttilio Rao 464774d251dSAttilio Rao /* 465774d251dSAttilio Rao * Returns the value stored at the index. If the index is not present, 466774d251dSAttilio Rao * NULL is returned. 467774d251dSAttilio Rao */ 468774d251dSAttilio Rao vm_page_t 469774d251dSAttilio Rao vm_radix_lookup(struct vm_radix *rtree, vm_pindex_t index) 470774d251dSAttilio Rao { 471774d251dSAttilio Rao struct vm_radix_node *rnode; 472774d251dSAttilio Rao vm_page_t m; 473774d251dSAttilio Rao int slot; 474774d251dSAttilio Rao 475774d251dSAttilio Rao rnode = vm_radix_getroot(rtree); 476774d251dSAttilio Rao while (rnode != NULL) { 47796f1a842SAlan Cox if (vm_radix_isleaf(rnode)) { 47896f1a842SAlan Cox m = vm_radix_topage(rnode); 479774d251dSAttilio Rao if (m->pindex == index) 480774d251dSAttilio Rao return (m); 481774d251dSAttilio Rao else 4826f9c0b15SAlan Cox break; 4836f9c0b15SAlan Cox } else if (vm_radix_keybarr(rnode, index)) 4846f9c0b15SAlan Cox break; 4856f9c0b15SAlan Cox slot = vm_radix_slot(index, rnode->rn_clev); 4866f9c0b15SAlan Cox rnode = rnode->rn_child[slot]; 487774d251dSAttilio Rao } 488774d251dSAttilio Rao return (NULL); 489774d251dSAttilio Rao } 490774d251dSAttilio Rao 491774d251dSAttilio Rao /* 492774d251dSAttilio Rao * Look up the nearest entry at a position bigger than or equal to index. 493774d251dSAttilio Rao */ 494774d251dSAttilio Rao vm_page_t 495774d251dSAttilio Rao vm_radix_lookup_ge(struct vm_radix *rtree, vm_pindex_t index) 496774d251dSAttilio Rao { 497774d251dSAttilio Rao vm_pindex_t inc; 498774d251dSAttilio Rao vm_page_t m; 49996f1a842SAlan Cox struct vm_radix_node *child, *rnode; 500774d251dSAttilio Rao int slot; 501774d251dSAttilio Rao uint16_t difflev; 502774d251dSAttilio Rao boolean_t maplevels[VM_RADIX_LIMIT + 1]; 503774d251dSAttilio Rao #ifdef INVARIANTS 504774d251dSAttilio Rao int loops = 0; 505774d251dSAttilio Rao #endif 506774d251dSAttilio Rao 5076f9c0b15SAlan Cox rnode = vm_radix_getroot(rtree); 5086f9c0b15SAlan Cox if (rnode == NULL) 5096f9c0b15SAlan Cox return (NULL); 5106f9c0b15SAlan Cox else if (vm_radix_isleaf(rnode)) { 5116f9c0b15SAlan Cox m = vm_radix_topage(rnode); 5126f9c0b15SAlan Cox if (m->pindex >= index) 5136f9c0b15SAlan Cox return (m); 5146f9c0b15SAlan Cox else 5156f9c0b15SAlan Cox return (NULL); 5166f9c0b15SAlan Cox } 517774d251dSAttilio Rao restart: 518774d251dSAttilio Rao KASSERT(++loops < 1000, ("%s: too many loops", __func__)); 519774d251dSAttilio Rao for (difflev = 0; difflev < (VM_RADIX_LIMIT + 1); difflev++) 520774d251dSAttilio Rao maplevels[difflev] = FALSE; 5216f9c0b15SAlan Cox for (;;) { 522774d251dSAttilio Rao maplevels[rnode->rn_clev] = TRUE; 523774d251dSAttilio Rao 524774d251dSAttilio Rao /* 525774d251dSAttilio Rao * If the keys differ before the current bisection node 526774d251dSAttilio Rao * the search key might rollback to the earliest 527774d251dSAttilio Rao * available bisection node, or to the smaller value 528774d251dSAttilio Rao * in the current domain (if the owner is bigger than the 529774d251dSAttilio Rao * search key). 530774d251dSAttilio Rao * The maplevels array records any node has been seen 531774d251dSAttilio Rao * at a given level. This aids the search for a valid 532774d251dSAttilio Rao * bisection node. 533774d251dSAttilio Rao */ 534774d251dSAttilio Rao if (vm_radix_keybarr(rnode, index)) { 535774d251dSAttilio Rao difflev = vm_radix_keydiff(index, rnode->rn_owner); 536774d251dSAttilio Rao if (index > rnode->rn_owner) { 537774d251dSAttilio Rao if (vm_radix_addlev(&index, maplevels, 538774d251dSAttilio Rao difflev) > 0) 539774d251dSAttilio Rao break; 540774d251dSAttilio Rao } else 541774d251dSAttilio Rao index = vm_radix_trimkey(rnode->rn_owner, 542774d251dSAttilio Rao difflev); 5436f9c0b15SAlan Cox rnode = vm_radix_getroot(rtree); 544774d251dSAttilio Rao goto restart; 545774d251dSAttilio Rao } 546774d251dSAttilio Rao slot = vm_radix_slot(index, rnode->rn_clev); 54796f1a842SAlan Cox child = rnode->rn_child[slot]; 54896f1a842SAlan Cox if (vm_radix_isleaf(child)) { 54996f1a842SAlan Cox m = vm_radix_topage(child); 55096f1a842SAlan Cox if (m->pindex >= index) 551774d251dSAttilio Rao return (m); 55296f1a842SAlan Cox } else if (child != NULL) 55396f1a842SAlan Cox goto descend; 554774d251dSAttilio Rao 555774d251dSAttilio Rao /* 556774d251dSAttilio Rao * Look for an available edge or page within the current 557774d251dSAttilio Rao * bisection node. 558774d251dSAttilio Rao */ 559774d251dSAttilio Rao if (slot < (VM_RADIX_COUNT - 1)) { 560774d251dSAttilio Rao inc = VM_RADIX_UNITLEVEL(rnode->rn_clev); 561774d251dSAttilio Rao index = vm_radix_trimkey(index, rnode->rn_clev); 56296f1a842SAlan Cox do { 563774d251dSAttilio Rao index += inc; 564774d251dSAttilio Rao slot++; 56596f1a842SAlan Cox child = rnode->rn_child[slot]; 56696f1a842SAlan Cox if (vm_radix_isleaf(child)) { 56796f1a842SAlan Cox m = vm_radix_topage(child); 56896f1a842SAlan Cox if (m->pindex >= index) 569774d251dSAttilio Rao return (m); 57096f1a842SAlan Cox } else if (child != NULL) 57196f1a842SAlan Cox goto descend; 57296f1a842SAlan Cox } while (slot < (VM_RADIX_COUNT - 1)); 573774d251dSAttilio Rao } 57496f1a842SAlan Cox KASSERT(child == NULL || vm_radix_isleaf(child), 57596f1a842SAlan Cox ("vm_radix_lookup_ge: child is radix node")); 576774d251dSAttilio Rao 577774d251dSAttilio Rao /* 578774d251dSAttilio Rao * If a valid page or edge bigger than the search slot is 579774d251dSAttilio Rao * found in the traversal, skip to the next higher-level key. 580774d251dSAttilio Rao */ 58196f1a842SAlan Cox if (rnode->rn_clev == 0 || vm_radix_addlev(&index, maplevels, 58296f1a842SAlan Cox rnode->rn_clev - 1) > 0) 583774d251dSAttilio Rao break; 5846f9c0b15SAlan Cox rnode = vm_radix_getroot(rtree); 585774d251dSAttilio Rao goto restart; 58696f1a842SAlan Cox descend: 58796f1a842SAlan Cox rnode = child; 588774d251dSAttilio Rao } 589774d251dSAttilio Rao return (NULL); 590774d251dSAttilio Rao } 591774d251dSAttilio Rao 592774d251dSAttilio Rao /* 593774d251dSAttilio Rao * Look up the nearest entry at a position less than or equal to index. 594774d251dSAttilio Rao */ 595774d251dSAttilio Rao vm_page_t 596774d251dSAttilio Rao vm_radix_lookup_le(struct vm_radix *rtree, vm_pindex_t index) 597774d251dSAttilio Rao { 598774d251dSAttilio Rao vm_pindex_t inc; 599774d251dSAttilio Rao vm_page_t m; 60096f1a842SAlan Cox struct vm_radix_node *child, *rnode; 601774d251dSAttilio Rao int slot; 602774d251dSAttilio Rao uint16_t difflev; 603774d251dSAttilio Rao boolean_t maplevels[VM_RADIX_LIMIT + 1]; 604774d251dSAttilio Rao #ifdef INVARIANTS 605774d251dSAttilio Rao int loops = 0; 606774d251dSAttilio Rao #endif 607774d251dSAttilio Rao 6086f9c0b15SAlan Cox rnode = vm_radix_getroot(rtree); 6096f9c0b15SAlan Cox if (rnode == NULL) 6106f9c0b15SAlan Cox return (NULL); 6116f9c0b15SAlan Cox else if (vm_radix_isleaf(rnode)) { 6126f9c0b15SAlan Cox m = vm_radix_topage(rnode); 6136f9c0b15SAlan Cox if (m->pindex <= index) 6146f9c0b15SAlan Cox return (m); 6156f9c0b15SAlan Cox else 6166f9c0b15SAlan Cox return (NULL); 6176f9c0b15SAlan Cox } 618774d251dSAttilio Rao restart: 619774d251dSAttilio Rao KASSERT(++loops < 1000, ("%s: too many loops", __func__)); 620774d251dSAttilio Rao for (difflev = 0; difflev < (VM_RADIX_LIMIT + 1); difflev++) 621774d251dSAttilio Rao maplevels[difflev] = FALSE; 6226f9c0b15SAlan Cox for (;;) { 623774d251dSAttilio Rao maplevels[rnode->rn_clev] = TRUE; 624774d251dSAttilio Rao 625774d251dSAttilio Rao /* 626774d251dSAttilio Rao * If the keys differ before the current bisection node 627774d251dSAttilio Rao * the search key might rollback to the earliest 628774d251dSAttilio Rao * available bisection node, or to the higher value 629774d251dSAttilio Rao * in the current domain (if the owner is smaller than the 630774d251dSAttilio Rao * search key). 631774d251dSAttilio Rao * The maplevels array records any node has been seen 632774d251dSAttilio Rao * at a given level. This aids the search for a valid 633774d251dSAttilio Rao * bisection node. 634774d251dSAttilio Rao */ 635774d251dSAttilio Rao if (vm_radix_keybarr(rnode, index)) { 636774d251dSAttilio Rao difflev = vm_radix_keydiff(index, rnode->rn_owner); 637774d251dSAttilio Rao if (index > rnode->rn_owner) { 638774d251dSAttilio Rao index = vm_radix_trimkey(rnode->rn_owner, 639774d251dSAttilio Rao difflev); 640774d251dSAttilio Rao index |= VM_RADIX_UNITLEVEL(difflev) - 1; 641774d251dSAttilio Rao } else if (vm_radix_declev(&index, maplevels, 642774d251dSAttilio Rao difflev) > 0) 643774d251dSAttilio Rao break; 6446f9c0b15SAlan Cox rnode = vm_radix_getroot(rtree); 645774d251dSAttilio Rao goto restart; 646774d251dSAttilio Rao } 647774d251dSAttilio Rao slot = vm_radix_slot(index, rnode->rn_clev); 64896f1a842SAlan Cox child = rnode->rn_child[slot]; 64996f1a842SAlan Cox if (vm_radix_isleaf(child)) { 65096f1a842SAlan Cox m = vm_radix_topage(child); 65196f1a842SAlan Cox if (m->pindex <= index) 652774d251dSAttilio Rao return (m); 65396f1a842SAlan Cox } else if (child != NULL) 65496f1a842SAlan Cox goto descend; 655774d251dSAttilio Rao 656774d251dSAttilio Rao /* 657774d251dSAttilio Rao * Look for an available edge or page within the current 658774d251dSAttilio Rao * bisection node. 659774d251dSAttilio Rao */ 660774d251dSAttilio Rao if (slot > 0) { 661774d251dSAttilio Rao inc = VM_RADIX_UNITLEVEL(rnode->rn_clev); 662774d251dSAttilio Rao index = vm_radix_trimkey(index, rnode->rn_clev); 663774d251dSAttilio Rao index |= inc - 1; 66496f1a842SAlan Cox do { 665774d251dSAttilio Rao index -= inc; 666774d251dSAttilio Rao slot--; 66796f1a842SAlan Cox child = rnode->rn_child[slot]; 66896f1a842SAlan Cox if (vm_radix_isleaf(child)) { 66996f1a842SAlan Cox m = vm_radix_topage(child); 67096f1a842SAlan Cox if (m->pindex <= index) 671774d251dSAttilio Rao return (m); 67296f1a842SAlan Cox } else if (child != NULL) 67396f1a842SAlan Cox goto descend; 67496f1a842SAlan Cox } while (slot > 0); 675774d251dSAttilio Rao } 67696f1a842SAlan Cox KASSERT(child == NULL || vm_radix_isleaf(child), 67796f1a842SAlan Cox ("vm_radix_lookup_le: child is radix node")); 678774d251dSAttilio Rao 679774d251dSAttilio Rao /* 680774d251dSAttilio Rao * If a valid page or edge smaller than the search slot is 681774d251dSAttilio Rao * found in the traversal, skip to the next higher-level key. 682774d251dSAttilio Rao */ 68396f1a842SAlan Cox if (rnode->rn_clev == 0 || vm_radix_declev(&index, maplevels, 68496f1a842SAlan Cox rnode->rn_clev - 1) > 0) 685774d251dSAttilio Rao break; 6866f9c0b15SAlan Cox rnode = vm_radix_getroot(rtree); 687774d251dSAttilio Rao goto restart; 68896f1a842SAlan Cox descend: 68996f1a842SAlan Cox rnode = child; 690774d251dSAttilio Rao } 691774d251dSAttilio Rao return (NULL); 692774d251dSAttilio Rao } 693774d251dSAttilio Rao 694774d251dSAttilio Rao /* 695774d251dSAttilio Rao * Remove the specified index from the tree. 696774d251dSAttilio Rao * Panics if the key is not present. 697774d251dSAttilio Rao */ 698774d251dSAttilio Rao void 699774d251dSAttilio Rao vm_radix_remove(struct vm_radix *rtree, vm_pindex_t index) 700774d251dSAttilio Rao { 701774d251dSAttilio Rao struct vm_radix_node *rnode, *parent; 702774d251dSAttilio Rao vm_page_t m; 703774d251dSAttilio Rao int i, slot; 704774d251dSAttilio Rao 705774d251dSAttilio Rao rnode = vm_radix_getroot(rtree); 706a08f2cf6SAlan Cox if (vm_radix_isleaf(rnode)) { 707a08f2cf6SAlan Cox m = vm_radix_topage(rnode); 708a08f2cf6SAlan Cox if (m->pindex != index) 709a08f2cf6SAlan Cox panic("%s: invalid key found", __func__); 710a08f2cf6SAlan Cox vm_radix_setroot(rtree, NULL); 711a08f2cf6SAlan Cox return; 712a08f2cf6SAlan Cox } 713a08f2cf6SAlan Cox parent = NULL; 714774d251dSAttilio Rao for (;;) { 715774d251dSAttilio Rao if (rnode == NULL) 716774d251dSAttilio Rao panic("vm_radix_remove: impossible to locate the key"); 717774d251dSAttilio Rao slot = vm_radix_slot(index, rnode->rn_clev); 71896f1a842SAlan Cox if (vm_radix_isleaf(rnode->rn_child[slot])) { 71996f1a842SAlan Cox m = vm_radix_topage(rnode->rn_child[slot]); 72096f1a842SAlan Cox if (m->pindex != index) 72196f1a842SAlan Cox panic("%s: invalid key found", __func__); 722774d251dSAttilio Rao rnode->rn_child[slot] = NULL; 723774d251dSAttilio Rao rnode->rn_count--; 724774d251dSAttilio Rao if (rnode->rn_count > 1) 725774d251dSAttilio Rao break; 726774d251dSAttilio Rao for (i = 0; i < VM_RADIX_COUNT; i++) 727774d251dSAttilio Rao if (rnode->rn_child[i] != NULL) 728774d251dSAttilio Rao break; 729774d251dSAttilio Rao KASSERT(i != VM_RADIX_COUNT, 730774d251dSAttilio Rao ("%s: invalid node configuration", __func__)); 731a08f2cf6SAlan Cox if (parent == NULL) 732a08f2cf6SAlan Cox vm_radix_setroot(rtree, rnode->rn_child[i]); 733a08f2cf6SAlan Cox else { 734774d251dSAttilio Rao slot = vm_radix_slot(index, parent->rn_clev); 735774d251dSAttilio Rao KASSERT(parent->rn_child[slot] == rnode, 736774d251dSAttilio Rao ("%s: invalid child value", __func__)); 737774d251dSAttilio Rao parent->rn_child[slot] = rnode->rn_child[i]; 738a08f2cf6SAlan Cox } 739774d251dSAttilio Rao rnode->rn_count--; 740774d251dSAttilio Rao rnode->rn_child[i] = NULL; 741774d251dSAttilio Rao vm_radix_node_put(rnode); 742774d251dSAttilio Rao break; 743774d251dSAttilio Rao } 744774d251dSAttilio Rao parent = rnode; 745774d251dSAttilio Rao rnode = rnode->rn_child[slot]; 746774d251dSAttilio Rao } 747774d251dSAttilio Rao } 748774d251dSAttilio Rao 749774d251dSAttilio Rao /* 750774d251dSAttilio Rao * Remove and free all the nodes from the radix tree. 751774d251dSAttilio Rao * This function is recursive but there is a tight control on it as the 752774d251dSAttilio Rao * maximum depth of the tree is fixed. 753774d251dSAttilio Rao */ 754774d251dSAttilio Rao void 755774d251dSAttilio Rao vm_radix_reclaim_allnodes(struct vm_radix *rtree) 756774d251dSAttilio Rao { 757774d251dSAttilio Rao struct vm_radix_node *root; 758774d251dSAttilio Rao 759774d251dSAttilio Rao root = vm_radix_getroot(rtree); 760774d251dSAttilio Rao if (root == NULL) 761774d251dSAttilio Rao return; 762774d251dSAttilio Rao vm_radix_setroot(rtree, NULL); 763a08f2cf6SAlan Cox if (!vm_radix_isleaf(root)) 764652615dcSAlan Cox vm_radix_reclaim_allnodes_int(root); 765774d251dSAttilio Rao } 766774d251dSAttilio Rao 767774d251dSAttilio Rao #ifdef DDB 768774d251dSAttilio Rao /* 769774d251dSAttilio Rao * Show details about the given radix node. 770774d251dSAttilio Rao */ 771774d251dSAttilio Rao DB_SHOW_COMMAND(radixnode, db_show_radixnode) 772774d251dSAttilio Rao { 773774d251dSAttilio Rao struct vm_radix_node *rnode; 774774d251dSAttilio Rao int i; 775774d251dSAttilio Rao 776774d251dSAttilio Rao if (!have_addr) 777774d251dSAttilio Rao return; 778774d251dSAttilio Rao rnode = (struct vm_radix_node *)addr; 779774d251dSAttilio Rao db_printf("radixnode %p, owner %jx, children count %u, level %u:\n", 780774d251dSAttilio Rao (void *)rnode, (uintmax_t)rnode->rn_owner, rnode->rn_count, 781774d251dSAttilio Rao rnode->rn_clev); 782774d251dSAttilio Rao for (i = 0; i < VM_RADIX_COUNT; i++) 783774d251dSAttilio Rao if (rnode->rn_child[i] != NULL) 784774d251dSAttilio Rao db_printf("slot: %d, val: %p, page: %p, clev: %d\n", 785774d251dSAttilio Rao i, (void *)rnode->rn_child[i], 78696f1a842SAlan Cox vm_radix_isleaf(rnode->rn_child[i]) ? 78796f1a842SAlan Cox vm_radix_topage(rnode->rn_child[i]) : NULL, 788774d251dSAttilio Rao rnode->rn_clev); 789774d251dSAttilio Rao } 790774d251dSAttilio Rao #endif /* DDB */ 791