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