xref: /titanic_53/usr/src/uts/common/fs/dnlc.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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