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