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