1 0 stevel /* 2 0 stevel * CDDL HEADER START 3 0 stevel * 4 0 stevel * The contents of this file are subject to the terms of the 5 1257 ah89892 * Common Development and Distribution License (the "License"). 6 1257 ah89892 * You may not use this file except in compliance with the License. 7 0 stevel * 8 0 stevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 0 stevel * or http://www.opensolaris.org/os/licensing. 10 0 stevel * See the License for the specific language governing permissions 11 0 stevel * and limitations under the License. 12 0 stevel * 13 0 stevel * When distributing Covered Code, include this CDDL HEADER in each 14 0 stevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 0 stevel * If applicable, add the following below this CDDL HEADER, with the 16 0 stevel * fields enclosed by brackets "[]" replaced with your own identifying 17 0 stevel * information: Portions Copyright [yyyy] [name of copyright owner] 18 0 stevel * 19 0 stevel * CDDL HEADER END 20 0 stevel */ 21 0 stevel /* 22 11066 rafael * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 0 stevel * Use is subject to license terms. 24 0 stevel */ 25 0 stevel 26 0 stevel /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 0 stevel /* All Rights Reserved */ 28 0 stevel 29 0 stevel /* 30 0 stevel * University Copyright- Copyright (c) 1982, 1986, 1988 31 0 stevel * The Regents of the University of California 32 0 stevel * All Rights Reserved 33 0 stevel * 34 0 stevel * University Acknowledgment- Portions of this document are derived from 35 0 stevel * software developed by the University of California, Berkeley, and its 36 0 stevel * contributors. 37 0 stevel */ 38 0 stevel 39 0 stevel #include <sys/types.h> 40 0 stevel #include <sys/systm.h> 41 0 stevel #include <sys/param.h> 42 0 stevel #include <sys/t_lock.h> 43 0 stevel #include <sys/systm.h> 44 0 stevel #include <sys/vfs.h> 45 0 stevel #include <sys/vnode.h> 46 0 stevel #include <sys/dnlc.h> 47 0 stevel #include <sys/kmem.h> 48 0 stevel #include <sys/cmn_err.h> 49 0 stevel #include <sys/vtrace.h> 50 0 stevel #include <sys/bitmap.h> 51 0 stevel #include <sys/var.h> 52 0 stevel #include <sys/sysmacros.h> 53 0 stevel #include <sys/kstat.h> 54 0 stevel #include <sys/atomic.h> 55 0 stevel #include <sys/taskq.h> 56 0 stevel 57 0 stevel /* 58 0 stevel * Directory name lookup cache. 59 0 stevel * Based on code originally done by Robert Elz at Melbourne. 60 0 stevel * 61 0 stevel * Names found by directory scans are retained in a cache 62 0 stevel * for future reference. Each hash chain is ordered by LRU 63 0 stevel * Cache is indexed by hash value obtained from (vp, name) 64 0 stevel * where the vp refers to the directory containing the name. 65 0 stevel */ 66 6712 tomee 67 6712 tomee /* 68 6712 tomee * We want to be able to identify files that are referenced only by the DNLC. 69 6712 tomee * When adding a reference from the DNLC, call VN_HOLD_DNLC instead of VN_HOLD, 70 6712 tomee * since multiple DNLC references should only be counted once in v_count. This 71 6712 tomee * file contains only two(2) calls to VN_HOLD, renamed VN_HOLD_CALLER in the 72 6712 tomee * hope that no one will mistakenly add a VN_HOLD to this file. (Unfortunately 73 6712 tomee * it is not possible to #undef VN_HOLD and retain VN_HOLD_CALLER. Ideally a 74 6712 tomee * Makefile rule would grep uncommented C tokens to check that VN_HOLD is 75 6712 tomee * referenced only once in this file, to define VN_HOLD_CALLER.) 76 6712 tomee */ 77 6712 tomee #define VN_HOLD_CALLER VN_HOLD 78 6712 tomee #define VN_HOLD_DNLC(vp) { \ 79 6712 tomee mutex_enter(&(vp)->v_lock); \ 80 6712 tomee if ((vp)->v_count_dnlc == 0) \ 81 6712 tomee (vp)->v_count++; \ 82 6712 tomee (vp)->v_count_dnlc++; \ 83 6712 tomee mutex_exit(&(vp)->v_lock); \ 84 6712 tomee } 85 6712 tomee #define VN_RELE_DNLC(vp) { \ 86 6712 tomee vn_rele_dnlc(vp); \ 87 6712 tomee } 88 0 stevel 89 0 stevel /* 90 0 stevel * Tunable nc_hashavelen is the average length desired for this chain, from 91 0 stevel * which the size of the nc_hash table is derived at create time. 92 0 stevel */ 93 0 stevel #define NC_HASHAVELEN_DEFAULT 4 94 0 stevel int nc_hashavelen = NC_HASHAVELEN_DEFAULT; 95 0 stevel 96 0 stevel /* 97 0 stevel * NC_MOVETOFRONT is the move-to-front threshold: if the hash lookup 98 0 stevel * depth exceeds this value, we move the looked-up entry to the front of 99 0 stevel * its hash chain. The idea is to make sure that the most frequently 100 0 stevel * accessed entries are found most quickly (by keeping them near the 101 0 stevel * front of their hash chains). 102 0 stevel */ 103 0 stevel #define NC_MOVETOFRONT 2 104 0 stevel 105 0 stevel /* 106 0 stevel * 107 0 stevel * DNLC_MAX_RELE is used to size an array on the stack when releasing 108 0 stevel * vnodes. This array is used rather than calling VN_RELE() inline because 109 0 stevel * all dnlc locks must be dropped by that time in order to avoid a 110 0 stevel * possible deadlock. This deadlock occurs when the dnlc holds the last 111 0 stevel * reference to the vnode and so the VOP_INACTIVE vector is called which 112 0 stevel * can in turn call back into the dnlc. A global array was used but had 113 0 stevel * many problems: 114 0 stevel * 1) Actually doesn't have an upper bound on the array size as 115 0 stevel * entries can be added after starting the purge. 116 0 stevel * 2) The locking scheme causes a hang. 117 0 stevel * 3) Caused serialisation on the global lock. 118 0 stevel * 4) The array was often unnecessarily huge. 119 0 stevel * 120 0 stevel * Note the current value 8 allows up to 4 cache entries (to be purged 121 0 stevel * from each hash chain), before having to cycle around and retry. 122 0 stevel * This ought to be ample given that nc_hashavelen is typically very small. 123 0 stevel */ 124 0 stevel #define DNLC_MAX_RELE 8 /* must be even */ 125 0 stevel 126 0 stevel /* 127 0 stevel * Hash table of name cache entries for fast lookup, dynamically 128 0 stevel * allocated at startup. 129 0 stevel */ 130 0 stevel nc_hash_t *nc_hash; 131 0 stevel 132 0 stevel /* 133 0 stevel * Rotors. Used to select entries on a round-robin basis. 134 0 stevel */ 135 0 stevel static nc_hash_t *dnlc_purge_fs1_rotor; 136 0 stevel static nc_hash_t *dnlc_free_rotor; 137 0 stevel 138 0 stevel /* 139 0 stevel * # of dnlc entries (uninitialized) 140 0 stevel * 141 0 stevel * the initial value was chosen as being 142 0 stevel * a random string of bits, probably not 143 0 stevel * normally chosen by a systems administrator 144 0 stevel */ 145 0 stevel int ncsize = -1; 146 1669 perrin volatile uint32_t dnlc_nentries = 0; /* current num of name cache entries */ 147 1669 perrin static int nc_hashsz; /* size of hash table */ 148 1669 perrin static int nc_hashmask; /* size of hash table minus 1 */ 149 0 stevel 150 0 stevel /* 151 0 stevel * The dnlc_reduce_cache() taskq queue is activated when there are 152 1484 ek110237 * ncsize name cache entries and if no parameter is provided, it reduces 153 1484 ek110237 * the size down to dnlc_nentries_low_water, which is by default one 154 1484 ek110237 * hundreth less (or 99%) of ncsize. 155 1484 ek110237 * 156 1484 ek110237 * If a parameter is provided to dnlc_reduce_cache(), then we reduce 157 1484 ek110237 * the size down based on ncsize_onepercent - where ncsize_onepercent 158 1669 perrin * is 1% of ncsize; however, we never let dnlc_reduce_cache() reduce 159 1669 perrin * the size below 3% of ncsize (ncsize_min_percent). 160 0 stevel */ 161 0 stevel #define DNLC_LOW_WATER_DIVISOR_DEFAULT 100 162 0 stevel uint_t dnlc_low_water_divisor = DNLC_LOW_WATER_DIVISOR_DEFAULT; 163 0 stevel uint_t dnlc_nentries_low_water; 164 0 stevel int dnlc_reduce_idle = 1; /* no locking needed */ 165 1484 ek110237 uint_t ncsize_onepercent; 166 1669 perrin uint_t ncsize_min_percent; 167 0 stevel 168 0 stevel /* 169 0 stevel * If dnlc_nentries hits dnlc_max_nentries (twice ncsize) 170 0 stevel * then this means the dnlc_reduce_cache() taskq is failing to 171 0 stevel * keep up. In this case we refuse to add new entries to the dnlc 172 0 stevel * until the taskq catches up. 173 0 stevel */ 174 0 stevel uint_t dnlc_max_nentries; /* twice ncsize */ 175 0 stevel uint64_t dnlc_max_nentries_cnt = 0; /* statistic on times we failed */ 176 0 stevel 177 0 stevel /* 178 0 stevel * Tunable to define when we should just remove items from 179 0 stevel * the end of the chain. 180 0 stevel */ 181 0 stevel #define DNLC_LONG_CHAIN 8 182 0 stevel uint_t dnlc_long_chain = DNLC_LONG_CHAIN; 183 0 stevel 184 0 stevel /* 185 0 stevel * ncstats has been deprecated, due to the integer size of the counters 186 0 stevel * which can easily overflow in the dnlc. 187 0 stevel * It is maintained (at some expense) for compatability. 188 0 stevel * The preferred interface is the kstat accessible nc_stats below. 189 0 stevel */ 190 0 stevel struct ncstats ncstats; 191 0 stevel 192 0 stevel struct nc_stats ncs = { 193 0 stevel { "hits", KSTAT_DATA_UINT64 }, 194 0 stevel { "misses", KSTAT_DATA_UINT64 }, 195 0 stevel { "negative_cache_hits", KSTAT_DATA_UINT64 }, 196 0 stevel { "enters", KSTAT_DATA_UINT64 }, 197 0 stevel { "double_enters", KSTAT_DATA_UINT64 }, 198 0 stevel { "purge_total_entries", KSTAT_DATA_UINT64 }, 199 0 stevel { "purge_all", KSTAT_DATA_UINT64 }, 200 0 stevel { "purge_vp", KSTAT_DATA_UINT64 }, 201 0 stevel { "purge_vfs", KSTAT_DATA_UINT64 }, 202 0 stevel { "purge_fs1", KSTAT_DATA_UINT64 }, 203 0 stevel { "pick_free", KSTAT_DATA_UINT64 }, 204 0 stevel { "pick_heuristic", KSTAT_DATA_UINT64 }, 205 0 stevel { "pick_last", KSTAT_DATA_UINT64 }, 206 0 stevel 207 0 stevel /* directory caching stats */ 208 0 stevel 209 0 stevel { "dir_hits", KSTAT_DATA_UINT64 }, 210 0 stevel { "dir_misses", KSTAT_DATA_UINT64 }, 211 0 stevel { "dir_cached_current", KSTAT_DATA_UINT64 }, 212 0 stevel { "dir_entries_cached_current", KSTAT_DATA_UINT64 }, 213 0 stevel { "dir_cached_total", KSTAT_DATA_UINT64 }, 214 0 stevel { "dir_start_no_memory", KSTAT_DATA_UINT64 }, 215 0 stevel { "dir_add_no_memory", KSTAT_DATA_UINT64 }, 216 0 stevel { "dir_add_abort", KSTAT_DATA_UINT64 }, 217 0 stevel { "dir_add_max", KSTAT_DATA_UINT64 }, 218 0 stevel { "dir_remove_entry_fail", KSTAT_DATA_UINT64 }, 219 0 stevel { "dir_remove_space_fail", KSTAT_DATA_UINT64 }, 220 0 stevel { "dir_update_fail", KSTAT_DATA_UINT64 }, 221 0 stevel { "dir_fini_purge", KSTAT_DATA_UINT64 }, 222 0 stevel { "dir_reclaim_last", KSTAT_DATA_UINT64 }, 223 0 stevel { "dir_reclaim_any", KSTAT_DATA_UINT64 }, 224 0 stevel }; 225 0 stevel 226 0 stevel static int doingcache = 1; 227 0 stevel 228 0 stevel vnode_t negative_cache_vnode; 229 0 stevel 230 0 stevel /* 231 0 stevel * Insert entry at the front of the queue 232 0 stevel */ 233 0 stevel #define nc_inshash(ncp, hp) \ 234 0 stevel { \ 235 0 stevel (ncp)->hash_next = (hp)->hash_next; \ 236 0 stevel (ncp)->hash_prev = (ncache_t *)(hp); \ 237 0 stevel (hp)->hash_next->hash_prev = (ncp); \ 238 0 stevel (hp)->hash_next = (ncp); \ 239 0 stevel } 240 0 stevel 241 0 stevel /* 242 0 stevel * Remove entry from hash queue 243 0 stevel */ 244 0 stevel #define nc_rmhash(ncp) \ 245 0 stevel { \ 246 0 stevel (ncp)->hash_prev->hash_next = (ncp)->hash_next; \ 247 0 stevel (ncp)->hash_next->hash_prev = (ncp)->hash_prev; \ 248 0 stevel (ncp)->hash_prev = NULL; \ 249 0 stevel (ncp)->hash_next = NULL; \ 250 0 stevel } 251 0 stevel 252 0 stevel /* 253 0 stevel * Free an entry. 254 0 stevel */ 255 0 stevel #define dnlc_free(ncp) \ 256 0 stevel { \ 257 0 stevel kmem_free((ncp), sizeof (ncache_t) + (ncp)->namlen); \ 258 0 stevel atomic_add_32(&dnlc_nentries, -1); \ 259 0 stevel } 260 0 stevel 261 0 stevel 262 0 stevel /* 263 0 stevel * Cached directory info. 264 0 stevel * ====================== 265 0 stevel */ 266 0 stevel 267 0 stevel /* 268 0 stevel * Cached directory free space hash function. 269 0 stevel * Needs the free space handle and the dcp to get the hash table size 270 0 stevel * Returns the hash index. 271 0 stevel */ 272 0 stevel #define DDFHASH(handle, dcp) ((handle >> 2) & (dcp)->dc_fhash_mask) 273 0 stevel 274 0 stevel /* 275 0 stevel * Cached directory name entry hash function. 276 0 stevel * Uses the name and returns in the input arguments the hash and the name 277 0 stevel * length. 278 0 stevel */ 279 0 stevel #define DNLC_DIR_HASH(name, hash, namelen) \ 280 0 stevel { \ 281 0 stevel char Xc, *Xcp; \ 282 0 stevel hash = *name; \ 283 0 stevel for (Xcp = (name + 1); (Xc = *Xcp) != 0; Xcp++) \ 284 0 stevel hash = (hash << 4) + hash + Xc; \ 285 0 stevel ASSERT((Xcp - (name)) <= ((1 << NBBY) - 1)); \ 286 0 stevel namelen = Xcp - (name); \ 287 0 stevel } 288 0 stevel 289 0 stevel /* special dircache_t pointer to indicate error should be returned */ 290 0 stevel /* 291 0 stevel * The anchor directory cache pointer can contain 3 types of values, 292 0 stevel * 1) NULL: No directory cache 293 0 stevel * 2) DC_RET_LOW_MEM (-1): There was a directory cache that found to be 294 0 stevel * too big or a memory shortage occurred. This value remains in the 295 0 stevel * pointer until a dnlc_dir_start() which returns the a DNOMEM error. 296 0 stevel * This is kludgy but efficient and only visible in this source file. 297 0 stevel * 3) A valid cache pointer. 298 0 stevel */ 299 0 stevel #define DC_RET_LOW_MEM (dircache_t *)1 300 0 stevel #define VALID_DIR_CACHE(dcp) ((dircache_t *)(dcp) > DC_RET_LOW_MEM) 301 0 stevel 302 0 stevel /* Tunables */ 303 0 stevel uint_t dnlc_dir_enable = 1; /* disable caching directories by setting to 0 */ 304 0 stevel uint_t dnlc_dir_min_size = 40; /* min no of directory entries before caching */ 305 0 stevel uint_t dnlc_dir_max_size = UINT_MAX; /* ditto maximum */ 306 0 stevel uint_t dnlc_dir_hash_size_shift = 3; /* 8 entries per hash bucket */ 307 0 stevel uint_t dnlc_dir_min_reclaim = 350000; /* approx 1MB of dcentrys */ 308 0 stevel /* 309 0 stevel * dnlc_dir_hash_resize_shift determines when the hash tables 310 0 stevel * get re-adjusted due to growth or shrinkage 311 0 stevel * - currently 2 indicating that there can be at most 4 312 0 stevel * times or at least one quarter the number of entries 313 0 stevel * before hash table readjustment. Note that with 314 0 stevel * dnlc_dir_hash_size_shift above set at 3 this would 315 0 stevel * mean readjustment would occur if the average number 316 0 stevel * of entries went above 32 or below 2 317 0 stevel */ 318 0 stevel uint_t dnlc_dir_hash_resize_shift = 2; /* readjust rate */ 319 0 stevel 320 0 stevel static kmem_cache_t *dnlc_dir_space_cache; /* free space entry cache */ 321 0 stevel static dchead_t dc_head; /* anchor of cached directories */ 322 0 stevel 323 0 stevel /* Prototypes */ 324 0 stevel static ncache_t *dnlc_get(uchar_t namlen); 325 0 stevel static ncache_t *dnlc_search(vnode_t *dp, char *name, uchar_t namlen, int hash); 326 0 stevel static void dnlc_dir_reclaim(void *unused); 327 0 stevel static void dnlc_dir_abort(dircache_t *dcp); 328 0 stevel static void dnlc_dir_adjust_fhash(dircache_t *dcp); 329 0 stevel static void dnlc_dir_adjust_nhash(dircache_t *dcp); 330 1669 perrin static void do_dnlc_reduce_cache(void *); 331 0 stevel 332 0 stevel 333 0 stevel /* 334 0 stevel * Initialize the directory cache. 335 0 stevel */ 336 0 stevel void 337 0 stevel dnlc_init() 338 0 stevel { 339 0 stevel nc_hash_t *hp; 340 0 stevel kstat_t *ksp; 341 0 stevel int i; 342 0 stevel 343 0 stevel /* 344 0 stevel * Set up the size of the dnlc (ncsize) and its low water mark. 345 0 stevel */ 346 0 stevel if (ncsize == -1) { 347 0 stevel /* calculate a reasonable size for the low water */ 348 0 stevel dnlc_nentries_low_water = 4 * (v.v_proc + maxusers) + 320; 349 0 stevel ncsize = dnlc_nentries_low_water + 350 0 stevel (dnlc_nentries_low_water / dnlc_low_water_divisor); 351 0 stevel } else { 352 0 stevel /* don't change the user specified ncsize */ 353 0 stevel dnlc_nentries_low_water = 354 0 stevel ncsize - (ncsize / dnlc_low_water_divisor); 355 0 stevel } 356 0 stevel if (ncsize <= 0) { 357 0 stevel doingcache = 0; 358 0 stevel dnlc_dir_enable = 0; /* also disable directory caching */ 359 0 stevel ncsize = 0; 360 0 stevel cmn_err(CE_NOTE, "name cache (dnlc) disabled"); 361 0 stevel return; 362 0 stevel } 363 0 stevel dnlc_max_nentries = ncsize * 2; 364 1484 ek110237 ncsize_onepercent = ncsize / 100; 365 1669 perrin ncsize_min_percent = ncsize_onepercent * 3; 366 0 stevel 367 0 stevel /* 368 0 stevel * Initialise the hash table. 369 0 stevel * Compute hash size rounding to the next power of two. 370 0 stevel */ 371 0 stevel nc_hashsz = ncsize / nc_hashavelen; 372 0 stevel nc_hashsz = 1 << highbit(nc_hashsz); 373 0 stevel nc_hashmask = nc_hashsz - 1; 374 0 stevel nc_hash = kmem_zalloc(nc_hashsz * sizeof (*nc_hash), KM_SLEEP); 375 0 stevel for (i = 0; i < nc_hashsz; i++) { 376 0 stevel hp = (nc_hash_t *)&nc_hash[i]; 377 0 stevel mutex_init(&hp->hash_lock, NULL, MUTEX_DEFAULT, NULL); 378 0 stevel hp->hash_next = (ncache_t *)hp; 379 0 stevel hp->hash_prev = (ncache_t *)hp; 380 0 stevel } 381 0 stevel 382 0 stevel /* 383 0 stevel * Initialize rotors 384 0 stevel */ 385 0 stevel dnlc_free_rotor = dnlc_purge_fs1_rotor = &nc_hash[0]; 386 0 stevel 387 0 stevel /* 388 0 stevel * Set up the directory caching to use kmem_cache_alloc 389 0 stevel * for its free space entries so that we can get a callback 390 0 stevel * when the system is short on memory, to allow us to free 391 0 stevel * up some memory. we don't use the constructor/deconstructor 392 0 stevel * functions. 393 0 stevel */ 394 0 stevel dnlc_dir_space_cache = kmem_cache_create("dnlc_space_cache", 395 0 stevel sizeof (dcfree_t), 0, NULL, NULL, dnlc_dir_reclaim, NULL, 396 0 stevel NULL, 0); 397 0 stevel 398 0 stevel /* 399 0 stevel * Initialise the head of the cached directory structures 400 0 stevel */ 401 0 stevel mutex_init(&dc_head.dch_lock, NULL, MUTEX_DEFAULT, NULL); 402 0 stevel dc_head.dch_next = (dircache_t *)&dc_head; 403 0 stevel dc_head.dch_prev = (dircache_t *)&dc_head; 404 0 stevel 405 0 stevel /* 406 0 stevel * Initialise the reference count of the negative cache vnode to 1 407 0 stevel * so that it never goes away (VOP_INACTIVE isn't called on it). 408 0 stevel */ 409 0 stevel negative_cache_vnode.v_count = 1; 410 6712 tomee negative_cache_vnode.v_count_dnlc = 0; 411 0 stevel 412 0 stevel /* 413 0 stevel * Initialise kstats - both the old compatability raw kind and 414 0 stevel * the more extensive named stats. 415 0 stevel */ 416 0 stevel ksp = kstat_create("unix", 0, "ncstats", "misc", KSTAT_TYPE_RAW, 417 6712 tomee sizeof (struct ncstats), KSTAT_FLAG_VIRTUAL); 418 0 stevel if (ksp) { 419 0 stevel ksp->ks_data = (void *) &ncstats; 420 0 stevel kstat_install(ksp); 421 0 stevel } 422 0 stevel ksp = kstat_create("unix", 0, "dnlcstats", "misc", KSTAT_TYPE_NAMED, 423 0 stevel sizeof (ncs) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL); 424 0 stevel if (ksp) { 425 0 stevel ksp->ks_data = (void *) &ncs; 426 0 stevel kstat_install(ksp); 427 0 stevel } 428 0 stevel } 429 0 stevel 430 0 stevel /* 431 0 stevel * Add a name to the directory cache. 432 0 stevel */ 433 0 stevel void 434 0 stevel dnlc_enter(vnode_t *dp, char *name, vnode_t *vp) 435 0 stevel { 436 0 stevel ncache_t *ncp; 437 0 stevel nc_hash_t *hp; 438 0 stevel uchar_t namlen; 439 0 stevel int hash; 440 0 stevel 441 0 stevel TRACE_0(TR_FAC_NFS, TR_DNLC_ENTER_START, "dnlc_enter_start:"); 442 0 stevel 443 0 stevel if (!doingcache) { 444 0 stevel TRACE_2(TR_FAC_NFS, TR_DNLC_ENTER_END, 445 0 stevel "dnlc_enter_end:(%S) %d", "not caching", 0); 446 0 stevel return; 447 0 stevel } 448 0 stevel 449 0 stevel /* 450 0 stevel * Get a new dnlc entry. Assume the entry won't be in the cache 451 0 stevel * and initialize it now 452 0 stevel */ 453 0 stevel DNLCHASH(name, dp, hash, namlen); 454 0 stevel if ((ncp = dnlc_get(namlen)) == NULL) 455 0 stevel return; 456 0 stevel ncp->dp = dp; 457 6712 tomee VN_HOLD_DNLC(dp); 458 0 stevel ncp->vp = vp; 459 6712 tomee VN_HOLD_DNLC(vp); 460 0 stevel bcopy(name, ncp->name, namlen + 1); /* name and null */ 461 0 stevel ncp->hash = hash; 462 0 stevel hp = &nc_hash[hash & nc_hashmask]; 463 0 stevel 464 0 stevel mutex_enter(&hp->hash_lock); 465 0 stevel if (dnlc_search(dp, name, namlen, hash) != NULL) { 466 0 stevel mutex_exit(&hp->hash_lock); 467 0 stevel ncstats.dbl_enters++; 468 0 stevel ncs.ncs_dbl_enters.value.ui64++; 469 6712 tomee VN_RELE_DNLC(dp); 470 6712 tomee VN_RELE_DNLC(vp); 471 0 stevel dnlc_free(ncp); /* crfree done here */ 472 0 stevel TRACE_2(TR_FAC_NFS, TR_DNLC_ENTER_END, 473 6712 tomee "dnlc_enter_end:(%S) %d", "dbl enter", ncstats.dbl_enters); 474 0 stevel return; 475 0 stevel } 476 0 stevel /* 477 0 stevel * Insert back into the hash chain. 478 0 stevel */ 479 0 stevel nc_inshash(ncp, hp); 480 0 stevel mutex_exit(&hp->hash_lock); 481 0 stevel ncstats.enters++; 482 0 stevel ncs.ncs_enters.value.ui64++; 483 0 stevel TRACE_2(TR_FAC_NFS, TR_DNLC_ENTER_END, 484 0 stevel "dnlc_enter_end:(%S) %d", "done", ncstats.enters); 485 0 stevel } 486 0 stevel 487 0 stevel /* 488 0 stevel * Add a name to the directory cache. 489 0 stevel * 490 0 stevel * This function is basically identical with 491 0 stevel * dnlc_enter(). The difference is that when the 492 0 stevel * desired dnlc entry is found, the vnode in the 493 0 stevel * ncache is compared with the vnode passed in. 494 0 stevel * 495 0 stevel * If they are not equal then the ncache is 496 0 stevel * updated with the passed in vnode. Otherwise 497 0 stevel * it just frees up the newly allocated dnlc entry. 498 0 stevel */ 499 0 stevel void 500 0 stevel dnlc_update(vnode_t *dp, char *name, vnode_t *vp) 501 0 stevel { 502 0 stevel ncache_t *ncp; 503 0 stevel ncache_t *tcp; 504 0 stevel vnode_t *tvp; 505 0 stevel nc_hash_t *hp; 506 0 stevel int hash; 507 0 stevel uchar_t namlen; 508 0 stevel 509 0 stevel TRACE_0(TR_FAC_NFS, TR_DNLC_ENTER_START, "dnlc_update_start:"); 510 0 stevel 511 0 stevel if (!doingcache) { 512 0 stevel TRACE_2(TR_FAC_NFS, TR_DNLC_ENTER_END, 513 0 stevel "dnlc_update_end:(%S) %d", "not caching", 0); 514 0 stevel return; 515 0 stevel } 516 0 stevel 517 0 stevel /* 518 0 stevel * Get a new dnlc entry and initialize it now. 519 0 stevel * If we fail to get a new entry, call dnlc_remove() to purge 520 0 stevel * any existing dnlc entry including negative cache (DNLC_NO_VNODE) 521 0 stevel * entry. 522 0 stevel * Failure to clear an existing entry could result in false dnlc 523 0 stevel * lookup (negative/stale entry). 524 0 stevel */ 525 0 stevel DNLCHASH(name, dp, hash, namlen); 526 0 stevel if ((ncp = dnlc_get(namlen)) == NULL) { 527 0 stevel dnlc_remove(dp, name); 528 0 stevel return; 529 0 stevel } 530 0 stevel ncp->dp = dp; 531 6712 tomee VN_HOLD_DNLC(dp); 532 0 stevel ncp->vp = vp; 533 6712 tomee VN_HOLD_DNLC(vp); 534 0 stevel bcopy(name, ncp->name, namlen + 1); /* name and null */ 535 0 stevel ncp->hash = hash; 536 0 stevel hp = &nc_hash[hash & nc_hashmask]; 537 0 stevel 538 0 stevel mutex_enter(&hp->hash_lock); 539 0 stevel if ((tcp = dnlc_search(dp, name, namlen, hash)) != NULL) { 540 0 stevel if (tcp->vp != vp) { 541 0 stevel tvp = tcp->vp; 542 0 stevel tcp->vp = vp; 543 0 stevel mutex_exit(&hp->hash_lock); 544 6712 tomee VN_RELE_DNLC(tvp); 545 0 stevel ncstats.enters++; 546 0 stevel ncs.ncs_enters.value.ui64++; 547 0 stevel TRACE_2(TR_FAC_NFS, TR_DNLC_ENTER_END, 548 0 stevel "dnlc_update_end:(%S) %d", "done", ncstats.enters); 549 0 stevel } else { 550 0 stevel mutex_exit(&hp->hash_lock); 551 6712 tomee VN_RELE_DNLC(vp); 552 0 stevel ncstats.dbl_enters++; 553 0 stevel ncs.ncs_dbl_enters.value.ui64++; 554 0 stevel TRACE_2(TR_FAC_NFS, TR_DNLC_ENTER_END, 555 0 stevel "dnlc_update_end:(%S) %d", 556 0 stevel "dbl enter", ncstats.dbl_enters); 557 0 stevel } 558 6712 tomee VN_RELE_DNLC(dp); 559 0 stevel dnlc_free(ncp); /* crfree done here */ 560 0 stevel return; 561 0 stevel } 562 0 stevel /* 563 0 stevel * insert the new entry, since it is not in dnlc yet 564 0 stevel */ 565 0 stevel nc_inshash(ncp, hp); 566 0 stevel mutex_exit(&hp->hash_lock); 567 0 stevel ncstats.enters++; 568 0 stevel ncs.ncs_enters.value.ui64++; 569 0 stevel TRACE_2(TR_FAC_NFS, TR_DNLC_ENTER_END, 570 0 stevel "dnlc_update_end:(%S) %d", "done", ncstats.enters); 571 0 stevel } 572 0 stevel 573 0 stevel /* 574 0 stevel * Look up a name in the directory name cache. 575 0 stevel * 576 0 stevel * Return a doubly-held vnode if found: one hold so that it may 577 0 stevel * remain in the cache for other users, the other hold so that 578 0 stevel * the cache is not re-cycled and the identity of the vnode is 579 0 stevel * lost before the caller can use the vnode. 580 0 stevel */ 581 0 stevel vnode_t * 582 0 stevel dnlc_lookup(vnode_t *dp, char *name) 583 0 stevel { 584 0 stevel ncache_t *ncp; 585 0 stevel nc_hash_t *hp; 586 0 stevel vnode_t *vp; 587 0 stevel int hash, depth; 588 0 stevel uchar_t namlen; 589 0 stevel 590 0 stevel TRACE_2(TR_FAC_NFS, TR_DNLC_LOOKUP_START, 591 0 stevel "dnlc_lookup_start:dp %x name %s", dp, name); 592 0 stevel 593 0 stevel if (!doingcache) { 594 0 stevel TRACE_4(TR_FAC_NFS, TR_DNLC_LOOKUP_END, 595 0 stevel "dnlc_lookup_end:%S %d vp %x name %s", 596 0 stevel "not_caching", 0, NULL, name); 597 0 stevel return (NULL); 598 0 stevel } 599 0 stevel 600 0 stevel DNLCHASH(name, dp, hash, namlen); 601 0 stevel depth = 1; 602 0 stevel hp = &nc_hash[hash & nc_hashmask]; 603 0 stevel mutex_enter(&hp->hash_lock); 604 0 stevel 605 0 stevel for (ncp = hp->hash_next; ncp != (ncache_t *)hp; 606 0 stevel ncp = ncp->hash_next) { 607 0 stevel if (ncp->hash == hash && /* fast signature check */ 608 0 stevel ncp->dp == dp && 609 0 stevel ncp->namlen == namlen && 610 0 stevel bcmp(ncp->name, name, namlen) == 0) { 611 0 stevel /* 612 0 stevel * Move this entry to the head of its hash chain 613 0 stevel * if it's not already close. 614 0 stevel */ 615 0 stevel if (depth > NC_MOVETOFRONT) { 616 0 stevel ncache_t *next = ncp->hash_next; 617 0 stevel ncache_t *prev = ncp->hash_prev; 618 0 stevel 619 0 stevel prev->hash_next = next; 620 0 stevel next->hash_prev = prev; 621 0 stevel ncp->hash_next = next = hp->hash_next; 622 0 stevel ncp->hash_prev = (ncache_t *)hp; 623 0 stevel next->hash_prev = ncp; 624 0 stevel hp->hash_next = ncp; 625 0 stevel 626 0 stevel ncstats.move_to_front++; 627 0 stevel } 628 0 stevel 629 0 stevel /* 630 0 stevel * Put a hold on the vnode now so its identity 631 0 stevel * can't change before the caller has a chance to 632 0 stevel * put a hold on it. 633 0 stevel */ 634 0 stevel vp = ncp->vp; 635 6712 tomee VN_HOLD_CALLER(vp); /* VN_HOLD 1 of 2 in this file */ 636 0 stevel mutex_exit(&hp->hash_lock); 637 0 stevel ncstats.hits++; 638 0 stevel ncs.ncs_hits.value.ui64++; 639 0 stevel if (vp == DNLC_NO_VNODE) { 640 0 stevel ncs.ncs_neg_hits.value.ui64++; 641 0 stevel } 642 0 stevel TRACE_4(TR_FAC_NFS, TR_DNLC_LOOKUP_END, 643 6712 tomee "dnlc_lookup_end:%S %d vp %x name %s", "hit", 644 6712 tomee ncstats.hits, vp, name); 645 0 stevel return (vp); 646 0 stevel } 647 0 stevel depth++; 648 0 stevel } 649 0 stevel 650 0 stevel mutex_exit(&hp->hash_lock); 651 0 stevel ncstats.misses++; 652 0 stevel ncs.ncs_misses.value.ui64++; 653 0 stevel TRACE_4(TR_FAC_NFS, TR_DNLC_LOOKUP_END, 654 6712 tomee "dnlc_lookup_end:%S %d vp %x name %s", "miss", ncstats.misses, 655 0 stevel NULL, name); 656 0 stevel return (NULL); 657 0 stevel } 658 0 stevel 659 0 stevel /* 660 0 stevel * Remove an entry in the directory name cache. 661 0 stevel */ 662 0 stevel void 663 0 stevel dnlc_remove(vnode_t *dp, char *name) 664 0 stevel { 665 0 stevel ncache_t *ncp; 666 0 stevel nc_hash_t *hp; 667 0 stevel uchar_t namlen; 668 0 stevel int hash; 669 0 stevel 670 0 stevel if (!doingcache) 671 0 stevel return; 672 0 stevel DNLCHASH(name, dp, hash, namlen); 673 0 stevel hp = &nc_hash[hash & nc_hashmask]; 674 0 stevel 675 0 stevel mutex_enter(&hp->hash_lock); 676 0 stevel if (ncp = dnlc_search(dp, name, namlen, hash)) { 677 0 stevel /* 678 0 stevel * Free up the entry 679 0 stevel */ 680 0 stevel nc_rmhash(ncp); 681 0 stevel mutex_exit(&hp->hash_lock); 682 6712 tomee VN_RELE_DNLC(ncp->vp); 683 6712 tomee VN_RELE_DNLC(ncp->dp); 684 0 stevel dnlc_free(ncp); 685 0 stevel return; 686 0 stevel } 687 0 stevel mutex_exit(&hp->hash_lock); 688 0 stevel } 689 0 stevel 690 0 stevel /* 691 0 stevel * Purge the entire cache. 692 0 stevel */ 693 0 stevel void 694 0 stevel dnlc_purge() 695 0 stevel { 696 0 stevel nc_hash_t *nch; 697 0 stevel ncache_t *ncp; 698 0 stevel int index; 699 0 stevel int i; 700 0 stevel vnode_t *nc_rele[DNLC_MAX_RELE]; 701 0 stevel 702 0 stevel if (!doingcache) 703 0 stevel return; 704 0 stevel 705 0 stevel ncstats.purges++; 706 0 stevel ncs.ncs_purge_all.value.ui64++; 707 0 stevel 708 0 stevel for (nch = nc_hash; nch < &nc_hash[nc_hashsz]; nch++) { 709 0 stevel index = 0; 710 0 stevel mutex_enter(&nch->hash_lock); 711 0 stevel ncp = nch->hash_next; 712 0 stevel while (ncp != (ncache_t *)nch) { 713 0 stevel ncache_t *np; 714 0 stevel 715 0 stevel np = ncp->hash_next; 716 0 stevel nc_rele[index++] = ncp->vp; 717 0 stevel nc_rele[index++] = ncp->dp; 718 0 stevel 719 0 stevel nc_rmhash(ncp); 720 0 stevel dnlc_free(ncp); 721 0 stevel ncp = np; 722 0 stevel ncs.ncs_purge_total.value.ui64++; 723 0 stevel if (index == DNLC_MAX_RELE) 724 0 stevel break; 725 0 stevel } 726 0 stevel mutex_exit(&nch->hash_lock); 727 0 stevel 728 0 stevel /* Release holds on all the vnodes now that we have no locks */ 729 0 stevel for (i = 0; i < index; i++) { 730 6712 tomee VN_RELE_DNLC(nc_rele[i]); 731 0 stevel } 732 0 stevel if (ncp != (ncache_t *)nch) { 733 0 stevel nch--; /* Do current hash chain again */ 734 0 stevel } 735 0 stevel } 736 0 stevel } 737 0 stevel 738 0 stevel /* 739 6712 tomee * Purge any cache entries referencing a vnode. Exit as soon as the dnlc 740 6712 tomee * reference count goes to zero (the caller still holds a reference). 741 0 stevel */ 742 0 stevel void 743 0 stevel dnlc_purge_vp(vnode_t *vp) 744 0 stevel { 745 0 stevel nc_hash_t *nch; 746 0 stevel ncache_t *ncp; 747 0 stevel int index; 748 0 stevel vnode_t *nc_rele[DNLC_MAX_RELE]; 749 0 stevel 750 0 stevel ASSERT(vp->v_count > 0); 751 6712 tomee if (vp->v_count_dnlc == 0) { 752 0 stevel return; 753 0 stevel } 754 0 stevel 755 0 stevel if (!doingcache) 756 0 stevel return; 757 0 stevel 758 0 stevel ncstats.purges++; 759 0 stevel ncs.ncs_purge_vp.value.ui64++; 760 0 stevel 761 0 stevel for (nch = nc_hash; nch < &nc_hash[nc_hashsz]; nch++) { 762 0 stevel index = 0; 763 0 stevel mutex_enter(&nch->hash_lock); 764 0 stevel ncp = nch->hash_next; 765 0 stevel while (ncp != (ncache_t *)nch) { 766 0 stevel ncache_t *np; 767 0 stevel 768 0 stevel np = ncp->hash_next; 769 0 stevel if (ncp->dp == vp || ncp->vp == vp) { 770 0 stevel nc_rele[index++] = ncp->vp; 771 0 stevel nc_rele[index++] = ncp->dp; 772 0 stevel nc_rmhash(ncp); 773 0 stevel dnlc_free(ncp); 774 0 stevel ncs.ncs_purge_total.value.ui64++; 775 0 stevel if (index == DNLC_MAX_RELE) { 776 0 stevel ncp = np; 777 0 stevel break; 778 0 stevel } 779 0 stevel } 780 0 stevel ncp = np; 781 0 stevel } 782 0 stevel mutex_exit(&nch->hash_lock); 783 0 stevel 784 0 stevel /* Release holds on all the vnodes now that we have no locks */ 785 1257 ah89892 while (index) { 786 6712 tomee VN_RELE_DNLC(nc_rele[--index]); 787 1257 ah89892 } 788 0 stevel 789 6712 tomee if (vp->v_count_dnlc == 0) { 790 6712 tomee return; 791 0 stevel } 792 0 stevel 793 0 stevel if (ncp != (ncache_t *)nch) { 794 0 stevel nch--; /* Do current hash chain again */ 795 0 stevel } 796 0 stevel } 797 0 stevel } 798 0 stevel 799 0 stevel /* 800 0 stevel * Purge cache entries referencing a vfsp. Caller supplies a count 801 0 stevel * of entries to purge; up to that many will be freed. A count of 802 0 stevel * zero indicates that all such entries should be purged. Returns 803 0 stevel * the number of entries that were purged. 804 0 stevel */ 805 0 stevel int 806 0 stevel dnlc_purge_vfsp(vfs_t *vfsp, int count) 807 0 stevel { 808 0 stevel nc_hash_t *nch; 809 0 stevel ncache_t *ncp; 810 0 stevel int n = 0; 811 0 stevel int index; 812 0 stevel int i; 813 0 stevel vnode_t *nc_rele[DNLC_MAX_RELE]; 814 0 stevel 815 0 stevel if (!doingcache) 816 0 stevel return (0); 817 0 stevel 818 0 stevel ncstats.purges++; 819 0 stevel ncs.ncs_purge_vfs.value.ui64++; 820 0 stevel 821 0 stevel for (nch = nc_hash; nch < &nc_hash[nc_hashsz]; nch++) { 822 0 stevel index = 0; 823 0 stevel mutex_enter(&nch->hash_lock); 824 0 stevel ncp = nch->hash_next; 825 0 stevel while (ncp != (ncache_t *)nch) { 826 0 stevel ncache_t *np; 827 0 stevel 828 0 stevel np = ncp->hash_next; 829 0 stevel ASSERT(ncp->dp != NULL); 830 0 stevel ASSERT(ncp->vp != NULL); 831 0 stevel if ((ncp->dp->v_vfsp == vfsp) || 832 0 stevel (ncp->vp->v_vfsp == vfsp)) { 833 0 stevel n++; 834 0 stevel nc_rele[index++] = ncp->vp; 835 0 stevel nc_rele[index++] = ncp->dp; 836 0 stevel nc_rmhash(ncp); 837 0 stevel dnlc_free(ncp); 838 0 stevel ncs.ncs_purge_total.value.ui64++; 839 0 stevel if (index == DNLC_MAX_RELE) { 840 0 stevel ncp = np; 841 0 stevel break; 842 0 stevel } 843 0 stevel if (count != 0 && n >= count) { 844 0 stevel break; 845 0 stevel } 846 0 stevel } 847 0 stevel ncp = np; 848 0 stevel } 849 0 stevel mutex_exit(&nch->hash_lock); 850 0 stevel /* Release holds on all the vnodes now that we have no locks */ 851 0 stevel for (i = 0; i < index; i++) { 852 6712 tomee VN_RELE_DNLC(nc_rele[i]); 853 0 stevel } 854 0 stevel if (count != 0 && n >= count) { 855 0 stevel return (n); 856 0 stevel } 857 0 stevel if (ncp != (ncache_t *)nch) { 858 0 stevel nch--; /* Do current hash chain again */ 859 0 stevel } 860 0 stevel } 861 0 stevel return (n); 862 0 stevel } 863 0 stevel 864 0 stevel /* 865 0 stevel * Purge 1 entry from the dnlc that is part of the filesystem(s) 866 0 stevel * represented by 'vop'. The purpose of this routine is to allow 867 0 stevel * users of the dnlc to free a vnode that is being held by the dnlc. 868 0 stevel * 869 0 stevel * If we find a vnode that we release which will result in 870 0 stevel * freeing the underlying vnode (count was 1), return 1, 0 871 0 stevel * if no appropriate vnodes found. 872 0 stevel * 873 0 stevel * Note, vop is not the 'right' identifier for a filesystem. 874 0 stevel */ 875 0 stevel int 876 0 stevel dnlc_fs_purge1(vnodeops_t *vop) 877 0 stevel { 878 0 stevel nc_hash_t *end; 879 0 stevel nc_hash_t *hp; 880 0 stevel ncache_t *ncp; 881 0 stevel vnode_t *vp; 882 0 stevel 883 0 stevel if (!doingcache) 884 0 stevel return (0); 885 0 stevel 886 0 stevel ncs.ncs_purge_fs1.value.ui64++; 887 0 stevel 888 0 stevel /* 889 0 stevel * Scan the dnlc entries looking for a likely candidate. 890 0 stevel */ 891 0 stevel hp = end = dnlc_purge_fs1_rotor; 892 0 stevel 893 0 stevel do { 894 0 stevel if (++hp == &nc_hash[nc_hashsz]) 895 0 stevel hp = nc_hash; 896 0 stevel dnlc_purge_fs1_rotor = hp; 897 0 stevel if (hp->hash_next == (ncache_t *)hp) 898 0 stevel continue; 899 0 stevel mutex_enter(&hp->hash_lock); 900 0 stevel for (ncp = hp->hash_prev; 901 0 stevel ncp != (ncache_t *)hp; 902 0 stevel ncp = ncp->hash_prev) { 903 0 stevel vp = ncp->vp; 904 0 stevel if (!vn_has_cached_data(vp) && (vp->v_count == 1) && 905 0 stevel vn_matchops(vp, vop)) 906 0 stevel break; 907 0 stevel } 908 0 stevel if (ncp != (ncache_t *)hp) { 909 0 stevel nc_rmhash(ncp); 910 0 stevel mutex_exit(&hp->hash_lock); 911 6712 tomee VN_RELE_DNLC(ncp->dp); 912 6712 tomee VN_RELE_DNLC(vp) 913 0 stevel dnlc_free(ncp); 914 0 stevel ncs.ncs_purge_total.value.ui64++; 915 0 stevel return (1); 916 0 stevel } 917 0 stevel mutex_exit(&hp->hash_lock); 918 0 stevel } while (hp != end); 919 0 stevel return (0); 920 0 stevel } 921 0 stevel 922 0 stevel /* 923 0 stevel * Perform a reverse lookup in the DNLC. This will find the first occurrence of 924 0 stevel * the vnode. If successful, it will return the vnode of the parent, and the 925 0 stevel * name of the entry in the given buffer. If it cannot be found, or the buffer 926 0 stevel * is too small, then it will return NULL. Note that this is a highly 927 0 stevel * inefficient function, since the DNLC is constructed solely for forward 928 0 stevel * lookups. 929 0 stevel */ 930 0 stevel vnode_t * 931 0 stevel dnlc_reverse_lookup(vnode_t *vp, char *buf, size_t buflen) 932 0 stevel { 933 0 stevel nc_hash_t *nch; 934 0 stevel ncache_t *ncp; 935 0 stevel vnode_t *pvp; 936 0 stevel 937 0 stevel if (!doingcache) 938 0 stevel return (NULL); 939 0 stevel 940 0 stevel for (nch = nc_hash; nch < &nc_hash[nc_hashsz]; nch++) { 941 0 stevel mutex_enter(&nch->hash_lock); 942 0 stevel ncp = nch->hash_next; 943 0 stevel while (ncp != (ncache_t *)nch) { 944 0 stevel /* 945 0 stevel * We ignore '..' entries since it can create 946 0 stevel * confusion and infinite loops. 947 0 stevel */ 948 0 stevel if (ncp->vp == vp && !(ncp->namlen == 2 && 949 0 stevel 0 == bcmp(ncp->name, "..", 2)) && 950 0 stevel ncp->namlen < buflen) { 951 0 stevel bcopy(ncp->name, buf, ncp->namlen); 952 0 stevel buf[ncp->namlen] = '\0'; 953 0 stevel pvp = ncp->dp; 954 6712 tomee /* VN_HOLD 2 of 2 in this file */ 955 6712 tomee VN_HOLD_CALLER(pvp); 956 0 stevel mutex_exit(&nch->hash_lock); 957 0 stevel return (pvp); 958 0 stevel } 959 0 stevel ncp = ncp->hash_next; 960 0 stevel } 961 0 stevel mutex_exit(&nch->hash_lock); 962 0 stevel } 963 0 stevel 964 0 stevel return (NULL); 965 0 stevel } 966 0 stevel /* 967 0 stevel * Utility routine to search for a cache entry. Return the 968 0 stevel * ncache entry if found, NULL otherwise. 969 0 stevel */ 970 0 stevel static ncache_t * 971 0 stevel dnlc_search(vnode_t *dp, char *name, uchar_t namlen, int hash) 972 0 stevel { 973 0 stevel nc_hash_t *hp; 974 0 stevel ncache_t *ncp; 975 0 stevel 976 0 stevel hp = &nc_hash[hash & nc_hashmask]; 977 0 stevel 978 0 stevel for (ncp = hp->hash_next; ncp != (ncache_t *)hp; ncp = ncp->hash_next) { 979 0 stevel if (ncp->hash == hash && 980 0 stevel ncp->dp == dp && 981 0 stevel ncp->namlen == namlen && 982 0 stevel bcmp(ncp->name, name, namlen) == 0) 983 0 stevel return (ncp); 984 0 stevel } 985 0 stevel return (NULL); 986 0 stevel } 987 0 stevel 988 0 stevel #if ((1 << NBBY) - 1) < (MAXNAMELEN - 1) 989 0 stevel #error ncache_t name length representation is too small 990 0 stevel #endif 991 0 stevel 992 1669 perrin void 993 1669 perrin dnlc_reduce_cache(void *reduce_percent) 994 1669 perrin { 995 1669 perrin if (dnlc_reduce_idle && (dnlc_nentries >= ncsize || reduce_percent)) { 996 1669 perrin dnlc_reduce_idle = 0; 997 1669 perrin if ((taskq_dispatch(system_taskq, do_dnlc_reduce_cache, 998 1669 perrin reduce_percent, TQ_NOSLEEP)) == NULL) 999 1669 perrin dnlc_reduce_idle = 1; 1000 1669 perrin } 1001 1669 perrin } 1002 1669 perrin 1003 0 stevel /* 1004 0 stevel * Get a new name cache entry. 1005 0 stevel * If the dnlc_reduce_cache() taskq isn't keeping up with demand, or memory 1006 0 stevel * is short then just return NULL. If we're over ncsize then kick off a 1007 0 stevel * thread to free some in use entries down to dnlc_nentries_low_water. 1008 0 stevel * Caller must initialise all fields except namlen. 1009 0 stevel * Component names are defined to be less than MAXNAMELEN 1010 0 stevel * which includes a null. 1011 0 stevel */ 1012 0 stevel static ncache_t * 1013 0 stevel dnlc_get(uchar_t namlen) 1014 0 stevel { 1015 0 stevel ncache_t *ncp; 1016 0 stevel 1017 0 stevel if (dnlc_nentries > dnlc_max_nentries) { 1018 0 stevel dnlc_max_nentries_cnt++; /* keep a statistic */ 1019 0 stevel return (NULL); 1020 0 stevel } 1021 0 stevel ncp = kmem_alloc(sizeof (ncache_t) + namlen, KM_NOSLEEP); 1022 0 stevel if (ncp == NULL) { 1023 0 stevel return (NULL); 1024 0 stevel } 1025 0 stevel ncp->namlen = namlen; 1026 0 stevel atomic_add_32(&dnlc_nentries, 1); 1027 1669 perrin dnlc_reduce_cache(NULL); 1028 0 stevel return (ncp); 1029 0 stevel } 1030 0 stevel 1031 0 stevel /* 1032 0 stevel * Taskq routine to free up name cache entries to reduce the 1033 1484 ek110237 * cache size to the low water mark if "reduce_percent" is not provided. 1034 1484 ek110237 * If "reduce_percent" is provided, reduce cache size by 1035 1484 ek110237 * (ncsize_onepercent * reduce_percent). 1036 0 stevel */ 1037 0 stevel /*ARGSUSED*/ 1038 1669 perrin static void 1039 1669 perrin do_dnlc_reduce_cache(void *reduce_percent) 1040 0 stevel { 1041 1669 perrin nc_hash_t *hp = dnlc_free_rotor, *start_hp = hp; 1042 0 stevel vnode_t *vp; 1043 0 stevel ncache_t *ncp; 1044 0 stevel int cnt; 1045 1484 ek110237 uint_t low_water = dnlc_nentries_low_water; 1046 1484 ek110237 1047 1484 ek110237 if (reduce_percent) { 1048 1484 ek110237 uint_t reduce_cnt; 1049 1484 ek110237 1050 1669 perrin /* 1051 1669 perrin * Never try to reduce the current number 1052 1669 perrin * of cache entries below 3% of ncsize. 1053 1669 perrin */ 1054 1669 perrin if (dnlc_nentries <= ncsize_min_percent) { 1055 1669 perrin dnlc_reduce_idle = 1; 1056 1669 perrin return; 1057 1669 perrin } 1058 1505 ek110237 reduce_cnt = ncsize_onepercent * 1059 1505 ek110237 (uint_t)(uintptr_t)reduce_percent; 1060 1669 perrin 1061 1669 perrin if (reduce_cnt > dnlc_nentries || 1062 1669 perrin dnlc_nentries - reduce_cnt < ncsize_min_percent) 1063 1669 perrin low_water = ncsize_min_percent; 1064 1484 ek110237 else 1065 1484 ek110237 low_water = dnlc_nentries - reduce_cnt; 1066 1484 ek110237 } 1067 0 stevel 1068 0 stevel do { 1069 0 stevel /* 1070 1669 perrin * Find the first non empty hash queue without locking. 1071 1669 perrin * Only look at each hash queue once to avoid an infinite loop. 1072 0 stevel */ 1073 0 stevel do { 1074 1669 perrin if (++hp == &nc_hash[nc_hashsz]) 1075 0 stevel hp = nc_hash; 1076 1669 perrin } while (hp->hash_next == (ncache_t *)hp && hp != start_hp); 1077 1669 perrin 1078 1669 perrin /* return if all hash queues are empty. */ 1079 1669 perrin if (hp->hash_next == (ncache_t *)hp) { 1080 1669 perrin dnlc_reduce_idle = 1; 1081 1669 perrin return; 1082 1669 perrin } 1083 0 stevel 1084 0 stevel mutex_enter(&hp->hash_lock); 1085 0 stevel for (cnt = 0, ncp = hp->hash_prev; ncp != (ncache_t *)hp; 1086 0 stevel ncp = ncp->hash_prev, cnt++) { 1087 0 stevel vp = ncp->vp; 1088 0 stevel /* 1089 0 stevel * A name cache entry with a reference count 1090 0 stevel * of one is only referenced by the dnlc. 1091 0 stevel * Also negative cache entries are purged first. 1092 0 stevel */ 1093 0 stevel if (!vn_has_cached_data(vp) && 1094 0 stevel ((vp->v_count == 1) || (vp == DNLC_NO_VNODE))) { 1095 0 stevel ncs.ncs_pick_heur.value.ui64++; 1096 0 stevel goto found; 1097 0 stevel } 1098 0 stevel /* 1099 0 stevel * Remove from the end of the chain if the 1100 0 stevel * chain is too long 1101 0 stevel */ 1102 0 stevel if (cnt > dnlc_long_chain) { 1103 0 stevel ncp = hp->hash_prev; 1104 0 stevel ncs.ncs_pick_last.value.ui64++; 1105 0 stevel vp = ncp->vp; 1106 0 stevel goto found; 1107 0 stevel } 1108 0 stevel } 1109 0 stevel /* check for race and continue */ 1110 0 stevel if (hp->hash_next == (ncache_t *)hp) { 1111 0 stevel mutex_exit(&hp->hash_lock); 1112 0 stevel continue; 1113 0 stevel } 1114 0 stevel 1115 0 stevel ncp = hp->hash_prev; /* pick the last one in the hash queue */ 1116 0 stevel ncs.ncs_pick_last.value.ui64++; 1117 0 stevel vp = ncp->vp; 1118 0 stevel found: 1119 0 stevel /* 1120 0 stevel * Remove from hash chain. 1121 0 stevel */ 1122 0 stevel nc_rmhash(ncp); 1123 0 stevel mutex_exit(&hp->hash_lock); 1124 6712 tomee VN_RELE_DNLC(vp); 1125 6712 tomee VN_RELE_DNLC(ncp->dp); 1126 0 stevel dnlc_free(ncp); 1127 1484 ek110237 } while (dnlc_nentries > low_water); 1128 0 stevel 1129 0 stevel dnlc_free_rotor = hp; 1130 0 stevel dnlc_reduce_idle = 1; 1131 0 stevel } 1132 0 stevel 1133 0 stevel /* 1134 0 stevel * Directory caching routines 1135 0 stevel * ========================== 1136 0 stevel * 1137 0 stevel * See dnlc.h for details of the interfaces below. 1138 0 stevel */ 1139 0 stevel 1140 0 stevel /* 1141 0 stevel * Lookup up an entry in a complete or partial directory cache. 1142 0 stevel */ 1143 0 stevel dcret_t 1144 0 stevel dnlc_dir_lookup(dcanchor_t *dcap, char *name, uint64_t *handle) 1145 0 stevel { 1146 0 stevel dircache_t *dcp; 1147 0 stevel dcentry_t *dep; 1148 0 stevel int hash; 1149 0 stevel int ret; 1150 0 stevel uchar_t namlen; 1151 0 stevel 1152 0 stevel /* 1153 0 stevel * can test without lock as we are only a cache 1154 0 stevel */ 1155 0 stevel if (!VALID_DIR_CACHE(dcap->dca_dircache)) { 1156 0 stevel ncs.ncs_dir_misses.value.ui64++; 1157 0 stevel return (DNOCACHE); 1158 0 stevel } 1159 0 stevel 1160 0 stevel if (!dnlc_dir_enable) { 1161 0 stevel return (DNOCACHE); 1162 0 stevel } 1163 0 stevel 1164 0 stevel mutex_enter(&dcap->dca_lock); 1165 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1166 0 stevel if (VALID_DIR_CACHE(dcp)) { 1167 11066 rafael dcp->dc_actime = ddi_get_lbolt64(); 1168 0 stevel DNLC_DIR_HASH(name, hash, namlen); 1169 0 stevel dep = dcp->dc_namehash[hash & dcp->dc_nhash_mask]; 1170 0 stevel while (dep != NULL) { 1171 0 stevel if ((dep->de_hash == hash) && 1172 0 stevel (namlen == dep->de_namelen) && 1173 0 stevel bcmp(dep->de_name, name, namlen) == 0) { 1174 0 stevel *handle = dep->de_handle; 1175 0 stevel mutex_exit(&dcap->dca_lock); 1176 0 stevel ncs.ncs_dir_hits.value.ui64++; 1177 0 stevel return (DFOUND); 1178 0 stevel } 1179 0 stevel dep = dep->de_next; 1180 0 stevel } 1181 0 stevel if (dcp->dc_complete) { 1182 0 stevel ret = DNOENT; 1183 0 stevel } else { 1184 0 stevel ret = DNOCACHE; 1185 0 stevel } 1186 0 stevel mutex_exit(&dcap->dca_lock); 1187 0 stevel return (ret); 1188 0 stevel } else { 1189 0 stevel mutex_exit(&dcap->dca_lock); 1190 0 stevel ncs.ncs_dir_misses.value.ui64++; 1191 0 stevel return (DNOCACHE); 1192 0 stevel } 1193 0 stevel } 1194 0 stevel 1195 0 stevel /* 1196 0 stevel * Start a new directory cache. An estimate of the number of 1197 0 stevel * entries is provided to as a quick check to ensure the directory 1198 0 stevel * is cacheable. 1199 0 stevel */ 1200 0 stevel dcret_t 1201 0 stevel dnlc_dir_start(dcanchor_t *dcap, uint_t num_entries) 1202 0 stevel { 1203 0 stevel dircache_t *dcp; 1204 0 stevel 1205 0 stevel if (!dnlc_dir_enable || 1206 0 stevel (num_entries < dnlc_dir_min_size)) { 1207 0 stevel return (DNOCACHE); 1208 0 stevel } 1209 0 stevel 1210 0 stevel if (num_entries > dnlc_dir_max_size) { 1211 0 stevel return (DTOOBIG); 1212 0 stevel } 1213 0 stevel 1214 0 stevel mutex_enter(&dc_head.dch_lock); 1215 0 stevel mutex_enter(&dcap->dca_lock); 1216 0 stevel 1217 0 stevel if (dcap->dca_dircache == DC_RET_LOW_MEM) { 1218 0 stevel dcap->dca_dircache = NULL; 1219 0 stevel mutex_exit(&dcap->dca_lock); 1220 0 stevel mutex_exit(&dc_head.dch_lock); 1221 0 stevel return (DNOMEM); 1222 0 stevel } 1223 0 stevel 1224 0 stevel /* 1225 0 stevel * Check if there's currently a cache. 1226 0 stevel * This probably only occurs on a race. 1227 0 stevel */ 1228 0 stevel if (dcap->dca_dircache != NULL) { 1229 0 stevel mutex_exit(&dcap->dca_lock); 1230 0 stevel mutex_exit(&dc_head.dch_lock); 1231 0 stevel return (DNOCACHE); 1232 0 stevel } 1233 0 stevel 1234 0 stevel /* 1235 0 stevel * Allocate the dircache struct, entry and free space hash tables. 1236 0 stevel * These tables are initially just one entry but dynamically resize 1237 0 stevel * when entries and free space are added or removed. 1238 0 stevel */ 1239 0 stevel if ((dcp = kmem_zalloc(sizeof (dircache_t), KM_NOSLEEP)) == NULL) { 1240 0 stevel goto error; 1241 0 stevel } 1242 0 stevel if ((dcp->dc_namehash = kmem_zalloc(sizeof (dcentry_t *), 1243 0 stevel KM_NOSLEEP)) == NULL) { 1244 0 stevel goto error; 1245 0 stevel } 1246 0 stevel if ((dcp->dc_freehash = kmem_zalloc(sizeof (dcfree_t *), 1247 0 stevel KM_NOSLEEP)) == NULL) { 1248 0 stevel goto error; 1249 0 stevel } 1250 0 stevel 1251 0 stevel dcp->dc_anchor = dcap; /* set back pointer to anchor */ 1252 0 stevel dcap->dca_dircache = dcp; 1253 0 stevel 1254 0 stevel /* add into head of global chain */ 1255 0 stevel dcp->dc_next = dc_head.dch_next; 1256 0 stevel dcp->dc_prev = (dircache_t *)&dc_head; 1257 0 stevel dcp->dc_next->dc_prev = dcp; 1258 0 stevel dc_head.dch_next = dcp; 1259 0 stevel 1260 0 stevel mutex_exit(&dcap->dca_lock); 1261 0 stevel mutex_exit(&dc_head.dch_lock); 1262 0 stevel ncs.ncs_cur_dirs.value.ui64++; 1263 0 stevel ncs.ncs_dirs_cached.value.ui64++; 1264 0 stevel return (DOK); 1265 0 stevel error: 1266 0 stevel if (dcp != NULL) { 1267 0 stevel if (dcp->dc_namehash) { 1268 0 stevel kmem_free(dcp->dc_namehash, sizeof (dcentry_t *)); 1269 0 stevel } 1270 0 stevel kmem_free(dcp, sizeof (dircache_t)); 1271 0 stevel } 1272 0 stevel /* 1273 0 stevel * Must also kmem_free dcp->dc_freehash if more error cases are added 1274 0 stevel */ 1275 0 stevel mutex_exit(&dcap->dca_lock); 1276 0 stevel mutex_exit(&dc_head.dch_lock); 1277 0 stevel ncs.ncs_dir_start_nm.value.ui64++; 1278 0 stevel return (DNOCACHE); 1279 0 stevel } 1280 0 stevel 1281 0 stevel /* 1282 0 stevel * Add a directopry entry to a partial or complete directory cache. 1283 0 stevel */ 1284 0 stevel dcret_t 1285 0 stevel dnlc_dir_add_entry(dcanchor_t *dcap, char *name, uint64_t handle) 1286 0 stevel { 1287 0 stevel dircache_t *dcp; 1288 0 stevel dcentry_t **hp, *dep; 1289 0 stevel int hash; 1290 0 stevel uint_t capacity; 1291 0 stevel uchar_t namlen; 1292 0 stevel 1293 0 stevel /* 1294 0 stevel * Allocate the dcentry struct, including the variable 1295 0 stevel * size name. Note, the null terminator is not copied. 1296 0 stevel * 1297 0 stevel * We do this outside the lock to avoid possible deadlock if 1298 0 stevel * dnlc_dir_reclaim() is called as a result of memory shortage. 1299 0 stevel */ 1300 0 stevel DNLC_DIR_HASH(name, hash, namlen); 1301 0 stevel dep = kmem_alloc(sizeof (dcentry_t) - 1 + namlen, KM_NOSLEEP); 1302 0 stevel if (dep == NULL) { 1303 0 stevel #ifdef DEBUG 1304 0 stevel /* 1305 0 stevel * The kmem allocator generates random failures for 1306 0 stevel * KM_NOSLEEP calls (see KMEM_RANDOM_ALLOCATION_FAILURE) 1307 0 stevel * So try again before we blow away a perfectly good cache. 1308 0 stevel * This is done not to cover an error but purely for 1309 0 stevel * performance running a debug kernel. 1310 0 stevel * This random error only occurs in debug mode. 1311 0 stevel */ 1312 0 stevel dep = kmem_alloc(sizeof (dcentry_t) - 1 + namlen, KM_NOSLEEP); 1313 0 stevel if (dep != NULL) 1314 0 stevel goto ok; 1315 0 stevel #endif 1316 0 stevel ncs.ncs_dir_add_nm.value.ui64++; 1317 0 stevel /* 1318 0 stevel * Free a directory cache. This may be the one we are 1319 0 stevel * called with. 1320 0 stevel */ 1321 0 stevel dnlc_dir_reclaim(NULL); 1322 0 stevel dep = kmem_alloc(sizeof (dcentry_t) - 1 + namlen, KM_NOSLEEP); 1323 0 stevel if (dep == NULL) { 1324 0 stevel /* 1325 0 stevel * still no memory, better delete this cache 1326 0 stevel */ 1327 0 stevel mutex_enter(&dcap->dca_lock); 1328 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1329 0 stevel if (VALID_DIR_CACHE(dcp)) { 1330 0 stevel dnlc_dir_abort(dcp); 1331 0 stevel dcap->dca_dircache = DC_RET_LOW_MEM; 1332 0 stevel } 1333 0 stevel mutex_exit(&dcap->dca_lock); 1334 0 stevel ncs.ncs_dir_addabort.value.ui64++; 1335 0 stevel return (DNOCACHE); 1336 0 stevel } 1337 0 stevel /* 1338 0 stevel * fall through as if the 1st kmem_alloc had worked 1339 0 stevel */ 1340 0 stevel } 1341 0 stevel #ifdef DEBUG 1342 0 stevel ok: 1343 0 stevel #endif 1344 0 stevel mutex_enter(&dcap->dca_lock); 1345 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1346 0 stevel if (VALID_DIR_CACHE(dcp)) { 1347 0 stevel /* 1348 0 stevel * If the total number of entries goes above the max 1349 0 stevel * then free this cache 1350 0 stevel */ 1351 0 stevel if ((dcp->dc_num_entries + dcp->dc_num_free) > 1352 6712 tomee dnlc_dir_max_size) { 1353 0 stevel mutex_exit(&dcap->dca_lock); 1354 0 stevel dnlc_dir_purge(dcap); 1355 0 stevel kmem_free(dep, sizeof (dcentry_t) - 1 + namlen); 1356 0 stevel ncs.ncs_dir_add_max.value.ui64++; 1357 0 stevel return (DTOOBIG); 1358 0 stevel } 1359 0 stevel dcp->dc_num_entries++; 1360 0 stevel capacity = (dcp->dc_nhash_mask + 1) << dnlc_dir_hash_size_shift; 1361 0 stevel if (dcp->dc_num_entries >= 1362 0 stevel (capacity << dnlc_dir_hash_resize_shift)) { 1363 0 stevel dnlc_dir_adjust_nhash(dcp); 1364 0 stevel } 1365 0 stevel hp = &dcp->dc_namehash[hash & dcp->dc_nhash_mask]; 1366 0 stevel 1367 0 stevel /* 1368 0 stevel * Initialise and chain in new entry 1369 0 stevel */ 1370 0 stevel dep->de_handle = handle; 1371 0 stevel dep->de_hash = hash; 1372 0 stevel /* 1373 0 stevel * Note de_namelen is a uchar_t to conserve space 1374 0 stevel * and alignment padding. The max length of any 1375 0 stevel * pathname component is defined as MAXNAMELEN 1376 0 stevel * which is 256 (including the terminating null). 1377 0 stevel * So provided this doesn't change, we don't include the null, 1378 0 stevel * we always use bcmp to compare strings, and we don't 1379 0 stevel * start storing full names, then we are ok. 1380 0 stevel * The space savings is worth it. 1381 0 stevel */ 1382 0 stevel dep->de_namelen = namlen; 1383 0 stevel bcopy(name, dep->de_name, namlen); 1384 0 stevel dep->de_next = *hp; 1385 0 stevel *hp = dep; 1386 11066 rafael dcp->dc_actime = ddi_get_lbolt64(); 1387 0 stevel mutex_exit(&dcap->dca_lock); 1388 0 stevel ncs.ncs_dir_num_ents.value.ui64++; 1389 0 stevel return (DOK); 1390 0 stevel } else { 1391 0 stevel mutex_exit(&dcap->dca_lock); 1392 0 stevel kmem_free(dep, sizeof (dcentry_t) - 1 + namlen); 1393 0 stevel return (DNOCACHE); 1394 0 stevel } 1395 0 stevel } 1396 0 stevel 1397 0 stevel /* 1398 0 stevel * Add free space to a partial or complete directory cache. 1399 0 stevel */ 1400 0 stevel dcret_t 1401 0 stevel dnlc_dir_add_space(dcanchor_t *dcap, uint_t len, uint64_t handle) 1402 0 stevel { 1403 0 stevel dircache_t *dcp; 1404 0 stevel dcfree_t *dfp, **hp; 1405 0 stevel uint_t capacity; 1406 0 stevel 1407 0 stevel /* 1408 0 stevel * We kmem_alloc outside the lock to avoid possible deadlock if 1409 0 stevel * dnlc_dir_reclaim() is called as a result of memory shortage. 1410 0 stevel */ 1411 0 stevel dfp = kmem_cache_alloc(dnlc_dir_space_cache, KM_NOSLEEP); 1412 0 stevel if (dfp == NULL) { 1413 0 stevel #ifdef DEBUG 1414 0 stevel /* 1415 0 stevel * The kmem allocator generates random failures for 1416 0 stevel * KM_NOSLEEP calls (see KMEM_RANDOM_ALLOCATION_FAILURE) 1417 0 stevel * So try again before we blow away a perfectly good cache. 1418 0 stevel * This random error only occurs in debug mode 1419 0 stevel */ 1420 0 stevel dfp = kmem_cache_alloc(dnlc_dir_space_cache, KM_NOSLEEP); 1421 0 stevel if (dfp != NULL) 1422 0 stevel goto ok; 1423 0 stevel #endif 1424 0 stevel ncs.ncs_dir_add_nm.value.ui64++; 1425 0 stevel /* 1426 0 stevel * Free a directory cache. This may be the one we are 1427 0 stevel * called with. 1428 0 stevel */ 1429 0 stevel dnlc_dir_reclaim(NULL); 1430 0 stevel dfp = kmem_cache_alloc(dnlc_dir_space_cache, KM_NOSLEEP); 1431 0 stevel if (dfp == NULL) { 1432 0 stevel /* 1433 0 stevel * still no memory, better delete this cache 1434 0 stevel */ 1435 0 stevel mutex_enter(&dcap->dca_lock); 1436 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1437 0 stevel if (VALID_DIR_CACHE(dcp)) { 1438 0 stevel dnlc_dir_abort(dcp); 1439 0 stevel dcap->dca_dircache = DC_RET_LOW_MEM; 1440 0 stevel } 1441 0 stevel mutex_exit(&dcap->dca_lock); 1442 0 stevel ncs.ncs_dir_addabort.value.ui64++; 1443 0 stevel return (DNOCACHE); 1444 0 stevel } 1445 0 stevel /* 1446 0 stevel * fall through as if the 1st kmem_alloc had worked 1447 0 stevel */ 1448 0 stevel } 1449 0 stevel 1450 0 stevel #ifdef DEBUG 1451 0 stevel ok: 1452 0 stevel #endif 1453 0 stevel mutex_enter(&dcap->dca_lock); 1454 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1455 0 stevel if (VALID_DIR_CACHE(dcp)) { 1456 0 stevel if ((dcp->dc_num_entries + dcp->dc_num_free) > 1457 6712 tomee dnlc_dir_max_size) { 1458 0 stevel mutex_exit(&dcap->dca_lock); 1459 0 stevel dnlc_dir_purge(dcap); 1460 0 stevel kmem_cache_free(dnlc_dir_space_cache, dfp); 1461 0 stevel ncs.ncs_dir_add_max.value.ui64++; 1462 0 stevel return (DTOOBIG); 1463 0 stevel } 1464 0 stevel dcp->dc_num_free++; 1465 0 stevel capacity = (dcp->dc_fhash_mask + 1) << dnlc_dir_hash_size_shift; 1466 0 stevel if (dcp->dc_num_free >= 1467 0 stevel (capacity << dnlc_dir_hash_resize_shift)) { 1468 0 stevel dnlc_dir_adjust_fhash(dcp); 1469 0 stevel } 1470 0 stevel /* 1471 0 stevel * Initialise and chain a new entry 1472 0 stevel */ 1473 0 stevel dfp->df_handle = handle; 1474 0 stevel dfp->df_len = len; 1475 11066 rafael dcp->dc_actime = ddi_get_lbolt64(); 1476 0 stevel hp = &(dcp->dc_freehash[DDFHASH(handle, dcp)]); 1477 0 stevel dfp->df_next = *hp; 1478 0 stevel *hp = dfp; 1479 0 stevel mutex_exit(&dcap->dca_lock); 1480 0 stevel ncs.ncs_dir_num_ents.value.ui64++; 1481 0 stevel return (DOK); 1482 0 stevel } else { 1483 0 stevel mutex_exit(&dcap->dca_lock); 1484 0 stevel kmem_cache_free(dnlc_dir_space_cache, dfp); 1485 0 stevel return (DNOCACHE); 1486 0 stevel } 1487 0 stevel } 1488 0 stevel 1489 0 stevel /* 1490 0 stevel * Mark a directory cache as complete. 1491 0 stevel */ 1492 0 stevel void 1493 0 stevel dnlc_dir_complete(dcanchor_t *dcap) 1494 0 stevel { 1495 0 stevel dircache_t *dcp; 1496 0 stevel 1497 0 stevel mutex_enter(&dcap->dca_lock); 1498 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1499 0 stevel if (VALID_DIR_CACHE(dcp)) { 1500 0 stevel dcp->dc_complete = B_TRUE; 1501 0 stevel } 1502 0 stevel mutex_exit(&dcap->dca_lock); 1503 0 stevel } 1504 0 stevel 1505 0 stevel /* 1506 0 stevel * Internal routine to delete a partial or full directory cache. 1507 0 stevel * No additional locking needed. 1508 0 stevel */ 1509 0 stevel static void 1510 0 stevel dnlc_dir_abort(dircache_t *dcp) 1511 0 stevel { 1512 0 stevel dcentry_t *dep, *nhp; 1513 0 stevel dcfree_t *fep, *fhp; 1514 0 stevel uint_t nhtsize = dcp->dc_nhash_mask + 1; /* name hash table size */ 1515 0 stevel uint_t fhtsize = dcp->dc_fhash_mask + 1; /* free hash table size */ 1516 0 stevel uint_t i; 1517 0 stevel 1518 0 stevel /* 1519 0 stevel * Free up the cached name entries and hash table 1520 0 stevel */ 1521 0 stevel for (i = 0; i < nhtsize; i++) { /* for each hash bucket */ 1522 0 stevel nhp = dcp->dc_namehash[i]; 1523 0 stevel while (nhp != NULL) { /* for each chained entry */ 1524 0 stevel dep = nhp->de_next; 1525 0 stevel kmem_free(nhp, sizeof (dcentry_t) - 1 + 1526 0 stevel nhp->de_namelen); 1527 0 stevel nhp = dep; 1528 0 stevel } 1529 0 stevel } 1530 0 stevel kmem_free(dcp->dc_namehash, sizeof (dcentry_t *) * nhtsize); 1531 0 stevel 1532 0 stevel /* 1533 0 stevel * Free up the free space entries and hash table 1534 0 stevel */ 1535 0 stevel for (i = 0; i < fhtsize; i++) { /* for each hash bucket */ 1536 0 stevel fhp = dcp->dc_freehash[i]; 1537 0 stevel while (fhp != NULL) { /* for each chained entry */ 1538 0 stevel fep = fhp->df_next; 1539 0 stevel kmem_cache_free(dnlc_dir_space_cache, fhp); 1540 0 stevel fhp = fep; 1541 0 stevel } 1542 0 stevel } 1543 0 stevel kmem_free(dcp->dc_freehash, sizeof (dcfree_t *) * fhtsize); 1544 0 stevel 1545 0 stevel /* 1546 0 stevel * Finally free the directory cache structure itself 1547 0 stevel */ 1548 0 stevel ncs.ncs_dir_num_ents.value.ui64 -= (dcp->dc_num_entries + 1549 0 stevel dcp->dc_num_free); 1550 0 stevel kmem_free(dcp, sizeof (dircache_t)); 1551 0 stevel ncs.ncs_cur_dirs.value.ui64--; 1552 0 stevel } 1553 0 stevel 1554 0 stevel /* 1555 0 stevel * Remove a partial or complete directory cache 1556 0 stevel */ 1557 0 stevel void 1558 0 stevel dnlc_dir_purge(dcanchor_t *dcap) 1559 0 stevel { 1560 0 stevel dircache_t *dcp; 1561 0 stevel 1562 0 stevel mutex_enter(&dc_head.dch_lock); 1563 0 stevel mutex_enter(&dcap->dca_lock); 1564 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1565 0 stevel if (!VALID_DIR_CACHE(dcp)) { 1566 0 stevel mutex_exit(&dcap->dca_lock); 1567 0 stevel mutex_exit(&dc_head.dch_lock); 1568 0 stevel return; 1569 0 stevel } 1570 0 stevel dcap->dca_dircache = NULL; 1571 0 stevel /* 1572 0 stevel * Unchain from global list 1573 0 stevel */ 1574 0 stevel dcp->dc_prev->dc_next = dcp->dc_next; 1575 0 stevel dcp->dc_next->dc_prev = dcp->dc_prev; 1576 0 stevel mutex_exit(&dcap->dca_lock); 1577 0 stevel mutex_exit(&dc_head.dch_lock); 1578 0 stevel dnlc_dir_abort(dcp); 1579 0 stevel } 1580 0 stevel 1581 0 stevel /* 1582 0 stevel * Remove an entry from a complete or partial directory cache. 1583 0 stevel * Return the handle if it's non null. 1584 0 stevel */ 1585 0 stevel dcret_t 1586 0 stevel dnlc_dir_rem_entry(dcanchor_t *dcap, char *name, uint64_t *handlep) 1587 0 stevel { 1588 0 stevel dircache_t *dcp; 1589 0 stevel dcentry_t **prevpp, *te; 1590 0 stevel uint_t capacity; 1591 0 stevel int hash; 1592 0 stevel int ret; 1593 0 stevel uchar_t namlen; 1594 0 stevel 1595 0 stevel if (!dnlc_dir_enable) { 1596 0 stevel return (DNOCACHE); 1597 0 stevel } 1598 0 stevel 1599 0 stevel mutex_enter(&dcap->dca_lock); 1600 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1601 0 stevel if (VALID_DIR_CACHE(dcp)) { 1602 11066 rafael dcp->dc_actime = ddi_get_lbolt64(); 1603 0 stevel if (dcp->dc_nhash_mask > 0) { /* ie not minimum */ 1604 0 stevel capacity = (dcp->dc_nhash_mask + 1) << 1605 0 stevel dnlc_dir_hash_size_shift; 1606 0 stevel if (dcp->dc_num_entries <= 1607 0 stevel (capacity >> dnlc_dir_hash_resize_shift)) { 1608 0 stevel dnlc_dir_adjust_nhash(dcp); 1609 0 stevel } 1610 0 stevel } 1611 0 stevel DNLC_DIR_HASH(name, hash, namlen); 1612 0 stevel prevpp = &dcp->dc_namehash[hash & dcp->dc_nhash_mask]; 1613 0 stevel while (*prevpp != NULL) { 1614 0 stevel if (((*prevpp)->de_hash == hash) && 1615 0 stevel (namlen == (*prevpp)->de_namelen) && 1616 0 stevel bcmp((*prevpp)->de_name, name, namlen) == 0) { 1617 0 stevel if (handlep != NULL) { 1618 0 stevel *handlep = (*prevpp)->de_handle; 1619 0 stevel } 1620 0 stevel te = *prevpp; 1621 0 stevel *prevpp = (*prevpp)->de_next; 1622 0 stevel kmem_free(te, sizeof (dcentry_t) - 1 + 1623 0 stevel te->de_namelen); 1624 0 stevel 1625 0 stevel /* 1626 0 stevel * If the total number of entries 1627 0 stevel * falls below half the minimum number 1628 0 stevel * of entries then free this cache. 1629 0 stevel */ 1630 0 stevel if (--dcp->dc_num_entries < 1631 0 stevel (dnlc_dir_min_size >> 1)) { 1632 0 stevel mutex_exit(&dcap->dca_lock); 1633 0 stevel dnlc_dir_purge(dcap); 1634 0 stevel } else { 1635 0 stevel mutex_exit(&dcap->dca_lock); 1636 0 stevel } 1637 0 stevel ncs.ncs_dir_num_ents.value.ui64--; 1638 0 stevel return (DFOUND); 1639 0 stevel } 1640 0 stevel prevpp = &((*prevpp)->de_next); 1641 0 stevel } 1642 0 stevel if (dcp->dc_complete) { 1643 0 stevel ncs.ncs_dir_reme_fai.value.ui64++; 1644 0 stevel ret = DNOENT; 1645 0 stevel } else { 1646 0 stevel ret = DNOCACHE; 1647 0 stevel } 1648 0 stevel mutex_exit(&dcap->dca_lock); 1649 0 stevel return (ret); 1650 0 stevel } else { 1651 0 stevel mutex_exit(&dcap->dca_lock); 1652 0 stevel return (DNOCACHE); 1653 0 stevel } 1654 0 stevel } 1655 0 stevel 1656 0 stevel 1657 0 stevel /* 1658 0 stevel * Remove free space of at least the given length from a complete 1659 0 stevel * or partial directory cache. 1660 0 stevel */ 1661 0 stevel dcret_t 1662 0 stevel dnlc_dir_rem_space_by_len(dcanchor_t *dcap, uint_t len, uint64_t *handlep) 1663 0 stevel { 1664 0 stevel dircache_t *dcp; 1665 0 stevel dcfree_t **prevpp, *tfp; 1666 0 stevel uint_t fhtsize; /* free hash table size */ 1667 0 stevel uint_t i; 1668 0 stevel uint_t capacity; 1669 0 stevel int ret; 1670 0 stevel 1671 0 stevel if (!dnlc_dir_enable) { 1672 0 stevel return (DNOCACHE); 1673 0 stevel } 1674 0 stevel 1675 0 stevel mutex_enter(&dcap->dca_lock); 1676 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1677 0 stevel if (VALID_DIR_CACHE(dcp)) { 1678 11066 rafael dcp->dc_actime = ddi_get_lbolt64(); 1679 0 stevel if (dcp->dc_fhash_mask > 0) { /* ie not minimum */ 1680 0 stevel capacity = (dcp->dc_fhash_mask + 1) << 1681 0 stevel dnlc_dir_hash_size_shift; 1682 0 stevel if (dcp->dc_num_free <= 1683 0 stevel (capacity >> dnlc_dir_hash_resize_shift)) { 1684 0 stevel dnlc_dir_adjust_fhash(dcp); 1685 0 stevel } 1686 0 stevel } 1687 0 stevel /* 1688 0 stevel * Search for an entry of the appropriate size 1689 0 stevel * on a first fit basis. 1690 0 stevel */ 1691 0 stevel fhtsize = dcp->dc_fhash_mask + 1; 1692 0 stevel for (i = 0; i < fhtsize; i++) { /* for each hash bucket */ 1693 0 stevel prevpp = &(dcp->dc_freehash[i]); 1694 0 stevel while (*prevpp != NULL) { 1695 0 stevel if ((*prevpp)->df_len >= len) { 1696 0 stevel *handlep = (*prevpp)->df_handle; 1697 0 stevel tfp = *prevpp; 1698 0 stevel *prevpp = (*prevpp)->df_next; 1699 0 stevel dcp->dc_num_free--; 1700 0 stevel mutex_exit(&dcap->dca_lock); 1701 0 stevel kmem_cache_free(dnlc_dir_space_cache, 1702 0 stevel tfp); 1703 0 stevel ncs.ncs_dir_num_ents.value.ui64--; 1704 0 stevel return (DFOUND); 1705 0 stevel } 1706 0 stevel prevpp = &((*prevpp)->df_next); 1707 0 stevel } 1708 0 stevel } 1709 0 stevel if (dcp->dc_complete) { 1710 0 stevel ret = DNOENT; 1711 0 stevel } else { 1712 0 stevel ret = DNOCACHE; 1713 0 stevel } 1714 0 stevel mutex_exit(&dcap->dca_lock); 1715 0 stevel return (ret); 1716 0 stevel } else { 1717 0 stevel mutex_exit(&dcap->dca_lock); 1718 0 stevel return (DNOCACHE); 1719 0 stevel } 1720 0 stevel } 1721 0 stevel 1722 0 stevel /* 1723 0 stevel * Remove free space with the given handle from a complete or partial 1724 0 stevel * directory cache. 1725 0 stevel */ 1726 0 stevel dcret_t 1727 0 stevel dnlc_dir_rem_space_by_handle(dcanchor_t *dcap, uint64_t handle) 1728 0 stevel { 1729 0 stevel dircache_t *dcp; 1730 0 stevel dcfree_t **prevpp, *tfp; 1731 0 stevel uint_t capacity; 1732 0 stevel int ret; 1733 0 stevel 1734 0 stevel if (!dnlc_dir_enable) { 1735 0 stevel return (DNOCACHE); 1736 0 stevel } 1737 0 stevel 1738 0 stevel mutex_enter(&dcap->dca_lock); 1739 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1740 0 stevel if (VALID_DIR_CACHE(dcp)) { 1741 11066 rafael dcp->dc_actime = ddi_get_lbolt64(); 1742 0 stevel if (dcp->dc_fhash_mask > 0) { /* ie not minimum */ 1743 0 stevel capacity = (dcp->dc_fhash_mask + 1) << 1744 0 stevel dnlc_dir_hash_size_shift; 1745 0 stevel if (dcp->dc_num_free <= 1746 0 stevel (capacity >> dnlc_dir_hash_resize_shift)) { 1747 0 stevel dnlc_dir_adjust_fhash(dcp); 1748 0 stevel } 1749 0 stevel } 1750 0 stevel 1751 0 stevel /* 1752 0 stevel * search for the exact entry 1753 0 stevel */ 1754 0 stevel prevpp = &(dcp->dc_freehash[DDFHASH(handle, dcp)]); 1755 0 stevel while (*prevpp != NULL) { 1756 0 stevel if ((*prevpp)->df_handle == handle) { 1757 0 stevel tfp = *prevpp; 1758 0 stevel *prevpp = (*prevpp)->df_next; 1759 0 stevel dcp->dc_num_free--; 1760 0 stevel mutex_exit(&dcap->dca_lock); 1761 0 stevel kmem_cache_free(dnlc_dir_space_cache, tfp); 1762 0 stevel ncs.ncs_dir_num_ents.value.ui64--; 1763 0 stevel return (DFOUND); 1764 0 stevel } 1765 0 stevel prevpp = &((*prevpp)->df_next); 1766 0 stevel } 1767 0 stevel if (dcp->dc_complete) { 1768 0 stevel ncs.ncs_dir_rems_fai.value.ui64++; 1769 0 stevel ret = DNOENT; 1770 0 stevel } else { 1771 0 stevel ret = DNOCACHE; 1772 0 stevel } 1773 0 stevel mutex_exit(&dcap->dca_lock); 1774 0 stevel return (ret); 1775 0 stevel } else { 1776 0 stevel mutex_exit(&dcap->dca_lock); 1777 0 stevel return (DNOCACHE); 1778 0 stevel } 1779 0 stevel } 1780 0 stevel 1781 0 stevel /* 1782 0 stevel * Update the handle of an directory cache entry. 1783 0 stevel */ 1784 0 stevel dcret_t 1785 0 stevel dnlc_dir_update(dcanchor_t *dcap, char *name, uint64_t handle) 1786 0 stevel { 1787 0 stevel dircache_t *dcp; 1788 0 stevel dcentry_t *dep; 1789 0 stevel int hash; 1790 0 stevel int ret; 1791 0 stevel uchar_t namlen; 1792 0 stevel 1793 0 stevel if (!dnlc_dir_enable) { 1794 0 stevel return (DNOCACHE); 1795 0 stevel } 1796 0 stevel 1797 0 stevel mutex_enter(&dcap->dca_lock); 1798 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1799 0 stevel if (VALID_DIR_CACHE(dcp)) { 1800 11066 rafael dcp->dc_actime = ddi_get_lbolt64(); 1801 0 stevel DNLC_DIR_HASH(name, hash, namlen); 1802 0 stevel dep = dcp->dc_namehash[hash & dcp->dc_nhash_mask]; 1803 0 stevel while (dep != NULL) { 1804 0 stevel if ((dep->de_hash == hash) && 1805 0 stevel (namlen == dep->de_namelen) && 1806 0 stevel bcmp(dep->de_name, name, namlen) == 0) { 1807 0 stevel dep->de_handle = handle; 1808 0 stevel mutex_exit(&dcap->dca_lock); 1809 0 stevel return (DFOUND); 1810 0 stevel } 1811 0 stevel dep = dep->de_next; 1812 0 stevel } 1813 0 stevel if (dcp->dc_complete) { 1814 0 stevel ncs.ncs_dir_upd_fail.value.ui64++; 1815 0 stevel ret = DNOENT; 1816 0 stevel } else { 1817 0 stevel ret = DNOCACHE; 1818 0 stevel } 1819 0 stevel mutex_exit(&dcap->dca_lock); 1820 0 stevel return (ret); 1821 0 stevel } else { 1822 0 stevel mutex_exit(&dcap->dca_lock); 1823 0 stevel return (DNOCACHE); 1824 0 stevel } 1825 0 stevel } 1826 0 stevel 1827 0 stevel void 1828 0 stevel dnlc_dir_fini(dcanchor_t *dcap) 1829 0 stevel { 1830 0 stevel dircache_t *dcp; 1831 0 stevel 1832 0 stevel mutex_enter(&dc_head.dch_lock); 1833 0 stevel mutex_enter(&dcap->dca_lock); 1834 0 stevel dcp = (dircache_t *)dcap->dca_dircache; 1835 0 stevel if (VALID_DIR_CACHE(dcp)) { 1836 0 stevel /* 1837 0 stevel * Unchain from global list 1838 0 stevel */ 1839 0 stevel ncs.ncs_dir_finipurg.value.ui64++; 1840 0 stevel dcp->dc_prev->dc_next = dcp->dc_next; 1841 0 stevel dcp->dc_next->dc_prev = dcp->dc_prev; 1842 0 stevel } else { 1843 0 stevel dcp = NULL; 1844 0 stevel } 1845 0 stevel dcap->dca_dircache = NULL; 1846 0 stevel mutex_exit(&dcap->dca_lock); 1847 0 stevel mutex_exit(&dc_head.dch_lock); 1848 0 stevel mutex_destroy(&dcap->dca_lock); 1849 0 stevel if (dcp) { 1850 0 stevel dnlc_dir_abort(dcp); 1851 0 stevel } 1852 0 stevel } 1853 0 stevel 1854 0 stevel /* 1855 0 stevel * Reclaim callback for dnlc directory caching. 1856 0 stevel * Invoked by the kernel memory allocator when memory gets tight. 1857 0 stevel * This is a pretty serious condition and can lead easily lead to system 1858 0 stevel * hangs if not enough space is returned. 1859 0 stevel * 1860 0 stevel * Deciding which directory (or directories) to purge is tricky. 1861 0 stevel * Purging everything is an overkill, but purging just the oldest used 1862 0 stevel * was found to lead to hangs. The largest cached directories use the 1863 0 stevel * most memory, but take the most effort to rebuild, whereas the smaller 1864 0 stevel * ones have little value and give back little space. So what to do? 1865 0 stevel * 1866 0 stevel * The current policy is to continue purging the oldest used directories 1867 0 stevel * until at least dnlc_dir_min_reclaim directory entries have been purged. 1868 0 stevel */ 1869 0 stevel /*ARGSUSED*/ 1870 0 stevel static void 1871 0 stevel dnlc_dir_reclaim(void *unused) 1872 0 stevel { 1873 0 stevel dircache_t *dcp, *oldest; 1874 0 stevel uint_t dirent_cnt = 0; 1875 0 stevel 1876 0 stevel mutex_enter(&dc_head.dch_lock); 1877 0 stevel while (dirent_cnt < dnlc_dir_min_reclaim) { 1878 0 stevel dcp = dc_head.dch_next; 1879 0 stevel oldest = NULL; 1880 0 stevel while (dcp != (dircache_t *)&dc_head) { 1881 0 stevel if (oldest == NULL) { 1882 0 stevel oldest = dcp; 1883 0 stevel } else { 1884 0 stevel if (dcp->dc_actime < oldest->dc_actime) { 1885 0 stevel oldest = dcp; 1886 0 stevel } 1887 0 stevel } 1888 0 stevel dcp = dcp->dc_next; 1889 0 stevel } 1890 0 stevel if (oldest == NULL) { 1891 0 stevel /* nothing to delete */ 1892 0 stevel mutex_exit(&dc_head.dch_lock); 1893 0 stevel return; 1894 0 stevel } 1895 0 stevel /* 1896 0 stevel * remove from directory chain and purge 1897 0 stevel */ 1898 0 stevel oldest->dc_prev->dc_next = oldest->dc_next; 1899 0 stevel oldest->dc_next->dc_prev = oldest->dc_prev; 1900 0 stevel mutex_enter(&oldest->dc_anchor->dca_lock); 1901 0 stevel /* 1902 0 stevel * If this was the last entry then it must be too large. 1903 0 stevel * Mark it as such by saving a special dircache_t 1904 0 stevel * pointer (DC_RET_LOW_MEM) in the anchor. The error DNOMEM 1905 0 stevel * will be presented to the caller of dnlc_dir_start() 1906 0 stevel */ 1907 0 stevel if (oldest->dc_next == oldest->dc_prev) { 1908 0 stevel oldest->dc_anchor->dca_dircache = DC_RET_LOW_MEM; 1909 0 stevel ncs.ncs_dir_rec_last.value.ui64++; 1910 0 stevel } else { 1911 0 stevel oldest->dc_anchor->dca_dircache = NULL; 1912 0 stevel ncs.ncs_dir_recl_any.value.ui64++; 1913 0 stevel } 1914 0 stevel mutex_exit(&oldest->dc_anchor->dca_lock); 1915 0 stevel dirent_cnt += oldest->dc_num_entries; 1916 0 stevel dnlc_dir_abort(oldest); 1917 0 stevel } 1918 0 stevel mutex_exit(&dc_head.dch_lock); 1919 0 stevel } 1920 0 stevel 1921 0 stevel /* 1922 0 stevel * Dynamically grow or shrink the size of the name hash table 1923 0 stevel */ 1924 0 stevel static void 1925 0 stevel dnlc_dir_adjust_nhash(dircache_t *dcp) 1926 0 stevel { 1927 0 stevel dcentry_t **newhash, *dep, **nhp, *tep; 1928 0 stevel uint_t newsize; 1929 0 stevel uint_t oldsize; 1930 0 stevel uint_t newsizemask; 1931 0 stevel int i; 1932 0 stevel 1933 0 stevel /* 1934 0 stevel * Allocate new hash table 1935 0 stevel */ 1936 0 stevel newsize = dcp->dc_num_entries >> dnlc_dir_hash_size_shift; 1937 0 stevel newhash = kmem_zalloc(sizeof (dcentry_t *) * newsize, KM_NOSLEEP); 1938 0 stevel if (newhash == NULL) { 1939 0 stevel /* 1940 0 stevel * System is short on memory just return 1941 0 stevel * Note, the old hash table is still usable. 1942 0 stevel * This return is unlikely to repeatedy occur, because 1943 0 stevel * either some other directory caches will be reclaimed 1944 0 stevel * due to memory shortage, thus freeing memory, or this 1945 0 stevel * directory cahe will be reclaimed. 1946 0 stevel */ 1947 0 stevel return; 1948 0 stevel } 1949 0 stevel oldsize = dcp->dc_nhash_mask + 1; 1950 0 stevel dcp->dc_nhash_mask = newsizemask = newsize - 1; 1951 0 stevel 1952 0 stevel /* 1953 0 stevel * Move entries from the old table to the new 1954 0 stevel */ 1955 0 stevel for (i = 0; i < oldsize; i++) { /* for each hash bucket */ 1956 0 stevel dep = dcp->dc_namehash[i]; 1957 0 stevel while (dep != NULL) { /* for each chained entry */ 1958 0 stevel tep = dep; 1959 0 stevel dep = dep->de_next; 1960 0 stevel nhp = &newhash[tep->de_hash & newsizemask]; 1961 0 stevel tep->de_next = *nhp; 1962 0 stevel *nhp = tep; 1963 0 stevel } 1964 0 stevel } 1965 0 stevel 1966 0 stevel /* 1967 0 stevel * delete old hash table and set new one in place 1968 0 stevel */ 1969 0 stevel kmem_free(dcp->dc_namehash, sizeof (dcentry_t *) * oldsize); 1970 0 stevel dcp->dc_namehash = newhash; 1971 0 stevel } 1972 0 stevel 1973 0 stevel /* 1974 0 stevel * Dynamically grow or shrink the size of the free space hash table 1975 0 stevel */ 1976 0 stevel static void 1977 0 stevel dnlc_dir_adjust_fhash(dircache_t *dcp) 1978 0 stevel { 1979 0 stevel dcfree_t **newhash, *dfp, **nhp, *tfp; 1980 0 stevel uint_t newsize; 1981 0 stevel uint_t oldsize; 1982 0 stevel int i; 1983 0 stevel 1984 0 stevel /* 1985 0 stevel * Allocate new hash table 1986 0 stevel */ 1987 0 stevel newsize = dcp->dc_num_free >> dnlc_dir_hash_size_shift; 1988 0 stevel newhash = kmem_zalloc(sizeof (dcfree_t *) * newsize, KM_NOSLEEP); 1989 0 stevel if (newhash == NULL) { 1990 0 stevel /* 1991 0 stevel * System is short on memory just return 1992 0 stevel * Note, the old hash table is still usable. 1993 0 stevel * This return is unlikely to repeatedy occur, because 1994 0 stevel * either some other directory caches will be reclaimed 1995 0 stevel * due to memory shortage, thus freeing memory, or this 1996 0 stevel * directory cahe will be reclaimed. 1997 0 stevel */ 1998 0 stevel return; 1999 0 stevel } 2000 0 stevel oldsize = dcp->dc_fhash_mask + 1; 2001 0 stevel dcp->dc_fhash_mask = newsize - 1; 2002 0 stevel 2003 0 stevel /* 2004 0 stevel * Move entries from the old table to the new 2005 0 stevel */ 2006 0 stevel for (i = 0; i < oldsize; i++) { /* for each hash bucket */ 2007 0 stevel dfp = dcp->dc_freehash[i]; 2008 0 stevel while (dfp != NULL) { /* for each chained entry */ 2009 0 stevel tfp = dfp; 2010 0 stevel dfp = dfp->df_next; 2011 0 stevel nhp = &newhash[DDFHASH(tfp->df_handle, dcp)]; 2012 0 stevel tfp->df_next = *nhp; 2013 0 stevel *nhp = tfp; 2014 0 stevel } 2015 0 stevel } 2016 0 stevel 2017 0 stevel /* 2018 0 stevel * delete old hash table and set new one in place 2019 0 stevel */ 2020 0 stevel kmem_free(dcp->dc_freehash, sizeof (dcfree_t *) * oldsize); 2021 0 stevel dcp->dc_freehash = newhash; 2022 0 stevel } 2023