xref: /onnv-gate/usr/src/uts/common/fs/dev/sdev_ncache.c (revision 2797:3782a13773c1)
12621Sllai1 /*
22621Sllai1  * CDDL HEADER START
32621Sllai1  *
42621Sllai1  * The contents of this file are subject to the terms of the
52621Sllai1  * Common Development and Distribution License (the "License").
62621Sllai1  * You may not use this file except in compliance with the License.
72621Sllai1  *
82621Sllai1  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92621Sllai1  * or http://www.opensolaris.org/os/licensing.
102621Sllai1  * See the License for the specific language governing permissions
112621Sllai1  * and limitations under the License.
122621Sllai1  *
132621Sllai1  * When distributing Covered Code, include this CDDL HEADER in each
142621Sllai1  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152621Sllai1  * If applicable, add the following below this CDDL HEADER, with the
162621Sllai1  * fields enclosed by brackets "[]" replaced with your own identifying
172621Sllai1  * information: Portions Copyright [yyyy] [name of copyright owner]
182621Sllai1  *
192621Sllai1  * CDDL HEADER END
202621Sllai1  */
212621Sllai1 /*
222621Sllai1  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
232621Sllai1  * Use is subject to license terms.
242621Sllai1  */
252621Sllai1 
262621Sllai1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
272621Sllai1 
282621Sllai1 /*
292621Sllai1  * negative cache handling for the /dev fs
302621Sllai1  */
312621Sllai1 
322621Sllai1 #include <sys/types.h>
332621Sllai1 #include <sys/param.h>
342621Sllai1 #include <sys/t_lock.h>
352621Sllai1 #include <sys/systm.h>
362621Sllai1 #include <sys/sysmacros.h>
372621Sllai1 #include <sys/user.h>
382621Sllai1 #include <sys/time.h>
392621Sllai1 #include <sys/vfs.h>
402621Sllai1 #include <sys/vnode.h>
412621Sllai1 #include <sys/file.h>
422621Sllai1 #include <sys/fcntl.h>
432621Sllai1 #include <sys/flock.h>
442621Sllai1 #include <sys/kmem.h>
452621Sllai1 #include <sys/uio.h>
462621Sllai1 #include <sys/errno.h>
472621Sllai1 #include <sys/stat.h>
482621Sllai1 #include <sys/cred.h>
492621Sllai1 #include <sys/cmn_err.h>
502621Sllai1 #include <sys/debug.h>
512621Sllai1 #include <sys/mode.h>
522621Sllai1 #include <sys/policy.h>
532621Sllai1 #include <fs/fs_subr.h>
542621Sllai1 #include <sys/mount.h>
552621Sllai1 #include <sys/fs/snode.h>
562621Sllai1 #include <sys/fs/dv_node.h>
572621Sllai1 #include <sys/fs/sdev_node.h>
582621Sllai1 #include <sys/sunndi.h>
592621Sllai1 #include <sys/sunmdi.h>
602621Sllai1 #include <sys/ddi.h>
612621Sllai1 #include <sys/modctl.h>
62*2797Sjg #include <sys/devcache.h>
632621Sllai1 
642621Sllai1 
652621Sllai1 /*
662621Sllai1  * ncache is a negative cache of failed lookups.  An entry
672621Sllai1  * is added after an attempt to configure a device by that
682621Sllai1  * name failed.  An accumulation of these entries over time
692621Sllai1  * gives us a set of device name for which implicit reconfiguration
702621Sllai1  * does not need to be attempted.  If a name is created matching
712621Sllai1  * an entry in ncache, that entry is removed, with the
722621Sllai1  * persistent store updated.
732621Sllai1  *
742621Sllai1  * Implicit reconfig is initiated for any name during lookup that
752621Sllai1  * can't be resolved from the backing store and that isn't
762621Sllai1  * present in the negative cache.  This functionality is
772621Sllai1  * enabled during system startup once communication with devfsadm
782621Sllai1  * can be achieved.  Since readdir is more general, implicit
792621Sllai1  * reconfig initiated by reading a directory isn't enabled until
802621Sllai1  * the system is more fully booted, at the time of the multi-user
812621Sllai1  * milestone, corresponding to init state 2.
822621Sllai1  *
832621Sllai1  * A maximum is imposed on the number of entries in the cache
842621Sllai1  * to limit some script going wild and as a defense against attack.
852621Sllai1  * The default limit is 64 and can be adjusted via sdev_nc_max_entries.
862621Sllai1  *
872621Sllai1  * Each entry also has a expiration count.  When looked up a name in
882621Sllai1  * the cache is set to the default.  Subsequent boots will decrement
892621Sllai1  * the count if a name isn't referenced.  This permits a once-only
902621Sllai1  * entry to eventually be removed over time.
912621Sllai1  *
922621Sllai1  * sdev_reconfig_delay implements a "debounce" of the timing beyond
932621Sllai1  * system available indication, providing what the filesystem considers
942621Sllai1  * to be the system-is-fully-booted state.  This is provided to adjust
952621Sllai1  * the timing if some application startup is performing a readdir
962621Sllai1  * in /dev that initiates a troublesome implicit reconfig on every boot.
972621Sllai1  *
982621Sllai1  * sdev_nc_disable_reset can be used to disable clearing the negative cache
992621Sllai1  * on reconfig boot.  The default is to clear the cache on reconfig boot.
1002621Sllai1  * sdev_nc_disable can be used to disable the negative cache itself.
1012621Sllai1  *
1022621Sllai1  * sdev_reconfig_disable can be used to disable implicit reconfig.
1032621Sllai1  * The default is that implicit reconfig is enabled.
1042621Sllai1  */
1052621Sllai1 
1062621Sllai1 /* tunables and defaults */
1072621Sllai1 #define	SDEV_NC_EXPIRECNT	4
1082621Sllai1 #define	SDEV_NC_MAX_ENTRIES	64
1092621Sllai1 #define	SEV_RECONFIG_DELAY	6	/* seconds */
1102621Sllai1 
111*2797Sjg /* tunables */
112*2797Sjg int	sdev_nc_expirecnt = SDEV_NC_EXPIRECNT;
113*2797Sjg int	sdev_nc_max_entries = SDEV_NC_MAX_ENTRIES;
114*2797Sjg int	sdev_reconfig_delay = SEV_RECONFIG_DELAY;
115*2797Sjg int	sdev_reconfig_verbose = 0;
116*2797Sjg int	sdev_reconfig_disable = 0;
117*2797Sjg int	sdev_nc_disable = 0;
118*2797Sjg int	sdev_nc_disable_reset = 0;
119*2797Sjg int	sdev_nc_verbose = 0;
120*2797Sjg int	sdev_cache_read_disable = 0;
121*2797Sjg int	sdev_cache_write_disable = 0;
1222621Sllai1 
1232621Sllai1 /* globals */
124*2797Sjg int	sdev_boot_state = SDEV_BOOT_STATE_INITIAL;
125*2797Sjg int	sdev_reconfig_boot = 0;
126*2797Sjg sdev_nc_list_t *sdev_ncache;
127*2797Sjg static timeout_id_t sdev_timeout_id = 0;
128*2797Sjg static nvf_handle_t sdevfd_handle;
1292621Sllai1 
1302621Sllai1 /* static prototypes */
131*2797Sjg static void sdev_ncache_write_complete(nvf_handle_t);
1322621Sllai1 static void sdev_ncache_write(void);
1332621Sllai1 static void sdev_ncache_process_store(void);
1342621Sllai1 static sdev_nc_list_t *sdev_nc_newlist(void);
1352621Sllai1 static void sdev_nc_free_unlinked_node(sdev_nc_node_t *);
1362621Sllai1 static void sdev_nc_free_all_nodes(sdev_nc_list_t *);
1372621Sllai1 static void sdev_nc_freelist(sdev_nc_list_t *);
1382621Sllai1 static sdev_nc_node_t *sdev_nc_findpath(sdev_nc_list_t *, char *);
1392621Sllai1 static void sdev_nc_insertnode(sdev_nc_list_t *, sdev_nc_node_t *);
1402621Sllai1 static void sdev_nc_free_bootonly(void);
141*2797Sjg static int sdev_ncache_unpack_nvlist(nvf_handle_t, nvlist_t *, char *);
142*2797Sjg static int sdev_ncache_pack_list(nvf_handle_t, nvlist_t **);
143*2797Sjg static void sdev_ncache_list_free(nvf_handle_t);
144*2797Sjg static void sdev_nvp_free(nvp_devname_t *);
1452621Sllai1 
146*2797Sjg /*
147*2797Sjg  * Registration for /etc/devices/devname_cache
148*2797Sjg  */
149*2797Sjg static nvf_ops_t sdev_cache_ops = {
150*2797Sjg 	"/etc/devices/devname_cache",		/* path to cache */
151*2797Sjg 	sdev_ncache_unpack_nvlist,		/* read: unpack nvlist */
152*2797Sjg 	sdev_ncache_pack_list,			/* write: pack list */
153*2797Sjg 	sdev_ncache_list_free,			/* free data list */
154*2797Sjg 	sdev_ncache_write_complete		/* write complete callback */
155*2797Sjg };
1562621Sllai1 
1572621Sllai1 /*
1582621Sllai1  * called once at filesystem initialization
1592621Sllai1  */
1602621Sllai1 void
1612621Sllai1 sdev_ncache_init(void)
1622621Sllai1 {
1632621Sllai1 	sdev_ncache = sdev_nc_newlist();
1642621Sllai1 }
1652621Sllai1 
1662621Sllai1 /*
1672621Sllai1  * called at mount of the global instance
1682621Sllai1  * currently the global instance is never unmounted
1692621Sllai1  */
1702621Sllai1 void
1712621Sllai1 sdev_ncache_setup(void)
1722621Sllai1 {
173*2797Sjg 	sdevfd_handle = nvf_register_file(&sdev_cache_ops);
174*2797Sjg 	ASSERT(sdevfd_handle);
175*2797Sjg 
176*2797Sjg 	list_create(nvf_list(sdevfd_handle), sizeof (nvp_devname_t),
177*2797Sjg 	    offsetof(nvp_devname_t, nvp_link));
1782621Sllai1 
179*2797Sjg 	rw_enter(nvf_lock(sdevfd_handle), RW_WRITER);
180*2797Sjg 	if (!sdev_cache_read_disable) {
181*2797Sjg 		(void) nvf_read_file(sdevfd_handle);
182*2797Sjg 	}
183*2797Sjg 	sdev_ncache_process_store();
184*2797Sjg 	rw_exit(nvf_lock(sdevfd_handle));
1852621Sllai1 
1862621Sllai1 	sdev_devstate_change();
1872621Sllai1 }
1882621Sllai1 
1892621Sllai1 static void
190*2797Sjg sdev_nvp_free(nvp_devname_t *dp)
1912621Sllai1 {
192*2797Sjg 	int	i;
193*2797Sjg 	char	**p;
194*2797Sjg 
195*2797Sjg 	if (dp->nvp_npaths > 0) {
196*2797Sjg 		p = dp->nvp_paths;
197*2797Sjg 		for (i = 0; i < dp->nvp_npaths; i++, p++) {
198*2797Sjg 			kmem_free(*p, strlen(*p)+1);
199*2797Sjg 		}
200*2797Sjg 		kmem_free(dp->nvp_paths,
201*2797Sjg 			dp->nvp_npaths * sizeof (char *));
202*2797Sjg 		kmem_free(dp->nvp_expirecnts,
203*2797Sjg 			dp->nvp_npaths * sizeof (int));
204*2797Sjg 	}
2052621Sllai1 
206*2797Sjg 	kmem_free(dp, sizeof (nvp_devname_t));
207*2797Sjg }
208*2797Sjg 
209*2797Sjg static void
210*2797Sjg sdev_ncache_list_free(nvf_handle_t fd)
211*2797Sjg {
212*2797Sjg 	list_t		*listp;
213*2797Sjg 	nvp_devname_t	*dp;
214*2797Sjg 
215*2797Sjg 	ASSERT(fd == sdevfd_handle);
216*2797Sjg 	ASSERT(RW_WRITE_HELD(nvf_lock(fd)));
217*2797Sjg 
218*2797Sjg 	listp = nvf_list(fd);
219*2797Sjg 	if ((dp = list_head(listp)) != NULL) {
220*2797Sjg 		list_remove(listp, dp);
221*2797Sjg 		sdev_nvp_free(dp);
2222621Sllai1 	}
2232621Sllai1 }
2242621Sllai1 
225*2797Sjg /*
226*2797Sjg  * Unpack a device path/nvlist pair to internal data list format.
227*2797Sjg  * Used to decode the nvlist format into the internal representation
228*2797Sjg  * when reading /etc/devices/devname_cache.
229*2797Sjg  * Note that the expiration counts are optional, for compatibility
230*2797Sjg  * with earlier instances of the cache.  If not present, the
231*2797Sjg  * expire counts are initialized to defaults.
232*2797Sjg  */
233*2797Sjg static int
234*2797Sjg sdev_ncache_unpack_nvlist(nvf_handle_t fd, nvlist_t *nvl, char *name)
235*2797Sjg {
236*2797Sjg 	nvp_devname_t *np;
237*2797Sjg 	char	**strs;
238*2797Sjg 	int	*cnts;
239*2797Sjg 	uint_t	nstrs, ncnts;
240*2797Sjg 	int	rval, i;
241*2797Sjg 
242*2797Sjg 	ASSERT(fd == sdevfd_handle);
243*2797Sjg 	ASSERT(RW_WRITE_HELD(nvf_lock(fd)));
244*2797Sjg 
245*2797Sjg 	/* name of the sublist must match what we created */
246*2797Sjg 	if (strcmp(name, DP_DEVNAME_ID) != 0) {
247*2797Sjg 		return (-1);
248*2797Sjg 	}
249*2797Sjg 
250*2797Sjg 	np = kmem_zalloc(sizeof (nvp_devname_t), KM_SLEEP);
251*2797Sjg 
252*2797Sjg 	rval = nvlist_lookup_string_array(nvl,
253*2797Sjg 	    DP_DEVNAME_NCACHE_ID, &strs, &nstrs);
254*2797Sjg 	if (rval) {
255*2797Sjg 		kmem_free(np, sizeof (nvp_devname_t));
256*2797Sjg 		return (-1);
257*2797Sjg 	}
258*2797Sjg 
259*2797Sjg 	np->nvp_npaths = nstrs;
260*2797Sjg 	np->nvp_paths = kmem_zalloc(nstrs * sizeof (char *), KM_SLEEP);
261*2797Sjg 	for (i = 0; i < nstrs; i++) {
262*2797Sjg 		np->nvp_paths[i] = i_ddi_strdup(strs[i], KM_SLEEP);
263*2797Sjg 	}
264*2797Sjg 	np->nvp_expirecnts = kmem_zalloc(nstrs * sizeof (int), KM_SLEEP);
265*2797Sjg 	for (i = 0; i < nstrs; i++) {
266*2797Sjg 		np->nvp_expirecnts[i] = sdev_nc_expirecnt;
267*2797Sjg 	}
268*2797Sjg 
269*2797Sjg 	rval = nvlist_lookup_int32_array(nvl,
270*2797Sjg 	    DP_DEVNAME_NC_EXPIRECNT_ID, &cnts, &ncnts);
271*2797Sjg 	if (rval == 0) {
272*2797Sjg 		ASSERT(ncnts == nstrs);
273*2797Sjg 		ncnts = min(ncnts, nstrs);
274*2797Sjg 		for (i = 0; i < nstrs; i++) {
275*2797Sjg 			np->nvp_expirecnts[i] = cnts[i];
276*2797Sjg 		}
277*2797Sjg 	}
278*2797Sjg 
279*2797Sjg 	list_insert_tail(nvf_list(sdevfd_handle), np);
280*2797Sjg 
281*2797Sjg 	return (0);
282*2797Sjg }
283*2797Sjg 
284*2797Sjg /*
285*2797Sjg  * Pack internal format cache data to a single nvlist.
286*2797Sjg  * Used when writing the nvlist file.
287*2797Sjg  * Note this is called indirectly by the nvpflush daemon.
288*2797Sjg  */
289*2797Sjg static int
290*2797Sjg sdev_ncache_pack_list(nvf_handle_t fd, nvlist_t **ret_nvl)
291*2797Sjg {
292*2797Sjg 	nvlist_t	*nvl, *sub_nvl;
293*2797Sjg 	nvp_devname_t	*np;
294*2797Sjg 	int		rval;
295*2797Sjg 	list_t		*listp;
296*2797Sjg 
297*2797Sjg 	ASSERT(fd == sdevfd_handle);
298*2797Sjg 	ASSERT(RW_WRITE_HELD(nvf_lock(fd)));
299*2797Sjg 
300*2797Sjg 	rval = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
301*2797Sjg 	if (rval != 0) {
302*2797Sjg 		nvf_error("%s: nvlist alloc error %d\n",
303*2797Sjg 			nvf_cache_name(fd), rval);
304*2797Sjg 		return (DDI_FAILURE);
305*2797Sjg 	}
306*2797Sjg 
307*2797Sjg 	listp = nvf_list(sdevfd_handle);
308*2797Sjg 	if ((np = list_head(listp)) != NULL) {
309*2797Sjg 		ASSERT(list_next(listp, np) == NULL);
310*2797Sjg 
311*2797Sjg 		rval = nvlist_alloc(&sub_nvl, NV_UNIQUE_NAME, KM_SLEEP);
312*2797Sjg 		if (rval != 0) {
313*2797Sjg 			nvf_error("%s: nvlist alloc error %d\n",
314*2797Sjg 				nvf_cache_name(fd), rval);
315*2797Sjg 			sub_nvl = NULL;
316*2797Sjg 			goto err;
317*2797Sjg 		}
318*2797Sjg 
319*2797Sjg 		rval = nvlist_add_string_array(sub_nvl,
320*2797Sjg 		    DP_DEVNAME_NCACHE_ID, np->nvp_paths, np->nvp_npaths);
321*2797Sjg 		if (rval != 0) {
322*2797Sjg 			nvf_error("%s: nvlist add error %d (sdev)\n",
323*2797Sjg 			    nvf_cache_name(fd), rval);
324*2797Sjg 			goto err;
325*2797Sjg 		}
326*2797Sjg 
327*2797Sjg 		rval = nvlist_add_int32_array(sub_nvl,
328*2797Sjg 		    DP_DEVNAME_NC_EXPIRECNT_ID,
329*2797Sjg 		    np->nvp_expirecnts, np->nvp_npaths);
330*2797Sjg 		if (rval != 0) {
331*2797Sjg 			nvf_error("%s: nvlist add error %d (sdev)\n",
332*2797Sjg 			    nvf_cache_name(fd), rval);
333*2797Sjg 			goto err;
334*2797Sjg 		}
335*2797Sjg 
336*2797Sjg 		rval = nvlist_add_nvlist(nvl, DP_DEVNAME_ID, sub_nvl);
337*2797Sjg 		if (rval != 0) {
338*2797Sjg 			nvf_error("%s: nvlist add error %d (sublist)\n",
339*2797Sjg 			    nvf_cache_name(fd), rval);
340*2797Sjg 			goto err;
341*2797Sjg 		}
342*2797Sjg 		nvlist_free(sub_nvl);
343*2797Sjg 	}
344*2797Sjg 
345*2797Sjg 	*ret_nvl = nvl;
346*2797Sjg 	return (DDI_SUCCESS);
347*2797Sjg 
348*2797Sjg err:
349*2797Sjg 	if (sub_nvl)
350*2797Sjg 		nvlist_free(sub_nvl);
351*2797Sjg 	nvlist_free(nvl);
352*2797Sjg 	*ret_nvl = NULL;
353*2797Sjg 	return (DDI_FAILURE);
354*2797Sjg }
355*2797Sjg 
356*2797Sjg /*
357*2797Sjg  * Run through the data read from the backing cache store
358*2797Sjg  * to establish the initial state of the neg. cache.
359*2797Sjg  */
3602621Sllai1 static void
3612621Sllai1 sdev_ncache_process_store(void)
3622621Sllai1 {
3632621Sllai1 	sdev_nc_list_t	*ncl = sdev_ncache;
3642621Sllai1 	nvp_devname_t	*np;
3652621Sllai1 	sdev_nc_node_t	*lp;
3662621Sllai1 	char		*path;
3672621Sllai1 	int		i, n;
368*2797Sjg 	list_t		*listp;
3692621Sllai1 
3702621Sllai1 	if (sdev_nc_disable)
3712621Sllai1 		return;
3722621Sllai1 
373*2797Sjg 	ASSERT(RW_WRITE_HELD(nvf_lock(sdevfd_handle)));
374*2797Sjg 
375*2797Sjg 	listp = nvf_list(sdevfd_handle);
376*2797Sjg 	for (np = list_head(listp); np; np = list_next(listp, np)) {
3772621Sllai1 		for (i = 0; i < np->nvp_npaths; i++) {
3782621Sllai1 			sdcmn_err5(("    %s %d\n",
3792621Sllai1 			    np->nvp_paths[i], np->nvp_expirecnts[i]));
3802621Sllai1 			if (ncl->ncl_nentries < sdev_nc_max_entries) {
3812621Sllai1 				path = np->nvp_paths[i];
3822621Sllai1 				n = strlen(path) + 1;
3832621Sllai1 				lp = kmem_alloc(sizeof (sdev_nc_node_t),
3842621Sllai1 				    KM_SLEEP);
3852621Sllai1 				lp->ncn_name = kmem_alloc(n, KM_SLEEP);
3862621Sllai1 				bcopy(path, lp->ncn_name, n);
3872621Sllai1 				lp->ncn_flags = NCN_SRC_STORE;
3882621Sllai1 				lp->ncn_expirecnt = np->nvp_expirecnts[i];
3892621Sllai1 				sdev_nc_insertnode(ncl, lp);
3902621Sllai1 			} else if (sdev_nc_verbose) {
3912621Sllai1 				cmn_err(CE_CONT,
3922621Sllai1 				    "?%s: truncating from ncache (max %d)\n",
3932621Sllai1 				    np->nvp_paths[i], sdev_nc_max_entries);
3942621Sllai1 			}
3952621Sllai1 		}
3962621Sllai1 	}
3972621Sllai1 }
3982621Sllai1 
399*2797Sjg /*
400*2797Sjg  * called by nvpflush daemon to inform us that an update of
401*2797Sjg  * the cache file has been completed.
402*2797Sjg  */
4032621Sllai1 static void
404*2797Sjg sdev_ncache_write_complete(nvf_handle_t fd)
4052621Sllai1 {
4062621Sllai1 	sdev_nc_list_t	*ncl = sdev_ncache;
4072621Sllai1 
408*2797Sjg 	ASSERT(fd == sdevfd_handle);
409*2797Sjg 
4102621Sllai1 	mutex_enter(&ncl->ncl_mutex);
4112621Sllai1 
4122621Sllai1 	ASSERT(ncl->ncl_flags & NCL_LIST_WRITING);
4132621Sllai1 
4142621Sllai1 	if (ncl->ncl_flags & NCL_LIST_DIRTY) {
4152621Sllai1 		sdcmn_err5(("ncache write complete but dirty again\n"));
4162621Sllai1 		ncl->ncl_flags &= ~NCL_LIST_DIRTY;
4172621Sllai1 		mutex_exit(&ncl->ncl_mutex);
4182621Sllai1 		sdev_ncache_write();
4192621Sllai1 	} else {
4202621Sllai1 		sdcmn_err5(("ncache write complete\n"));
4212621Sllai1 		ncl->ncl_flags &= ~NCL_LIST_WRITING;
4222621Sllai1 		mutex_exit(&ncl->ncl_mutex);
423*2797Sjg 		rw_enter(nvf_lock(fd), RW_WRITER);
424*2797Sjg 		sdev_ncache_list_free(fd);
425*2797Sjg 		rw_exit(nvf_lock(fd));
4262621Sllai1 	}
4272621Sllai1 }
4282621Sllai1 
429*2797Sjg /*
430*2797Sjg  * Prepare to perform an update of the neg. cache backing store.
431*2797Sjg  */
4322621Sllai1 static void
4332621Sllai1 sdev_ncache_write(void)
4342621Sllai1 {
4352621Sllai1 	sdev_nc_list_t	*ncl = sdev_ncache;
4362621Sllai1 	nvp_devname_t	*np;
4372621Sllai1 	sdev_nc_node_t	*lp;
4382621Sllai1 	int		n, i;
4392621Sllai1 
4402621Sllai1 	if (sdev_cache_write_disable) {
4412621Sllai1 		mutex_enter(&ncl->ncl_mutex);
4422621Sllai1 		ncl->ncl_flags &= ~NCL_LIST_WRITING;
4432621Sllai1 		mutex_exit(&ncl->ncl_mutex);
4442621Sllai1 		return;
4452621Sllai1 	}
4462621Sllai1 
4472621Sllai1 	/* proper lock ordering here is essential */
448*2797Sjg 	rw_enter(nvf_lock(sdevfd_handle), RW_WRITER);
449*2797Sjg 	sdev_ncache_list_free(sdevfd_handle);
4502621Sllai1 
4512621Sllai1 	rw_enter(&ncl->ncl_lock, RW_READER);
4522621Sllai1 	n = ncl->ncl_nentries;
4532621Sllai1 	ASSERT(n <= sdev_nc_max_entries);
4542621Sllai1 
4552621Sllai1 	np = kmem_zalloc(sizeof (nvp_devname_t), KM_SLEEP);
4562621Sllai1 	np->nvp_npaths = n;
4572621Sllai1 	np->nvp_paths = kmem_zalloc(n * sizeof (char *), KM_SLEEP);
4582621Sllai1 	np->nvp_expirecnts = kmem_zalloc(n * sizeof (int), KM_SLEEP);
4592621Sllai1 
4602621Sllai1 	i = 0;
4612621Sllai1 	for (lp = list_head(&ncl->ncl_list); lp;
4622621Sllai1 	    lp = list_next(&ncl->ncl_list, lp)) {
4632621Sllai1 		np->nvp_paths[i] = i_ddi_strdup(lp->ncn_name, KM_SLEEP);
4642621Sllai1 		np->nvp_expirecnts[i] = lp->ncn_expirecnt;
4652621Sllai1 		sdcmn_err5(("    %s %d\n",
4662621Sllai1 		    np->nvp_paths[i], np->nvp_expirecnts[i]));
4672621Sllai1 		i++;
4682621Sllai1 	}
4692621Sllai1 
4702621Sllai1 	rw_exit(&ncl->ncl_lock);
4712621Sllai1 
472*2797Sjg 	nvf_mark_dirty(sdevfd_handle);
473*2797Sjg 	list_insert_tail(nvf_list(sdevfd_handle), np);
474*2797Sjg 	rw_exit(nvf_lock(sdevfd_handle));
4752621Sllai1 
476*2797Sjg 	nvf_wake_daemon();
4772621Sllai1 }
4782621Sllai1 
4792621Sllai1 static void
4802621Sllai1 sdev_nc_flush_updates(void)
4812621Sllai1 {
4822621Sllai1 	sdev_nc_list_t *ncl = sdev_ncache;
4832621Sllai1 
4842621Sllai1 	if (sdev_nc_disable || sdev_cache_write_disable)
4852621Sllai1 		return;
4862621Sllai1 
4872621Sllai1 	mutex_enter(&ncl->ncl_mutex);
4882621Sllai1 	if (((ncl->ncl_flags &
4892621Sllai1 	    (NCL_LIST_DIRTY | NCL_LIST_WENABLE | NCL_LIST_WRITING)) ==
4902621Sllai1 	    (NCL_LIST_DIRTY | NCL_LIST_WENABLE))) {
4912621Sllai1 		ncl->ncl_flags &= ~NCL_LIST_DIRTY;
4922621Sllai1 		ncl->ncl_flags |= NCL_LIST_WRITING;
4932621Sllai1 		mutex_exit(&ncl->ncl_mutex);
4942621Sllai1 		sdev_ncache_write();
4952621Sllai1 	} else {
4962621Sllai1 		mutex_exit(&ncl->ncl_mutex);
4972621Sllai1 	}
4982621Sllai1 }
4992621Sllai1 
5002621Sllai1 static void
5012621Sllai1 sdev_nc_flush_boot_update(void)
5022621Sllai1 {
5032621Sllai1 	sdev_nc_list_t *ncl = sdev_ncache;
5042621Sllai1 
5052621Sllai1 	if (sdev_nc_disable || sdev_cache_write_disable ||
5062621Sllai1 	    (sdev_boot_state == SDEV_BOOT_STATE_INITIAL)) {
5072621Sllai1 		return;
5082621Sllai1 	}
5092621Sllai1 	mutex_enter(&ncl->ncl_mutex);
5102621Sllai1 	if (ncl->ncl_flags & NCL_LIST_WENABLE) {
5112621Sllai1 		mutex_exit(&ncl->ncl_mutex);
5122621Sllai1 		sdev_nc_flush_updates();
5132621Sllai1 	} else {
5142621Sllai1 		mutex_exit(&ncl->ncl_mutex);
5152621Sllai1 	}
5162621Sllai1 
5172621Sllai1 }
5182621Sllai1 
5192621Sllai1 static void
5202621Sllai1 sdev_state_boot_complete()
5212621Sllai1 {
5222621Sllai1 	sdev_nc_list_t	*ncl = sdev_ncache;
5232621Sllai1 	sdev_nc_node_t	*lp, *next;
5242621Sllai1 
5252621Sllai1 	/*
5262621Sllai1 	 * Once boot is complete, decrement the expire count of each entry
5272621Sllai1 	 * in the cache not touched by a reference.  Remove any that
5282621Sllai1 	 * goes to zero.  This effectively removes random entries over
5292621Sllai1 	 * time.
5302621Sllai1 	 */
5312621Sllai1 	rw_enter(&ncl->ncl_lock, RW_WRITER);
5322621Sllai1 	mutex_enter(&ncl->ncl_mutex);
5332621Sllai1 
5342621Sllai1 	for (lp = list_head(&ncl->ncl_list); lp; lp = next) {
5352621Sllai1 		next = list_next(&ncl->ncl_list, lp);
5362621Sllai1 		if (sdev_nc_expirecnt > 0 && lp->ncn_expirecnt > 0) {
5372621Sllai1 			if (lp->ncn_flags & NCN_ACTIVE) {
5382621Sllai1 				if (lp->ncn_expirecnt != sdev_nc_expirecnt) {
5392621Sllai1 					lp->ncn_expirecnt = sdev_nc_expirecnt;
5402621Sllai1 					ncl->ncl_flags |= NCL_LIST_DIRTY;
5412621Sllai1 				}
5422621Sllai1 			} else {
5432621Sllai1 				if (--lp->ncn_expirecnt == 0) {
5442621Sllai1 					list_remove(&ncl->ncl_list, lp);
5452621Sllai1 					sdev_nc_free_unlinked_node(lp);
5462621Sllai1 					ncl->ncl_nentries--;
5472621Sllai1 				}
5482621Sllai1 				ncl->ncl_flags |= NCL_LIST_DIRTY;
5492621Sllai1 			}
5502621Sllai1 		}
5512621Sllai1 	}
5522621Sllai1 
5532621Sllai1 	mutex_exit(&ncl->ncl_mutex);
5542621Sllai1 	rw_exit(&ncl->ncl_lock);
5552621Sllai1 
5562621Sllai1 	sdev_nc_flush_boot_update();
5572621Sllai1 	sdev_boot_state = SDEV_BOOT_STATE_COMPLETE;
5582621Sllai1 }
5592621Sllai1 
5602621Sllai1 /*
5612621Sllai1  * Upon transition to the login state on a reconfigure boot,
5622621Sllai1  * a debounce timer is set up so that we cache all the nonsense
5632621Sllai1  * lookups we're hit with by the windowing system startup.
5642621Sllai1  */
5652621Sllai1 
5662621Sllai1 /*ARGSUSED*/
5672621Sllai1 static void
5682621Sllai1 sdev_state_timeout(void *arg)
5692621Sllai1 {
5702621Sllai1 	sdev_timeout_id = 0;
5712621Sllai1 	sdev_state_boot_complete();
5722621Sllai1 }
5732621Sllai1 
5742621Sllai1 static void
5752621Sllai1 sdev_state_sysavail()
5762621Sllai1 {
5772621Sllai1 	sdev_nc_list_t *ncl = sdev_ncache;
5782621Sllai1 	clock_t	nticks;
5792621Sllai1 	int nsecs;
5802621Sllai1 
5812621Sllai1 	mutex_enter(&ncl->ncl_mutex);
5822621Sllai1 	ncl->ncl_flags |= NCL_LIST_WENABLE;
5832621Sllai1 	mutex_exit(&ncl->ncl_mutex);
5842621Sllai1 
5852621Sllai1 	nsecs = sdev_reconfig_delay;
5862621Sllai1 	if (nsecs == 0) {
5872621Sllai1 		sdev_state_boot_complete();
5882621Sllai1 	} else {
5892621Sllai1 		nticks = drv_usectohz(1000000 * nsecs);
5902621Sllai1 		sdcmn_err5(("timeout initiated %ld\n", nticks));
5912621Sllai1 		sdev_timeout_id = timeout(sdev_state_timeout, NULL, nticks);
5922621Sllai1 		sdev_nc_flush_boot_update();
5932621Sllai1 	}
5942621Sllai1 }
5952621Sllai1 
5962621Sllai1 /*
5972621Sllai1  * Called to inform the filesystem of progress during boot,
5982621Sllai1  * either a notice of reconfiguration boot or an indication of
5992621Sllai1  * system boot complete.  At system boot complete, set up a
6002621Sllai1  * timer at the expiration of which no further failed lookups
6012621Sllai1  * will be added to the negative cache.
6022621Sllai1  *
6032621Sllai1  * The dev filesystem infers from reconfig boot that implicit
6042621Sllai1  * reconfig need not be invoked at all as all available devices
6052621Sllai1  * will have already been named.
6062621Sllai1  *
6072621Sllai1  * The dev filesystem infers from "system available" that devfsadmd
6082621Sllai1  * can now be run and hence implicit reconfiguration may be initiated.
6092621Sllai1  * During early stages of system startup, implicit reconfig is
6102621Sllai1  * not done to avoid impacting boot performance.
6112621Sllai1  */
6122621Sllai1 void
6132621Sllai1 sdev_devstate_change(void)
6142621Sllai1 {
6152621Sllai1 	int new_state;
6162621Sllai1 
6172621Sllai1 	/*
6182621Sllai1 	 * Track system state and manage interesting transitions
6192621Sllai1 	 */
6202621Sllai1 	new_state = SDEV_BOOT_STATE_INITIAL;
6212621Sllai1 	if (i_ddi_reconfig())
6222621Sllai1 		new_state = SDEV_BOOT_STATE_RECONFIG;
6232621Sllai1 	if (i_ddi_sysavail())
6242621Sllai1 		new_state = SDEV_BOOT_STATE_SYSAVAIL;
6252621Sllai1 
6262621Sllai1 	if (sdev_boot_state < new_state) {
6272621Sllai1 		switch (new_state) {
6282621Sllai1 		case SDEV_BOOT_STATE_RECONFIG:
6292621Sllai1 			sdcmn_err5(("state change: reconfigure boot\n"));
6302621Sllai1 			sdev_boot_state = new_state;
6312621Sllai1 			sdev_reconfig_boot = 1;
6322621Sllai1 			if (!sdev_nc_disable_reset)
6332621Sllai1 				sdev_nc_free_bootonly();
6342621Sllai1 			break;
6352621Sllai1 		case SDEV_BOOT_STATE_SYSAVAIL:
6362621Sllai1 			sdcmn_err5(("system available\n"));
6372621Sllai1 			sdev_boot_state = new_state;
6382621Sllai1 			sdev_state_sysavail();
6392621Sllai1 			break;
6402621Sllai1 		}
6412621Sllai1 	}
6422621Sllai1 }
6432621Sllai1 
6442621Sllai1 /*
6452621Sllai1  * Lookup: filter out entries in the negative cache
6462621Sllai1  * Return 1 if the lookup should not cause a reconfig.
6472621Sllai1  */
6482621Sllai1 int
6492621Sllai1 sdev_lookup_filter(sdev_node_t *dv, char *nm)
6502621Sllai1 {
6512621Sllai1 	int n;
6522621Sllai1 	sdev_nc_list_t *ncl = sdev_ncache;
6532621Sllai1 	sdev_nc_node_t *lp;
6542621Sllai1 	char *path;
6552621Sllai1 	int rval = 0;
6562621Sllai1 	int changed = 0;
6572621Sllai1 
6582621Sllai1 	ASSERT(i_ddi_io_initialized());
6592621Sllai1 	ASSERT(SDEVTOV(dv)->v_type == VDIR);
6602621Sllai1 
6612621Sllai1 	if (sdev_nc_disable)
6622621Sllai1 		return (0);
6632621Sllai1 
6642621Sllai1 	n = strlen(dv->sdev_path) + strlen(nm) + 2;
6652621Sllai1 	path = kmem_alloc(n, KM_SLEEP);
6662621Sllai1 	(void) sprintf(path, "%s/%s", dv->sdev_path, nm);
6672621Sllai1 
6682621Sllai1 	rw_enter(&ncl->ncl_lock, RW_READER);
6692621Sllai1 	if ((lp = sdev_nc_findpath(ncl, path)) != NULL) {
6702621Sllai1 		sdcmn_err5(("%s/%s: lookup by %s cached, no reconfig\n",
6712621Sllai1 		    dv->sdev_name, nm, curproc->p_user.u_comm));
6722621Sllai1 		if (sdev_nc_verbose) {
6732621Sllai1 			cmn_err(CE_CONT,
6742621Sllai1 			    "?%s/%s: lookup by %s cached, no reconfig\n",
6752621Sllai1 			    dv->sdev_name, nm, curproc->p_user.u_comm);
6762621Sllai1 		}
6772621Sllai1 		mutex_enter(&ncl->ncl_mutex);
6782621Sllai1 		lp->ncn_flags |= NCN_ACTIVE;
6792621Sllai1 		if (sdev_nc_expirecnt > 0 && lp->ncn_expirecnt > 0 &&
6802621Sllai1 		    lp->ncn_expirecnt < sdev_nc_expirecnt) {
6812621Sllai1 			lp->ncn_expirecnt = sdev_nc_expirecnt;
6822621Sllai1 			ncl->ncl_flags |= NCL_LIST_DIRTY;
6832621Sllai1 			changed = 1;
6842621Sllai1 		}
6852621Sllai1 		mutex_exit(&ncl->ncl_mutex);
6862621Sllai1 		rval = 1;
6872621Sllai1 	}
6882621Sllai1 	rw_exit(&ncl->ncl_lock);
6892621Sllai1 	kmem_free(path, n);
6902621Sllai1 	if (changed)
6912621Sllai1 		sdev_nc_flush_boot_update();
6922621Sllai1 	return (rval);
6932621Sllai1 }
6942621Sllai1 
6952621Sllai1 void
6962621Sllai1 sdev_lookup_failed(sdev_node_t *dv, char *nm, int failed_flags)
6972621Sllai1 {
6982621Sllai1 	if (sdev_nc_disable)
6992621Sllai1 		return;
7002621Sllai1 
7012621Sllai1 	/*
7022621Sllai1 	 * If we're still in the initial boot stage, always update
7032621Sllai1 	 * the cache - we may not have received notice of the
7042621Sllai1 	 * reconfig boot state yet.  On a reconfigure boot, entries
7052621Sllai1 	 * from the backing store are not re-persisted on update,
7062621Sllai1 	 * but new entries are marked as needing an update.
7072621Sllai1 	 * Never cache dynamic or non-global nodes.
7082621Sllai1 	 */
7092621Sllai1 	if (SDEV_IS_GLOBAL(dv) && !SDEV_IS_DYNAMIC(dv) &&
7102621Sllai1 	    !SDEV_IS_NO_NCACHE(dv) &&
7112621Sllai1 	    ((failed_flags & SLF_NO_NCACHE) == 0) &&
7122621Sllai1 	    ((sdev_reconfig_boot &&
7132621Sllai1 		(sdev_boot_state != SDEV_BOOT_STATE_COMPLETE)) ||
7142621Sllai1 	    (!sdev_reconfig_boot && ((failed_flags & SLF_REBUILT))))) {
7152621Sllai1 			sdev_nc_addname(sdev_ncache,
7162621Sllai1 			    dv, nm, NCN_SRC_CURRENT|NCN_ACTIVE);
7172621Sllai1 	}
7182621Sllai1 }
7192621Sllai1 
7202621Sllai1 static sdev_nc_list_t *
7212621Sllai1 sdev_nc_newlist(void)
7222621Sllai1 {
7232621Sllai1 	sdev_nc_list_t	*ncl;
7242621Sllai1 
7252621Sllai1 	ncl = kmem_zalloc(sizeof (sdev_nc_list_t), KM_SLEEP);
7262621Sllai1 
7272621Sllai1 	rw_init(&ncl->ncl_lock, NULL, RW_DEFAULT, NULL);
7282621Sllai1 	mutex_init(&ncl->ncl_mutex, NULL, MUTEX_DEFAULT, NULL);
7292621Sllai1 	list_create(&ncl->ncl_list, sizeof (sdev_nc_node_t),
7302621Sllai1 	    offsetof(sdev_nc_node_t, ncn_link));
7312621Sllai1 
7322621Sllai1 	return (ncl);
7332621Sllai1 }
7342621Sllai1 
7352621Sllai1 static void
7362621Sllai1 sdev_nc_free_unlinked_node(sdev_nc_node_t *lp)
7372621Sllai1 {
7382621Sllai1 	kmem_free(lp->ncn_name, strlen(lp->ncn_name) + 1);
7392621Sllai1 	kmem_free(lp, sizeof (sdev_nc_node_t));
7402621Sllai1 }
7412621Sllai1 
7422621Sllai1 static void
7432621Sllai1 sdev_nc_free_all_nodes(sdev_nc_list_t *ncl)
7442621Sllai1 {
7452621Sllai1 	sdev_nc_node_t *lp;
7462621Sllai1 
7472621Sllai1 	while ((lp = list_head(&ncl->ncl_list)) != NULL) {
7482621Sllai1 		list_remove(&ncl->ncl_list, lp);
7492621Sllai1 		sdev_nc_free_unlinked_node(lp);
7502621Sllai1 		ncl->ncl_nentries--;
7512621Sllai1 	}
7522621Sllai1 	ASSERT(ncl->ncl_nentries == 0);
7532621Sllai1 }
7542621Sllai1 
7552621Sllai1 static void
7562621Sllai1 sdev_nc_freelist(sdev_nc_list_t *ncl)
7572621Sllai1 {
7582621Sllai1 	if (!list_is_empty(&ncl->ncl_list))
7592621Sllai1 		sdev_nc_free_all_nodes(ncl);
7602621Sllai1 	ASSERT(list_is_empty(&ncl->ncl_list));
7612621Sllai1 	ASSERT(ncl->ncl_nentries == 0);
7622621Sllai1 
7632621Sllai1 	mutex_destroy(&ncl->ncl_mutex);
7642621Sllai1 	rw_destroy(&ncl->ncl_lock);
7652621Sllai1 	list_destroy(&ncl->ncl_list);
7662621Sllai1 	kmem_free(ncl, sizeof (sdev_nc_list_t));
7672621Sllai1 }
7682621Sllai1 
7692621Sllai1 static sdev_nc_node_t *
7702621Sllai1 sdev_nc_findpath(sdev_nc_list_t *ncl, char *path)
7712621Sllai1 {
7722621Sllai1 	sdev_nc_node_t *lp;
7732621Sllai1 
7742621Sllai1 	ASSERT(RW_LOCK_HELD(&ncl->ncl_lock));
7752621Sllai1 
7762621Sllai1 	for (lp = list_head(&ncl->ncl_list); lp;
7772621Sllai1 	    lp = list_next(&ncl->ncl_list, lp)) {
7782621Sllai1 		if (strcmp(path, lp->ncn_name) == 0)
7792621Sllai1 			return (lp);
7802621Sllai1 	}
7812621Sllai1 
7822621Sllai1 	return (NULL);
7832621Sllai1 }
7842621Sllai1 
7852621Sllai1 static void
7862621Sllai1 sdev_nc_insertnode(sdev_nc_list_t *ncl, sdev_nc_node_t *new)
7872621Sllai1 {
7882621Sllai1 	sdev_nc_node_t *lp;
7892621Sllai1 
7902621Sllai1 	rw_enter(&ncl->ncl_lock, RW_WRITER);
7912621Sllai1 
7922621Sllai1 	lp = sdev_nc_findpath(ncl, new->ncn_name);
7932621Sllai1 	if (lp == NULL) {
7942621Sllai1 		if (ncl->ncl_nentries == sdev_nc_max_entries) {
7952621Sllai1 			sdcmn_err5((
7962621Sllai1 			    "%s by %s: not adding to ncache (max %d)\n",
7972621Sllai1 			    new->ncn_name, curproc->p_user.u_comm,
7982621Sllai1 			    ncl->ncl_nentries));
7992621Sllai1 			if (sdev_nc_verbose) {
8002621Sllai1 				cmn_err(CE_CONT, "?%s by %s: "
8012621Sllai1 				    "not adding to ncache (max %d)\n",
8022621Sllai1 				    new->ncn_name, curproc->p_user.u_comm,
8032621Sllai1 				    ncl->ncl_nentries);
8042621Sllai1 			}
8052621Sllai1 			rw_exit(&ncl->ncl_lock);
8062621Sllai1 			sdev_nc_free_unlinked_node(new);
8072621Sllai1 		} else {
8082621Sllai1 
8092621Sllai1 			list_insert_tail(&ncl->ncl_list, new);
8102621Sllai1 			ncl->ncl_nentries++;
8112621Sllai1 
8122621Sllai1 			/* don't mark list dirty for nodes from store */
8132621Sllai1 			mutex_enter(&ncl->ncl_mutex);
8142621Sllai1 			if ((new->ncn_flags & NCN_SRC_STORE) == 0) {
8152621Sllai1 				sdcmn_err5(("%s by %s: add to ncache\n",
8162621Sllai1 				    new->ncn_name, curproc->p_user.u_comm));
8172621Sllai1 				if (sdev_nc_verbose) {
8182621Sllai1 					cmn_err(CE_CONT,
8192621Sllai1 					    "?%s by %s: add to ncache\n",
8202621Sllai1 					    new->ncn_name,
8212621Sllai1 					    curproc->p_user.u_comm);
8222621Sllai1 				}
8232621Sllai1 				ncl->ncl_flags |= NCL_LIST_DIRTY;
8242621Sllai1 			}
8252621Sllai1 			mutex_exit(&ncl->ncl_mutex);
8262621Sllai1 			rw_exit(&ncl->ncl_lock);
8272621Sllai1 			lp = new;
8282621Sllai1 			sdev_nc_flush_boot_update();
8292621Sllai1 		}
8302621Sllai1 	} else {
8312621Sllai1 		mutex_enter(&ncl->ncl_mutex);
8322621Sllai1 		lp->ncn_flags |= new->ncn_flags;
8332621Sllai1 		mutex_exit(&ncl->ncl_mutex);
8342621Sllai1 		rw_exit(&ncl->ncl_lock);
8352621Sllai1 		sdev_nc_free_unlinked_node(new);
8362621Sllai1 	}
8372621Sllai1 }
8382621Sllai1 
8392621Sllai1 void
8402621Sllai1 sdev_nc_addname(sdev_nc_list_t *ncl, sdev_node_t *dv, char *nm, int flags)
8412621Sllai1 {
8422621Sllai1 	int n;
8432621Sllai1 	sdev_nc_node_t *lp;
8442621Sllai1 
8452621Sllai1 	ASSERT(SDEVTOV(dv)->v_type == VDIR);
8462621Sllai1 
8472621Sllai1 	lp = kmem_zalloc(sizeof (sdev_nc_node_t), KM_SLEEP);
8482621Sllai1 
8492621Sllai1 	n = strlen(dv->sdev_path) + strlen(nm) + 2;
8502621Sllai1 	lp->ncn_name = kmem_alloc(n, KM_SLEEP);
8512621Sllai1 	(void) sprintf(lp->ncn_name, "%s/%s",
8522621Sllai1 		dv->sdev_path, nm);
8532621Sllai1 	lp->ncn_flags = flags;
8542621Sllai1 	lp->ncn_expirecnt = sdev_nc_expirecnt;
8552621Sllai1 	sdev_nc_insertnode(ncl, lp);
8562621Sllai1 }
8572621Sllai1 
8582621Sllai1 void
8592621Sllai1 sdev_nc_node_exists(sdev_node_t *dv)
8602621Sllai1 {
8612621Sllai1 	/* dynamic and non-global nodes are never cached */
8622621Sllai1 	if (SDEV_IS_GLOBAL(dv) && !SDEV_IS_DYNAMIC(dv) &&
8632621Sllai1 	    !SDEV_IS_NO_NCACHE(dv)) {
8642621Sllai1 		sdev_nc_path_exists(sdev_ncache, dv->sdev_path);
8652621Sllai1 	}
8662621Sllai1 }
8672621Sllai1 
8682621Sllai1 void
8692621Sllai1 sdev_nc_path_exists(sdev_nc_list_t *ncl, char *path)
8702621Sllai1 {
8712621Sllai1 	sdev_nc_node_t *lp;
8722621Sllai1 
8732621Sllai1 	if (sdev_nc_disable)
8742621Sllai1 		return;
8752621Sllai1 
8762621Sllai1 	rw_enter(&ncl->ncl_lock, RW_READER);
8772621Sllai1 	if ((lp = sdev_nc_findpath(ncl, path)) == NULL) {
8782621Sllai1 		rw_exit(&ncl->ncl_lock);
8792621Sllai1 		return;
8802621Sllai1 	}
8812621Sllai1 	if (rw_tryupgrade(&ncl->ncl_lock) == 0) {
8822621Sllai1 		rw_exit(&ncl->ncl_lock);
8832621Sllai1 		rw_enter(&ncl->ncl_lock, RW_WRITER);
8842621Sllai1 		lp = sdev_nc_findpath(ncl, path);
8852621Sllai1 	}
8862621Sllai1 	if (lp) {
8872621Sllai1 		list_remove(&ncl->ncl_list, lp);
8882621Sllai1 		ncl->ncl_nentries--;
8892621Sllai1 		mutex_enter(&ncl->ncl_mutex);
8902621Sllai1 		ncl->ncl_flags |= NCL_LIST_DIRTY;
8912621Sllai1 		if (ncl->ncl_flags & NCL_LIST_WENABLE) {
8922621Sllai1 			mutex_exit(&ncl->ncl_mutex);
8932621Sllai1 			rw_exit(&ncl->ncl_lock);
8942621Sllai1 			sdev_nc_flush_updates();
8952621Sllai1 		} else {
8962621Sllai1 			mutex_exit(&ncl->ncl_mutex);
8972621Sllai1 			rw_exit(&ncl->ncl_lock);
8982621Sllai1 		}
8992621Sllai1 		sdev_nc_free_unlinked_node(lp);
9002621Sllai1 		sdcmn_err5(("%s by %s: removed from ncache\n",
9012621Sllai1 		    path, curproc->p_user.u_comm));
9022621Sllai1 		if (sdev_nc_verbose) {
9032621Sllai1 			cmn_err(CE_CONT, "?%s by %s: removed from ncache\n",
9042621Sllai1 			    path, curproc->p_user.u_comm);
9052621Sllai1 		}
9062621Sllai1 	} else
9072621Sllai1 		rw_exit(&ncl->ncl_lock);
9082621Sllai1 }
9092621Sllai1 
9102621Sllai1 static void
9112621Sllai1 sdev_nc_free_bootonly(void)
9122621Sllai1 {
9132621Sllai1 	sdev_nc_list_t	*ncl = sdev_ncache;
9142621Sllai1 	sdev_nc_node_t *lp;
9152621Sllai1 	sdev_nc_node_t *next;
9162621Sllai1 
9172621Sllai1 	ASSERT(sdev_reconfig_boot);
9182621Sllai1 
9192621Sllai1 	rw_enter(&ncl->ncl_lock, RW_WRITER);
9202621Sllai1 
9212621Sllai1 	for (lp = list_head(&ncl->ncl_list); lp; lp = next) {
9222621Sllai1 		next = list_next(&ncl->ncl_list, lp);
9232621Sllai1 		if ((lp->ncn_flags & NCN_SRC_CURRENT) == 0) {
9242621Sllai1 			sdcmn_err5(("freeing %s\n", lp->ncn_name));
9252621Sllai1 			mutex_enter(&ncl->ncl_mutex);
9262621Sllai1 			ncl->ncl_flags |= NCL_LIST_DIRTY;
9272621Sllai1 			mutex_exit(&ncl->ncl_mutex);
9282621Sllai1 			list_remove(&ncl->ncl_list, lp);
9292621Sllai1 			sdev_nc_free_unlinked_node(lp);
9302621Sllai1 			ncl->ncl_nentries--;
9312621Sllai1 		}
9322621Sllai1 	}
9332621Sllai1 
9342621Sllai1 	rw_exit(&ncl->ncl_lock);
9352621Sllai1 }
936