xref: /onnv-gate/usr/src/uts/common/os/devid_cache.c (revision 12537:d4a9da515619)
12797Sjg /*
22797Sjg  * CDDL HEADER START
32797Sjg  *
42797Sjg  * The contents of this file are subject to the terms of the
52797Sjg  * Common Development and Distribution License (the "License").
62797Sjg  * You may not use this file except in compliance with the License.
72797Sjg  *
82797Sjg  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92797Sjg  * or http://www.opensolaris.org/os/licensing.
102797Sjg  * See the License for the specific language governing permissions
112797Sjg  * and limitations under the License.
122797Sjg  *
132797Sjg  * When distributing Covered Code, include this CDDL HEADER in each
142797Sjg  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152797Sjg  * If applicable, add the following below this CDDL HEADER, with the
162797Sjg  * fields enclosed by brackets "[]" replaced with your own identifying
172797Sjg  * information: Portions Copyright [yyyy] [name of copyright owner]
182797Sjg  *
192797Sjg  * CDDL HEADER END
202797Sjg  */
212797Sjg /*
2212121SReed.Liu@Sun.COM  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
232797Sjg  */
242797Sjg 
252797Sjg #include <sys/note.h>
262797Sjg #include <sys/t_lock.h>
272797Sjg #include <sys/cmn_err.h>
282797Sjg #include <sys/instance.h>
292797Sjg #include <sys/conf.h>
302797Sjg #include <sys/stat.h>
312797Sjg #include <sys/ddi.h>
322797Sjg #include <sys/hwconf.h>
332797Sjg #include <sys/sunddi.h>
342797Sjg #include <sys/sunndi.h>
3512213SGavin.Maltby@Sun.COM #include <sys/sunmdi.h>
362797Sjg #include <sys/ddi_impldefs.h>
372797Sjg #include <sys/ndi_impldefs.h>
382797Sjg #include <sys/kobj.h>
392797Sjg #include <sys/devcache.h>
402797Sjg #include <sys/devid_cache.h>
412797Sjg #include <sys/sysmacros.h>
422797Sjg 
432797Sjg /*
442797Sjg  * Discovery refers to the heroic effort made to discover a device which
452797Sjg  * cannot be accessed at the physical path where it once resided.  Discovery
462797Sjg  * involves walking the entire device tree attaching all possible disk
472797Sjg  * instances, to search for the device referenced by a devid.  Obviously,
482797Sjg  * full device discovery is something to be avoided where possible.
492797Sjg  * Note that simply invoking devfsadm(1M) is equivalent to running full
502797Sjg  * discovery at the devid cache level.
512797Sjg  *
522797Sjg  * Reasons why a disk may not be accessible:
532797Sjg  *	disk powered off
542797Sjg  *	disk removed or cable disconnected
552797Sjg  *	disk or adapter broken
562797Sjg  *
572797Sjg  * Note that discovery is not needed and cannot succeed in any of these
582797Sjg  * cases.
592797Sjg  *
602797Sjg  * When discovery may succeed:
612797Sjg  *	Discovery will result in success when a device has been moved
622797Sjg  *	to a different address.  Note that it's recommended that
632797Sjg  *	devfsadm(1M) be invoked (no arguments required) whenever a system's
642797Sjg  *	h/w configuration has been updated.  Alternatively, a
652797Sjg  *	reconfiguration boot can be used to accomplish the same result.
662797Sjg  *
672797Sjg  * Note that discovery is not necessary to be able to correct an access
682797Sjg  * failure for a device which was powered off.  Assuming the cache has an
692797Sjg  * entry for such a device, simply powering it on should permit the system
702797Sjg  * to access it.  If problems persist after powering it on, invoke
712797Sjg  * devfsadm(1M).
722797Sjg  *
732797Sjg  * Discovery prior to mounting root is only of interest when booting
742797Sjg  * from a filesystem which accesses devices by device id, which of
752797Sjg  * not all do.
762797Sjg  *
772797Sjg  * Tunables
782797Sjg  *
792797Sjg  * devid_discovery_boot (default 1)
802797Sjg  *	Number of times discovery will be attempted prior to mounting root.
812797Sjg  *	Must be done at least once to recover from corrupted or missing
822797Sjg  *	devid cache backing store.  Probably there's no reason to ever
8312213SGavin.Maltby@Sun.COM  *	set this to greater than one as a missing device will remain
842797Sjg  *	unavailable no matter how often the system searches for it.
852797Sjg  *
862797Sjg  * devid_discovery_postboot (default 1)
872797Sjg  *	Number of times discovery will be attempted after mounting root.
882797Sjg  *	This must be performed at least once to discover any devices
892797Sjg  *	needed after root is mounted which may have been powered
902797Sjg  *	off and moved before booting.
912797Sjg  *	Setting this to a larger positive number will introduce
922797Sjg  *	some inconsistency in system operation.  Searching for a device
932797Sjg  *	will take an indeterminate amount of time, sometimes slower,
942797Sjg  *	sometimes faster.  In addition, the system will sometimes
952797Sjg  *	discover a newly powered on device, sometimes it won't.
962797Sjg  *	Use of this option is not therefore recommended.
972797Sjg  *
982797Sjg  * devid_discovery_postboot_always (default 0)
992797Sjg  *	Set to 1, the system will always attempt full discovery.
1002797Sjg  *
1012797Sjg  * devid_discovery_secs (default 0)
1022797Sjg  *	Set to a positive value, the system will attempt full discovery
1032797Sjg  *	but with a minimum delay between attempts.  A device search
1042797Sjg  *	within the period of time specified will result in failure.
1052797Sjg  *
1062797Sjg  * devid_cache_read_disable (default 0)
1072797Sjg  *	Set to 1 to disable reading /etc/devices/devid_cache.
1082797Sjg  *	Devid cache will continue to operate normally but
1092797Sjg  *	at least one discovery attempt will be required.
1102797Sjg  *
1112797Sjg  * devid_cache_write_disable (default 0)
1122797Sjg  *	Set to 1 to disable updates to /etc/devices/devid_cache.
1132797Sjg  *	Any updates to the devid cache will not be preserved across a reboot.
1142797Sjg  *
1152797Sjg  * devid_report_error (default 0)
1162797Sjg  *	Set to 1 to enable some error messages related to devid
1172797Sjg  *	cache failures.
1182797Sjg  *
1192797Sjg  * The devid is packed in the cache file as a byte array.  For
1202797Sjg  * portability, this could be done in the encoded string format.
1212797Sjg  */
1222797Sjg 
1232797Sjg 
1242797Sjg int devid_discovery_boot = 1;
1252797Sjg int devid_discovery_postboot = 1;
1262797Sjg int devid_discovery_postboot_always = 0;
1272797Sjg int devid_discovery_secs = 0;
1282797Sjg 
1292797Sjg int devid_cache_read_disable = 0;
1302797Sjg int devid_cache_write_disable = 0;
1312797Sjg 
1322797Sjg int devid_report_error = 0;
1332797Sjg 
1342797Sjg 
1352797Sjg /*
1362797Sjg  * State to manage discovery of devices providing a devid
1372797Sjg  */
1382797Sjg static int		devid_discovery_busy = 0;
1392797Sjg static kmutex_t		devid_discovery_mutex;
1402797Sjg static kcondvar_t	devid_discovery_cv;
1412797Sjg static clock_t		devid_last_discovery = 0;
1422797Sjg 
1432797Sjg 
1442797Sjg #ifdef	DEBUG
1452797Sjg int nvp_devid_debug = 0;
1462797Sjg int devid_debug = 0;
1472797Sjg int devid_log_registers = 0;
1482797Sjg int devid_log_finds = 0;
1492797Sjg int devid_log_lookups = 0;
1502797Sjg int devid_log_discovery = 0;
1512797Sjg int devid_log_matches = 0;
1522797Sjg int devid_log_paths = 0;
1532797Sjg int devid_log_failures = 0;
1542797Sjg int devid_log_hold = 0;
1552797Sjg int devid_log_unregisters = 0;
1562797Sjg int devid_log_removes = 0;
1572797Sjg int devid_register_debug = 0;
1582797Sjg int devid_log_stale = 0;
1592797Sjg int devid_log_detaches = 0;
1602797Sjg #endif	/* DEBUG */
1612797Sjg 
1622797Sjg /*
1632797Sjg  * devid cache file registration for cache reads and updates
1642797Sjg  */
1652797Sjg static nvf_ops_t devid_cache_ops = {
1662797Sjg 	"/etc/devices/devid_cache",		/* path to cache */
1672797Sjg 	devid_cache_unpack_nvlist,		/* read: nvlist to nvp */
1682797Sjg 	devid_cache_pack_list,			/* write: nvp to nvlist */
1692797Sjg 	devid_list_free,			/* free data list */
1702797Sjg 	NULL					/* write complete callback */
1712797Sjg };
1722797Sjg 
1732797Sjg /*
1742797Sjg  * handle to registered devid cache handlers
1752797Sjg  */
1762797Sjg nvf_handle_t	dcfd_handle;
1772797Sjg 
1782797Sjg 
1792797Sjg /*
1802797Sjg  * Initialize devid cache file management
1812797Sjg  */
1822797Sjg void
1832797Sjg devid_cache_init(void)
1842797Sjg {
1852797Sjg 	dcfd_handle = nvf_register_file(&devid_cache_ops);
1862797Sjg 	ASSERT(dcfd_handle);
1872797Sjg 
1882797Sjg 	list_create(nvf_list(dcfd_handle), sizeof (nvp_devid_t),
1892797Sjg 	    offsetof(nvp_devid_t, nvp_link));
1902797Sjg 
1912797Sjg 	mutex_init(&devid_discovery_mutex, NULL, MUTEX_DEFAULT, NULL);
1922797Sjg 	cv_init(&devid_discovery_cv, NULL, CV_DRIVER, NULL);
1932797Sjg }
1942797Sjg 
1952797Sjg /*
1962797Sjg  * Read and initialize the devid cache from the persistent store
1972797Sjg  */
1982797Sjg void
1992797Sjg devid_cache_read(void)
2002797Sjg {
2012797Sjg 	if (!devid_cache_read_disable) {
2022797Sjg 		rw_enter(nvf_lock(dcfd_handle), RW_WRITER);
2032797Sjg 		ASSERT(list_head(nvf_list(dcfd_handle)) == NULL);
2042797Sjg 		(void) nvf_read_file(dcfd_handle);
2052797Sjg 		rw_exit(nvf_lock(dcfd_handle));
2062797Sjg 	}
2072797Sjg }
2082797Sjg 
2092797Sjg static void
2102797Sjg devid_nvp_free(nvp_devid_t *dp)
2112797Sjg {
2122797Sjg 	if (dp->nvp_devpath)
2132797Sjg 		kmem_free(dp->nvp_devpath, strlen(dp->nvp_devpath)+1);
2142797Sjg 	if (dp->nvp_devid)
2152797Sjg 		kmem_free(dp->nvp_devid, ddi_devid_sizeof(dp->nvp_devid));
2162797Sjg 
2172797Sjg 	kmem_free(dp, sizeof (nvp_devid_t));
2182797Sjg }
2192797Sjg 
2202797Sjg static void
2212797Sjg devid_list_free(nvf_handle_t fd)
2222797Sjg {
2232797Sjg 	list_t		*listp;
2242797Sjg 	nvp_devid_t	*np;
2252797Sjg 
2262797Sjg 	ASSERT(RW_WRITE_HELD(nvf_lock(dcfd_handle)));
2272797Sjg 
2282797Sjg 	listp = nvf_list(fd);
2292797Sjg 	while (np = list_head(listp)) {
2302797Sjg 		list_remove(listp, np);
2312797Sjg 		devid_nvp_free(np);
2322797Sjg 	}
2332797Sjg }
2342797Sjg 
2352797Sjg /*
2362797Sjg  * Free an nvp element in a list
2372797Sjg  */
2382797Sjg static void
2392797Sjg devid_nvp_unlink_and_free(nvf_handle_t fd, nvp_devid_t *np)
2402797Sjg {
2412797Sjg 	list_remove(nvf_list(fd), np);
2422797Sjg 	devid_nvp_free(np);
2432797Sjg }
2442797Sjg 
2452797Sjg /*
2462797Sjg  * Unpack a device path/nvlist pair to the list of devid cache elements.
2472797Sjg  * Used to parse the nvlist format when reading
2482797Sjg  * /etc/devices/devid_cache
2492797Sjg  */
2502797Sjg static int
2512797Sjg devid_cache_unpack_nvlist(nvf_handle_t fd, nvlist_t *nvl, char *name)
2522797Sjg {
2532797Sjg 	nvp_devid_t *np;
2542797Sjg 	ddi_devid_t devidp;
2552797Sjg 	int rval;
2562797Sjg 	uint_t n;
2572797Sjg 
2582797Sjg 	NVP_DEVID_DEBUG_PATH((name));
2592797Sjg 	ASSERT(RW_WRITE_HELD(nvf_lock(dcfd_handle)));
2602797Sjg 
2612797Sjg 	/*
2622797Sjg 	 * check path for a devid
2632797Sjg 	 */
2642797Sjg 	rval = nvlist_lookup_byte_array(nvl,
2657009Scth 	    DP_DEVID_ID, (uchar_t **)&devidp, &n);
2662797Sjg 	if (rval == 0) {
2672797Sjg 		if (ddi_devid_valid(devidp) == DDI_SUCCESS) {
2682797Sjg 			ASSERT(n == ddi_devid_sizeof(devidp));
2692797Sjg 			np = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP);
2702797Sjg 			np->nvp_devpath = i_ddi_strdup(name, KM_SLEEP);
2712797Sjg 			np->nvp_devid = kmem_alloc(n, KM_SLEEP);
2722797Sjg 			(void) bcopy(devidp, np->nvp_devid, n);
2732797Sjg 			list_insert_tail(nvf_list(fd), np);
2742797Sjg 			NVP_DEVID_DEBUG_DEVID((np->nvp_devid));
2752797Sjg 		} else {
2762797Sjg 			DEVIDERR((CE_CONT,
2772797Sjg 			    "%s: invalid devid\n", name));
2782797Sjg 		}
2792797Sjg 	} else {
2802797Sjg 		DEVIDERR((CE_CONT,
2812797Sjg 		    "%s: devid not available\n", name));
2822797Sjg 	}
2832797Sjg 
2842797Sjg 	return (0);
2852797Sjg }
2862797Sjg 
2872797Sjg /*
2882797Sjg  * Pack the list of devid cache elements into a single nvlist
2892797Sjg  * Used when writing the nvlist file.
2902797Sjg  */
2912797Sjg static int
2922797Sjg devid_cache_pack_list(nvf_handle_t fd, nvlist_t **ret_nvl)
2932797Sjg {
2942797Sjg 	nvlist_t	*nvl, *sub_nvl;
2952797Sjg 	nvp_devid_t	*np;
2962797Sjg 	int		rval;
2972797Sjg 	list_t		*listp;
2982797Sjg 
2992797Sjg 	ASSERT(RW_WRITE_HELD(nvf_lock(dcfd_handle)));
3002797Sjg 
3012797Sjg 	rval = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
3022797Sjg 	if (rval != 0) {
3032797Sjg 		nvf_error("%s: nvlist alloc error %d\n",
3047009Scth 		    nvf_cache_name(fd), rval);
3052797Sjg 		return (DDI_FAILURE);
3062797Sjg 	}
3072797Sjg 
3082797Sjg 	listp = nvf_list(fd);
3092797Sjg 	for (np = list_head(listp); np; np = list_next(listp, np)) {
3102797Sjg 		if (np->nvp_devid == NULL)
3117009Scth 			continue;
3122797Sjg 		NVP_DEVID_DEBUG_PATH(np->nvp_devpath);
3132797Sjg 		rval = nvlist_alloc(&sub_nvl, NV_UNIQUE_NAME, KM_SLEEP);
3142797Sjg 		if (rval != 0) {
3152797Sjg 			nvf_error("%s: nvlist alloc error %d\n",
3167009Scth 			    nvf_cache_name(fd), rval);
3172797Sjg 			sub_nvl = NULL;
3182797Sjg 			goto err;
3192797Sjg 		}
3202797Sjg 
3212797Sjg 		rval = nvlist_add_byte_array(sub_nvl, DP_DEVID_ID,
3227009Scth 		    (uchar_t *)np->nvp_devid,
3237009Scth 		    ddi_devid_sizeof(np->nvp_devid));
3242797Sjg 		if (rval == 0) {
3252797Sjg 			NVP_DEVID_DEBUG_DEVID(np->nvp_devid);
3262797Sjg 		} else {
3272797Sjg 			nvf_error(
3282797Sjg 			    "%s: nvlist add error %d (devid)\n",
3292797Sjg 			    nvf_cache_name(fd), rval);
3302797Sjg 			goto err;
3312797Sjg 		}
3322797Sjg 
3332797Sjg 		rval = nvlist_add_nvlist(nvl, np->nvp_devpath, sub_nvl);
3342797Sjg 		if (rval != 0) {
3352797Sjg 			nvf_error("%s: nvlist add error %d (sublist)\n",
3362797Sjg 			    nvf_cache_name(fd), rval);
3372797Sjg 			goto err;
3382797Sjg 		}
3392797Sjg 		nvlist_free(sub_nvl);
3402797Sjg 	}
3412797Sjg 
3422797Sjg 	*ret_nvl = nvl;
3432797Sjg 	return (DDI_SUCCESS);
3442797Sjg 
3452797Sjg err:
3462797Sjg 	if (sub_nvl)
3472797Sjg 		nvlist_free(sub_nvl);
3482797Sjg 	nvlist_free(nvl);
3492797Sjg 	*ret_nvl = NULL;
3502797Sjg 	return (DDI_FAILURE);
3512797Sjg }
3522797Sjg 
3532797Sjg static int
3542797Sjg e_devid_do_discovery(void)
3552797Sjg {
3562797Sjg 	ASSERT(mutex_owned(&devid_discovery_mutex));
3572797Sjg 
3582797Sjg 	if (i_ddi_io_initialized() == 0) {
3592797Sjg 		if (devid_discovery_boot > 0) {
3602797Sjg 			devid_discovery_boot--;
3612797Sjg 			return (1);
3622797Sjg 		}
3632797Sjg 	} else {
3642797Sjg 		if (devid_discovery_postboot_always > 0)
3652797Sjg 			return (1);
3662797Sjg 		if (devid_discovery_postboot > 0) {
3672797Sjg 			devid_discovery_postboot--;
3682797Sjg 			return (1);
3692797Sjg 		}
3702797Sjg 		if (devid_discovery_secs > 0) {
3712797Sjg 			if ((ddi_get_lbolt() - devid_last_discovery) >
3722797Sjg 			    drv_usectohz(devid_discovery_secs * MICROSEC)) {
3732797Sjg 				return (1);
3742797Sjg 			}
3752797Sjg 		}
3762797Sjg 	}
3772797Sjg 
3782797Sjg 	DEVID_LOG_DISC((CE_CONT, "devid_discovery: no discovery\n"));
3792797Sjg 	return (0);
3802797Sjg }
3812797Sjg 
3822797Sjg static void
3832797Sjg e_ddi_devid_hold_by_major(major_t major)
3842797Sjg {
3852797Sjg 	DEVID_LOG_DISC((CE_CONT,
3862797Sjg 	    "devid_discovery: ddi_hold_installed_driver %d\n", major));
3872797Sjg 
3882797Sjg 	if (ddi_hold_installed_driver(major) == NULL)
3892797Sjg 		return;
3902797Sjg 
3912797Sjg 	ddi_rele_driver(major);
3922797Sjg }
3932797Sjg 
3942797Sjg static char *e_ddi_devid_hold_driver_list[] = { "sd", "ssd", "dad" };
3952797Sjg 
3962797Sjg #define	N_DRIVERS_TO_HOLD	\
3972797Sjg 	(sizeof (e_ddi_devid_hold_driver_list) / sizeof (char *))
3982797Sjg 
3992797Sjg 
4002797Sjg static void
4012797Sjg e_ddi_devid_hold_installed_driver(ddi_devid_t devid)
4022797Sjg {
4032797Sjg 	impl_devid_t	*id = (impl_devid_t *)devid;
4042797Sjg 	major_t		major, hint_major;
4052797Sjg 	char		hint[DEVID_HINT_SIZE + 1];
4062797Sjg 	char		**drvp;
4072797Sjg 	int		i;
4082797Sjg 
4092797Sjg 	/* Count non-null bytes */
4102797Sjg 	for (i = 0; i < DEVID_HINT_SIZE; i++)
4112797Sjg 		if (id->did_driver[i] == '\0')
4122797Sjg 			break;
4132797Sjg 
4142797Sjg 	/* Make a copy of the driver hint */
4152797Sjg 	bcopy(id->did_driver, hint, i);
4162797Sjg 	hint[i] = '\0';
4172797Sjg 
4182797Sjg 	/* search for the devid using the hint driver */
4192797Sjg 	hint_major = ddi_name_to_major(hint);
4207009Scth 	if (hint_major != DDI_MAJOR_T_NONE) {
4212797Sjg 		e_ddi_devid_hold_by_major(hint_major);
4222797Sjg 	}
4232797Sjg 
4242797Sjg 	drvp = e_ddi_devid_hold_driver_list;
4252797Sjg 	for (i = 0; i < N_DRIVERS_TO_HOLD; i++, drvp++) {
4262797Sjg 		major = ddi_name_to_major(*drvp);
4277009Scth 		if (major != DDI_MAJOR_T_NONE && major != hint_major) {
4282797Sjg 			e_ddi_devid_hold_by_major(major);
4292797Sjg 		}
4302797Sjg 	}
4312797Sjg }
4322797Sjg 
4332797Sjg 
4342797Sjg /*
4352797Sjg  * Return success if discovery was attempted, to indicate
4362797Sjg  * that the desired device may now be available.
4372797Sjg  */
4382797Sjg int
4392797Sjg e_ddi_devid_discovery(ddi_devid_t devid)
4402797Sjg {
4412797Sjg 	int flags;
4422797Sjg 	int rval = DDI_SUCCESS;
4432797Sjg 
4442797Sjg 	mutex_enter(&devid_discovery_mutex);
4452797Sjg 
4462797Sjg 	if (devid_discovery_busy) {
4472797Sjg 		DEVID_LOG_DISC((CE_CONT, "devid_discovery: busy\n"));
4482797Sjg 		while (devid_discovery_busy) {
4492797Sjg 			cv_wait(&devid_discovery_cv, &devid_discovery_mutex);
4502797Sjg 		}
4512797Sjg 	} else if (e_devid_do_discovery()) {
4522797Sjg 		devid_discovery_busy = 1;
4532797Sjg 		mutex_exit(&devid_discovery_mutex);
4542797Sjg 
4552797Sjg 		if (i_ddi_io_initialized() == 0) {
4562797Sjg 			e_ddi_devid_hold_installed_driver(devid);
4572797Sjg 		} else {
4582797Sjg 			DEVID_LOG_DISC((CE_CONT,
4592797Sjg 			    "devid_discovery: ndi_devi_config\n"));
4602797Sjg 			flags = NDI_DEVI_PERSIST | NDI_CONFIG | NDI_NO_EVENT;
4612797Sjg 			if (i_ddi_io_initialized())
4622797Sjg 				flags |= NDI_DRV_CONF_REPROBE;
4632797Sjg 			(void) ndi_devi_config(ddi_root_node(), flags);
4642797Sjg 		}
4652797Sjg 
4662797Sjg 		mutex_enter(&devid_discovery_mutex);
4672797Sjg 		devid_discovery_busy = 0;
4682797Sjg 		cv_broadcast(&devid_discovery_cv);
4692797Sjg 		if (devid_discovery_secs > 0)
4702797Sjg 			devid_last_discovery = ddi_get_lbolt();
4712797Sjg 		DEVID_LOG_DISC((CE_CONT, "devid_discovery: done\n"));
4722797Sjg 	} else {
4732797Sjg 		rval = DDI_FAILURE;
4742797Sjg 		DEVID_LOG_DISC((CE_CONT, "no devid discovery\n"));
4752797Sjg 	}
4762797Sjg 
4772797Sjg 	mutex_exit(&devid_discovery_mutex);
4782797Sjg 
4792797Sjg 	return (rval);
4802797Sjg }
4812797Sjg 
4822797Sjg /*
4832797Sjg  * As part of registering a devid for a device,
4842797Sjg  * update the devid cache with this device/devid pair
4852797Sjg  * or note that this combination has registered.
48612213SGavin.Maltby@Sun.COM  *
48712213SGavin.Maltby@Sun.COM  * If a devpath is provided it will be used as the path to register the
48812213SGavin.Maltby@Sun.COM  * devid against, otherwise we use ddi_pathname(dip).  In both cases
48912213SGavin.Maltby@Sun.COM  * we duplicate the path string so that it can be cached/freed indepdently
49012213SGavin.Maltby@Sun.COM  * of the original owner.
4912797Sjg  */
49212213SGavin.Maltby@Sun.COM static int
49312213SGavin.Maltby@Sun.COM e_devid_cache_register_cmn(dev_info_t *dip, ddi_devid_t devid, char *devpath)
4942797Sjg {
4952797Sjg 	nvp_devid_t *np;
4962797Sjg 	nvp_devid_t *new_nvp;
4972797Sjg 	ddi_devid_t new_devid;
4982797Sjg 	int new_devid_size;
4992797Sjg 	char *path, *fullpath;
5002797Sjg 	ddi_devid_t free_devid = NULL;
5012797Sjg 	int pathlen;
5022797Sjg 	list_t *listp;
5032797Sjg 	int is_dirty = 0;
5042797Sjg 
50512121SReed.Liu@Sun.COM 
5062797Sjg 	ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS);
5072797Sjg 
50812213SGavin.Maltby@Sun.COM 	if (devpath) {
50912213SGavin.Maltby@Sun.COM 		pathlen = strlen(devpath) + 1;
51012213SGavin.Maltby@Sun.COM 		path = kmem_alloc(pathlen, KM_SLEEP);
51112213SGavin.Maltby@Sun.COM 		bcopy(devpath, path, pathlen);
51212213SGavin.Maltby@Sun.COM 	} else {
51312213SGavin.Maltby@Sun.COM 		/*
51412213SGavin.Maltby@Sun.COM 		 * We are willing to accept DS_BOUND nodes if we can form a full
51512213SGavin.Maltby@Sun.COM 		 * ddi_pathname (i.e. the node is part way to becomming
51612213SGavin.Maltby@Sun.COM 		 * DS_INITIALIZED and devi_addr/ddi_get_name_addr are non-NULL).
51712213SGavin.Maltby@Sun.COM 		 */
51812213SGavin.Maltby@Sun.COM 		if (ddi_get_name_addr(dip) == NULL)
51912213SGavin.Maltby@Sun.COM 			return (DDI_FAILURE);
52012213SGavin.Maltby@Sun.COM 
52112213SGavin.Maltby@Sun.COM 		fullpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
52212213SGavin.Maltby@Sun.COM 		(void) ddi_pathname(dip, fullpath);
52312213SGavin.Maltby@Sun.COM 		pathlen = strlen(fullpath) + 1;
52412213SGavin.Maltby@Sun.COM 		path = kmem_alloc(pathlen, KM_SLEEP);
52512213SGavin.Maltby@Sun.COM 		bcopy(fullpath, path, pathlen);
52612213SGavin.Maltby@Sun.COM 		kmem_free(fullpath, MAXPATHLEN);
52712213SGavin.Maltby@Sun.COM 	}
5282797Sjg 
5292797Sjg 	DEVID_LOG_REG(("register", devid, path));
5302797Sjg 
5312797Sjg 	new_nvp = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP);
5322797Sjg 	new_devid_size = ddi_devid_sizeof(devid);
5332797Sjg 	new_devid = kmem_alloc(new_devid_size, KM_SLEEP);
5342797Sjg 	(void) bcopy(devid, new_devid, new_devid_size);
5352797Sjg 
5362797Sjg 	rw_enter(nvf_lock(dcfd_handle), RW_WRITER);
5372797Sjg 
5382797Sjg 	listp = nvf_list(dcfd_handle);
5392797Sjg 	for (np = list_head(listp); np; np = list_next(listp, np)) {
5402797Sjg 		if (strcmp(path, np->nvp_devpath) == 0) {
5412797Sjg 			DEVID_DEBUG2((CE_CONT,
5422797Sjg 			    "register: %s path match\n", path));
5432797Sjg 			if (np->nvp_devid == NULL) {
5447009Scth replace:			np->nvp_devid = new_devid;
5452797Sjg 				np->nvp_flags |=
5467009Scth 				    NVP_DEVID_DIP | NVP_DEVID_REGISTERED;
5472797Sjg 				np->nvp_dip = dip;
5482797Sjg 				if (!devid_cache_write_disable) {
5492797Sjg 					nvf_mark_dirty(dcfd_handle);
5502797Sjg 					is_dirty = 1;
5512797Sjg 				}
5522797Sjg 				rw_exit(nvf_lock(dcfd_handle));
5532797Sjg 				kmem_free(new_nvp, sizeof (nvp_devid_t));
5542797Sjg 				kmem_free(path, pathlen);
5552797Sjg 				goto exit;
5562797Sjg 			}
5572797Sjg 			if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) {
5582797Sjg 				/* replace invalid devid */
5592797Sjg 				free_devid = np->nvp_devid;
5602797Sjg 				goto replace;
5612797Sjg 			}
5622797Sjg 			/*
5632797Sjg 			 * We're registering an already-cached path
5642797Sjg 			 * Does the device's devid match the cache?
5652797Sjg 			 */
5662797Sjg 			if (ddi_devid_compare(devid, np->nvp_devid) != 0) {
5672797Sjg 				DEVID_DEBUG((CE_CONT, "devid register: "
5682797Sjg 				    "devid %s does not match\n", path));
5692797Sjg 				/*
5702797Sjg 				 * Replace cached devid for this path
5712797Sjg 				 * with newly registered devid.  A devid
5722797Sjg 				 * may map to multiple paths but one path
5732797Sjg 				 * should only map to one devid.
5742797Sjg 				 */
5752797Sjg 				devid_nvp_unlink_and_free(dcfd_handle, np);
5762797Sjg 				np = NULL;
5772797Sjg 				break;
5782797Sjg 			} else {
5792797Sjg 				DEVID_DEBUG2((CE_CONT,
5802797Sjg 				    "devid register: %s devid match\n", path));
5812797Sjg 				np->nvp_flags |=
5827009Scth 				    NVP_DEVID_DIP | NVP_DEVID_REGISTERED;
5832797Sjg 				np->nvp_dip = dip;
5842797Sjg 				rw_exit(nvf_lock(dcfd_handle));
5852797Sjg 				kmem_free(new_nvp, sizeof (nvp_devid_t));
5862797Sjg 				kmem_free(path, pathlen);
5872797Sjg 				kmem_free(new_devid, new_devid_size);
5882797Sjg 				return (DDI_SUCCESS);
5892797Sjg 			}
5902797Sjg 		}
5912797Sjg 	}
5922797Sjg 
5932797Sjg 	/*
5942797Sjg 	 * Add newly registered devid to the cache
5952797Sjg 	 */
5962797Sjg 	ASSERT(np == NULL);
5972797Sjg 
5982797Sjg 	new_nvp->nvp_devpath = path;
5992797Sjg 	new_nvp->nvp_flags = NVP_DEVID_DIP | NVP_DEVID_REGISTERED;
6002797Sjg 	new_nvp->nvp_dip = dip;
6012797Sjg 	new_nvp->nvp_devid = new_devid;
6022797Sjg 
6032797Sjg 	if (!devid_cache_write_disable) {
6042797Sjg 		is_dirty = 1;
6052797Sjg 		nvf_mark_dirty(dcfd_handle);
6062797Sjg 	}
6072797Sjg 	list_insert_tail(nvf_list(dcfd_handle), new_nvp);
6082797Sjg 
6092797Sjg 	rw_exit(nvf_lock(dcfd_handle));
6102797Sjg 
6112797Sjg exit:
6122797Sjg 	if (free_devid)
6132797Sjg 		kmem_free(free_devid, ddi_devid_sizeof(free_devid));
6142797Sjg 
6152797Sjg 	if (is_dirty)
6162797Sjg 		nvf_wake_daemon();
6172797Sjg 
6182797Sjg 	return (DDI_SUCCESS);
6192797Sjg }
6202797Sjg 
62112213SGavin.Maltby@Sun.COM int
62212213SGavin.Maltby@Sun.COM e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid)
62312213SGavin.Maltby@Sun.COM {
62412213SGavin.Maltby@Sun.COM 	return (e_devid_cache_register_cmn(dip, devid, NULL));
62512213SGavin.Maltby@Sun.COM }
62612213SGavin.Maltby@Sun.COM 
6272797Sjg /*
628*12537SGavin.Maltby@Sun.COM  * Unregister a device's devid; the devinfo may hit on multiple entries
629*12537SGavin.Maltby@Sun.COM  * arising from both pHCI and vHCI paths.
630*12537SGavin.Maltby@Sun.COM  * Called as an instance detachs.
631*12537SGavin.Maltby@Sun.COM  * Invalidate the devid's devinfo reference.
632*12537SGavin.Maltby@Sun.COM  * Devid-path remains in the cache.
6332797Sjg  */
634*12537SGavin.Maltby@Sun.COM 
6352797Sjg void
6362797Sjg e_devid_cache_unregister(dev_info_t *dip)
6372797Sjg {
6382797Sjg 	nvp_devid_t *np;
6392797Sjg 	list_t *listp;
6402797Sjg 
6412797Sjg 	rw_enter(nvf_lock(dcfd_handle), RW_WRITER);
6422797Sjg 
6432797Sjg 	listp = nvf_list(dcfd_handle);
6442797Sjg 	for (np = list_head(listp); np; np = list_next(listp, np)) {
6452797Sjg 		if (np->nvp_devid == NULL)
6462797Sjg 			continue;
6472797Sjg 		if ((np->nvp_flags & NVP_DEVID_DIP) && np->nvp_dip == dip) {
6482797Sjg 			DEVID_LOG_UNREG((CE_CONT,
6497009Scth 			    "unregister: %s\n", np->nvp_devpath));
6502797Sjg 			np->nvp_flags &= ~NVP_DEVID_DIP;
6512797Sjg 			np->nvp_dip = NULL;
6522797Sjg 		}
6532797Sjg 	}
6542797Sjg 
6552797Sjg 	rw_exit(nvf_lock(dcfd_handle));
6562797Sjg }
6572797Sjg 
65812213SGavin.Maltby@Sun.COM int
65912213SGavin.Maltby@Sun.COM e_devid_cache_pathinfo(mdi_pathinfo_t *pip, ddi_devid_t devid)
66012213SGavin.Maltby@Sun.COM {
66112213SGavin.Maltby@Sun.COM 	char *path = mdi_pi_pathname(pip);
66212213SGavin.Maltby@Sun.COM 
66312213SGavin.Maltby@Sun.COM 	return (e_devid_cache_register_cmn(mdi_pi_get_client(pip), devid,
66412213SGavin.Maltby@Sun.COM 	    path));
66512213SGavin.Maltby@Sun.COM }
66612213SGavin.Maltby@Sun.COM 
6672797Sjg /*
6682797Sjg  * Purge devid cache of stale devids
6692797Sjg  */
6702797Sjg void
6712797Sjg devid_cache_cleanup(void)
6722797Sjg {
6732797Sjg 	nvp_devid_t *np, *next;
6742797Sjg 	list_t *listp;
6752797Sjg 	int is_dirty = 0;
6762797Sjg 
6772797Sjg 	rw_enter(nvf_lock(dcfd_handle), RW_WRITER);
6782797Sjg 
6792797Sjg 	listp = nvf_list(dcfd_handle);
6802797Sjg 	for (np = list_head(listp); np; np = next) {
6812797Sjg 		next = list_next(listp, np);
6822797Sjg 		if (np->nvp_devid == NULL)
6832797Sjg 			continue;
6842797Sjg 		if ((np->nvp_flags & NVP_DEVID_REGISTERED) == 0) {
6852797Sjg 			DEVID_LOG_REMOVE((CE_CONT,
6867009Scth 			    "cleanup: %s\n", np->nvp_devpath));
6872797Sjg 			if (!devid_cache_write_disable) {
6882797Sjg 				nvf_mark_dirty(dcfd_handle);
6892797Sjg 				is_dirty = 0;
6902797Sjg 			}
6912797Sjg 			devid_nvp_unlink_and_free(dcfd_handle, np);
6922797Sjg 		}
6932797Sjg 	}
6942797Sjg 
6952797Sjg 	rw_exit(nvf_lock(dcfd_handle));
6962797Sjg 
6972797Sjg 	if (is_dirty)
6982797Sjg 		nvf_wake_daemon();
6992797Sjg }
7002797Sjg 
7012797Sjg 
7022797Sjg /*
7032797Sjg  * Build a list of dev_t's for a device/devid
7042797Sjg  *
7052797Sjg  * The effect of this function is cumulative, adding dev_t's
7062797Sjg  * for the device to the list of all dev_t's for a given
7072797Sjg  * devid.
7082797Sjg  */
7092797Sjg static void
7102797Sjg e_devid_minor_to_devlist(
7112797Sjg 	dev_info_t	*dip,
7122797Sjg 	char		*minor_name,
7132797Sjg 	int		ndevts_alloced,
7142797Sjg 	int		*devtcntp,
7152797Sjg 	dev_t		*devtsp)
7162797Sjg {
7177224Scth 	int			circ;
7182797Sjg 	struct ddi_minor_data	*dmdp;
7192797Sjg 	int			minor_all = 0;
7202797Sjg 	int			ndevts = *devtcntp;
7212797Sjg 
7222797Sjg 	ASSERT(i_ddi_devi_attached(dip));
7232797Sjg 
7242797Sjg 	/* are we looking for a set of minor nodes? */
7252797Sjg 	if ((minor_name == DEVID_MINOR_NAME_ALL) ||
7262797Sjg 	    (minor_name == DEVID_MINOR_NAME_ALL_CHR) ||
7272797Sjg 	    (minor_name == DEVID_MINOR_NAME_ALL_BLK))
7282797Sjg 		minor_all = 1;
7292797Sjg 
7302797Sjg 	/* Find matching minor names */
7317224Scth 	ndi_devi_enter(dip, &circ);
7322797Sjg 	for (dmdp = DEVI(dip)->devi_minor; dmdp; dmdp = dmdp->next) {
7332797Sjg 
7342797Sjg 		/* Skip non-minors, and non matching minor names */
7352797Sjg 		if ((dmdp->type != DDM_MINOR) || ((minor_all == 0) &&
7362797Sjg 		    strcmp(dmdp->ddm_name, minor_name)))
7372797Sjg 			continue;
7382797Sjg 
7392797Sjg 		/* filter out minor_all mismatches */
7402797Sjg 		if (minor_all &&
7412797Sjg 		    (((minor_name == DEVID_MINOR_NAME_ALL_CHR) &&
7422797Sjg 		    (dmdp->ddm_spec_type != S_IFCHR)) ||
7432797Sjg 		    ((minor_name == DEVID_MINOR_NAME_ALL_BLK) &&
7442797Sjg 		    (dmdp->ddm_spec_type != S_IFBLK))))
7452797Sjg 			continue;
7462797Sjg 
7472797Sjg 		if (ndevts < ndevts_alloced)
7482797Sjg 			devtsp[ndevts] = dmdp->ddm_dev;
7492797Sjg 		ndevts++;
7502797Sjg 	}
7517224Scth 	ndi_devi_exit(dip, circ);
7522797Sjg 
7532797Sjg 	*devtcntp = ndevts;
7542797Sjg }
7552797Sjg 
7562797Sjg /*
7572797Sjg  * Search for cached entries matching a devid
7582797Sjg  * Return two lists:
7592797Sjg  *	a list of dev_info nodes, for those devices in the attached state
7602797Sjg  *	a list of pathnames whose instances registered the given devid
7612797Sjg  * If the lists passed in are not sufficient to return the matching
7622797Sjg  * references, return the size of lists required.
7632797Sjg  * The dev_info nodes are returned with a hold that the caller must release.
7642797Sjg  */
7652797Sjg static int
7662797Sjg e_devid_cache_devi_path_lists(ddi_devid_t devid, int retmax,
7672797Sjg 	int *retndevis, dev_info_t **retdevis, int *retnpaths, char **retpaths)
7682797Sjg {
7692797Sjg 	nvp_devid_t *np;
7702797Sjg 	int ndevis, npaths;
7712797Sjg 	dev_info_t *dip, *pdip;
7722797Sjg 	int circ;
7732797Sjg 	int maxdevis = 0;
7742797Sjg 	int maxpaths = 0;
7752797Sjg 	list_t *listp;
7762797Sjg 
7772797Sjg 	ndevis = 0;
7782797Sjg 	npaths = 0;
7792797Sjg 	listp = nvf_list(dcfd_handle);
7802797Sjg 	for (np = list_head(listp); np; np = list_next(listp, np)) {
7812797Sjg 		if (np->nvp_devid == NULL)
7822797Sjg 			continue;
7832797Sjg 		if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) {
7842797Sjg 			DEVIDERR((CE_CONT,
7852797Sjg 			    "find: invalid devid %s\n",
7862797Sjg 			    np->nvp_devpath));
7872797Sjg 			continue;
7882797Sjg 		}
7892797Sjg 		if (ddi_devid_compare(devid, np->nvp_devid) == 0) {
7902797Sjg 			DEVID_DEBUG2((CE_CONT,
7912797Sjg 			    "find: devid match: %s 0x%x\n",
7922797Sjg 			    np->nvp_devpath, np->nvp_flags));
7932797Sjg 			DEVID_LOG_MATCH(("find", devid, np->nvp_devpath));
7942797Sjg 			DEVID_LOG_PATHS((CE_CONT, "%s\n", np->nvp_devpath));
7952797Sjg 
7962797Sjg 			/*
7972797Sjg 			 * Check if we have a cached devinfo reference for this
7982797Sjg 			 * devid.  Place a hold on it to prevent detach
7992797Sjg 			 * Otherwise, use the path instead.
8002797Sjg 			 * Note: returns with a hold on each dev_info
8012797Sjg 			 * node in the list.
8022797Sjg 			 */
8032797Sjg 			dip = NULL;
8042797Sjg 			if (np->nvp_flags & NVP_DEVID_DIP) {
8052797Sjg 				pdip = ddi_get_parent(np->nvp_dip);
8062797Sjg 				if (ndi_devi_tryenter(pdip, &circ)) {
8072797Sjg 					dip = np->nvp_dip;
8082797Sjg 					ndi_hold_devi(dip);
8092797Sjg 					ndi_devi_exit(pdip, circ);
8102797Sjg 					ASSERT(!DEVI_IS_ATTACHING(dip));
8112797Sjg 					ASSERT(!DEVI_IS_DETACHING(dip));
8122797Sjg 				} else {
8132797Sjg 					DEVID_LOG_DETACH((CE_CONT,
8142797Sjg 					    "may be detaching: %s\n",
8152797Sjg 					    np->nvp_devpath));
8162797Sjg 				}
8172797Sjg 			}
8182797Sjg 
8192797Sjg 			if (dip) {
8202797Sjg 				if (ndevis < retmax) {
8212797Sjg 					retdevis[ndevis++] = dip;
8222797Sjg 				} else {
8232797Sjg 					ndi_rele_devi(dip);
8242797Sjg 				}
8252797Sjg 				maxdevis++;
8262797Sjg 			} else {
8272797Sjg 				if (npaths < retmax)
8282797Sjg 					retpaths[npaths++] = np->nvp_devpath;
8292797Sjg 				maxpaths++;
8302797Sjg 			}
8312797Sjg 		}
8322797Sjg 	}
8332797Sjg 
8342797Sjg 	*retndevis = ndevis;
8352797Sjg 	*retnpaths = npaths;
8362797Sjg 	return (maxdevis > maxpaths ? maxdevis : maxpaths);
8372797Sjg }
8382797Sjg 
8392797Sjg 
8402797Sjg /*
8412797Sjg  * Search the devid cache, returning dev_t list for all
8422797Sjg  * device paths mapping to the device identified by the
8432797Sjg  * given devid.
8442797Sjg  *
8452797Sjg  * Primary interface used by ddi_lyr_devid_to_devlist()
8462797Sjg  */
8472797Sjg int
8482797Sjg e_devid_cache_to_devt_list(ddi_devid_t devid, char *minor_name,
8492797Sjg 	int *retndevts, dev_t **retdevts)
8502797Sjg {
8512797Sjg 	char		*path, **paths;
8522797Sjg 	int		i, j, n;
8532797Sjg 	dev_t		*devts, *udevts;
8542797Sjg 	dev_t		tdevt;
8552797Sjg 	int		ndevts, undevts, ndevts_alloced;
8562797Sjg 	dev_info_t	*devi, **devis;
8572797Sjg 	int		ndevis, npaths, nalloced;
8582797Sjg 	ddi_devid_t	match_devid;
8592797Sjg 
8602797Sjg 	DEVID_LOG_FIND(("find", devid, NULL));
8612797Sjg 
8622797Sjg 	ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS);
8632797Sjg 	if (ddi_devid_valid(devid) != DDI_SUCCESS) {
8642797Sjg 		DEVID_LOG_ERR(("invalid devid", devid, NULL));
8652797Sjg 		return (DDI_FAILURE);
8662797Sjg 	}
8672797Sjg 
8682797Sjg 	nalloced = 128;
8692797Sjg 
8702797Sjg 	for (;;) {
8712797Sjg 		paths = kmem_zalloc(nalloced * sizeof (char *), KM_SLEEP);
8722797Sjg 		devis = kmem_zalloc(nalloced * sizeof (dev_info_t *), KM_SLEEP);
8732797Sjg 
8742797Sjg 		rw_enter(nvf_lock(dcfd_handle), RW_READER);
8752797Sjg 		n = e_devid_cache_devi_path_lists(devid, nalloced,
8767009Scth 		    &ndevis, devis, &npaths, paths);
8772797Sjg 		if (n <= nalloced)
8782797Sjg 			break;
8792797Sjg 		rw_exit(nvf_lock(dcfd_handle));
8802797Sjg 		for (i = 0; i < ndevis; i++)
8812797Sjg 			ndi_rele_devi(devis[i]);
8822797Sjg 		kmem_free(paths, nalloced * sizeof (char *));
8832797Sjg 		kmem_free(devis, nalloced * sizeof (dev_info_t *));
8842797Sjg 		nalloced = n + 128;
8852797Sjg 	}
8862797Sjg 
8872797Sjg 	for (i = 0; i < npaths; i++) {
8882797Sjg 		path = i_ddi_strdup(paths[i], KM_SLEEP);
8892797Sjg 		paths[i] = path;
8902797Sjg 	}
8912797Sjg 	rw_exit(nvf_lock(dcfd_handle));
8922797Sjg 
8932797Sjg 	if (ndevis == 0 && npaths == 0) {
8942797Sjg 		DEVID_LOG_ERR(("no devid found", devid, NULL));
8952797Sjg 		kmem_free(paths, nalloced * sizeof (char *));
8962797Sjg 		kmem_free(devis, nalloced * sizeof (dev_info_t *));
8972797Sjg 		return (DDI_FAILURE);
8982797Sjg 	}
8992797Sjg 
9002797Sjg 	ndevts_alloced = 128;
9012797Sjg restart:
9022797Sjg 	ndevts = 0;
9032797Sjg 	devts = kmem_alloc(ndevts_alloced * sizeof (dev_t), KM_SLEEP);
9042797Sjg 	for (i = 0; i < ndevis; i++) {
9052797Sjg 		ASSERT(!DEVI_IS_ATTACHING(devis[i]));
9062797Sjg 		ASSERT(!DEVI_IS_DETACHING(devis[i]));
9072797Sjg 		e_devid_minor_to_devlist(devis[i], minor_name,
9087009Scth 		    ndevts_alloced, &ndevts, devts);
9092797Sjg 		if (ndevts > ndevts_alloced) {
9102797Sjg 			kmem_free(devts, ndevts_alloced * sizeof (dev_t));
9112797Sjg 			ndevts_alloced += 128;
9122797Sjg 			goto restart;
9132797Sjg 		}
9142797Sjg 	}
9152797Sjg 	for (i = 0; i < npaths; i++) {
9162797Sjg 		DEVID_LOG_LOOKUP((CE_CONT, "lookup %s\n", paths[i]));
9172797Sjg 		devi = e_ddi_hold_devi_by_path(paths[i], 0);
9182797Sjg 		if (devi == NULL) {
9192797Sjg 			DEVID_LOG_STALE(("stale device reference",
9202797Sjg 			    devid, paths[i]));
9212797Sjg 			continue;
9222797Sjg 		}
9232797Sjg 		/*
9242797Sjg 		 * Verify the newly attached device registered a matching devid
9252797Sjg 		 */
9262797Sjg 		if (i_ddi_devi_get_devid(DDI_DEV_T_ANY, devi,
9272797Sjg 		    &match_devid) != DDI_SUCCESS) {
9282797Sjg 			DEVIDERR((CE_CONT,
9292797Sjg 			    "%s: no devid registered on attach\n",
9302797Sjg 			    paths[i]));
9312797Sjg 			ddi_release_devi(devi);
9322797Sjg 			continue;
9332797Sjg 		}
9342797Sjg 
9352797Sjg 		if (ddi_devid_compare(devid, match_devid) != 0) {
9362797Sjg 			DEVID_LOG_STALE(("new devid registered",
9372797Sjg 			    devid, paths[i]));
9382797Sjg 			ddi_release_devi(devi);
9392797Sjg 			ddi_devid_free(match_devid);
9402797Sjg 			continue;
9412797Sjg 		}
9422797Sjg 		ddi_devid_free(match_devid);
9432797Sjg 
9442797Sjg 		e_devid_minor_to_devlist(devi, minor_name,
9457009Scth 		    ndevts_alloced, &ndevts, devts);
9462797Sjg 		ddi_release_devi(devi);
9472797Sjg 		if (ndevts > ndevts_alloced) {
9482797Sjg 			kmem_free(devts,
9492797Sjg 			    ndevts_alloced * sizeof (dev_t));
9502797Sjg 			ndevts_alloced += 128;
9512797Sjg 			goto restart;
9522797Sjg 		}
9532797Sjg 	}
9542797Sjg 
9552797Sjg 	/* drop hold from e_devid_cache_devi_path_lists */
9562797Sjg 	for (i = 0; i < ndevis; i++) {
9572797Sjg 		ndi_rele_devi(devis[i]);
9582797Sjg 	}
9592797Sjg 	for (i = 0; i < npaths; i++) {
9602797Sjg 		kmem_free(paths[i], strlen(paths[i]) + 1);
9612797Sjg 	}
9622797Sjg 	kmem_free(paths, nalloced * sizeof (char *));
9632797Sjg 	kmem_free(devis, nalloced * sizeof (dev_info_t *));
9642797Sjg 
9652797Sjg 	if (ndevts == 0) {
9662797Sjg 		DEVID_LOG_ERR(("no devid found", devid, NULL));
9672797Sjg 		kmem_free(devts, ndevts_alloced * sizeof (dev_t));
9682797Sjg 		return (DDI_FAILURE);
9692797Sjg 	}
9702797Sjg 
9712797Sjg 	/*
9722797Sjg 	 * Build the final list of sorted dev_t's with duplicates collapsed so
9732797Sjg 	 * returned results are consistent. This prevents implementation
9742797Sjg 	 * artifacts from causing unnecessary changes in SVM namespace.
9752797Sjg 	 */
9762797Sjg 	/* bubble sort */
9772797Sjg 	for (i = 0; i < (ndevts - 1); i++) {
9782797Sjg 		for (j = 0; j < ((ndevts - 1) - i); j++) {
9792797Sjg 			if (devts[j + 1] < devts[j]) {
9802797Sjg 				tdevt = devts[j];
9812797Sjg 				devts[j] = devts[j + 1];
9822797Sjg 				devts[j + 1] = tdevt;
9832797Sjg 			}
9842797Sjg 		}
9852797Sjg 	}
9862797Sjg 
9872797Sjg 	/* determine number of unique values */
9882797Sjg 	for (undevts = ndevts, i = 1; i < ndevts; i++) {
9892797Sjg 		if (devts[i - 1] == devts[i])
9902797Sjg 			undevts--;
9912797Sjg 	}
9922797Sjg 
9932797Sjg 	/* allocate unique */
9942797Sjg 	udevts = kmem_alloc(undevts * sizeof (dev_t), KM_SLEEP);
9952797Sjg 
9962797Sjg 	/* copy unique */
9972797Sjg 	udevts[0] = devts[0];
9982797Sjg 	for (i = 1, j = 1; i < ndevts; i++) {
9992797Sjg 		if (devts[i - 1] != devts[i])
10002797Sjg 			udevts[j++] = devts[i];
10012797Sjg 	}
10022797Sjg 	ASSERT(j == undevts);
10032797Sjg 
10042797Sjg 	kmem_free(devts, ndevts_alloced * sizeof (dev_t));
10052797Sjg 
10062797Sjg 	*retndevts = undevts;
10072797Sjg 	*retdevts = udevts;
10082797Sjg 
10092797Sjg 	return (DDI_SUCCESS);
10102797Sjg }
10112797Sjg 
10122797Sjg void
10132797Sjg e_devid_cache_free_devt_list(int ndevts, dev_t *devt_list)
10142797Sjg {
10152797Sjg 	kmem_free(devt_list, ndevts * sizeof (dev_t *));
10162797Sjg }
10172797Sjg 
101812213SGavin.Maltby@Sun.COM /*
101912213SGavin.Maltby@Sun.COM  * If given a full path and NULL ua, search for a cache entry
102012213SGavin.Maltby@Sun.COM  * whose path matches the full path.  On a cache hit duplicate the
102112213SGavin.Maltby@Sun.COM  * devid of the matched entry into the given devid (caller
102212213SGavin.Maltby@Sun.COM  * must free);  nodenamebuf is not touched for this usage.
102312213SGavin.Maltby@Sun.COM  *
102412213SGavin.Maltby@Sun.COM  * Given a path and a non-NULL unit address, search the cache for any entry
102512213SGavin.Maltby@Sun.COM  * matching "<path>/%@<unit-address>" where '%' is a wildcard meaning
102612213SGavin.Maltby@Sun.COM  * any node name.  The path should not end a '/'.  On a cache hit
102712213SGavin.Maltby@Sun.COM  * duplicate the devid as before (caller must free) and copy into
102812213SGavin.Maltby@Sun.COM  * the caller-provided nodenamebuf (if not NULL) the nodename of the
102912213SGavin.Maltby@Sun.COM  * matched entry.
103012213SGavin.Maltby@Sun.COM  *
103112213SGavin.Maltby@Sun.COM  * We must not make use of nvp_dip since that may be NULL for cached
103212213SGavin.Maltby@Sun.COM  * entries that are not present in the current tree.
103312213SGavin.Maltby@Sun.COM  */
103412213SGavin.Maltby@Sun.COM int
103512213SGavin.Maltby@Sun.COM e_devid_cache_path_to_devid(char *path, char *ua,
103612213SGavin.Maltby@Sun.COM     char *nodenamebuf, ddi_devid_t *devidp)
103712213SGavin.Maltby@Sun.COM {
103812213SGavin.Maltby@Sun.COM 	size_t pathlen, ualen;
103912213SGavin.Maltby@Sun.COM 	int rv = DDI_FAILURE;
104012213SGavin.Maltby@Sun.COM 	nvp_devid_t *np;
104112213SGavin.Maltby@Sun.COM 	list_t *listp;
104212213SGavin.Maltby@Sun.COM 	char *cand;
104312213SGavin.Maltby@Sun.COM 
104412213SGavin.Maltby@Sun.COM 	if (path == NULL || *path == '\0' || (ua && *ua == '\0') ||
104512213SGavin.Maltby@Sun.COM 	    devidp == NULL)
104612213SGavin.Maltby@Sun.COM 		return (DDI_FAILURE);
104712213SGavin.Maltby@Sun.COM 
104812213SGavin.Maltby@Sun.COM 	*devidp = NULL;
104912213SGavin.Maltby@Sun.COM 
105012213SGavin.Maltby@Sun.COM 	if (ua) {
105112213SGavin.Maltby@Sun.COM 		pathlen = strlen(path);
105212213SGavin.Maltby@Sun.COM 		ualen = strlen(ua);
105312213SGavin.Maltby@Sun.COM 	}
105412213SGavin.Maltby@Sun.COM 
105512213SGavin.Maltby@Sun.COM 	rw_enter(nvf_lock(dcfd_handle), RW_READER);
105612213SGavin.Maltby@Sun.COM 
105712213SGavin.Maltby@Sun.COM 	listp = nvf_list(dcfd_handle);
105812213SGavin.Maltby@Sun.COM 	for (np = list_head(listp); np; np = list_next(listp, np)) {
105912213SGavin.Maltby@Sun.COM 		size_t nodelen, candlen, n;
106012213SGavin.Maltby@Sun.COM 		ddi_devid_t devid_dup;
106112213SGavin.Maltby@Sun.COM 		char *uasep, *node;
106212213SGavin.Maltby@Sun.COM 
106312213SGavin.Maltby@Sun.COM 		if (np->nvp_devid == NULL)
106412213SGavin.Maltby@Sun.COM 			continue;
106512213SGavin.Maltby@Sun.COM 
106612213SGavin.Maltby@Sun.COM 		if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) {
106712213SGavin.Maltby@Sun.COM 			DEVIDERR((CE_CONT,
106812213SGavin.Maltby@Sun.COM 			    "pathsearch: invalid devid %s\n",
106912213SGavin.Maltby@Sun.COM 			    np->nvp_devpath));
107012213SGavin.Maltby@Sun.COM 			continue;
107112213SGavin.Maltby@Sun.COM 		}
107212213SGavin.Maltby@Sun.COM 
107312213SGavin.Maltby@Sun.COM 		cand = np->nvp_devpath;		/* candidate path */
107412213SGavin.Maltby@Sun.COM 
107512213SGavin.Maltby@Sun.COM 		/* If a full pathname was provided the compare is easy */
107612213SGavin.Maltby@Sun.COM 		if (ua == NULL) {
107712213SGavin.Maltby@Sun.COM 			if (strcmp(cand, path) == 0)
107812213SGavin.Maltby@Sun.COM 				goto match;
107912213SGavin.Maltby@Sun.COM 			else
108012213SGavin.Maltby@Sun.COM 				continue;
108112213SGavin.Maltby@Sun.COM 		}
108212213SGavin.Maltby@Sun.COM 
108312213SGavin.Maltby@Sun.COM 		/*
108412213SGavin.Maltby@Sun.COM 		 * The compare for initial path plus ua and unknown nodename
108512213SGavin.Maltby@Sun.COM 		 * is trickier.
108612213SGavin.Maltby@Sun.COM 		 *
108712213SGavin.Maltby@Sun.COM 		 * Does the initial path component match 'path'?
108812213SGavin.Maltby@Sun.COM 		 */
108912213SGavin.Maltby@Sun.COM 		if (strncmp(path, cand, pathlen) != 0)
109012213SGavin.Maltby@Sun.COM 			continue;
109112213SGavin.Maltby@Sun.COM 
109212213SGavin.Maltby@Sun.COM 		candlen = strlen(cand);
109312213SGavin.Maltby@Sun.COM 
109412213SGavin.Maltby@Sun.COM 		/*
109512213SGavin.Maltby@Sun.COM 		 * The next character must be a '/' and there must be no
109612213SGavin.Maltby@Sun.COM 		 * further '/' thereafter.  Begin by checking that the
109712213SGavin.Maltby@Sun.COM 		 * candidate is long enough to include at mininum a
109812213SGavin.Maltby@Sun.COM 		 * "/<nodename>@<ua>" after the initial portion already
109912213SGavin.Maltby@Sun.COM 		 * matched assuming a nodename length of 1.
110012213SGavin.Maltby@Sun.COM 		 */
110112213SGavin.Maltby@Sun.COM 		if (candlen < pathlen + 1 + 1 + 1 + ualen ||
110212213SGavin.Maltby@Sun.COM 		    cand[pathlen] != '/' ||
110312213SGavin.Maltby@Sun.COM 		    strchr(cand + pathlen + 1, '/') != NULL)
110412213SGavin.Maltby@Sun.COM 			continue;
110512213SGavin.Maltby@Sun.COM 
110612213SGavin.Maltby@Sun.COM 		node = cand + pathlen + 1;	/* <node>@<ua> string */
110712213SGavin.Maltby@Sun.COM 
110812213SGavin.Maltby@Sun.COM 		/*
110912213SGavin.Maltby@Sun.COM 		 * Find the '@' before the unit address.  Check for
111012213SGavin.Maltby@Sun.COM 		 * unit address match.
111112213SGavin.Maltby@Sun.COM 		 */
111212213SGavin.Maltby@Sun.COM 		if ((uasep = strchr(node, '@')) == NULL)
111312213SGavin.Maltby@Sun.COM 			continue;
111412213SGavin.Maltby@Sun.COM 
111512213SGavin.Maltby@Sun.COM 		/*
111612213SGavin.Maltby@Sun.COM 		 * Check we still have enough length and that ua matches
111712213SGavin.Maltby@Sun.COM 		 */
111812213SGavin.Maltby@Sun.COM 		nodelen = (uintptr_t)uasep - (uintptr_t)node;
111912213SGavin.Maltby@Sun.COM 		if (candlen < pathlen + 1 + nodelen + 1 + ualen ||
112012213SGavin.Maltby@Sun.COM 		    strncmp(ua, uasep + 1, ualen) != 0)
112112213SGavin.Maltby@Sun.COM 			continue;
112212213SGavin.Maltby@Sun.COM match:
112312213SGavin.Maltby@Sun.COM 		n = ddi_devid_sizeof(np->nvp_devid);
112412213SGavin.Maltby@Sun.COM 		devid_dup = kmem_alloc(n, KM_SLEEP);	/* caller must free */
112512213SGavin.Maltby@Sun.COM 		(void) bcopy(np->nvp_devid, devid_dup, n);
112612213SGavin.Maltby@Sun.COM 		*devidp = devid_dup;
112712213SGavin.Maltby@Sun.COM 
112812213SGavin.Maltby@Sun.COM 		if (ua && nodenamebuf) {
112912213SGavin.Maltby@Sun.COM 			(void) strncpy(nodenamebuf, node, nodelen);
113012213SGavin.Maltby@Sun.COM 			nodenamebuf[nodelen] = '\0';
113112213SGavin.Maltby@Sun.COM 		}
113212213SGavin.Maltby@Sun.COM 
113312213SGavin.Maltby@Sun.COM 		rv = DDI_SUCCESS;
113412213SGavin.Maltby@Sun.COM 		break;
113512213SGavin.Maltby@Sun.COM 	}
113612213SGavin.Maltby@Sun.COM 
113712213SGavin.Maltby@Sun.COM 	rw_exit(nvf_lock(dcfd_handle));
113812213SGavin.Maltby@Sun.COM 
113912213SGavin.Maltby@Sun.COM 	return (rv);
114012213SGavin.Maltby@Sun.COM }
114112213SGavin.Maltby@Sun.COM 
11422797Sjg #ifdef	DEBUG
11432797Sjg static void
11442797Sjg devid_log(char *fmt, ddi_devid_t devid, char *path)
11452797Sjg {
11462797Sjg 	char *devidstr = ddi_devid_str_encode(devid, NULL);
11472797Sjg 	if (path) {
11482797Sjg 		cmn_err(CE_CONT, "%s: %s %s\n", fmt, path, devidstr);
11492797Sjg 	} else {
11502797Sjg 		cmn_err(CE_CONT, "%s: %s\n", fmt, devidstr);
11512797Sjg 	}
11522797Sjg 	ddi_devid_str_free(devidstr);
11532797Sjg }
11542797Sjg #endif	/* DEBUG */
1155