183c4dfe9Sjg /* 283c4dfe9Sjg * CDDL HEADER START 383c4dfe9Sjg * 483c4dfe9Sjg * The contents of this file are subject to the terms of the 583c4dfe9Sjg * Common Development and Distribution License (the "License"). 683c4dfe9Sjg * You may not use this file except in compliance with the License. 783c4dfe9Sjg * 883c4dfe9Sjg * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 983c4dfe9Sjg * or http://www.opensolaris.org/os/licensing. 1083c4dfe9Sjg * See the License for the specific language governing permissions 1183c4dfe9Sjg * and limitations under the License. 1283c4dfe9Sjg * 1383c4dfe9Sjg * When distributing Covered Code, include this CDDL HEADER in each 1483c4dfe9Sjg * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1583c4dfe9Sjg * If applicable, add the following below this CDDL HEADER, with the 1683c4dfe9Sjg * fields enclosed by brackets "[]" replaced with your own identifying 1783c4dfe9Sjg * information: Portions Copyright [yyyy] [name of copyright owner] 1883c4dfe9Sjg * 1983c4dfe9Sjg * CDDL HEADER END 2083c4dfe9Sjg */ 2183c4dfe9Sjg /* 22*4f1e984dSReed * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 2383c4dfe9Sjg */ 2483c4dfe9Sjg 2583c4dfe9Sjg #include <sys/note.h> 2683c4dfe9Sjg #include <sys/t_lock.h> 2783c4dfe9Sjg #include <sys/cmn_err.h> 2883c4dfe9Sjg #include <sys/instance.h> 2983c4dfe9Sjg #include <sys/conf.h> 3083c4dfe9Sjg #include <sys/stat.h> 3183c4dfe9Sjg #include <sys/ddi.h> 3283c4dfe9Sjg #include <sys/hwconf.h> 3383c4dfe9Sjg #include <sys/sunddi.h> 3483c4dfe9Sjg #include <sys/sunndi.h> 3583c4dfe9Sjg #include <sys/ddi_impldefs.h> 3683c4dfe9Sjg #include <sys/ndi_impldefs.h> 3783c4dfe9Sjg #include <sys/kobj.h> 3883c4dfe9Sjg #include <sys/devcache.h> 3983c4dfe9Sjg #include <sys/devid_cache.h> 4083c4dfe9Sjg #include <sys/sysmacros.h> 4183c4dfe9Sjg 4283c4dfe9Sjg /* 4383c4dfe9Sjg * Discovery refers to the heroic effort made to discover a device which 4483c4dfe9Sjg * cannot be accessed at the physical path where it once resided. Discovery 4583c4dfe9Sjg * involves walking the entire device tree attaching all possible disk 4683c4dfe9Sjg * instances, to search for the device referenced by a devid. Obviously, 4783c4dfe9Sjg * full device discovery is something to be avoided where possible. 4883c4dfe9Sjg * Note that simply invoking devfsadm(1M) is equivalent to running full 4983c4dfe9Sjg * discovery at the devid cache level. 5083c4dfe9Sjg * 5183c4dfe9Sjg * Reasons why a disk may not be accessible: 5283c4dfe9Sjg * disk powered off 5383c4dfe9Sjg * disk removed or cable disconnected 5483c4dfe9Sjg * disk or adapter broken 5583c4dfe9Sjg * 5683c4dfe9Sjg * Note that discovery is not needed and cannot succeed in any of these 5783c4dfe9Sjg * cases. 5883c4dfe9Sjg * 5983c4dfe9Sjg * When discovery may succeed: 6083c4dfe9Sjg * Discovery will result in success when a device has been moved 6183c4dfe9Sjg * to a different address. Note that it's recommended that 6283c4dfe9Sjg * devfsadm(1M) be invoked (no arguments required) whenever a system's 6383c4dfe9Sjg * h/w configuration has been updated. Alternatively, a 6483c4dfe9Sjg * reconfiguration boot can be used to accomplish the same result. 6583c4dfe9Sjg * 6683c4dfe9Sjg * Note that discovery is not necessary to be able to correct an access 6783c4dfe9Sjg * failure for a device which was powered off. Assuming the cache has an 6883c4dfe9Sjg * entry for such a device, simply powering it on should permit the system 6983c4dfe9Sjg * to access it. If problems persist after powering it on, invoke 7083c4dfe9Sjg * devfsadm(1M). 7183c4dfe9Sjg * 7283c4dfe9Sjg * Discovery prior to mounting root is only of interest when booting 7383c4dfe9Sjg * from a filesystem which accesses devices by device id, which of 7483c4dfe9Sjg * not all do. 7583c4dfe9Sjg * 7683c4dfe9Sjg * Tunables 7783c4dfe9Sjg * 7883c4dfe9Sjg * devid_discovery_boot (default 1) 7983c4dfe9Sjg * Number of times discovery will be attempted prior to mounting root. 8083c4dfe9Sjg * Must be done at least once to recover from corrupted or missing 8183c4dfe9Sjg * devid cache backing store. Probably there's no reason to ever 8283c4dfe9Sjg * set this to greater than one as a missing device will remain 8383c4dfe9Sjg * unavailable no matter how often the system searches for it. 8483c4dfe9Sjg * 8583c4dfe9Sjg * devid_discovery_postboot (default 1) 8683c4dfe9Sjg * Number of times discovery will be attempted after mounting root. 8783c4dfe9Sjg * This must be performed at least once to discover any devices 8883c4dfe9Sjg * needed after root is mounted which may have been powered 8983c4dfe9Sjg * off and moved before booting. 9083c4dfe9Sjg * Setting this to a larger positive number will introduce 9183c4dfe9Sjg * some inconsistency in system operation. Searching for a device 9283c4dfe9Sjg * will take an indeterminate amount of time, sometimes slower, 9383c4dfe9Sjg * sometimes faster. In addition, the system will sometimes 9483c4dfe9Sjg * discover a newly powered on device, sometimes it won't. 9583c4dfe9Sjg * Use of this option is not therefore recommended. 9683c4dfe9Sjg * 9783c4dfe9Sjg * devid_discovery_postboot_always (default 0) 9883c4dfe9Sjg * Set to 1, the system will always attempt full discovery. 9983c4dfe9Sjg * 10083c4dfe9Sjg * devid_discovery_secs (default 0) 10183c4dfe9Sjg * Set to a positive value, the system will attempt full discovery 10283c4dfe9Sjg * but with a minimum delay between attempts. A device search 10383c4dfe9Sjg * within the period of time specified will result in failure. 10483c4dfe9Sjg * 10583c4dfe9Sjg * devid_cache_read_disable (default 0) 10683c4dfe9Sjg * Set to 1 to disable reading /etc/devices/devid_cache. 10783c4dfe9Sjg * Devid cache will continue to operate normally but 10883c4dfe9Sjg * at least one discovery attempt will be required. 10983c4dfe9Sjg * 11083c4dfe9Sjg * devid_cache_write_disable (default 0) 11183c4dfe9Sjg * Set to 1 to disable updates to /etc/devices/devid_cache. 11283c4dfe9Sjg * Any updates to the devid cache will not be preserved across a reboot. 11383c4dfe9Sjg * 11483c4dfe9Sjg * devid_report_error (default 0) 11583c4dfe9Sjg * Set to 1 to enable some error messages related to devid 11683c4dfe9Sjg * cache failures. 11783c4dfe9Sjg * 11883c4dfe9Sjg * The devid is packed in the cache file as a byte array. For 11983c4dfe9Sjg * portability, this could be done in the encoded string format. 12083c4dfe9Sjg */ 12183c4dfe9Sjg 12283c4dfe9Sjg 12383c4dfe9Sjg int devid_discovery_boot = 1; 12483c4dfe9Sjg int devid_discovery_postboot = 1; 12583c4dfe9Sjg int devid_discovery_postboot_always = 0; 12683c4dfe9Sjg int devid_discovery_secs = 0; 12783c4dfe9Sjg 12883c4dfe9Sjg int devid_cache_read_disable = 0; 12983c4dfe9Sjg int devid_cache_write_disable = 0; 13083c4dfe9Sjg 13183c4dfe9Sjg int devid_report_error = 0; 13283c4dfe9Sjg 13383c4dfe9Sjg 13483c4dfe9Sjg /* 13583c4dfe9Sjg * State to manage discovery of devices providing a devid 13683c4dfe9Sjg */ 13783c4dfe9Sjg static int devid_discovery_busy = 0; 13883c4dfe9Sjg static kmutex_t devid_discovery_mutex; 13983c4dfe9Sjg static kcondvar_t devid_discovery_cv; 14083c4dfe9Sjg static clock_t devid_last_discovery = 0; 14183c4dfe9Sjg 14283c4dfe9Sjg 14383c4dfe9Sjg #ifdef DEBUG 14483c4dfe9Sjg int nvp_devid_debug = 0; 14583c4dfe9Sjg int devid_debug = 0; 14683c4dfe9Sjg int devid_log_registers = 0; 14783c4dfe9Sjg int devid_log_finds = 0; 14883c4dfe9Sjg int devid_log_lookups = 0; 14983c4dfe9Sjg int devid_log_discovery = 0; 15083c4dfe9Sjg int devid_log_matches = 0; 15183c4dfe9Sjg int devid_log_paths = 0; 15283c4dfe9Sjg int devid_log_failures = 0; 15383c4dfe9Sjg int devid_log_hold = 0; 15483c4dfe9Sjg int devid_log_unregisters = 0; 15583c4dfe9Sjg int devid_log_removes = 0; 15683c4dfe9Sjg int devid_register_debug = 0; 15783c4dfe9Sjg int devid_log_stale = 0; 15883c4dfe9Sjg int devid_log_detaches = 0; 15983c4dfe9Sjg #endif /* DEBUG */ 16083c4dfe9Sjg 16183c4dfe9Sjg /* 16283c4dfe9Sjg * devid cache file registration for cache reads and updates 16383c4dfe9Sjg */ 16483c4dfe9Sjg static nvf_ops_t devid_cache_ops = { 16583c4dfe9Sjg "/etc/devices/devid_cache", /* path to cache */ 16683c4dfe9Sjg devid_cache_unpack_nvlist, /* read: nvlist to nvp */ 16783c4dfe9Sjg devid_cache_pack_list, /* write: nvp to nvlist */ 16883c4dfe9Sjg devid_list_free, /* free data list */ 16983c4dfe9Sjg NULL /* write complete callback */ 17083c4dfe9Sjg }; 17183c4dfe9Sjg 17283c4dfe9Sjg /* 17383c4dfe9Sjg * handle to registered devid cache handlers 17483c4dfe9Sjg */ 17583c4dfe9Sjg nvf_handle_t dcfd_handle; 17683c4dfe9Sjg 17783c4dfe9Sjg 17883c4dfe9Sjg /* 17983c4dfe9Sjg * Initialize devid cache file management 18083c4dfe9Sjg */ 18183c4dfe9Sjg void 18283c4dfe9Sjg devid_cache_init(void) 18383c4dfe9Sjg { 18483c4dfe9Sjg dcfd_handle = nvf_register_file(&devid_cache_ops); 18583c4dfe9Sjg ASSERT(dcfd_handle); 18683c4dfe9Sjg 18783c4dfe9Sjg list_create(nvf_list(dcfd_handle), sizeof (nvp_devid_t), 18883c4dfe9Sjg offsetof(nvp_devid_t, nvp_link)); 18983c4dfe9Sjg 19083c4dfe9Sjg mutex_init(&devid_discovery_mutex, NULL, MUTEX_DEFAULT, NULL); 19183c4dfe9Sjg cv_init(&devid_discovery_cv, NULL, CV_DRIVER, NULL); 19283c4dfe9Sjg } 19383c4dfe9Sjg 19483c4dfe9Sjg /* 19583c4dfe9Sjg * Read and initialize the devid cache from the persistent store 19683c4dfe9Sjg */ 19783c4dfe9Sjg void 19883c4dfe9Sjg devid_cache_read(void) 19983c4dfe9Sjg { 20083c4dfe9Sjg if (!devid_cache_read_disable) { 20183c4dfe9Sjg rw_enter(nvf_lock(dcfd_handle), RW_WRITER); 20283c4dfe9Sjg ASSERT(list_head(nvf_list(dcfd_handle)) == NULL); 20383c4dfe9Sjg (void) nvf_read_file(dcfd_handle); 20483c4dfe9Sjg rw_exit(nvf_lock(dcfd_handle)); 20583c4dfe9Sjg } 20683c4dfe9Sjg } 20783c4dfe9Sjg 20883c4dfe9Sjg static void 20983c4dfe9Sjg devid_nvp_free(nvp_devid_t *dp) 21083c4dfe9Sjg { 21183c4dfe9Sjg if (dp->nvp_devpath) 21283c4dfe9Sjg kmem_free(dp->nvp_devpath, strlen(dp->nvp_devpath)+1); 21383c4dfe9Sjg if (dp->nvp_devid) 21483c4dfe9Sjg kmem_free(dp->nvp_devid, ddi_devid_sizeof(dp->nvp_devid)); 21583c4dfe9Sjg 21683c4dfe9Sjg kmem_free(dp, sizeof (nvp_devid_t)); 21783c4dfe9Sjg } 21883c4dfe9Sjg 21983c4dfe9Sjg static void 22083c4dfe9Sjg devid_list_free(nvf_handle_t fd) 22183c4dfe9Sjg { 22283c4dfe9Sjg list_t *listp; 22383c4dfe9Sjg nvp_devid_t *np; 22483c4dfe9Sjg 22583c4dfe9Sjg ASSERT(RW_WRITE_HELD(nvf_lock(dcfd_handle))); 22683c4dfe9Sjg 22783c4dfe9Sjg listp = nvf_list(fd); 22883c4dfe9Sjg while (np = list_head(listp)) { 22983c4dfe9Sjg list_remove(listp, np); 23083c4dfe9Sjg devid_nvp_free(np); 23183c4dfe9Sjg } 23283c4dfe9Sjg } 23383c4dfe9Sjg 23483c4dfe9Sjg /* 23583c4dfe9Sjg * Free an nvp element in a list 23683c4dfe9Sjg */ 23783c4dfe9Sjg static void 23883c4dfe9Sjg devid_nvp_unlink_and_free(nvf_handle_t fd, nvp_devid_t *np) 23983c4dfe9Sjg { 24083c4dfe9Sjg list_remove(nvf_list(fd), np); 24183c4dfe9Sjg devid_nvp_free(np); 24283c4dfe9Sjg } 24383c4dfe9Sjg 24483c4dfe9Sjg /* 24583c4dfe9Sjg * Unpack a device path/nvlist pair to the list of devid cache elements. 24683c4dfe9Sjg * Used to parse the nvlist format when reading 24783c4dfe9Sjg * /etc/devices/devid_cache 24883c4dfe9Sjg */ 24983c4dfe9Sjg static int 25083c4dfe9Sjg devid_cache_unpack_nvlist(nvf_handle_t fd, nvlist_t *nvl, char *name) 25183c4dfe9Sjg { 25283c4dfe9Sjg nvp_devid_t *np; 25383c4dfe9Sjg ddi_devid_t devidp; 25483c4dfe9Sjg int rval; 25583c4dfe9Sjg uint_t n; 25683c4dfe9Sjg 25783c4dfe9Sjg NVP_DEVID_DEBUG_PATH((name)); 25883c4dfe9Sjg ASSERT(RW_WRITE_HELD(nvf_lock(dcfd_handle))); 25983c4dfe9Sjg 26083c4dfe9Sjg /* 26183c4dfe9Sjg * check path for a devid 26283c4dfe9Sjg */ 26383c4dfe9Sjg rval = nvlist_lookup_byte_array(nvl, 264a204de77Scth DP_DEVID_ID, (uchar_t **)&devidp, &n); 26583c4dfe9Sjg if (rval == 0) { 26683c4dfe9Sjg if (ddi_devid_valid(devidp) == DDI_SUCCESS) { 26783c4dfe9Sjg ASSERT(n == ddi_devid_sizeof(devidp)); 26883c4dfe9Sjg np = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP); 26983c4dfe9Sjg np->nvp_devpath = i_ddi_strdup(name, KM_SLEEP); 27083c4dfe9Sjg np->nvp_devid = kmem_alloc(n, KM_SLEEP); 27183c4dfe9Sjg (void) bcopy(devidp, np->nvp_devid, n); 27283c4dfe9Sjg list_insert_tail(nvf_list(fd), np); 27383c4dfe9Sjg NVP_DEVID_DEBUG_DEVID((np->nvp_devid)); 27483c4dfe9Sjg } else { 27583c4dfe9Sjg DEVIDERR((CE_CONT, 27683c4dfe9Sjg "%s: invalid devid\n", name)); 27783c4dfe9Sjg } 27883c4dfe9Sjg } else { 27983c4dfe9Sjg DEVIDERR((CE_CONT, 28083c4dfe9Sjg "%s: devid not available\n", name)); 28183c4dfe9Sjg } 28283c4dfe9Sjg 28383c4dfe9Sjg return (0); 28483c4dfe9Sjg } 28583c4dfe9Sjg 28683c4dfe9Sjg /* 28783c4dfe9Sjg * Pack the list of devid cache elements into a single nvlist 28883c4dfe9Sjg * Used when writing the nvlist file. 28983c4dfe9Sjg */ 29083c4dfe9Sjg static int 29183c4dfe9Sjg devid_cache_pack_list(nvf_handle_t fd, nvlist_t **ret_nvl) 29283c4dfe9Sjg { 29383c4dfe9Sjg nvlist_t *nvl, *sub_nvl; 29483c4dfe9Sjg nvp_devid_t *np; 29583c4dfe9Sjg int rval; 29683c4dfe9Sjg list_t *listp; 29783c4dfe9Sjg 29883c4dfe9Sjg ASSERT(RW_WRITE_HELD(nvf_lock(dcfd_handle))); 29983c4dfe9Sjg 30083c4dfe9Sjg rval = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 30183c4dfe9Sjg if (rval != 0) { 30283c4dfe9Sjg nvf_error("%s: nvlist alloc error %d\n", 303a204de77Scth nvf_cache_name(fd), rval); 30483c4dfe9Sjg return (DDI_FAILURE); 30583c4dfe9Sjg } 30683c4dfe9Sjg 30783c4dfe9Sjg listp = nvf_list(fd); 30883c4dfe9Sjg for (np = list_head(listp); np; np = list_next(listp, np)) { 30983c4dfe9Sjg if (np->nvp_devid == NULL) 310a204de77Scth continue; 31183c4dfe9Sjg NVP_DEVID_DEBUG_PATH(np->nvp_devpath); 31283c4dfe9Sjg rval = nvlist_alloc(&sub_nvl, NV_UNIQUE_NAME, KM_SLEEP); 31383c4dfe9Sjg if (rval != 0) { 31483c4dfe9Sjg nvf_error("%s: nvlist alloc error %d\n", 315a204de77Scth nvf_cache_name(fd), rval); 31683c4dfe9Sjg sub_nvl = NULL; 31783c4dfe9Sjg goto err; 31883c4dfe9Sjg } 31983c4dfe9Sjg 32083c4dfe9Sjg rval = nvlist_add_byte_array(sub_nvl, DP_DEVID_ID, 321a204de77Scth (uchar_t *)np->nvp_devid, 322a204de77Scth ddi_devid_sizeof(np->nvp_devid)); 32383c4dfe9Sjg if (rval == 0) { 32483c4dfe9Sjg NVP_DEVID_DEBUG_DEVID(np->nvp_devid); 32583c4dfe9Sjg } else { 32683c4dfe9Sjg nvf_error( 32783c4dfe9Sjg "%s: nvlist add error %d (devid)\n", 32883c4dfe9Sjg nvf_cache_name(fd), rval); 32983c4dfe9Sjg goto err; 33083c4dfe9Sjg } 33183c4dfe9Sjg 33283c4dfe9Sjg rval = nvlist_add_nvlist(nvl, np->nvp_devpath, sub_nvl); 33383c4dfe9Sjg if (rval != 0) { 33483c4dfe9Sjg nvf_error("%s: nvlist add error %d (sublist)\n", 33583c4dfe9Sjg nvf_cache_name(fd), rval); 33683c4dfe9Sjg goto err; 33783c4dfe9Sjg } 33883c4dfe9Sjg nvlist_free(sub_nvl); 33983c4dfe9Sjg } 34083c4dfe9Sjg 34183c4dfe9Sjg *ret_nvl = nvl; 34283c4dfe9Sjg return (DDI_SUCCESS); 34383c4dfe9Sjg 34483c4dfe9Sjg err: 34583c4dfe9Sjg if (sub_nvl) 34683c4dfe9Sjg nvlist_free(sub_nvl); 34783c4dfe9Sjg nvlist_free(nvl); 34883c4dfe9Sjg *ret_nvl = NULL; 34983c4dfe9Sjg return (DDI_FAILURE); 35083c4dfe9Sjg } 35183c4dfe9Sjg 35283c4dfe9Sjg static int 35383c4dfe9Sjg e_devid_do_discovery(void) 35483c4dfe9Sjg { 35583c4dfe9Sjg ASSERT(mutex_owned(&devid_discovery_mutex)); 35683c4dfe9Sjg 35783c4dfe9Sjg if (i_ddi_io_initialized() == 0) { 35883c4dfe9Sjg if (devid_discovery_boot > 0) { 35983c4dfe9Sjg devid_discovery_boot--; 36083c4dfe9Sjg return (1); 36183c4dfe9Sjg } 36283c4dfe9Sjg } else { 36383c4dfe9Sjg if (devid_discovery_postboot_always > 0) 36483c4dfe9Sjg return (1); 36583c4dfe9Sjg if (devid_discovery_postboot > 0) { 36683c4dfe9Sjg devid_discovery_postboot--; 36783c4dfe9Sjg return (1); 36883c4dfe9Sjg } 36983c4dfe9Sjg if (devid_discovery_secs > 0) { 37083c4dfe9Sjg if ((ddi_get_lbolt() - devid_last_discovery) > 37183c4dfe9Sjg drv_usectohz(devid_discovery_secs * MICROSEC)) { 37283c4dfe9Sjg return (1); 37383c4dfe9Sjg } 37483c4dfe9Sjg } 37583c4dfe9Sjg } 37683c4dfe9Sjg 37783c4dfe9Sjg DEVID_LOG_DISC((CE_CONT, "devid_discovery: no discovery\n")); 37883c4dfe9Sjg return (0); 37983c4dfe9Sjg } 38083c4dfe9Sjg 38183c4dfe9Sjg static void 38283c4dfe9Sjg e_ddi_devid_hold_by_major(major_t major) 38383c4dfe9Sjg { 38483c4dfe9Sjg DEVID_LOG_DISC((CE_CONT, 38583c4dfe9Sjg "devid_discovery: ddi_hold_installed_driver %d\n", major)); 38683c4dfe9Sjg 38783c4dfe9Sjg if (ddi_hold_installed_driver(major) == NULL) 38883c4dfe9Sjg return; 38983c4dfe9Sjg 39083c4dfe9Sjg ddi_rele_driver(major); 39183c4dfe9Sjg } 39283c4dfe9Sjg 39383c4dfe9Sjg static char *e_ddi_devid_hold_driver_list[] = { "sd", "ssd", "dad" }; 39483c4dfe9Sjg 39583c4dfe9Sjg #define N_DRIVERS_TO_HOLD \ 39683c4dfe9Sjg (sizeof (e_ddi_devid_hold_driver_list) / sizeof (char *)) 39783c4dfe9Sjg 39883c4dfe9Sjg 39983c4dfe9Sjg static void 40083c4dfe9Sjg e_ddi_devid_hold_installed_driver(ddi_devid_t devid) 40183c4dfe9Sjg { 40283c4dfe9Sjg impl_devid_t *id = (impl_devid_t *)devid; 40383c4dfe9Sjg major_t major, hint_major; 40483c4dfe9Sjg char hint[DEVID_HINT_SIZE + 1]; 40583c4dfe9Sjg char **drvp; 40683c4dfe9Sjg int i; 40783c4dfe9Sjg 40883c4dfe9Sjg /* Count non-null bytes */ 40983c4dfe9Sjg for (i = 0; i < DEVID_HINT_SIZE; i++) 41083c4dfe9Sjg if (id->did_driver[i] == '\0') 41183c4dfe9Sjg break; 41283c4dfe9Sjg 41383c4dfe9Sjg /* Make a copy of the driver hint */ 41483c4dfe9Sjg bcopy(id->did_driver, hint, i); 41583c4dfe9Sjg hint[i] = '\0'; 41683c4dfe9Sjg 41783c4dfe9Sjg /* search for the devid using the hint driver */ 41883c4dfe9Sjg hint_major = ddi_name_to_major(hint); 419a204de77Scth if (hint_major != DDI_MAJOR_T_NONE) { 42083c4dfe9Sjg e_ddi_devid_hold_by_major(hint_major); 42183c4dfe9Sjg } 42283c4dfe9Sjg 42383c4dfe9Sjg drvp = e_ddi_devid_hold_driver_list; 42483c4dfe9Sjg for (i = 0; i < N_DRIVERS_TO_HOLD; i++, drvp++) { 42583c4dfe9Sjg major = ddi_name_to_major(*drvp); 426a204de77Scth if (major != DDI_MAJOR_T_NONE && major != hint_major) { 42783c4dfe9Sjg e_ddi_devid_hold_by_major(major); 42883c4dfe9Sjg } 42983c4dfe9Sjg } 43083c4dfe9Sjg } 43183c4dfe9Sjg 43283c4dfe9Sjg 43383c4dfe9Sjg /* 43483c4dfe9Sjg * Return success if discovery was attempted, to indicate 43583c4dfe9Sjg * that the desired device may now be available. 43683c4dfe9Sjg */ 43783c4dfe9Sjg int 43883c4dfe9Sjg e_ddi_devid_discovery(ddi_devid_t devid) 43983c4dfe9Sjg { 44083c4dfe9Sjg int flags; 44183c4dfe9Sjg int rval = DDI_SUCCESS; 44283c4dfe9Sjg 44383c4dfe9Sjg mutex_enter(&devid_discovery_mutex); 44483c4dfe9Sjg 44583c4dfe9Sjg if (devid_discovery_busy) { 44683c4dfe9Sjg DEVID_LOG_DISC((CE_CONT, "devid_discovery: busy\n")); 44783c4dfe9Sjg while (devid_discovery_busy) { 44883c4dfe9Sjg cv_wait(&devid_discovery_cv, &devid_discovery_mutex); 44983c4dfe9Sjg } 45083c4dfe9Sjg } else if (e_devid_do_discovery()) { 45183c4dfe9Sjg devid_discovery_busy = 1; 45283c4dfe9Sjg mutex_exit(&devid_discovery_mutex); 45383c4dfe9Sjg 45483c4dfe9Sjg if (i_ddi_io_initialized() == 0) { 45583c4dfe9Sjg e_ddi_devid_hold_installed_driver(devid); 45683c4dfe9Sjg } else { 45783c4dfe9Sjg DEVID_LOG_DISC((CE_CONT, 45883c4dfe9Sjg "devid_discovery: ndi_devi_config\n")); 45983c4dfe9Sjg flags = NDI_DEVI_PERSIST | NDI_CONFIG | NDI_NO_EVENT; 46083c4dfe9Sjg if (i_ddi_io_initialized()) 46183c4dfe9Sjg flags |= NDI_DRV_CONF_REPROBE; 46283c4dfe9Sjg (void) ndi_devi_config(ddi_root_node(), flags); 46383c4dfe9Sjg } 46483c4dfe9Sjg 46583c4dfe9Sjg mutex_enter(&devid_discovery_mutex); 46683c4dfe9Sjg devid_discovery_busy = 0; 46783c4dfe9Sjg cv_broadcast(&devid_discovery_cv); 46883c4dfe9Sjg if (devid_discovery_secs > 0) 46983c4dfe9Sjg devid_last_discovery = ddi_get_lbolt(); 47083c4dfe9Sjg DEVID_LOG_DISC((CE_CONT, "devid_discovery: done\n")); 47183c4dfe9Sjg } else { 47283c4dfe9Sjg rval = DDI_FAILURE; 47383c4dfe9Sjg DEVID_LOG_DISC((CE_CONT, "no devid discovery\n")); 47483c4dfe9Sjg } 47583c4dfe9Sjg 47683c4dfe9Sjg mutex_exit(&devid_discovery_mutex); 47783c4dfe9Sjg 47883c4dfe9Sjg return (rval); 47983c4dfe9Sjg } 48083c4dfe9Sjg 48183c4dfe9Sjg /* 48283c4dfe9Sjg * As part of registering a devid for a device, 48383c4dfe9Sjg * update the devid cache with this device/devid pair 48483c4dfe9Sjg * or note that this combination has registered. 48583c4dfe9Sjg */ 48683c4dfe9Sjg int 48783c4dfe9Sjg e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid) 48883c4dfe9Sjg { 48983c4dfe9Sjg nvp_devid_t *np; 49083c4dfe9Sjg nvp_devid_t *new_nvp; 49183c4dfe9Sjg ddi_devid_t new_devid; 49283c4dfe9Sjg int new_devid_size; 49383c4dfe9Sjg char *path, *fullpath; 49483c4dfe9Sjg ddi_devid_t free_devid = NULL; 49583c4dfe9Sjg int pathlen; 49683c4dfe9Sjg list_t *listp; 49783c4dfe9Sjg int is_dirty = 0; 49883c4dfe9Sjg 499*4f1e984dSReed /* 500*4f1e984dSReed * We are willing to accept DS_BOUND nodes if we can form a full 501*4f1e984dSReed * ddi_pathname (i.e. the node is part way to becomming 502*4f1e984dSReed * DS_INITIALIZED and devi_addr/ddi_get_name_addr are non-NULL). 503*4f1e984dSReed */ 504*4f1e984dSReed if (ddi_get_name_addr(dip) == NULL) { 505*4f1e984dSReed return (DDI_FAILURE); 506*4f1e984dSReed } 507*4f1e984dSReed 50883c4dfe9Sjg ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS); 50983c4dfe9Sjg 51083c4dfe9Sjg fullpath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 51183c4dfe9Sjg (void) ddi_pathname(dip, fullpath); 51283c4dfe9Sjg pathlen = strlen(fullpath) + 1; 51383c4dfe9Sjg path = kmem_alloc(pathlen, KM_SLEEP); 51483c4dfe9Sjg bcopy(fullpath, path, pathlen); 51583c4dfe9Sjg kmem_free(fullpath, MAXPATHLEN); 51683c4dfe9Sjg 51783c4dfe9Sjg DEVID_LOG_REG(("register", devid, path)); 51883c4dfe9Sjg 51983c4dfe9Sjg new_nvp = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP); 52083c4dfe9Sjg new_devid_size = ddi_devid_sizeof(devid); 52183c4dfe9Sjg new_devid = kmem_alloc(new_devid_size, KM_SLEEP); 52283c4dfe9Sjg (void) bcopy(devid, new_devid, new_devid_size); 52383c4dfe9Sjg 52483c4dfe9Sjg rw_enter(nvf_lock(dcfd_handle), RW_WRITER); 52583c4dfe9Sjg 52683c4dfe9Sjg listp = nvf_list(dcfd_handle); 52783c4dfe9Sjg for (np = list_head(listp); np; np = list_next(listp, np)) { 52883c4dfe9Sjg if (strcmp(path, np->nvp_devpath) == 0) { 52983c4dfe9Sjg DEVID_DEBUG2((CE_CONT, 53083c4dfe9Sjg "register: %s path match\n", path)); 53183c4dfe9Sjg if (np->nvp_devid == NULL) { 532a204de77Scth replace: np->nvp_devid = new_devid; 53383c4dfe9Sjg np->nvp_flags |= 534a204de77Scth NVP_DEVID_DIP | NVP_DEVID_REGISTERED; 53583c4dfe9Sjg np->nvp_dip = dip; 53683c4dfe9Sjg if (!devid_cache_write_disable) { 53783c4dfe9Sjg nvf_mark_dirty(dcfd_handle); 53883c4dfe9Sjg is_dirty = 1; 53983c4dfe9Sjg } 54083c4dfe9Sjg rw_exit(nvf_lock(dcfd_handle)); 54183c4dfe9Sjg kmem_free(new_nvp, sizeof (nvp_devid_t)); 54283c4dfe9Sjg kmem_free(path, pathlen); 54383c4dfe9Sjg goto exit; 54483c4dfe9Sjg } 54583c4dfe9Sjg if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) { 54683c4dfe9Sjg /* replace invalid devid */ 54783c4dfe9Sjg free_devid = np->nvp_devid; 54883c4dfe9Sjg goto replace; 54983c4dfe9Sjg } 55083c4dfe9Sjg /* 55183c4dfe9Sjg * We're registering an already-cached path 55283c4dfe9Sjg * Does the device's devid match the cache? 55383c4dfe9Sjg */ 55483c4dfe9Sjg if (ddi_devid_compare(devid, np->nvp_devid) != 0) { 55583c4dfe9Sjg DEVID_DEBUG((CE_CONT, "devid register: " 55683c4dfe9Sjg "devid %s does not match\n", path)); 55783c4dfe9Sjg /* 55883c4dfe9Sjg * Replace cached devid for this path 55983c4dfe9Sjg * with newly registered devid. A devid 56083c4dfe9Sjg * may map to multiple paths but one path 56183c4dfe9Sjg * should only map to one devid. 56283c4dfe9Sjg */ 56383c4dfe9Sjg devid_nvp_unlink_and_free(dcfd_handle, np); 56483c4dfe9Sjg np = NULL; 56583c4dfe9Sjg break; 56683c4dfe9Sjg } else { 56783c4dfe9Sjg DEVID_DEBUG2((CE_CONT, 56883c4dfe9Sjg "devid register: %s devid match\n", path)); 56983c4dfe9Sjg np->nvp_flags |= 570a204de77Scth NVP_DEVID_DIP | NVP_DEVID_REGISTERED; 57183c4dfe9Sjg np->nvp_dip = dip; 57283c4dfe9Sjg rw_exit(nvf_lock(dcfd_handle)); 57383c4dfe9Sjg kmem_free(new_nvp, sizeof (nvp_devid_t)); 57483c4dfe9Sjg kmem_free(path, pathlen); 57583c4dfe9Sjg kmem_free(new_devid, new_devid_size); 57683c4dfe9Sjg return (DDI_SUCCESS); 57783c4dfe9Sjg } 57883c4dfe9Sjg } 57983c4dfe9Sjg } 58083c4dfe9Sjg 58183c4dfe9Sjg /* 58283c4dfe9Sjg * Add newly registered devid to the cache 58383c4dfe9Sjg */ 58483c4dfe9Sjg ASSERT(np == NULL); 58583c4dfe9Sjg 58683c4dfe9Sjg new_nvp->nvp_devpath = path; 58783c4dfe9Sjg new_nvp->nvp_flags = NVP_DEVID_DIP | NVP_DEVID_REGISTERED; 58883c4dfe9Sjg new_nvp->nvp_dip = dip; 58983c4dfe9Sjg new_nvp->nvp_devid = new_devid; 59083c4dfe9Sjg 59183c4dfe9Sjg if (!devid_cache_write_disable) { 59283c4dfe9Sjg is_dirty = 1; 59383c4dfe9Sjg nvf_mark_dirty(dcfd_handle); 59483c4dfe9Sjg } 59583c4dfe9Sjg list_insert_tail(nvf_list(dcfd_handle), new_nvp); 59683c4dfe9Sjg 59783c4dfe9Sjg rw_exit(nvf_lock(dcfd_handle)); 59883c4dfe9Sjg 59983c4dfe9Sjg exit: 60083c4dfe9Sjg if (free_devid) 60183c4dfe9Sjg kmem_free(free_devid, ddi_devid_sizeof(free_devid)); 60283c4dfe9Sjg 60383c4dfe9Sjg if (is_dirty) 60483c4dfe9Sjg nvf_wake_daemon(); 60583c4dfe9Sjg 60683c4dfe9Sjg return (DDI_SUCCESS); 60783c4dfe9Sjg } 60883c4dfe9Sjg 60983c4dfe9Sjg /* 61083c4dfe9Sjg * Unregister a device's devid 61183c4dfe9Sjg * Called as an instance detachs 61283c4dfe9Sjg * Invalidate the devid's devinfo reference 61383c4dfe9Sjg * Devid-path remains in the cache 61483c4dfe9Sjg */ 61583c4dfe9Sjg void 61683c4dfe9Sjg e_devid_cache_unregister(dev_info_t *dip) 61783c4dfe9Sjg { 61883c4dfe9Sjg nvp_devid_t *np; 61983c4dfe9Sjg list_t *listp; 62083c4dfe9Sjg 62183c4dfe9Sjg rw_enter(nvf_lock(dcfd_handle), RW_WRITER); 62283c4dfe9Sjg 62383c4dfe9Sjg listp = nvf_list(dcfd_handle); 62483c4dfe9Sjg for (np = list_head(listp); np; np = list_next(listp, np)) { 62583c4dfe9Sjg if (np->nvp_devid == NULL) 62683c4dfe9Sjg continue; 62783c4dfe9Sjg if ((np->nvp_flags & NVP_DEVID_DIP) && np->nvp_dip == dip) { 62883c4dfe9Sjg DEVID_LOG_UNREG((CE_CONT, 629a204de77Scth "unregister: %s\n", np->nvp_devpath)); 63083c4dfe9Sjg np->nvp_flags &= ~NVP_DEVID_DIP; 63183c4dfe9Sjg np->nvp_dip = NULL; 63283c4dfe9Sjg break; 63383c4dfe9Sjg } 63483c4dfe9Sjg } 63583c4dfe9Sjg 63683c4dfe9Sjg rw_exit(nvf_lock(dcfd_handle)); 63783c4dfe9Sjg } 63883c4dfe9Sjg 63983c4dfe9Sjg /* 64083c4dfe9Sjg * Purge devid cache of stale devids 64183c4dfe9Sjg */ 64283c4dfe9Sjg void 64383c4dfe9Sjg devid_cache_cleanup(void) 64483c4dfe9Sjg { 64583c4dfe9Sjg nvp_devid_t *np, *next; 64683c4dfe9Sjg list_t *listp; 64783c4dfe9Sjg int is_dirty = 0; 64883c4dfe9Sjg 64983c4dfe9Sjg rw_enter(nvf_lock(dcfd_handle), RW_WRITER); 65083c4dfe9Sjg 65183c4dfe9Sjg listp = nvf_list(dcfd_handle); 65283c4dfe9Sjg for (np = list_head(listp); np; np = next) { 65383c4dfe9Sjg next = list_next(listp, np); 65483c4dfe9Sjg if (np->nvp_devid == NULL) 65583c4dfe9Sjg continue; 65683c4dfe9Sjg if ((np->nvp_flags & NVP_DEVID_REGISTERED) == 0) { 65783c4dfe9Sjg DEVID_LOG_REMOVE((CE_CONT, 658a204de77Scth "cleanup: %s\n", np->nvp_devpath)); 65983c4dfe9Sjg if (!devid_cache_write_disable) { 66083c4dfe9Sjg nvf_mark_dirty(dcfd_handle); 66183c4dfe9Sjg is_dirty = 0; 66283c4dfe9Sjg } 66383c4dfe9Sjg devid_nvp_unlink_and_free(dcfd_handle, np); 66483c4dfe9Sjg } 66583c4dfe9Sjg } 66683c4dfe9Sjg 66783c4dfe9Sjg rw_exit(nvf_lock(dcfd_handle)); 66883c4dfe9Sjg 66983c4dfe9Sjg if (is_dirty) 67083c4dfe9Sjg nvf_wake_daemon(); 67183c4dfe9Sjg } 67283c4dfe9Sjg 67383c4dfe9Sjg 67483c4dfe9Sjg /* 67583c4dfe9Sjg * Build a list of dev_t's for a device/devid 67683c4dfe9Sjg * 67783c4dfe9Sjg * The effect of this function is cumulative, adding dev_t's 67883c4dfe9Sjg * for the device to the list of all dev_t's for a given 67983c4dfe9Sjg * devid. 68083c4dfe9Sjg */ 68183c4dfe9Sjg static void 68283c4dfe9Sjg e_devid_minor_to_devlist( 68383c4dfe9Sjg dev_info_t *dip, 68483c4dfe9Sjg char *minor_name, 68583c4dfe9Sjg int ndevts_alloced, 68683c4dfe9Sjg int *devtcntp, 68783c4dfe9Sjg dev_t *devtsp) 68883c4dfe9Sjg { 689b9ccdc5aScth int circ; 69083c4dfe9Sjg struct ddi_minor_data *dmdp; 69183c4dfe9Sjg int minor_all = 0; 69283c4dfe9Sjg int ndevts = *devtcntp; 69383c4dfe9Sjg 69483c4dfe9Sjg ASSERT(i_ddi_devi_attached(dip)); 69583c4dfe9Sjg 69683c4dfe9Sjg /* are we looking for a set of minor nodes? */ 69783c4dfe9Sjg if ((minor_name == DEVID_MINOR_NAME_ALL) || 69883c4dfe9Sjg (minor_name == DEVID_MINOR_NAME_ALL_CHR) || 69983c4dfe9Sjg (minor_name == DEVID_MINOR_NAME_ALL_BLK)) 70083c4dfe9Sjg minor_all = 1; 70183c4dfe9Sjg 70283c4dfe9Sjg /* Find matching minor names */ 703b9ccdc5aScth ndi_devi_enter(dip, &circ); 70483c4dfe9Sjg for (dmdp = DEVI(dip)->devi_minor; dmdp; dmdp = dmdp->next) { 70583c4dfe9Sjg 70683c4dfe9Sjg /* Skip non-minors, and non matching minor names */ 70783c4dfe9Sjg if ((dmdp->type != DDM_MINOR) || ((minor_all == 0) && 70883c4dfe9Sjg strcmp(dmdp->ddm_name, minor_name))) 70983c4dfe9Sjg continue; 71083c4dfe9Sjg 71183c4dfe9Sjg /* filter out minor_all mismatches */ 71283c4dfe9Sjg if (minor_all && 71383c4dfe9Sjg (((minor_name == DEVID_MINOR_NAME_ALL_CHR) && 71483c4dfe9Sjg (dmdp->ddm_spec_type != S_IFCHR)) || 71583c4dfe9Sjg ((minor_name == DEVID_MINOR_NAME_ALL_BLK) && 71683c4dfe9Sjg (dmdp->ddm_spec_type != S_IFBLK)))) 71783c4dfe9Sjg continue; 71883c4dfe9Sjg 71983c4dfe9Sjg if (ndevts < ndevts_alloced) 72083c4dfe9Sjg devtsp[ndevts] = dmdp->ddm_dev; 72183c4dfe9Sjg ndevts++; 72283c4dfe9Sjg } 723b9ccdc5aScth ndi_devi_exit(dip, circ); 72483c4dfe9Sjg 72583c4dfe9Sjg *devtcntp = ndevts; 72683c4dfe9Sjg } 72783c4dfe9Sjg 72883c4dfe9Sjg /* 72983c4dfe9Sjg * Search for cached entries matching a devid 73083c4dfe9Sjg * Return two lists: 73183c4dfe9Sjg * a list of dev_info nodes, for those devices in the attached state 73283c4dfe9Sjg * a list of pathnames whose instances registered the given devid 73383c4dfe9Sjg * If the lists passed in are not sufficient to return the matching 73483c4dfe9Sjg * references, return the size of lists required. 73583c4dfe9Sjg * The dev_info nodes are returned with a hold that the caller must release. 73683c4dfe9Sjg */ 73783c4dfe9Sjg static int 73883c4dfe9Sjg e_devid_cache_devi_path_lists(ddi_devid_t devid, int retmax, 73983c4dfe9Sjg int *retndevis, dev_info_t **retdevis, int *retnpaths, char **retpaths) 74083c4dfe9Sjg { 74183c4dfe9Sjg nvp_devid_t *np; 74283c4dfe9Sjg int ndevis, npaths; 74383c4dfe9Sjg dev_info_t *dip, *pdip; 74483c4dfe9Sjg int circ; 74583c4dfe9Sjg int maxdevis = 0; 74683c4dfe9Sjg int maxpaths = 0; 74783c4dfe9Sjg list_t *listp; 74883c4dfe9Sjg 74983c4dfe9Sjg ndevis = 0; 75083c4dfe9Sjg npaths = 0; 75183c4dfe9Sjg listp = nvf_list(dcfd_handle); 75283c4dfe9Sjg for (np = list_head(listp); np; np = list_next(listp, np)) { 75383c4dfe9Sjg if (np->nvp_devid == NULL) 75483c4dfe9Sjg continue; 75583c4dfe9Sjg if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) { 75683c4dfe9Sjg DEVIDERR((CE_CONT, 75783c4dfe9Sjg "find: invalid devid %s\n", 75883c4dfe9Sjg np->nvp_devpath)); 75983c4dfe9Sjg continue; 76083c4dfe9Sjg } 76183c4dfe9Sjg if (ddi_devid_compare(devid, np->nvp_devid) == 0) { 76283c4dfe9Sjg DEVID_DEBUG2((CE_CONT, 76383c4dfe9Sjg "find: devid match: %s 0x%x\n", 76483c4dfe9Sjg np->nvp_devpath, np->nvp_flags)); 76583c4dfe9Sjg DEVID_LOG_MATCH(("find", devid, np->nvp_devpath)); 76683c4dfe9Sjg DEVID_LOG_PATHS((CE_CONT, "%s\n", np->nvp_devpath)); 76783c4dfe9Sjg 76883c4dfe9Sjg /* 76983c4dfe9Sjg * Check if we have a cached devinfo reference for this 77083c4dfe9Sjg * devid. Place a hold on it to prevent detach 77183c4dfe9Sjg * Otherwise, use the path instead. 77283c4dfe9Sjg * Note: returns with a hold on each dev_info 77383c4dfe9Sjg * node in the list. 77483c4dfe9Sjg */ 77583c4dfe9Sjg dip = NULL; 77683c4dfe9Sjg if (np->nvp_flags & NVP_DEVID_DIP) { 77783c4dfe9Sjg pdip = ddi_get_parent(np->nvp_dip); 77883c4dfe9Sjg if (ndi_devi_tryenter(pdip, &circ)) { 77983c4dfe9Sjg dip = np->nvp_dip; 78083c4dfe9Sjg ndi_hold_devi(dip); 78183c4dfe9Sjg ndi_devi_exit(pdip, circ); 78283c4dfe9Sjg ASSERT(!DEVI_IS_ATTACHING(dip)); 78383c4dfe9Sjg ASSERT(!DEVI_IS_DETACHING(dip)); 78483c4dfe9Sjg } else { 78583c4dfe9Sjg DEVID_LOG_DETACH((CE_CONT, 78683c4dfe9Sjg "may be detaching: %s\n", 78783c4dfe9Sjg np->nvp_devpath)); 78883c4dfe9Sjg } 78983c4dfe9Sjg } 79083c4dfe9Sjg 79183c4dfe9Sjg if (dip) { 79283c4dfe9Sjg if (ndevis < retmax) { 79383c4dfe9Sjg retdevis[ndevis++] = dip; 79483c4dfe9Sjg } else { 79583c4dfe9Sjg ndi_rele_devi(dip); 79683c4dfe9Sjg } 79783c4dfe9Sjg maxdevis++; 79883c4dfe9Sjg } else { 79983c4dfe9Sjg if (npaths < retmax) 80083c4dfe9Sjg retpaths[npaths++] = np->nvp_devpath; 80183c4dfe9Sjg maxpaths++; 80283c4dfe9Sjg } 80383c4dfe9Sjg } 80483c4dfe9Sjg } 80583c4dfe9Sjg 80683c4dfe9Sjg *retndevis = ndevis; 80783c4dfe9Sjg *retnpaths = npaths; 80883c4dfe9Sjg return (maxdevis > maxpaths ? maxdevis : maxpaths); 80983c4dfe9Sjg } 81083c4dfe9Sjg 81183c4dfe9Sjg 81283c4dfe9Sjg /* 81383c4dfe9Sjg * Search the devid cache, returning dev_t list for all 81483c4dfe9Sjg * device paths mapping to the device identified by the 81583c4dfe9Sjg * given devid. 81683c4dfe9Sjg * 81783c4dfe9Sjg * Primary interface used by ddi_lyr_devid_to_devlist() 81883c4dfe9Sjg */ 81983c4dfe9Sjg int 82083c4dfe9Sjg e_devid_cache_to_devt_list(ddi_devid_t devid, char *minor_name, 82183c4dfe9Sjg int *retndevts, dev_t **retdevts) 82283c4dfe9Sjg { 82383c4dfe9Sjg char *path, **paths; 82483c4dfe9Sjg int i, j, n; 82583c4dfe9Sjg dev_t *devts, *udevts; 82683c4dfe9Sjg dev_t tdevt; 82783c4dfe9Sjg int ndevts, undevts, ndevts_alloced; 82883c4dfe9Sjg dev_info_t *devi, **devis; 82983c4dfe9Sjg int ndevis, npaths, nalloced; 83083c4dfe9Sjg ddi_devid_t match_devid; 83183c4dfe9Sjg 83283c4dfe9Sjg DEVID_LOG_FIND(("find", devid, NULL)); 83383c4dfe9Sjg 83483c4dfe9Sjg ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS); 83583c4dfe9Sjg if (ddi_devid_valid(devid) != DDI_SUCCESS) { 83683c4dfe9Sjg DEVID_LOG_ERR(("invalid devid", devid, NULL)); 83783c4dfe9Sjg return (DDI_FAILURE); 83883c4dfe9Sjg } 83983c4dfe9Sjg 84083c4dfe9Sjg nalloced = 128; 84183c4dfe9Sjg 84283c4dfe9Sjg for (;;) { 84383c4dfe9Sjg paths = kmem_zalloc(nalloced * sizeof (char *), KM_SLEEP); 84483c4dfe9Sjg devis = kmem_zalloc(nalloced * sizeof (dev_info_t *), KM_SLEEP); 84583c4dfe9Sjg 84683c4dfe9Sjg rw_enter(nvf_lock(dcfd_handle), RW_READER); 84783c4dfe9Sjg n = e_devid_cache_devi_path_lists(devid, nalloced, 848a204de77Scth &ndevis, devis, &npaths, paths); 84983c4dfe9Sjg if (n <= nalloced) 85083c4dfe9Sjg break; 85183c4dfe9Sjg rw_exit(nvf_lock(dcfd_handle)); 85283c4dfe9Sjg for (i = 0; i < ndevis; i++) 85383c4dfe9Sjg ndi_rele_devi(devis[i]); 85483c4dfe9Sjg kmem_free(paths, nalloced * sizeof (char *)); 85583c4dfe9Sjg kmem_free(devis, nalloced * sizeof (dev_info_t *)); 85683c4dfe9Sjg nalloced = n + 128; 85783c4dfe9Sjg } 85883c4dfe9Sjg 85983c4dfe9Sjg for (i = 0; i < npaths; i++) { 86083c4dfe9Sjg path = i_ddi_strdup(paths[i], KM_SLEEP); 86183c4dfe9Sjg paths[i] = path; 86283c4dfe9Sjg } 86383c4dfe9Sjg rw_exit(nvf_lock(dcfd_handle)); 86483c4dfe9Sjg 86583c4dfe9Sjg if (ndevis == 0 && npaths == 0) { 86683c4dfe9Sjg DEVID_LOG_ERR(("no devid found", devid, NULL)); 86783c4dfe9Sjg kmem_free(paths, nalloced * sizeof (char *)); 86883c4dfe9Sjg kmem_free(devis, nalloced * sizeof (dev_info_t *)); 86983c4dfe9Sjg return (DDI_FAILURE); 87083c4dfe9Sjg } 87183c4dfe9Sjg 87283c4dfe9Sjg ndevts_alloced = 128; 87383c4dfe9Sjg restart: 87483c4dfe9Sjg ndevts = 0; 87583c4dfe9Sjg devts = kmem_alloc(ndevts_alloced * sizeof (dev_t), KM_SLEEP); 87683c4dfe9Sjg for (i = 0; i < ndevis; i++) { 87783c4dfe9Sjg ASSERT(!DEVI_IS_ATTACHING(devis[i])); 87883c4dfe9Sjg ASSERT(!DEVI_IS_DETACHING(devis[i])); 87983c4dfe9Sjg e_devid_minor_to_devlist(devis[i], minor_name, 880a204de77Scth ndevts_alloced, &ndevts, devts); 88183c4dfe9Sjg if (ndevts > ndevts_alloced) { 88283c4dfe9Sjg kmem_free(devts, ndevts_alloced * sizeof (dev_t)); 88383c4dfe9Sjg ndevts_alloced += 128; 88483c4dfe9Sjg goto restart; 88583c4dfe9Sjg } 88683c4dfe9Sjg } 88783c4dfe9Sjg for (i = 0; i < npaths; i++) { 88883c4dfe9Sjg DEVID_LOG_LOOKUP((CE_CONT, "lookup %s\n", paths[i])); 88983c4dfe9Sjg devi = e_ddi_hold_devi_by_path(paths[i], 0); 89083c4dfe9Sjg if (devi == NULL) { 89183c4dfe9Sjg DEVID_LOG_STALE(("stale device reference", 89283c4dfe9Sjg devid, paths[i])); 89383c4dfe9Sjg continue; 89483c4dfe9Sjg } 89583c4dfe9Sjg /* 89683c4dfe9Sjg * Verify the newly attached device registered a matching devid 89783c4dfe9Sjg */ 89883c4dfe9Sjg if (i_ddi_devi_get_devid(DDI_DEV_T_ANY, devi, 89983c4dfe9Sjg &match_devid) != DDI_SUCCESS) { 90083c4dfe9Sjg DEVIDERR((CE_CONT, 90183c4dfe9Sjg "%s: no devid registered on attach\n", 90283c4dfe9Sjg paths[i])); 90383c4dfe9Sjg ddi_release_devi(devi); 90483c4dfe9Sjg continue; 90583c4dfe9Sjg } 90683c4dfe9Sjg 90783c4dfe9Sjg if (ddi_devid_compare(devid, match_devid) != 0) { 90883c4dfe9Sjg DEVID_LOG_STALE(("new devid registered", 90983c4dfe9Sjg devid, paths[i])); 91083c4dfe9Sjg ddi_release_devi(devi); 91183c4dfe9Sjg ddi_devid_free(match_devid); 91283c4dfe9Sjg continue; 91383c4dfe9Sjg } 91483c4dfe9Sjg ddi_devid_free(match_devid); 91583c4dfe9Sjg 91683c4dfe9Sjg e_devid_minor_to_devlist(devi, minor_name, 917a204de77Scth ndevts_alloced, &ndevts, devts); 91883c4dfe9Sjg ddi_release_devi(devi); 91983c4dfe9Sjg if (ndevts > ndevts_alloced) { 92083c4dfe9Sjg kmem_free(devts, 92183c4dfe9Sjg ndevts_alloced * sizeof (dev_t)); 92283c4dfe9Sjg ndevts_alloced += 128; 92383c4dfe9Sjg goto restart; 92483c4dfe9Sjg } 92583c4dfe9Sjg } 92683c4dfe9Sjg 92783c4dfe9Sjg /* drop hold from e_devid_cache_devi_path_lists */ 92883c4dfe9Sjg for (i = 0; i < ndevis; i++) { 92983c4dfe9Sjg ndi_rele_devi(devis[i]); 93083c4dfe9Sjg } 93183c4dfe9Sjg for (i = 0; i < npaths; i++) { 93283c4dfe9Sjg kmem_free(paths[i], strlen(paths[i]) + 1); 93383c4dfe9Sjg } 93483c4dfe9Sjg kmem_free(paths, nalloced * sizeof (char *)); 93583c4dfe9Sjg kmem_free(devis, nalloced * sizeof (dev_info_t *)); 93683c4dfe9Sjg 93783c4dfe9Sjg if (ndevts == 0) { 93883c4dfe9Sjg DEVID_LOG_ERR(("no devid found", devid, NULL)); 93983c4dfe9Sjg kmem_free(devts, ndevts_alloced * sizeof (dev_t)); 94083c4dfe9Sjg return (DDI_FAILURE); 94183c4dfe9Sjg } 94283c4dfe9Sjg 94383c4dfe9Sjg /* 94483c4dfe9Sjg * Build the final list of sorted dev_t's with duplicates collapsed so 94583c4dfe9Sjg * returned results are consistent. This prevents implementation 94683c4dfe9Sjg * artifacts from causing unnecessary changes in SVM namespace. 94783c4dfe9Sjg */ 94883c4dfe9Sjg /* bubble sort */ 94983c4dfe9Sjg for (i = 0; i < (ndevts - 1); i++) { 95083c4dfe9Sjg for (j = 0; j < ((ndevts - 1) - i); j++) { 95183c4dfe9Sjg if (devts[j + 1] < devts[j]) { 95283c4dfe9Sjg tdevt = devts[j]; 95383c4dfe9Sjg devts[j] = devts[j + 1]; 95483c4dfe9Sjg devts[j + 1] = tdevt; 95583c4dfe9Sjg } 95683c4dfe9Sjg } 95783c4dfe9Sjg } 95883c4dfe9Sjg 95983c4dfe9Sjg /* determine number of unique values */ 96083c4dfe9Sjg for (undevts = ndevts, i = 1; i < ndevts; i++) { 96183c4dfe9Sjg if (devts[i - 1] == devts[i]) 96283c4dfe9Sjg undevts--; 96383c4dfe9Sjg } 96483c4dfe9Sjg 96583c4dfe9Sjg /* allocate unique */ 96683c4dfe9Sjg udevts = kmem_alloc(undevts * sizeof (dev_t), KM_SLEEP); 96783c4dfe9Sjg 96883c4dfe9Sjg /* copy unique */ 96983c4dfe9Sjg udevts[0] = devts[0]; 97083c4dfe9Sjg for (i = 1, j = 1; i < ndevts; i++) { 97183c4dfe9Sjg if (devts[i - 1] != devts[i]) 97283c4dfe9Sjg udevts[j++] = devts[i]; 97383c4dfe9Sjg } 97483c4dfe9Sjg ASSERT(j == undevts); 97583c4dfe9Sjg 97683c4dfe9Sjg kmem_free(devts, ndevts_alloced * sizeof (dev_t)); 97783c4dfe9Sjg 97883c4dfe9Sjg *retndevts = undevts; 97983c4dfe9Sjg *retdevts = udevts; 98083c4dfe9Sjg 98183c4dfe9Sjg return (DDI_SUCCESS); 98283c4dfe9Sjg } 98383c4dfe9Sjg 98483c4dfe9Sjg void 98583c4dfe9Sjg e_devid_cache_free_devt_list(int ndevts, dev_t *devt_list) 98683c4dfe9Sjg { 98783c4dfe9Sjg kmem_free(devt_list, ndevts * sizeof (dev_t *)); 98883c4dfe9Sjg } 98983c4dfe9Sjg 99083c4dfe9Sjg #ifdef DEBUG 99183c4dfe9Sjg static void 99283c4dfe9Sjg devid_log(char *fmt, ddi_devid_t devid, char *path) 99383c4dfe9Sjg { 99483c4dfe9Sjg char *devidstr = ddi_devid_str_encode(devid, NULL); 99583c4dfe9Sjg if (path) { 99683c4dfe9Sjg cmn_err(CE_CONT, "%s: %s %s\n", fmt, path, devidstr); 99783c4dfe9Sjg } else { 99883c4dfe9Sjg cmn_err(CE_CONT, "%s: %s\n", fmt, devidstr); 99983c4dfe9Sjg } 100083c4dfe9Sjg ddi_devid_str_free(devidstr); 100183c4dfe9Sjg } 100283c4dfe9Sjg #endif /* DEBUG */ 1003