1 /* $NetBSD: ufs_dirhash.c,v 1.43 2026/01/22 03:24:19 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 2001, 2002 Ian Dowse. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: src/sys/ufs/ufs/ufs_dirhash.c,v 1.3.2.8 2004/12/08 11:54:13 dwmalone Exp $ 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: ufs_dirhash.c,v 1.43 2026/01/22 03:24:19 riastradh Exp $"); 32 33 /* 34 * This implements a hash-based lookup scheme for UFS directories. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/types.h> 39 40 #include <sys/atomic.h> 41 #include <sys/buf.h> 42 #include <sys/hash.h> 43 #include <sys/kernel.h> 44 #include <sys/kmem.h> 45 #include <sys/mount.h> 46 #include <sys/pool.h> 47 #include <sys/proc.h> 48 #include <sys/sdt.h> 49 #include <sys/sysctl.h> 50 #include <sys/systm.h> 51 #include <sys/vnode.h> 52 53 #include <ufs/ufs/dir.h> 54 #include <ufs/ufs/dirhash.h> 55 #include <ufs/ufs/inode.h> 56 #include <ufs/ufs/ufs_bswap.h> 57 #include <ufs/ufs/ufs_extern.h> 58 #include <ufs/ufs/ufsmount.h> 59 60 61 /* 62 * Defaults for dirhash cache sizes: 63 * - use up to 1/64th of system memory. 64 * - disable dirhash (set the cache size to 0 bytes) if the 65 * calculated value of hash is less than 2MB. 66 * - cap maximum size of the dirhash cache at 32MB. 67 */ 68 #define DIRHASH_DEFAULT_DIVIDER 64 69 #define MIN_DEFAULT_DIRHASH_MEM (2 * 1024 * 1024) 70 #define MAX_DEFAULT_DIRHASH_MEM (32 * 1024 * 1024) 71 72 73 #define WRAPINCR(val, limit) (((val) + 1 == (limit)) ? 0 : ((val) + 1)) 74 #define WRAPDECR(val, limit) (((val) == 0) ? ((limit) - 1) : ((val) - 1)) 75 #define OFSFMT(ip) ((ip)->i_ump->um_maxsymlinklen <= 0) 76 #define BLKFREE2IDX(n) ((n) > DH_NFSTATS ? DH_NFSTATS : (n)) 77 78 static u_int ufs_dirhashminblks = 5; 79 static u_int ufs_dirhashmaxmem = 0; 80 static u_int ufs_dirhashmem; 81 static u_int ufs_dirhashcheck = 0; 82 83 static int ufsdirhash_hash(struct dirhash *dh, const char *name, int namelen); 84 static void ufsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff, 85 int dirblksiz); 86 static void ufsdirhash_delslot(struct dirhash *dh, int slot); 87 static int ufsdirhash_findslot(struct dirhash *dh, const char *name, 88 int namelen, doff_t offset); 89 static doff_t ufsdirhash_getprev(struct direct *dp, doff_t offset, 90 int dirblksiz); 91 static int ufsdirhash_recycle(int wanted); 92 93 static pool_cache_t ufsdirhashblk_cache; 94 static pool_cache_t ufsdirhash_cache; 95 96 #define DIRHASHLIST_LOCK() mutex_enter(&ufsdirhash_lock) 97 #define DIRHASHLIST_UNLOCK() mutex_exit(&ufsdirhash_lock) 98 #define DIRHASH_LOCK(dh) mutex_enter(&(dh)->dh_lock) 99 #define DIRHASH_UNLOCK(dh) mutex_exit(&(dh)->dh_lock) 100 #define DIRHASH_BLKALLOC() \ 101 pool_cache_get(ufsdirhashblk_cache, PR_NOWAIT) 102 #define DIRHASH_BLKFREE(ptr) \ 103 pool_cache_put(ufsdirhashblk_cache, ptr) 104 105 /* Dirhash list; recently-used entries are near the tail. */ 106 static TAILQ_HEAD(, dirhash) ufsdirhash_list; 107 108 /* Protects: ufsdirhash_list, `dh_list' field, ufs_dirhashmem. */ 109 static kmutex_t ufsdirhash_lock; 110 111 /* 112 * Locking order: 113 * ufsdirhash_lock 114 * dh_lock 115 * 116 * The dh_lock mutex should be acquired either via the inode lock, or via 117 * ufsdirhash_lock. Only the owner of the inode may free the associated 118 * dirhash, but anything can steal its memory and set dh_hash to NULL. 119 */ 120 121 /* 122 * Attempt to build up a hash table for the directory contents in 123 * inode 'ip'. Returns 0 on success, or -1 of the operation failed. 124 */ 125 int 126 ufsdirhash_build(struct inode *ip) 127 { 128 struct dirhash *dh; 129 struct buf *bp = NULL; 130 struct direct *ep; 131 struct vnode *vp; 132 doff_t bmask, pos; 133 int dirblocks, i, j, memreqd, nblocks, narrays, nslots, slot; 134 const int needswap = UFS_MPNEEDSWAP(ip->i_ump); 135 int dirblksiz = ip->i_ump->um_dirblksiz; 136 137 /* Check if we can/should use dirhash. */ 138 if (ip->i_dirhash == NULL) { 139 if (ufs_dirhashmaxmem == 0 || 140 ip->i_size < (ufs_dirhashminblks * dirblksiz) || 141 OFSFMT(ip)) 142 return -1; 143 } else { 144 /* Hash exists, but sysctls could have changed. */ 145 if (ip->i_size < (ufs_dirhashminblks * dirblksiz) || 146 ufs_dirhashmem > ufs_dirhashmaxmem) { 147 ufsdirhash_free(ip); 148 return -1; 149 } 150 /* Check if hash exists and is intact (note: unlocked read). */ 151 if (ip->i_dirhash->dh_hash != NULL) 152 return 0; 153 /* Free the old, recycled hash and build a new one. */ 154 ufsdirhash_free(ip); 155 } 156 157 /* Don't hash removed directories. */ 158 if (ip->i_nlink == 0) 159 return -1; 160 161 vp = ip->i_vnode; 162 /* Allocate 50% more entries than this dir size could ever need. */ 163 KASSERT(ip->i_size >= dirblksiz); 164 nslots = ip->i_size / UFS_DIRECTSIZ(1); 165 nslots = (nslots * 3 + 1) / 2; 166 narrays = howmany(nslots, DH_NBLKOFF); 167 nslots = narrays * DH_NBLKOFF; 168 dirblocks = howmany(ip->i_size, dirblksiz); 169 nblocks = (dirblocks * 3 + 1) / 2; 170 171 memreqd = sizeof(*dh) + narrays * sizeof(*dh->dh_hash) + 172 narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) + 173 nblocks * sizeof(*dh->dh_blkfree); 174 175 while (atomic_add_int_nv(&ufs_dirhashmem, memreqd) > 176 ufs_dirhashmaxmem) { 177 atomic_add_int(&ufs_dirhashmem, -memreqd); 178 if (memreqd > ufs_dirhashmaxmem / 2) 179 return -1; 180 /* Try to free some space. */ 181 if (ufsdirhash_recycle(memreqd) != 0) 182 return -1; 183 else 184 DIRHASHLIST_UNLOCK(); 185 } 186 187 /* 188 * Use non-blocking mallocs so that we will revert to a linear 189 * lookup on failure rather than potentially blocking forever. 190 */ 191 dh = pool_cache_get(ufsdirhash_cache, PR_NOWAIT); 192 if (dh == NULL) { 193 atomic_add_int(&ufs_dirhashmem, -memreqd); 194 return -1; 195 } 196 memset(dh, 0, sizeof(*dh)); 197 mutex_init(&dh->dh_lock, MUTEX_DEFAULT, IPL_NONE); 198 DIRHASH_LOCK(dh); 199 dh->dh_hashsz = narrays * sizeof(dh->dh_hash[0]); 200 dh->dh_hash = kmem_zalloc(dh->dh_hashsz, KM_NOSLEEP); 201 dh->dh_blkfreesz = nblocks * sizeof(dh->dh_blkfree[0]); 202 dh->dh_blkfree = kmem_zalloc(dh->dh_blkfreesz, KM_NOSLEEP); 203 if (dh->dh_hash == NULL || dh->dh_blkfree == NULL) 204 goto fail; 205 for (i = 0; i < narrays; i++) { 206 if ((dh->dh_hash[i] = DIRHASH_BLKALLOC()) == NULL) 207 goto fail; 208 for (j = 0; j < DH_NBLKOFF; j++) 209 dh->dh_hash[i][j] = DIRHASH_EMPTY; 210 } 211 212 /* Initialise the hash table and block statistics. */ 213 dh->dh_narrays = narrays; 214 dh->dh_hlen = nslots; 215 dh->dh_nblk = nblocks; 216 dh->dh_dirblks = dirblocks; 217 for (i = 0; i < dirblocks; i++) 218 dh->dh_blkfree[i] = dirblksiz / DIRALIGN; 219 for (i = 0; i < DH_NFSTATS; i++) 220 dh->dh_firstfree[i] = -1; 221 dh->dh_firstfree[DH_NFSTATS] = 0; 222 dh->dh_seqopt = 0; 223 dh->dh_seqoff = 0; 224 dh->dh_score = DH_SCOREINIT; 225 ip->i_dirhash = dh; 226 227 bmask = VFSTOUFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 228 pos = 0; 229 while (pos < ip->i_size) { 230 preempt_point(); 231 232 /* If necessary, get the next directory block. */ 233 if ((pos & bmask) == 0) { 234 if (bp != NULL) 235 brelse(bp, 0); 236 if (ufs_blkatoff(vp, (off_t)pos, NULL, &bp, false) != 0) 237 goto fail; 238 } 239 240 /* Add this entry to the hash. */ 241 ep = (struct direct *)((char *)bp->b_data + (pos & bmask)); 242 if (ep->d_reclen == 0 || ep->d_reclen > 243 dirblksiz - (pos & (dirblksiz - 1))) { 244 /* Corrupted directory. */ 245 brelse(bp, 0); 246 goto fail; 247 } 248 if (ep->d_ino != 0) { 249 /* Add the entry (simplified ufsdirhash_add). */ 250 slot = ufsdirhash_hash(dh, ep->d_name, ep->d_namlen); 251 while (DH_ENTRY(dh, slot) != DIRHASH_EMPTY) 252 slot = WRAPINCR(slot, dh->dh_hlen); 253 dh->dh_hused++; 254 DH_ENTRY(dh, slot) = pos; 255 ufsdirhash_adjfree(dh, pos, -UFS_DIRSIZ(0, ep, needswap), 256 dirblksiz); 257 } 258 pos += ep->d_reclen; 259 } 260 261 if (bp != NULL) 262 brelse(bp, 0); 263 DIRHASHLIST_LOCK(); 264 TAILQ_INSERT_TAIL(&ufsdirhash_list, dh, dh_list); 265 dh->dh_onlist = 1; 266 DIRHASH_UNLOCK(dh); 267 DIRHASHLIST_UNLOCK(); 268 return 0; 269 270 fail: 271 ip->i_dirhash = NULL; 272 DIRHASH_UNLOCK(dh); 273 if (dh->dh_hash != NULL) { 274 for (i = 0; i < narrays; i++) 275 if (dh->dh_hash[i] != NULL) 276 DIRHASH_BLKFREE(dh->dh_hash[i]); 277 kmem_free(dh->dh_hash, dh->dh_hashsz); 278 } 279 if (dh->dh_blkfree != NULL) 280 kmem_free(dh->dh_blkfree, dh->dh_blkfreesz); 281 mutex_destroy(&dh->dh_lock); 282 pool_cache_put(ufsdirhash_cache, dh); 283 atomic_add_int(&ufs_dirhashmem, -memreqd); 284 return -1; 285 } 286 287 /* 288 * Free any hash table associated with inode 'ip'. 289 */ 290 void 291 ufsdirhash_free(struct inode *ip) 292 { 293 struct dirhash *dh; 294 int i, mem; 295 296 if ((dh = ip->i_dirhash) == NULL) 297 return; 298 299 ip->i_dirhash = NULL; 300 301 DIRHASHLIST_LOCK(); 302 if (dh->dh_onlist) 303 TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list); 304 DIRHASHLIST_UNLOCK(); 305 306 /* The dirhash pointed to by 'dh' is exclusively ours now. */ 307 mem = sizeof(*dh); 308 if (dh->dh_hash != NULL) { 309 for (i = 0; i < dh->dh_narrays; i++) 310 DIRHASH_BLKFREE(dh->dh_hash[i]); 311 kmem_free(dh->dh_hash, dh->dh_hashsz); 312 kmem_free(dh->dh_blkfree, dh->dh_blkfreesz); 313 mem += dh->dh_hashsz; 314 mem += dh->dh_narrays * DH_NBLKOFF * sizeof(**dh->dh_hash); 315 mem += dh->dh_nblk * sizeof(*dh->dh_blkfree); 316 } 317 mutex_destroy(&dh->dh_lock); 318 pool_cache_put(ufsdirhash_cache, dh); 319 320 atomic_add_int(&ufs_dirhashmem, -mem); 321 } 322 323 /* 324 * Find the offset of the specified name within the given inode. 325 * Returns 0 on success, ENOENT if the entry does not exist, or 326 * EJUSTRETURN if the caller should revert to a linear search. 327 * 328 * If successful, the directory offset is stored in *offp, and a 329 * pointer to a struct buf containing the entry is stored in *bpp. If 330 * prevoffp is non-NULL, the offset of the previous entry within 331 * the UFS_DIRBLKSIZ-sized block is stored in *prevoffp (if the entry 332 * is the first in a block, the start of the block is used). 333 */ 334 int 335 ufsdirhash_lookup(struct inode *ip, const char *name, int namelen, doff_t *offp, 336 struct buf **bpp, doff_t *prevoffp) 337 { 338 struct dirhash *dh, *dh_next; 339 struct direct *dp; 340 struct vnode *vp; 341 struct buf *bp; 342 doff_t blkoff, bmask, offset, prevoff; 343 int i, slot; 344 const int needswap = UFS_MPNEEDSWAP(ip->i_ump); 345 int dirblksiz = ip->i_ump->um_dirblksiz; 346 347 if ((dh = ip->i_dirhash) == NULL) 348 return SET_ERROR(EJUSTRETURN); 349 350 /* 351 * Move this dirhash towards the end of the list if it has a 352 * score higher than the next entry, and acquire the dh_lock. 353 * Optimise the case where it's already the last by performing 354 * an unlocked read of the TAILQ_NEXT pointer. 355 * 356 * In both cases, end up holding just dh_lock. 357 */ 358 if (TAILQ_NEXT(dh, dh_list) != NULL) { 359 DIRHASHLIST_LOCK(); 360 DIRHASH_LOCK(dh); 361 /* 362 * If the new score will be greater than that of the next 363 * entry, then move this entry past it. With both mutexes 364 * held, dh_next won't go away, but its dh_score could 365 * change; that's not important since it is just a hint. 366 */ 367 if (dh->dh_hash != NULL && 368 (dh_next = TAILQ_NEXT(dh, dh_list)) != NULL && 369 dh->dh_score >= dh_next->dh_score) { 370 KASSERT(dh->dh_onlist); 371 TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list); 372 TAILQ_INSERT_AFTER(&ufsdirhash_list, dh_next, dh, 373 dh_list); 374 } 375 DIRHASHLIST_UNLOCK(); 376 } else { 377 /* Already the last, though that could change as we wait. */ 378 DIRHASH_LOCK(dh); 379 } 380 if (dh->dh_hash == NULL) { 381 DIRHASH_UNLOCK(dh); 382 ufsdirhash_free(ip); 383 return SET_ERROR(EJUSTRETURN); 384 } 385 386 /* Update the score. */ 387 if (dh->dh_score < DH_SCOREMAX) 388 dh->dh_score++; 389 390 vp = ip->i_vnode; 391 bmask = VFSTOUFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 392 blkoff = -1; 393 bp = NULL; 394 restart: 395 slot = ufsdirhash_hash(dh, name, namelen); 396 397 if (dh->dh_seqopt) { 398 /* 399 * Sequential access optimisation. dh_seqoff contains the 400 * offset of the directory entry immediately following 401 * the last entry that was looked up. Check if this offset 402 * appears in the hash chain for the name we are looking for. 403 */ 404 for (i = slot; (offset = DH_ENTRY(dh, i)) != DIRHASH_EMPTY; 405 i = WRAPINCR(i, dh->dh_hlen)) 406 if (offset == dh->dh_seqoff) 407 break; 408 if (offset == dh->dh_seqoff) { 409 /* 410 * We found an entry with the expected offset. This 411 * is probably the entry we want, but if not, the 412 * code below will turn off seqoff and retry. 413 */ 414 slot = i; 415 } else 416 dh->dh_seqopt = 0; 417 } 418 419 for (; (offset = DH_ENTRY(dh, slot)) != DIRHASH_EMPTY; 420 slot = WRAPINCR(slot, dh->dh_hlen)) { 421 if (offset == DIRHASH_DEL) 422 continue; 423 424 if (offset < 0 || offset >= ip->i_size) 425 panic("ufsdirhash_lookup: bad offset in hash array"); 426 if ((offset & ~bmask) != blkoff) { 427 if (bp != NULL) 428 brelse(bp, 0); 429 blkoff = offset & ~bmask; 430 if (ufs_blkatoff(vp, (off_t)blkoff, 431 NULL, &bp, false) != 0) { 432 DIRHASH_UNLOCK(dh); 433 return SET_ERROR(EJUSTRETURN); 434 } 435 } 436 dp = (struct direct *)((char *)bp->b_data + (offset & bmask)); 437 if (dp->d_reclen == 0 || dp->d_reclen > 438 dirblksiz - (offset & (dirblksiz - 1))) { 439 /* Corrupted directory. */ 440 DIRHASH_UNLOCK(dh); 441 brelse(bp, 0); 442 return SET_ERROR(EJUSTRETURN); 443 } 444 if (dp->d_namlen == namelen && 445 memcmp(dp->d_name, name, namelen) == 0) { 446 /* Found. Get the prev offset if needed. */ 447 if (prevoffp != NULL) { 448 if (offset & (dirblksiz - 1)) { 449 prevoff = ufsdirhash_getprev(dp, 450 offset, dirblksiz); 451 if (prevoff == -1) { 452 brelse(bp, 0); 453 return SET_ERROR(EJUSTRETURN); 454 } 455 } else 456 prevoff = offset; 457 *prevoffp = prevoff; 458 } 459 460 /* Check for sequential access, and update offset. */ 461 if (dh->dh_seqopt == 0 && dh->dh_seqoff == offset) 462 dh->dh_seqopt = 1; 463 dh->dh_seqoff = offset + UFS_DIRSIZ(0, dp, needswap); 464 DIRHASH_UNLOCK(dh); 465 466 *bpp = bp; 467 *offp = offset; 468 return 0; 469 } 470 471 if (dh->dh_hash == NULL) { 472 DIRHASH_UNLOCK(dh); 473 if (bp != NULL) 474 brelse(bp, 0); 475 ufsdirhash_free(ip); 476 return SET_ERROR(EJUSTRETURN); 477 } 478 /* 479 * When the name doesn't match in the seqopt case, go back 480 * and search normally. 481 */ 482 if (dh->dh_seqopt) { 483 dh->dh_seqopt = 0; 484 goto restart; 485 } 486 } 487 DIRHASH_UNLOCK(dh); 488 if (bp != NULL) 489 brelse(bp, 0); 490 return SET_ERROR(ENOENT); 491 } 492 493 /* 494 * Find a directory block with room for 'slotneeded' bytes. Returns 495 * the offset of the directory entry that begins the free space. 496 * This will either be the offset of an existing entry that has free 497 * space at the end, or the offset of an entry with d_ino == 0 at 498 * the start of a UFS_DIRBLKSIZ block. 499 * 500 * To use the space, the caller may need to compact existing entries in 501 * the directory. The total number of bytes in all of the entries involved 502 * in the compaction is stored in *slotsize. In other words, all of 503 * the entries that must be compacted are exactly contained in the 504 * region beginning at the returned offset and spanning *slotsize bytes. 505 * 506 * Returns -1 if no space was found, indicating that the directory 507 * must be extended. 508 */ 509 doff_t 510 ufsdirhash_findfree(struct inode *ip, int slotneeded, int *slotsize) 511 { 512 struct direct *dp; 513 struct dirhash *dh; 514 struct buf *bp; 515 doff_t pos, slotstart; 516 int dirblock, error, freebytes, i; 517 const int needswap = UFS_MPNEEDSWAP(ip->i_ump); 518 int dirblksiz = ip->i_ump->um_dirblksiz; 519 520 if ((dh = ip->i_dirhash) == NULL) 521 return -1; 522 523 DIRHASH_LOCK(dh); 524 if (dh->dh_hash == NULL) { 525 DIRHASH_UNLOCK(dh); 526 ufsdirhash_free(ip); 527 return -1; 528 } 529 530 /* Find a directory block with the desired free space. */ 531 dirblock = -1; 532 for (i = howmany(slotneeded, DIRALIGN); i <= DH_NFSTATS; i++) 533 if ((dirblock = dh->dh_firstfree[i]) != -1) 534 break; 535 if (dirblock == -1) { 536 DIRHASH_UNLOCK(dh); 537 return -1; 538 } 539 540 KASSERT(dirblock < dh->dh_nblk && 541 dh->dh_blkfree[dirblock] >= howmany(slotneeded, DIRALIGN)); 542 pos = dirblock * dirblksiz; 543 error = ufs_blkatoff(ip->i_vnode, (off_t)pos, (void *)&dp, &bp, false); 544 if (error) { 545 DIRHASH_UNLOCK(dh); 546 return -1; 547 } 548 /* Find the first entry with free space. */ 549 for (i = 0; i < dirblksiz; ) { 550 if (dp->d_reclen == 0) { 551 DIRHASH_UNLOCK(dh); 552 brelse(bp, 0); 553 return -1; 554 } 555 if (dp->d_ino == 0 || dp->d_reclen > UFS_DIRSIZ(0, dp, needswap)) 556 break; 557 i += dp->d_reclen; 558 dp = (struct direct *)((char *)dp + dp->d_reclen); 559 } 560 if (i > dirblksiz) { 561 DIRHASH_UNLOCK(dh); 562 brelse(bp, 0); 563 return -1; 564 } 565 slotstart = pos + i; 566 567 /* Find the range of entries needed to get enough space */ 568 freebytes = 0; 569 while (i < dirblksiz && freebytes < slotneeded) { 570 freebytes += dp->d_reclen; 571 if (dp->d_ino != 0) 572 freebytes -= UFS_DIRSIZ(0, dp, needswap); 573 if (dp->d_reclen == 0) { 574 DIRHASH_UNLOCK(dh); 575 brelse(bp, 0); 576 return -1; 577 } 578 i += dp->d_reclen; 579 dp = (struct direct *)((char *)dp + dp->d_reclen); 580 } 581 if (i > dirblksiz) { 582 DIRHASH_UNLOCK(dh); 583 brelse(bp, 0); 584 return -1; 585 } 586 if (freebytes < slotneeded) 587 panic("ufsdirhash_findfree: free mismatch"); 588 DIRHASH_UNLOCK(dh); 589 brelse(bp, 0); 590 *slotsize = pos + i - slotstart; 591 return slotstart; 592 } 593 594 /* 595 * Return the start of the unused space at the end of a directory, or 596 * -1 if there are no trailing unused blocks. 597 */ 598 doff_t 599 ufsdirhash_enduseful(struct inode *ip) 600 { 601 struct dirhash *dh; 602 int i; 603 int dirblksiz = ip->i_ump->um_dirblksiz; 604 605 if ((dh = ip->i_dirhash) == NULL) 606 return -1; 607 608 DIRHASH_LOCK(dh); 609 if (dh->dh_hash == NULL) { 610 DIRHASH_UNLOCK(dh); 611 ufsdirhash_free(ip); 612 return -1; 613 } 614 615 if (dh->dh_blkfree[dh->dh_dirblks - 1] != dirblksiz / DIRALIGN) { 616 DIRHASH_UNLOCK(dh); 617 return -1; 618 } 619 620 for (i = dh->dh_dirblks - 1; i >= 0; i--) 621 if (dh->dh_blkfree[i] != dirblksiz / DIRALIGN) 622 break; 623 DIRHASH_UNLOCK(dh); 624 return ((doff_t)(i + 1) * dirblksiz); 625 } 626 627 /* 628 * Insert information into the hash about a new directory entry. dirp 629 * points to a struct direct containing the entry, and offset specifies 630 * the offset of this entry. 631 */ 632 void 633 ufsdirhash_add(struct inode *ip, struct direct *dirp, doff_t offset) 634 { 635 struct dirhash *dh; 636 int slot; 637 const int needswap = UFS_MPNEEDSWAP(ip->i_ump); 638 int dirblksiz = ip->i_ump->um_dirblksiz; 639 640 if ((dh = ip->i_dirhash) == NULL) 641 return; 642 643 DIRHASH_LOCK(dh); 644 if (dh->dh_hash == NULL) { 645 DIRHASH_UNLOCK(dh); 646 ufsdirhash_free(ip); 647 return; 648 } 649 650 KASSERT(offset < dh->dh_dirblks * dirblksiz); 651 /* 652 * Normal hash usage is < 66%. If the usage gets too high then 653 * remove the hash entirely and let it be rebuilt later. 654 */ 655 if (dh->dh_hused >= (dh->dh_hlen * 3) / 4) { 656 DIRHASH_UNLOCK(dh); 657 ufsdirhash_free(ip); 658 return; 659 } 660 661 /* Find a free hash slot (empty or deleted), and add the entry. */ 662 slot = ufsdirhash_hash(dh, dirp->d_name, dirp->d_namlen); 663 while (DH_ENTRY(dh, slot) >= 0) 664 slot = WRAPINCR(slot, dh->dh_hlen); 665 if (DH_ENTRY(dh, slot) == DIRHASH_EMPTY) 666 dh->dh_hused++; 667 DH_ENTRY(dh, slot) = offset; 668 669 /* Update the per-block summary info. */ 670 ufsdirhash_adjfree(dh, offset, -UFS_DIRSIZ(0, dirp, needswap), dirblksiz); 671 DIRHASH_UNLOCK(dh); 672 } 673 674 /* 675 * Remove the specified directory entry from the hash. The entry to remove 676 * is defined by the name in `dirp', which must exist at the specified 677 * `offset' within the directory. 678 */ 679 void 680 ufsdirhash_remove(struct inode *ip, struct direct *dirp, doff_t offset) 681 { 682 struct dirhash *dh; 683 int slot; 684 const int needswap = UFS_MPNEEDSWAP(ip->i_ump); 685 int dirblksiz = ip->i_ump->um_dirblksiz; 686 687 if ((dh = ip->i_dirhash) == NULL) 688 return; 689 690 DIRHASH_LOCK(dh); 691 if (dh->dh_hash == NULL) { 692 DIRHASH_UNLOCK(dh); 693 ufsdirhash_free(ip); 694 return; 695 } 696 697 KASSERT(offset < dh->dh_dirblks * dirblksiz); 698 /* Find the entry */ 699 slot = ufsdirhash_findslot(dh, dirp->d_name, dirp->d_namlen, offset); 700 701 /* Remove the hash entry. */ 702 ufsdirhash_delslot(dh, slot); 703 704 /* Update the per-block summary info. */ 705 ufsdirhash_adjfree(dh, offset, UFS_DIRSIZ(0, dirp, needswap), dirblksiz); 706 DIRHASH_UNLOCK(dh); 707 } 708 709 /* 710 * Change the offset associated with a directory entry in the hash. Used 711 * when compacting directory blocks. 712 */ 713 void 714 ufsdirhash_move(struct inode *ip, struct direct *dirp, doff_t oldoff, 715 doff_t newoff) 716 { 717 struct dirhash *dh; 718 int slot; 719 720 if ((dh = ip->i_dirhash) == NULL) 721 return; 722 DIRHASH_LOCK(dh); 723 if (dh->dh_hash == NULL) { 724 DIRHASH_UNLOCK(dh); 725 ufsdirhash_free(ip); 726 return; 727 } 728 729 KASSERT(oldoff < dh->dh_dirblks * ip->i_ump->um_dirblksiz && 730 newoff < dh->dh_dirblks * ip->i_ump->um_dirblksiz); 731 /* Find the entry, and update the offset. */ 732 slot = ufsdirhash_findslot(dh, dirp->d_name, dirp->d_namlen, oldoff); 733 DH_ENTRY(dh, slot) = newoff; 734 DIRHASH_UNLOCK(dh); 735 } 736 737 /* 738 * Inform dirhash that the directory has grown by one block that 739 * begins at offset (i.e. the new length is offset + UFS_DIRBLKSIZ). 740 */ 741 void 742 ufsdirhash_newblk(struct inode *ip, doff_t offset) 743 { 744 struct dirhash *dh; 745 int block; 746 int dirblksiz = ip->i_ump->um_dirblksiz; 747 748 if ((dh = ip->i_dirhash) == NULL) 749 return; 750 DIRHASH_LOCK(dh); 751 if (dh->dh_hash == NULL) { 752 DIRHASH_UNLOCK(dh); 753 ufsdirhash_free(ip); 754 return; 755 } 756 757 KASSERT(offset == dh->dh_dirblks * dirblksiz); 758 block = offset / dirblksiz; 759 if (block >= dh->dh_nblk) { 760 /* Out of space; must rebuild. */ 761 DIRHASH_UNLOCK(dh); 762 ufsdirhash_free(ip); 763 return; 764 } 765 dh->dh_dirblks = block + 1; 766 767 /* Account for the new free block. */ 768 dh->dh_blkfree[block] = dirblksiz / DIRALIGN; 769 if (dh->dh_firstfree[DH_NFSTATS] == -1) 770 dh->dh_firstfree[DH_NFSTATS] = block; 771 DIRHASH_UNLOCK(dh); 772 } 773 774 /* 775 * Inform dirhash that the directory is being truncated. 776 */ 777 void 778 ufsdirhash_dirtrunc(struct inode *ip, doff_t offset) 779 { 780 struct dirhash *dh; 781 int block, i; 782 int dirblksiz = ip->i_ump->um_dirblksiz; 783 784 if ((dh = ip->i_dirhash) == NULL) 785 return; 786 787 DIRHASH_LOCK(dh); 788 if (dh->dh_hash == NULL) { 789 DIRHASH_UNLOCK(dh); 790 ufsdirhash_free(ip); 791 return; 792 } 793 794 KASSERT(offset <= dh->dh_dirblks * dirblksiz); 795 block = howmany(offset, dirblksiz); 796 /* 797 * If the directory shrinks to less than 1/8 of dh_nblk blocks 798 * (about 20% of its original size due to the 50% extra added in 799 * ufsdirhash_build) then free it, and let the caller rebuild 800 * if necessary. 801 */ 802 if (block < dh->dh_nblk / 8 && dh->dh_narrays > 1) { 803 DIRHASH_UNLOCK(dh); 804 ufsdirhash_free(ip); 805 return; 806 } 807 808 /* 809 * Remove any `first free' information pertaining to the 810 * truncated blocks. All blocks we're removing should be 811 * completely unused. 812 */ 813 if (dh->dh_firstfree[DH_NFSTATS] >= block) 814 dh->dh_firstfree[DH_NFSTATS] = -1; 815 for (i = block; i < dh->dh_dirblks; i++) 816 if (dh->dh_blkfree[i] != dirblksiz / DIRALIGN) 817 panic("ufsdirhash_dirtrunc: blocks in use"); 818 for (i = 0; i < DH_NFSTATS; i++) 819 if (dh->dh_firstfree[i] >= block) 820 panic("ufsdirhash_dirtrunc: first free corrupt"); 821 dh->dh_dirblks = block; 822 DIRHASH_UNLOCK(dh); 823 } 824 825 /* 826 * Debugging function to check that the dirhash information about 827 * a directory block matches its actual contents. Panics if a mismatch 828 * is detected. 829 * 830 * On entry, `sbuf' should point to the start of an in-core 831 * DIRBLKSIZ-sized directory block, and `offset' should contain the 832 * offset from the start of the directory of that block. 833 */ 834 void 835 ufsdirhash_checkblock(struct inode *ip, char *sbuf, doff_t offset) 836 { 837 struct dirhash *dh; 838 struct direct *dp; 839 int block, ffslot, i, nfree; 840 const int needswap = UFS_MPNEEDSWAP(ip->i_ump); 841 int dirblksiz = ip->i_ump->um_dirblksiz; 842 843 if (!ufs_dirhashcheck) 844 return; 845 if ((dh = ip->i_dirhash) == NULL) 846 return; 847 848 DIRHASH_LOCK(dh); 849 if (dh->dh_hash == NULL) { 850 DIRHASH_UNLOCK(dh); 851 ufsdirhash_free(ip); 852 return; 853 } 854 855 block = offset / dirblksiz; 856 if ((offset & (dirblksiz - 1)) != 0 || block >= dh->dh_dirblks) 857 panic("ufsdirhash_checkblock: bad offset"); 858 859 nfree = 0; 860 for (i = 0; i < dirblksiz; i += dp->d_reclen) { 861 dp = (struct direct *)(sbuf + i); 862 if (dp->d_reclen == 0 || i + dp->d_reclen > dirblksiz) 863 panic("ufsdirhash_checkblock: bad dir"); 864 865 if (dp->d_ino == 0) { 866 #if 0 867 /* 868 * XXX entries with d_ino == 0 should only occur 869 * at the start of a DIRBLKSIZ block. However the 870 * ufs code is tolerant of such entries at other 871 * offsets, and fsck does not fix them. 872 */ 873 if (i != 0) 874 panic("ufsdirhash_checkblock: bad dir inode"); 875 #endif 876 nfree += dp->d_reclen; 877 continue; 878 } 879 880 /* Check that the entry exists (will panic if it doesn't). */ 881 ufsdirhash_findslot(dh, dp->d_name, dp->d_namlen, offset + i); 882 883 nfree += dp->d_reclen - UFS_DIRSIZ(0, dp, needswap); 884 } 885 if (i != dirblksiz) 886 panic("ufsdirhash_checkblock: bad dir end"); 887 888 if (dh->dh_blkfree[block] * DIRALIGN != nfree) 889 panic("ufsdirhash_checkblock: bad free count"); 890 891 ffslot = BLKFREE2IDX(nfree / DIRALIGN); 892 for (i = 0; i <= DH_NFSTATS; i++) 893 if (dh->dh_firstfree[i] == block && i != ffslot) 894 panic("ufsdirhash_checkblock: bad first-free"); 895 if (dh->dh_firstfree[ffslot] == -1) 896 panic("ufsdirhash_checkblock: missing first-free entry"); 897 DIRHASH_UNLOCK(dh); 898 } 899 900 /* 901 * Hash the specified filename into a dirhash slot. 902 */ 903 static int 904 ufsdirhash_hash(struct dirhash *dh, const char *name, int namelen) 905 { 906 u_int32_t hash; 907 908 /* 909 * We hash the name and then some other bit of data that is 910 * invariant over the dirhash's lifetime. Otherwise names 911 * differing only in the last byte are placed close to one 912 * another in the table, which is bad for linear probing. 913 */ 914 hash = hash32_buf(name, namelen, HASH32_BUF_INIT); 915 hash = hash32_buf(&dh, sizeof(dh), hash); 916 return (hash % dh->dh_hlen); 917 } 918 919 /* 920 * Adjust the number of free bytes in the block containing `offset' 921 * by the value specified by `diff'. 922 * 923 * The caller must ensure we have exclusive access to `dh'; normally 924 * that means that dh_lock should be held, but this is also called 925 * from ufsdirhash_build() where exclusive access can be assumed. 926 */ 927 static void 928 ufsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff, int dirblksiz) 929 { 930 int block, i, nfidx, ofidx; 931 932 KASSERT(mutex_owned(&dh->dh_lock)); 933 934 /* Update the per-block summary info. */ 935 block = offset / dirblksiz; 936 KASSERT(block < dh->dh_nblk && block < dh->dh_dirblks); 937 ofidx = BLKFREE2IDX(dh->dh_blkfree[block]); 938 dh->dh_blkfree[block] = (int)dh->dh_blkfree[block] + (diff / DIRALIGN); 939 nfidx = BLKFREE2IDX(dh->dh_blkfree[block]); 940 941 /* Update the `first free' list if necessary. */ 942 if (ofidx != nfidx) { 943 /* If removing, scan forward for the next block. */ 944 if (dh->dh_firstfree[ofidx] == block) { 945 for (i = block + 1; i < dh->dh_dirblks; i++) 946 if (BLKFREE2IDX(dh->dh_blkfree[i]) == ofidx) 947 break; 948 dh->dh_firstfree[ofidx] = (i < dh->dh_dirblks) ? i : -1; 949 } 950 951 /* Make this the new `first free' if necessary */ 952 if (dh->dh_firstfree[nfidx] > block || 953 dh->dh_firstfree[nfidx] == -1) 954 dh->dh_firstfree[nfidx] = block; 955 } 956 } 957 958 /* 959 * Find the specified name which should have the specified offset. 960 * Returns a slot number, and panics on failure. 961 * 962 * `dh' must be locked on entry and remains so on return. 963 */ 964 static int 965 ufsdirhash_findslot(struct dirhash *dh, const char *name, int namelen, 966 doff_t offset) 967 { 968 int slot; 969 970 KASSERT(mutex_owned(&dh->dh_lock)); 971 972 /* Find the entry. */ 973 KASSERT(dh->dh_hused < dh->dh_hlen); 974 slot = ufsdirhash_hash(dh, name, namelen); 975 while (DH_ENTRY(dh, slot) != offset && 976 DH_ENTRY(dh, slot) != DIRHASH_EMPTY) 977 slot = WRAPINCR(slot, dh->dh_hlen); 978 if (DH_ENTRY(dh, slot) != offset) 979 panic("ufsdirhash_findslot: '%.*s' not found", namelen, name); 980 981 return slot; 982 } 983 984 /* 985 * Remove the entry corresponding to the specified slot from the hash array. 986 * 987 * `dh' must be locked on entry and remains so on return. 988 */ 989 static void 990 ufsdirhash_delslot(struct dirhash *dh, int slot) 991 { 992 int i; 993 994 KASSERT(mutex_owned(&dh->dh_lock)); 995 996 /* Mark the entry as deleted. */ 997 DH_ENTRY(dh, slot) = DIRHASH_DEL; 998 999 /* If this is the end of a chain of DIRHASH_DEL slots, remove them. */ 1000 for (i = slot; DH_ENTRY(dh, i) == DIRHASH_DEL; ) 1001 i = WRAPINCR(i, dh->dh_hlen); 1002 if (DH_ENTRY(dh, i) == DIRHASH_EMPTY) { 1003 i = WRAPDECR(i, dh->dh_hlen); 1004 while (DH_ENTRY(dh, i) == DIRHASH_DEL) { 1005 DH_ENTRY(dh, i) = DIRHASH_EMPTY; 1006 dh->dh_hused--; 1007 i = WRAPDECR(i, dh->dh_hlen); 1008 } 1009 KASSERT(dh->dh_hused >= 0); 1010 } 1011 } 1012 1013 /* 1014 * Given a directory entry and its offset, find the offset of the 1015 * previous entry in the same UFS_DIRBLKSIZ-sized block. Returns an 1016 * offset, or -1 if there is no previous entry in the block or some 1017 * other problem occurred. 1018 */ 1019 static doff_t 1020 ufsdirhash_getprev(struct direct *dirp, doff_t offset, int dirblksiz) 1021 { 1022 struct direct *dp; 1023 char *blkbuf; 1024 doff_t blkoff, prevoff; 1025 int entrypos, i; 1026 1027 blkoff = offset & ~(dirblksiz - 1); /* offset of start of block */ 1028 entrypos = offset & (dirblksiz - 1); /* entry relative to block */ 1029 blkbuf = (char *)dirp - entrypos; 1030 prevoff = blkoff; 1031 1032 /* If `offset' is the start of a block, there is no previous entry. */ 1033 if (entrypos == 0) 1034 return -1; 1035 1036 /* Scan from the start of the block until we get to the entry. */ 1037 for (i = 0; i < entrypos; i += dp->d_reclen) { 1038 dp = (struct direct *)(blkbuf + i); 1039 if (dp->d_reclen == 0 || i + dp->d_reclen > entrypos) 1040 return -1; /* Corrupted directory. */ 1041 prevoff = blkoff + i; 1042 } 1043 return prevoff; 1044 } 1045 1046 /* 1047 * Try to free up `wanted' bytes by stealing memory from existing 1048 * dirhashes. Returns zero with list locked if successful. 1049 */ 1050 static int 1051 ufsdirhash_recycle(int wanted) 1052 { 1053 struct dirhash *dh; 1054 doff_t **hash; 1055 u_int8_t *blkfree; 1056 int i, mem, narrays; 1057 size_t hashsz, blkfreesz; 1058 1059 DIRHASHLIST_LOCK(); 1060 while (wanted + ufs_dirhashmem > ufs_dirhashmaxmem) { 1061 /* Find a dirhash, and lock it. */ 1062 if ((dh = TAILQ_FIRST(&ufsdirhash_list)) == NULL) { 1063 DIRHASHLIST_UNLOCK(); 1064 return -1; 1065 } 1066 DIRHASH_LOCK(dh); 1067 KASSERT(dh->dh_hash != NULL); 1068 1069 /* Decrement the score; only recycle if it becomes zero. */ 1070 if (--dh->dh_score > 0) { 1071 DIRHASH_UNLOCK(dh); 1072 DIRHASHLIST_UNLOCK(); 1073 return -1; 1074 } 1075 1076 /* Remove it from the list and detach its memory. */ 1077 TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list); 1078 dh->dh_onlist = 0; 1079 hash = dh->dh_hash; 1080 hashsz = dh->dh_hashsz; 1081 dh->dh_hash = NULL; 1082 blkfree = dh->dh_blkfree; 1083 blkfreesz = dh->dh_blkfreesz; 1084 dh->dh_blkfree = NULL; 1085 narrays = dh->dh_narrays; 1086 mem = narrays * sizeof(*dh->dh_hash) + 1087 narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) + 1088 dh->dh_nblk * sizeof(*dh->dh_blkfree); 1089 1090 /* Unlock everything, free the detached memory. */ 1091 DIRHASH_UNLOCK(dh); 1092 DIRHASHLIST_UNLOCK(); 1093 1094 for (i = 0; i < narrays; i++) 1095 DIRHASH_BLKFREE(hash[i]); 1096 kmem_free(hash, hashsz); 1097 kmem_free(blkfree, blkfreesz); 1098 1099 /* Account for the returned memory, and repeat if necessary. */ 1100 DIRHASHLIST_LOCK(); 1101 atomic_add_int(&ufs_dirhashmem, -mem); 1102 } 1103 /* Success. */ 1104 return 0; 1105 } 1106 1107 SYSCTL_SETUP(ufsdirhash_sysctl_init, "ufs_dirhash sysctl") 1108 { 1109 const struct sysctlnode *rnode, *cnode; 1110 1111 sysctl_createv(clog, 0, NULL, &rnode, 1112 CTLFLAG_PERMANENT, 1113 CTLTYPE_NODE, "ufs", 1114 SYSCTL_DESCR("ufs"), 1115 NULL, 0, NULL, 0, 1116 CTL_VFS, CTL_CREATE, CTL_EOL); 1117 1118 sysctl_createv(clog, 0, &rnode, &rnode, 1119 CTLFLAG_PERMANENT, 1120 CTLTYPE_NODE, "dirhash", 1121 SYSCTL_DESCR("dirhash"), 1122 NULL, 0, NULL, 0, 1123 CTL_CREATE, CTL_EOL); 1124 1125 sysctl_createv(clog, 0, &rnode, &cnode, 1126 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 1127 CTLTYPE_INT, "minblocks", 1128 SYSCTL_DESCR("minimum hashed directory size in blocks"), 1129 NULL, 0, &ufs_dirhashminblks, 0, 1130 CTL_CREATE, CTL_EOL); 1131 1132 sysctl_createv(clog, 0, &rnode, &cnode, 1133 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 1134 CTLTYPE_INT, "maxmem", 1135 SYSCTL_DESCR("maximum dirhash memory usage"), 1136 NULL, 0, &ufs_dirhashmaxmem, 0, 1137 CTL_CREATE, CTL_EOL); 1138 1139 sysctl_createv(clog, 0, &rnode, &cnode, 1140 CTLFLAG_PERMANENT|CTLFLAG_READONLY, 1141 CTLTYPE_INT, "memused", 1142 SYSCTL_DESCR("current dirhash memory usage"), 1143 NULL, 0, &ufs_dirhashmem, 0, 1144 CTL_CREATE, CTL_EOL); 1145 1146 sysctl_createv(clog, 0, &rnode, &cnode, 1147 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 1148 CTLTYPE_INT, "docheck", 1149 SYSCTL_DESCR("enable extra sanity checks"), 1150 NULL, 0, &ufs_dirhashcheck, 0, 1151 CTL_CREATE, CTL_EOL); 1152 } 1153 1154 void 1155 ufsdirhash_init(void) 1156 { 1157 1158 /* 1159 * Only initialise defaults for the dirhash size if it hasn't 1160 * hasn't been set. 1161 */ 1162 if (ufs_dirhashmaxmem == 0) { 1163 /* Use 64-bit math to avoid overflows. */ 1164 uint64_t physmem_bytes, hash_bytes; 1165 1166 physmem_bytes = ctob((uint64_t)physmem); 1167 hash_bytes = physmem_bytes / DIRHASH_DEFAULT_DIVIDER; 1168 1169 if (hash_bytes < MIN_DEFAULT_DIRHASH_MEM) 1170 hash_bytes = 0; 1171 1172 if (hash_bytes > MAX_DEFAULT_DIRHASH_MEM) 1173 hash_bytes = MAX_DEFAULT_DIRHASH_MEM; 1174 1175 ufs_dirhashmaxmem = (u_int)hash_bytes; 1176 } 1177 1178 mutex_init(&ufsdirhash_lock, MUTEX_DEFAULT, IPL_NONE); 1179 ufsdirhashblk_cache = pool_cache_init(DH_NBLKOFF * sizeof(daddr_t), 0, 1180 0, 0, "dirhashblk", NULL, IPL_NONE, NULL, NULL, NULL); 1181 ufsdirhash_cache = pool_cache_init(sizeof(struct dirhash), 0, 1182 0, 0, "dirhash", NULL, IPL_NONE, NULL, NULL, NULL); 1183 TAILQ_INIT(&ufsdirhash_list); 1184 } 1185 1186 void 1187 ufsdirhash_done(void) 1188 { 1189 1190 KASSERT(TAILQ_EMPTY(&ufsdirhash_list)); 1191 pool_cache_destroy(ufsdirhashblk_cache); 1192 pool_cache_destroy(ufsdirhash_cache); 1193 mutex_destroy(&ufsdirhash_lock); 1194 } 1195