diff options
author | jg <none@none> | 2006-09-25 08:38:36 -0700 |
---|---|---|
committer | jg <none@none> | 2006-09-25 08:38:36 -0700 |
commit | 83c4dfe9546fd839e7a52bca7e9920da918f916e (patch) | |
tree | b20d087d1ba86d49a6059c9bf59daeda3cc69db0 | |
parent | f92daba9919b6e68875ccdc9a5532cadf37959f1 (diff) | |
download | illumos-joyent-83c4dfe9546fd839e7a52bca7e9920da918f916e.tar.gz |
6466789 decouple devices cache management implementation from consumers
--HG--
rename : usr/src/uts/common/os/devctl.c => usr/src/uts/common/os/devcache.c
rename : usr/src/uts/common/sys/devctl_impl.h => usr/src/uts/common/sys/devcache_impl.h
-rw-r--r-- | usr/src/pkgdefs/SUNWhea/prototype_com | 5 | ||||
-rw-r--r-- | usr/src/uts/common/Makefile.files | 3 | ||||
-rw-r--r-- | usr/src/uts/common/fs/dev/sdev_ncache.c | 265 | ||||
-rw-r--r-- | usr/src/uts/common/os/devcache.c | 1146 | ||||
-rw-r--r-- | usr/src/uts/common/os/devctl.c | 1943 | ||||
-rw-r--r-- | usr/src/uts/common/os/devid_cache.c | 998 | ||||
-rw-r--r-- | usr/src/uts/common/sys/Makefile | 5 | ||||
-rw-r--r-- | usr/src/uts/common/sys/ddi_implfuncs.h | 11 | ||||
-rw-r--r-- | usr/src/uts/common/sys/devcache.h | 81 | ||||
-rw-r--r-- | usr/src/uts/common/sys/devcache_impl.h | 166 | ||||
-rw-r--r-- | usr/src/uts/common/sys/devctl_impl.h | 371 | ||||
-rw-r--r-- | usr/src/uts/common/sys/devid_cache.h | 153 | ||||
-rw-r--r-- | usr/src/uts/common/sys/fs/sdev_impl.h | 16 |
13 files changed, 2805 insertions, 2358 deletions
diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com index ae04a36b45..33003b4635 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_com +++ b/usr/src/pkgdefs/SUNWhea/prototype_com @@ -598,8 +598,9 @@ f none usr/include/sys/ddipropdefs.h 644 root bin f none usr/include/sys/dditypes.h 644 root bin f none usr/include/sys/debug.h 644 root bin f none usr/include/sys/des.h 644 root bin -f none usr/include/sys/devctl.h 644 root bin -f none usr/include/sys/devctl_impl.h 644 root bin +f none usr/include/sys/devcache.h 644 root bin +f none usr/include/sys/devcache_impl.h 644 root bin +f none usr/include/sys/devid_cache.h 644 root bin f none usr/include/sys/devinfo_impl.h 644 root bin f none usr/include/sys/devops.h 644 root bin f none usr/include/sys/devpolicy.h 644 root bin diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 20038e8bf4..9744a194f9 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -111,8 +111,9 @@ GENUNIX_OBJS += \ ddi_nodeid.o \ ddi_strtol.o \ devcfg.o \ - devctl.o \ + devcache.o \ devid.o \ + devid_cache.o \ devid_scsi.o \ devpolicy.o \ disp_lock.o \ diff --git a/usr/src/uts/common/fs/dev/sdev_ncache.c b/usr/src/uts/common/fs/dev/sdev_ncache.c index f66532508d..e689f909d0 100644 --- a/usr/src/uts/common/fs/dev/sdev_ncache.c +++ b/usr/src/uts/common/fs/dev/sdev_ncache.c @@ -59,7 +59,7 @@ #include <sys/sunmdi.h> #include <sys/ddi.h> #include <sys/modctl.h> -#include <sys/devctl_impl.h> +#include <sys/devcache.h> /* @@ -108,23 +108,27 @@ #define SDEV_NC_MAX_ENTRIES 64 #define SEV_RECONFIG_DELAY 6 /* seconds */ -int sdev_nc_expirecnt = SDEV_NC_EXPIRECNT; -int sdev_nc_max_entries = SDEV_NC_MAX_ENTRIES; -int sdev_reconfig_delay = SEV_RECONFIG_DELAY; -int sdev_reconfig_verbose = 0; -int sdev_reconfig_disable = 0; -int sdev_nc_disable = 0; -int sdev_nc_disable_reset = 0; -int sdev_nc_verbose = 0; +/* tunables */ +int sdev_nc_expirecnt = SDEV_NC_EXPIRECNT; +int sdev_nc_max_entries = SDEV_NC_MAX_ENTRIES; +int sdev_reconfig_delay = SEV_RECONFIG_DELAY; +int sdev_reconfig_verbose = 0; +int sdev_reconfig_disable = 0; +int sdev_nc_disable = 0; +int sdev_nc_disable_reset = 0; +int sdev_nc_verbose = 0; +int sdev_cache_read_disable = 0; +int sdev_cache_write_disable = 0; /* globals */ -sdev_nc_list_t *sdev_ncache; -int sdev_boot_state = SDEV_BOOT_STATE_INITIAL; -int sdev_reconfig_boot = 0; -static timeout_id_t sdev_timeout_id = 0; +int sdev_boot_state = SDEV_BOOT_STATE_INITIAL; +int sdev_reconfig_boot = 0; +sdev_nc_list_t *sdev_ncache; +static timeout_id_t sdev_timeout_id = 0; +static nvf_handle_t sdevfd_handle; /* static prototypes */ -static void sdev_ncache_write_complete(nvfd_t *); +static void sdev_ncache_write_complete(nvf_handle_t); static void sdev_ncache_write(void); static void sdev_ncache_process_store(void); static sdev_nc_list_t *sdev_nc_newlist(void); @@ -134,7 +138,21 @@ static void sdev_nc_freelist(sdev_nc_list_t *); static sdev_nc_node_t *sdev_nc_findpath(sdev_nc_list_t *, char *); static void sdev_nc_insertnode(sdev_nc_list_t *, sdev_nc_node_t *); static void sdev_nc_free_bootonly(void); +static int sdev_ncache_unpack_nvlist(nvf_handle_t, nvlist_t *, char *); +static int sdev_ncache_pack_list(nvf_handle_t, nvlist_t **); +static void sdev_ncache_list_free(nvf_handle_t); +static void sdev_nvp_free(nvp_devname_t *); +/* + * Registration for /etc/devices/devname_cache + */ +static nvf_ops_t sdev_cache_ops = { + "/etc/devices/devname_cache", /* path to cache */ + sdev_ncache_unpack_nvlist, /* read: unpack nvlist */ + sdev_ncache_pack_list, /* write: pack list */ + sdev_ncache_list_free, /* free data list */ + sdev_ncache_write_complete /* write complete callback */ +}; /* * called once at filesystem initialization @@ -152,41 +170,210 @@ sdev_ncache_init(void) void sdev_ncache_setup(void) { - nvfd_t *nvf = sdevfd; + sdevfd_handle = nvf_register_file(&sdev_cache_ops); + ASSERT(sdevfd_handle); - nvf_register_write_complete(nvf, sdev_ncache_write_complete); + list_create(nvf_list(sdevfd_handle), sizeof (nvp_devname_t), + offsetof(nvp_devname_t, nvp_link)); - i_ddi_read_devname_file(); + rw_enter(nvf_lock(sdevfd_handle), RW_WRITER); + if (!sdev_cache_read_disable) { + (void) nvf_read_file(sdevfd_handle); + } sdev_ncache_process_store(); + rw_exit(nvf_lock(sdevfd_handle)); + sdev_devstate_change(); } static void -sdev_nvp_cache_free(nvfd_t *nvf) +sdev_nvp_free(nvp_devname_t *dp) +{ + int i; + char **p; + + if (dp->nvp_npaths > 0) { + p = dp->nvp_paths; + for (i = 0; i < dp->nvp_npaths; i++, p++) { + kmem_free(*p, strlen(*p)+1); + } + kmem_free(dp->nvp_paths, + dp->nvp_npaths * sizeof (char *)); + kmem_free(dp->nvp_expirecnts, + dp->nvp_npaths * sizeof (int)); + } + + kmem_free(dp, sizeof (nvp_devname_t)); +} + +static void +sdev_ncache_list_free(nvf_handle_t fd) +{ + list_t *listp; + nvp_devname_t *dp; + + ASSERT(fd == sdevfd_handle); + ASSERT(RW_WRITE_HELD(nvf_lock(fd))); + + listp = nvf_list(fd); + if ((dp = list_head(listp)) != NULL) { + list_remove(listp, dp); + sdev_nvp_free(dp); + } +} + +/* + * Unpack a device path/nvlist pair to internal data list format. + * Used to decode the nvlist format into the internal representation + * when reading /etc/devices/devname_cache. + * Note that the expiration counts are optional, for compatibility + * with earlier instances of the cache. If not present, the + * expire counts are initialized to defaults. + */ +static int +sdev_ncache_unpack_nvlist(nvf_handle_t fd, nvlist_t *nvl, char *name) { + nvp_devname_t *np; + char **strs; + int *cnts; + uint_t nstrs, ncnts; + int rval, i; + + ASSERT(fd == sdevfd_handle); + ASSERT(RW_WRITE_HELD(nvf_lock(fd))); + + /* name of the sublist must match what we created */ + if (strcmp(name, DP_DEVNAME_ID) != 0) { + return (-1); + } + + np = kmem_zalloc(sizeof (nvp_devname_t), KM_SLEEP); + + rval = nvlist_lookup_string_array(nvl, + DP_DEVNAME_NCACHE_ID, &strs, &nstrs); + if (rval) { + kmem_free(np, sizeof (nvp_devname_t)); + return (-1); + } + + np->nvp_npaths = nstrs; + np->nvp_paths = kmem_zalloc(nstrs * sizeof (char *), KM_SLEEP); + for (i = 0; i < nstrs; i++) { + np->nvp_paths[i] = i_ddi_strdup(strs[i], KM_SLEEP); + } + np->nvp_expirecnts = kmem_zalloc(nstrs * sizeof (int), KM_SLEEP); + for (i = 0; i < nstrs; i++) { + np->nvp_expirecnts[i] = sdev_nc_expirecnt; + } + + rval = nvlist_lookup_int32_array(nvl, + DP_DEVNAME_NC_EXPIRECNT_ID, &cnts, &ncnts); + if (rval == 0) { + ASSERT(ncnts == nstrs); + ncnts = min(ncnts, nstrs); + for (i = 0; i < nstrs; i++) { + np->nvp_expirecnts[i] = cnts[i]; + } + } + + list_insert_tail(nvf_list(sdevfd_handle), np); + + return (0); +} + +/* + * Pack internal format cache data to a single nvlist. + * Used when writing the nvlist file. + * Note this is called indirectly by the nvpflush daemon. + */ +static int +sdev_ncache_pack_list(nvf_handle_t fd, nvlist_t **ret_nvl) +{ + nvlist_t *nvl, *sub_nvl; nvp_devname_t *np; - nvp_devname_t *next; + int rval; + list_t *listp; - for (np = NVF_DEVNAME_LIST(nvf); np; np = next) { - next = NVP_DEVNAME_NEXT(np); - nfd_nvp_free_and_unlink(nvf, NVPLIST(np)); + ASSERT(fd == sdevfd_handle); + ASSERT(RW_WRITE_HELD(nvf_lock(fd))); + + rval = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); + if (rval != 0) { + nvf_error("%s: nvlist alloc error %d\n", + nvf_cache_name(fd), rval); + return (DDI_FAILURE); } + + listp = nvf_list(sdevfd_handle); + if ((np = list_head(listp)) != NULL) { + ASSERT(list_next(listp, np) == NULL); + + rval = nvlist_alloc(&sub_nvl, NV_UNIQUE_NAME, KM_SLEEP); + if (rval != 0) { + nvf_error("%s: nvlist alloc error %d\n", + nvf_cache_name(fd), rval); + sub_nvl = NULL; + goto err; + } + + rval = nvlist_add_string_array(sub_nvl, + DP_DEVNAME_NCACHE_ID, np->nvp_paths, np->nvp_npaths); + if (rval != 0) { + nvf_error("%s: nvlist add error %d (sdev)\n", + nvf_cache_name(fd), rval); + goto err; + } + + rval = nvlist_add_int32_array(sub_nvl, + DP_DEVNAME_NC_EXPIRECNT_ID, + np->nvp_expirecnts, np->nvp_npaths); + if (rval != 0) { + nvf_error("%s: nvlist add error %d (sdev)\n", + nvf_cache_name(fd), rval); + goto err; + } + + rval = nvlist_add_nvlist(nvl, DP_DEVNAME_ID, sub_nvl); + if (rval != 0) { + nvf_error("%s: nvlist add error %d (sublist)\n", + nvf_cache_name(fd), rval); + goto err; + } + nvlist_free(sub_nvl); + } + + *ret_nvl = nvl; + return (DDI_SUCCESS); + +err: + if (sub_nvl) + nvlist_free(sub_nvl); + nvlist_free(nvl); + *ret_nvl = NULL; + return (DDI_FAILURE); } +/* + * Run through the data read from the backing cache store + * to establish the initial state of the neg. cache. + */ static void sdev_ncache_process_store(void) { - nvfd_t *nvf = sdevfd; sdev_nc_list_t *ncl = sdev_ncache; nvp_devname_t *np; sdev_nc_node_t *lp; char *path; int i, n; + list_t *listp; if (sdev_nc_disable) return; - for (np = NVF_DEVNAME_LIST(nvf); np; np = NVP_DEVNAME_NEXT(np)) { + ASSERT(RW_WRITE_HELD(nvf_lock(sdevfd_handle))); + + listp = nvf_list(sdevfd_handle); + for (np = list_head(listp); np; np = list_next(listp, np)) { for (i = 0; i < np->nvp_npaths; i++) { sdcmn_err5((" %s %d\n", np->nvp_paths[i], np->nvp_expirecnts[i])); @@ -209,11 +396,17 @@ sdev_ncache_process_store(void) } } +/* + * called by nvpflush daemon to inform us that an update of + * the cache file has been completed. + */ static void -sdev_ncache_write_complete(nvfd_t *nvf) +sdev_ncache_write_complete(nvf_handle_t fd) { sdev_nc_list_t *ncl = sdev_ncache; + ASSERT(fd == sdevfd_handle); + mutex_enter(&ncl->ncl_mutex); ASSERT(ncl->ncl_flags & NCL_LIST_WRITING); @@ -227,16 +420,18 @@ sdev_ncache_write_complete(nvfd_t *nvf) sdcmn_err5(("ncache write complete\n")); ncl->ncl_flags &= ~NCL_LIST_WRITING; mutex_exit(&ncl->ncl_mutex); - rw_enter(&nvf->nvf_lock, RW_WRITER); - sdev_nvp_cache_free(nvf); - rw_exit(&nvf->nvf_lock); + rw_enter(nvf_lock(fd), RW_WRITER); + sdev_ncache_list_free(fd); + rw_exit(nvf_lock(fd)); } } +/* + * Prepare to perform an update of the neg. cache backing store. + */ static void sdev_ncache_write(void) { - nvfd_t *nvf = sdevfd; sdev_nc_list_t *ncl = sdev_ncache; nvp_devname_t *np; sdev_nc_node_t *lp; @@ -250,8 +445,8 @@ sdev_ncache_write(void) } /* proper lock ordering here is essential */ - rw_enter(&nvf->nvf_lock, RW_WRITER); - sdev_nvp_cache_free(nvf); + rw_enter(nvf_lock(sdevfd_handle), RW_WRITER); + sdev_ncache_list_free(sdevfd_handle); rw_enter(&ncl->ncl_lock, RW_READER); n = ncl->ncl_nentries; @@ -274,11 +469,11 @@ sdev_ncache_write(void) rw_exit(&ncl->ncl_lock); - NVF_MARK_DIRTY(nvf); - nfd_nvp_link(nvf, NVPLIST(np)); - rw_exit(&nvf->nvf_lock); + nvf_mark_dirty(sdevfd_handle); + list_insert_tail(nvf_list(sdevfd_handle), np); + rw_exit(nvf_lock(sdevfd_handle)); - wake_nvpflush_daemon(); + nvf_wake_daemon(); } static void diff --git a/usr/src/uts/common/os/devcache.c b/usr/src/uts/common/os/devcache.c new file mode 100644 index 0000000000..14cde49faf --- /dev/null +++ b/usr/src/uts/common/os/devcache.c @@ -0,0 +1,1146 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/note.h> +#include <sys/t_lock.h> +#include <sys/cmn_err.h> +#include <sys/instance.h> +#include <sys/conf.h> +#include <sys/stat.h> +#include <sys/ddi.h> +#include <sys/hwconf.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/ddi_impldefs.h> +#include <sys/ndi_impldefs.h> +#include <sys/modctl.h> +#include <sys/dacf.h> +#include <sys/promif.h> +#include <sys/cpuvar.h> +#include <sys/pathname.h> +#include <sys/kobj.h> +#include <sys/devcache.h> +#include <sys/devcache_impl.h> +#include <sys/sysmacros.h> +#include <sys/varargs.h> +#include <sys/callb.h> + +/* + * This facility provides interfaces to clients to register, + * read and update cache data in persisted backing store files, + * usually in /etc/devices. The data persisted through this + * mechanism should be stateless data, functioning in the sense + * of a cache. Writes are performed by a background daemon + * thread, permitting a client to schedule an update without + * blocking, then continue updating the data state in + * parallel. The data is only locked by the daemon thread + * to pack the data in preparation for the write. + * + * Data persisted through this mechanism should be capable + * of being regenerated through normal system operation, + * for example attaching all disk devices would cause all + * devids to be registered for those devices. By caching + * a devid-device tuple, the system can operate in a + * more optimal way, directly attaching the device mapped + * to a devid, rather than burdensomely driving attach of + * the entire device tree to discover a single device. + * + * Note that a client should only need to include + * <sys/devcache.h> for the supported interfaces. + * + * The data per client is entirely within the control of + * the client. When reading, data unpacked from the backing + * store should be inserted in the list. The pointer to + * the list can be retreived via nvf_list(). When writing, + * the data on the list is to be packed and returned to the + * nvpdaemon as an nvlist. + * + * Obvious restrictions are imposed by the limits of the + * nvlist format. The data cannot be read or written + * piecemeal, and large amounts of data aren't recommended. + * However, nvlists do allow that data be named and typed + * and can be size-of-int invariant, and the cached data + * can be versioned conveniently. + * + * The registration involves two steps: a handle is + * allocated by calling the registration function. + * This sets up the data referenced by the handle and + * initializes the lock. Following registration, the + * client must initialize the data list. The list + * interfaces require that the list element with offset + * to the node link be provided. The format of the + * list element is under the control of the client. + * + * Locking: the address of the data list r/w lock provided + * can be accessed with nvf_lock(). The lock must be held + * as reader when traversing the list or checking state, + * such as nvf_is_dirty(). The lock must be held as + * writer when updating the list or marking it dirty. + * The lock must not be held when waking the daemon. + * + * The data r/w lock is held as writer when the pack, + * unpack and free list handlers are called. The + * lock should not be dropped and must be still held + * upon return. The client should also hold the lock + * as reader when checking if the list is dirty, and + * as writer when marking the list dirty or initiating + * a read. + * + * The asynchronous nature of updates allows for the + * possibility that the data may continue to be updated + * once the daemon has been notified that an update is + * desired. The data only needs to be locked against + * updates when packing the data into the form to be + * written. When the write of the packed data has + * completed, the daemon will automatically reschedule + * an update if the data was marked dirty after the + * point at which it was packed. Before beginning an + * update, the daemon attempts to lock the data as + * writer; if the writer lock is already held, it + * backs off and retries later. The model is to give + * priority to the kernel processes generating the + * data, and that the nature of the data is that + * it does not change often, can be re-generated when + * needed, so updates should not happen often and + * can be delayed until the data stops changing. + * The client may update the list or mark it dirty + * any time it is able to acquire the lock as + * writer first. + * + * A failed write will be retried after some delay, + * in the hope that the cause of the error will be + * transient, for example a filesystem with no space + * available. An update on a read-only filesystem + * is failed silently and not retried; this would be + * the case when booted off install media. + * + * There is no unregister mechanism as of yet, as it + * hasn't been needed so far. + */ + +/* + * Global list of files registered and updated by the nvpflush + * daemon, protected by the nvf_cache_mutex. While an + * update is taking place, a file is temporarily moved to + * the dirty list to avoid locking the primary list for + * the duration of the update. + */ +list_t nvf_cache_files; +list_t nvf_dirty_files; +kmutex_t nvf_cache_mutex; + + +/* + * Allow some delay from an update of the data before flushing + * to permit simultaneous updates of multiple changes. + * Changes in the data are expected to be bursty, ie + * reconfig or hot-plug of a new adapter. + * + * kfio_report_error (default 0) + * Set to 1 to enable some error messages related to low-level + * kernel file i/o operations. + * + * nvpflush_delay (default 10) + * The number of seconds after data is marked dirty before the + * flush daemon is triggered to flush the data. A longer period + * of time permits more data updates per write. Note that + * every update resets the timer so no repository write will + * occur while data is being updated continuously. + * + * nvpdaemon_idle_time (default 60) + * The number of seconds the daemon will sleep idle before exiting. + * + */ +#define NVPFLUSH_DELAY 10 +#define NVPDAEMON_IDLE_TIME 60 + +#define TICKS_PER_SECOND (drv_usectohz(1000000)) + +/* + * Tunables + */ +int kfio_report_error = 0; /* kernel file i/o operations */ +int kfio_disable_read = 0; /* disable all reads */ +int kfio_disable_write = 0; /* disable all writes */ + +int nvpflush_delay = NVPFLUSH_DELAY; +int nvpdaemon_idle_time = NVPDAEMON_IDLE_TIME; + +static timeout_id_t nvpflush_id = 0; +static int nvpflush_timer_busy = 0; +static int nvpflush_daemon_active = 0; +static kthread_t *nvpflush_thr_id = 0; + +static int do_nvpflush = 0; +static int nvpbusy = 0; +static kmutex_t nvpflush_lock; +static kcondvar_t nvpflush_cv; +static kthread_id_t nvpflush_thread; +static clock_t nvpticks; + +static void nvpflush_daemon(void); + +#ifdef DEBUG +int nvpdaemon_debug = 0; +int kfio_debug = 0; +#endif /* DEBUG */ + +extern int modrootloaded; +extern void mdi_read_devices_files(void); +extern void mdi_clean_vhcache(void); + +/* + * Initialize the overall cache file management + */ +void +i_ddi_devices_init(void) +{ + list_create(&nvf_cache_files, sizeof (nvfd_t), + offsetof(nvfd_t, nvf_link)); + list_create(&nvf_dirty_files, sizeof (nvfd_t), + offsetof(nvfd_t, nvf_link)); + mutex_init(&nvf_cache_mutex, NULL, MUTEX_DEFAULT, NULL); + devid_cache_init(); +} + +/* + * Read cache files + * The files read here should be restricted to those + * that may be required to mount root. + */ +void +i_ddi_read_devices_files(void) +{ + if (!kfio_disable_read) { + mdi_read_devices_files(); + devid_cache_read(); + } +} + +void +i_ddi_start_flush_daemon(void) +{ + nvfd_t *nvfdp; + + ASSERT(i_ddi_io_initialized()); + + mutex_init(&nvpflush_lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&nvpflush_cv, NULL, CV_DRIVER, NULL); + + mutex_enter(&nvf_cache_mutex); + for (nvfdp = list_head(&nvf_cache_files); nvfdp; + nvfdp = list_next(&nvf_cache_files, nvfdp)) { + if (NVF_IS_DIRTY(nvfdp)) { + nvf_wake_daemon(); + break; + } + } + mutex_exit(&nvf_cache_mutex); +} + +void +i_ddi_clean_devices_files(void) +{ + devid_cache_cleanup(); + mdi_clean_vhcache(); +} + +/* + * Register a cache file to be managed and updated by the nvpflush daemon. + * All operations are performed through the returned handle. + * There is no unregister mechanism for now. + */ +nvf_handle_t +nvf_register_file(nvf_ops_t *ops) +{ + nvfd_t *nvfdp; + + nvfdp = kmem_zalloc(sizeof (*nvfdp), KM_SLEEP); + + nvfdp->nvf_ops = ops; + nvfdp->nvf_flags = 0; + rw_init(&nvfdp->nvf_lock, NULL, RW_DRIVER, NULL); + + mutex_enter(&nvf_cache_mutex); + list_insert_tail(&nvf_cache_files, nvfdp); + mutex_exit(&nvf_cache_mutex); + + return ((nvf_handle_t)nvfdp); +} + +/*PRINTFLIKE1*/ +void +nvf_error(const char *fmt, ...) +{ + va_list ap; + + if (kfio_report_error) { + va_start(ap, fmt); + vcmn_err(CE_NOTE, fmt, ap); + va_end(ap); + } +} + +/* + * Some operations clients may use to manage the data + * to be persisted in a cache file. + */ +char * +nvf_cache_name(nvf_handle_t handle) +{ + return (((nvfd_t *)handle)->nvf_cache_path); +} + +krwlock_t * +nvf_lock(nvf_handle_t handle) +{ + return (&(((nvfd_t *)handle)->nvf_lock)); +} + +list_t * +nvf_list(nvf_handle_t handle) +{ + return (&(((nvfd_t *)handle)->nvf_data_list)); +} + +void +nvf_mark_dirty(nvf_handle_t handle) +{ + ASSERT(RW_WRITE_HELD(&(((nvfd_t *)handle)->nvf_lock))); + NVF_MARK_DIRTY((nvfd_t *)handle); +} + +int +nvf_is_dirty(nvf_handle_t handle) +{ + ASSERT(RW_LOCK_HELD(&(((nvfd_t *)handle)->nvf_lock))); + return (NVF_IS_DIRTY((nvfd_t *)handle)); +} + +static uint16_t +nvp_cksum(uchar_t *buf, int64_t buflen) +{ + uint16_t cksum = 0; + uint16_t *p = (uint16_t *)buf; + int64_t n; + + if ((buflen & 0x01) != 0) { + buflen--; + cksum = buf[buflen]; + } + n = buflen / 2; + while (n-- > 0) + cksum ^= *p++; + return (cksum); +} + +int +fread_nvlist(char *filename, nvlist_t **ret_nvlist) +{ + struct _buf *file; + nvpf_hdr_t hdr; + char *buf; + nvlist_t *nvl; + int rval; + uint_t offset; + int n; + char c; + uint16_t cksum, hdrsum; + + *ret_nvlist = NULL; + + file = kobj_open_file(filename); + if (file == (struct _buf *)-1) { + KFDEBUG((CE_CONT, "cannot open file: %s\n", filename)); + return (ENOENT); + } + + offset = 0; + n = kobj_read_file(file, (char *)&hdr, sizeof (hdr), offset); + if (n != sizeof (hdr)) { + kobj_close_file(file); + if (n < 0) { + nvf_error("error reading header: %s\n", filename); + return (EIO); + } else if (n == 0) { + KFDEBUG((CE_CONT, "file empty: %s\n", filename)); + } else { + nvf_error("header size incorrect: %s\n", filename); + } + return (EINVAL); + } + offset += n; + + KFDEBUG2((CE_CONT, "nvpf_magic: 0x%x\n", hdr.nvpf_magic)); + KFDEBUG2((CE_CONT, "nvpf_version: %d\n", hdr.nvpf_version)); + KFDEBUG2((CE_CONT, "nvpf_size: %lld\n", + (longlong_t)hdr.nvpf_size)); + KFDEBUG2((CE_CONT, "nvpf_hdr_chksum: 0x%x\n", + hdr.nvpf_hdr_chksum)); + KFDEBUG2((CE_CONT, "nvpf_chksum: 0x%x\n", hdr.nvpf_chksum)); + + cksum = hdr.nvpf_hdr_chksum; + hdr.nvpf_hdr_chksum = 0; + hdrsum = nvp_cksum((uchar_t *)&hdr, sizeof (hdr)); + + if (hdr.nvpf_magic != NVPF_HDR_MAGIC || + hdr.nvpf_version != NVPF_HDR_VERSION || hdrsum != cksum) { + kobj_close_file(file); + if (hdrsum != cksum) { + nvf_error("%s: checksum error " + "(actual 0x%x, expected 0x%x)\n", + filename, hdrsum, cksum); + } + nvf_error("%s: header information incorrect", filename); + return (EINVAL); + } + + ASSERT(hdr.nvpf_size >= 0); + + buf = kmem_alloc(hdr.nvpf_size, KM_SLEEP); + n = kobj_read_file(file, buf, hdr.nvpf_size, offset); + if (n != hdr.nvpf_size) { + kmem_free(buf, hdr.nvpf_size); + kobj_close_file(file); + if (n < 0) { + nvf_error("%s: read error %d", filename, n); + } else { + nvf_error("%s: incomplete read %d/%lld", + filename, n, (longlong_t)hdr.nvpf_size); + } + return (EINVAL); + } + offset += n; + + rval = kobj_read_file(file, &c, 1, offset); + kobj_close_file(file); + if (rval > 0) { + nvf_error("%s is larger than %lld\n", + filename, (longlong_t)hdr.nvpf_size); + kmem_free(buf, hdr.nvpf_size); + return (EINVAL); + } + + cksum = nvp_cksum((uchar_t *)buf, hdr.nvpf_size); + if (hdr.nvpf_chksum != cksum) { + nvf_error("%s: checksum error (actual 0x%x, expected 0x%x)\n", + filename, hdr.nvpf_chksum, cksum); + kmem_free(buf, hdr.nvpf_size); + return (EINVAL); + } + + nvl = NULL; + rval = nvlist_unpack(buf, hdr.nvpf_size, &nvl, 0); + if (rval != 0) { + nvf_error("%s: error %d unpacking nvlist\n", + filename, rval); + kmem_free(buf, hdr.nvpf_size); + return (EINVAL); + } + + kmem_free(buf, hdr.nvpf_size); + *ret_nvlist = nvl; + return (0); +} + +static int +kfcreate(char *filename, kfile_t **kfilep) +{ + kfile_t *fp; + int rval; + + ASSERT(modrootloaded); + + fp = kmem_alloc(sizeof (kfile_t), KM_SLEEP); + + fp->kf_vnflags = FCREAT | FWRITE | FTRUNC; + fp->kf_fname = filename; + fp->kf_fpos = 0; + fp->kf_state = 0; + + KFDEBUG((CE_CONT, "create: %s flags 0x%x\n", + filename, fp->kf_vnflags)); + rval = vn_open(filename, UIO_SYSSPACE, fp->kf_vnflags, + 0444, &fp->kf_vp, CRCREAT, 0); + if (rval != 0) { + kmem_free(fp, sizeof (kfile_t)); + KFDEBUG((CE_CONT, "%s: create error %d\n", + filename, rval)); + return (rval); + } + + *kfilep = fp; + return (0); +} + +static int +kfremove(char *filename) +{ + int rval; + + KFDEBUG((CE_CONT, "remove: %s\n", filename)); + rval = vn_remove(filename, UIO_SYSSPACE, RMFILE); + if (rval != 0) { + KFDEBUG((CE_CONT, "%s: remove error %d\n", + filename, rval)); + } + return (rval); +} + +static int +kfread(kfile_t *fp, char *buf, ssize_t bufsiz, ssize_t *ret_n) +{ + ssize_t resid; + int err; + ssize_t n; + + ASSERT(modrootloaded); + + if (fp->kf_state != 0) + return (fp->kf_state); + + err = vn_rdwr(UIO_READ, fp->kf_vp, buf, bufsiz, fp->kf_fpos, + UIO_SYSSPACE, 0, (rlim64_t)0, kcred, &resid); + if (err != 0) { + KFDEBUG((CE_CONT, "%s: read error %d\n", + fp->kf_fname, err)); + fp->kf_state = err; + return (err); + } + + ASSERT(resid >= 0 && resid <= bufsiz); + n = bufsiz - resid; + + KFDEBUG1((CE_CONT, "%s: read %ld bytes ok %ld bufsiz, %ld resid\n", + fp->kf_fname, n, bufsiz, resid)); + + fp->kf_fpos += n; + *ret_n = n; + return (0); +} + +static int +kfwrite(kfile_t *fp, char *buf, ssize_t bufsiz, ssize_t *ret_n) +{ + rlim64_t rlimit; + ssize_t resid; + int err; + ssize_t len; + ssize_t n = 0; + + ASSERT(modrootloaded); + + if (fp->kf_state != 0) + return (fp->kf_state); + + len = bufsiz; + rlimit = bufsiz + 1; + for (;;) { + err = vn_rdwr(UIO_WRITE, fp->kf_vp, buf, len, fp->kf_fpos, + UIO_SYSSPACE, FSYNC, rlimit, kcred, &resid); + if (err) { + KFDEBUG((CE_CONT, "%s: write error %d\n", + fp->kf_fname, err)); + fp->kf_state = err; + return (err); + } + + KFDEBUG1((CE_CONT, "%s: write %ld bytes ok %ld resid\n", + fp->kf_fname, len-resid, resid)); + + ASSERT(resid >= 0 && resid <= len); + + n += (len - resid); + if (resid == 0) + break; + + if (resid == len) { + KFDEBUG((CE_CONT, "%s: filesystem full?\n", + fp->kf_fname)); + fp->kf_state = ENOSPC; + return (ENOSPC); + } + + len -= resid; + buf += len; + fp->kf_fpos += len; + len = resid; + } + + ASSERT(n == bufsiz); + KFDEBUG1((CE_CONT, "%s: wrote %ld bytes ok\n", fp->kf_fname, n)); + + *ret_n = n; + return (0); +} + + +static int +kfclose(kfile_t *fp) +{ + int rval; + + KFDEBUG((CE_CONT, "close: %s\n", fp->kf_fname)); + + if ((fp->kf_vnflags & FWRITE) && fp->kf_state == 0) { + rval = VOP_FSYNC(fp->kf_vp, FSYNC, kcred); + if (rval != 0) { + nvf_error("%s: sync error %d\n", + fp->kf_fname, rval); + } + KFDEBUG((CE_CONT, "%s: sync ok\n", fp->kf_fname)); + } + + rval = VOP_CLOSE(fp->kf_vp, fp->kf_vnflags, 1, (offset_t)0, kcred); + if (rval != 0) { + if (fp->kf_state == 0) { + nvf_error("%s: close error %d\n", + fp->kf_fname, rval); + } + } else { + if (fp->kf_state == 0) + KFDEBUG((CE_CONT, "%s: close ok\n", fp->kf_fname)); + } + + VN_RELE(fp->kf_vp); + kmem_free(fp, sizeof (kfile_t)); + return (rval); +} + +static int +kfrename(char *oldname, char *newname) +{ + int rval; + + ASSERT(modrootloaded); + + KFDEBUG((CE_CONT, "renaming %s to %s\n", oldname, newname)); + + if ((rval = vn_rename(oldname, newname, UIO_SYSSPACE)) != 0) { + KFDEBUG((CE_CONT, "rename %s to %s: %d\n", + oldname, newname, rval)); + } + + return (rval); +} + +int +fwrite_nvlist(char *filename, nvlist_t *nvl) +{ + char *buf; + char *nvbuf; + kfile_t *fp; + char *newname; + int len, err, err1; + size_t buflen; + ssize_t n; + + ASSERT(modrootloaded); + + nvbuf = NULL; + err = nvlist_pack(nvl, &nvbuf, &buflen, NV_ENCODE_NATIVE, 0); + if (err != 0) { + nvf_error("%s: error %d packing nvlist\n", + filename, err); + return (err); + } + + buf = kmem_alloc(sizeof (nvpf_hdr_t) + buflen, KM_SLEEP); + bzero(buf, sizeof (nvpf_hdr_t)); + + ((nvpf_hdr_t *)buf)->nvpf_magic = NVPF_HDR_MAGIC; + ((nvpf_hdr_t *)buf)->nvpf_version = NVPF_HDR_VERSION; + ((nvpf_hdr_t *)buf)->nvpf_size = buflen; + ((nvpf_hdr_t *)buf)->nvpf_chksum = nvp_cksum((uchar_t *)nvbuf, buflen); + ((nvpf_hdr_t *)buf)->nvpf_hdr_chksum = + nvp_cksum((uchar_t *)buf, sizeof (nvpf_hdr_t)); + + bcopy(nvbuf, buf + sizeof (nvpf_hdr_t), buflen); + kmem_free(nvbuf, buflen); + buflen += sizeof (nvpf_hdr_t); + + len = strlen(filename) + MAX_SUFFIX_LEN + 2; + newname = kmem_alloc(len, KM_SLEEP); + + + (void) sprintf(newname, "%s.%s", + filename, NEW_FILENAME_SUFFIX); + + /* + * To make it unlikely we suffer data loss, write + * data to the new temporary file. Once successful + * complete the transaction by renaming the new file + * to replace the previous. + */ + + if ((err = kfcreate(newname, &fp)) == 0) { + err = kfwrite(fp, buf, buflen, &n); + if (err) { + nvf_error("%s: write error - %d\n", + newname, err); + } else { + if (n != buflen) { + nvf_error( + "%s: partial write %ld of %ld bytes\n", + newname, n, buflen); + nvf_error("%s: filesystem may be full?\n", + newname); + err = EIO; + } + } + if ((err1 = kfclose(fp)) != 0) { + nvf_error("%s: close error\n", newname); + if (err == 0) + err = err1; + } + if (err != 0) { + if (kfremove(newname) != 0) { + nvf_error("%s: remove failed\n", + newname); + } + } + } else { + nvf_error("%s: create failed - %d\n", filename, err); + } + + if (err == 0) { + if ((err = kfrename(newname, filename)) != 0) { + nvf_error("%s: rename from %s failed\n", + newname, filename); + } + } + + kmem_free(newname, len); + kmem_free(buf, buflen); + + return (err); +} + +static int +e_fwrite_nvlist(nvfd_t *nvfd, nvlist_t *nvl) +{ + int err; + + if ((err = fwrite_nvlist(nvfd->nvf_cache_path, nvl)) == 0) + return (DDI_SUCCESS); + else { + if (err == EROFS) + NVF_MARK_READONLY(nvfd); + return (DDI_FAILURE); + } +} + +static void +nvp_list_free(nvfd_t *nvf) +{ + ASSERT(RW_WRITE_HELD(&nvf->nvf_lock)); + (nvf->nvf_list_free)((nvf_handle_t)nvf); + ASSERT(RW_WRITE_HELD(&nvf->nvf_lock)); +} + +/* + * Read a file in the nvlist format + * EIO - i/o error during read + * ENOENT - file not found + * EINVAL - file contents corrupted + */ +static int +fread_nvp_list(nvfd_t *nvfd) +{ + nvlist_t *nvl; + nvpair_t *nvp; + char *name; + nvlist_t *sublist; + int rval; + int rv; + + ASSERT(RW_WRITE_HELD(&(nvfd->nvf_lock))); + + rval = fread_nvlist(nvfd->nvf_cache_path, &nvl); + if (rval != 0) + return (rval); + ASSERT(nvl != NULL); + + nvp = NULL; + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + ASSERT(strlen(name) > 0); + + switch (nvpair_type(nvp)) { + case DATA_TYPE_NVLIST: + rval = nvpair_value_nvlist(nvp, &sublist); + if (rval != 0) { + nvf_error( + "nvpair_value_nvlist error %s %d\n", + name, rval); + goto error; + } + + /* + * unpack nvlist for this device and + * add elements to data list. + */ + ASSERT(RW_WRITE_HELD(&(nvfd->nvf_lock))); + rv = (nvfd->nvf_unpack_nvlist) + ((nvf_handle_t)nvfd, sublist, name); + ASSERT(RW_WRITE_HELD(&(nvfd->nvf_lock))); + if (rv != 0) { + nvf_error( + "%s: %s invalid list element\n", + nvfd->nvf_cache_path, name); + rval = EINVAL; + goto error; + } + break; + + default: + nvf_error("%s: %s unsupported data type %d\n", + nvfd->nvf_cache_path, name, nvpair_type(nvp)); + rval = EINVAL; + goto error; + } + } + + nvlist_free(nvl); + + return (0); + +error: + nvlist_free(nvl); + nvp_list_free(nvfd); + return (rval); +} + + +int +nvf_read_file(nvf_handle_t nvf_handle) +{ + nvfd_t *nvfd = (nvfd_t *)nvf_handle; + int rval; + + ASSERT(RW_WRITE_HELD(&nvfd->nvf_lock)); + + if (kfio_disable_read) + return (0); + + KFDEBUG((CE_CONT, "reading %s\n", nvfd->nvf_cache_path)); + + rval = fread_nvp_list(nvfd); + if (rval) { + switch (rval) { + case EIO: + nvfd->nvf_flags |= NVF_F_REBUILD_MSG; + cmn_err(CE_WARN, "%s: I/O error", + nvfd->nvf_cache_path); + break; + case ENOENT: + nvfd->nvf_flags |= NVF_F_CREATE_MSG; + nvf_error("%s: not found\n", + nvfd->nvf_cache_path); + break; + case EINVAL: + default: + nvfd->nvf_flags |= NVF_F_REBUILD_MSG; + cmn_err(CE_WARN, "%s: data file corrupted", + nvfd->nvf_cache_path); + break; + } + } + return (rval); +} + +static void +nvf_write_is_complete(nvfd_t *fd) +{ + if (fd->nvf_write_complete) { + (fd->nvf_write_complete)((nvf_handle_t)fd); + } +} + +/*ARGSUSED*/ +static void +nvpflush_timeout(void *arg) +{ + clock_t nticks; + + mutex_enter(&nvpflush_lock); + nticks = nvpticks - ddi_get_lbolt(); + if (nticks > 4) { + nvpflush_timer_busy = 1; + mutex_exit(&nvpflush_lock); + nvpflush_id = timeout(nvpflush_timeout, NULL, nticks); + } else { + do_nvpflush = 1; + NVPDAEMON_DEBUG((CE_CONT, "signal nvpdaemon\n")); + cv_signal(&nvpflush_cv); + nvpflush_id = 0; + nvpflush_timer_busy = 0; + mutex_exit(&nvpflush_lock); + } +} + +/* + * After marking a list as dirty, wake the nvpflush daemon + * to perform the update. + */ +void +nvf_wake_daemon(void) +{ + clock_t nticks; + + /* + * If the system isn't up yet + * don't even think about starting a flush. + */ + if (!i_ddi_io_initialized()) + return; + + mutex_enter(&nvpflush_lock); + + if (nvpflush_daemon_active == 0) { + nvpflush_daemon_active = 1; + mutex_exit(&nvpflush_lock); + NVPDAEMON_DEBUG((CE_CONT, "starting nvpdaemon thread\n")); + nvpflush_thr_id = thread_create(NULL, 0, + (void (*)())nvpflush_daemon, + NULL, 0, &p0, TS_RUN, minclsyspri); + mutex_enter(&nvpflush_lock); + } + + nticks = nvpflush_delay * TICKS_PER_SECOND; + nvpticks = ddi_get_lbolt() + nticks; + if (nvpflush_timer_busy == 0) { + nvpflush_timer_busy = 1; + mutex_exit(&nvpflush_lock); + nvpflush_id = timeout(nvpflush_timeout, NULL, nticks + 4); + } else + mutex_exit(&nvpflush_lock); +} + +static int +nvpflush_one(nvfd_t *nvfd) +{ + int rval = DDI_SUCCESS; + nvlist_t *nvl; + + rw_enter(&nvfd->nvf_lock, RW_READER); + + ASSERT((nvfd->nvf_flags & NVF_F_FLUSHING) == 0); + + if (!NVF_IS_DIRTY(nvfd) || + NVF_IS_READONLY(nvfd) || kfio_disable_write) { + NVF_CLEAR_DIRTY(nvfd); + rw_exit(&nvfd->nvf_lock); + return (DDI_SUCCESS); + } + + if (rw_tryupgrade(&nvfd->nvf_lock) == 0) { + nvf_error("nvpflush: " + "%s rw upgrade failed\n", nvfd->nvf_cache_path); + rw_exit(&nvfd->nvf_lock); + return (DDI_FAILURE); + } + if (((nvfd->nvf_pack_list) + ((nvf_handle_t)nvfd, &nvl)) != DDI_SUCCESS) { + nvf_error("nvpflush: " + "%s nvlist construction failed\n", nvfd->nvf_cache_path); + ASSERT(RW_WRITE_HELD(&nvfd->nvf_lock)); + rw_exit(&nvfd->nvf_lock); + return (DDI_FAILURE); + } + ASSERT(RW_WRITE_HELD(&nvfd->nvf_lock)); + + NVF_CLEAR_DIRTY(nvfd); + nvfd->nvf_flags |= NVF_F_FLUSHING; + rw_exit(&nvfd->nvf_lock); + + rval = e_fwrite_nvlist(nvfd, nvl); + nvlist_free(nvl); + + rw_enter(&nvfd->nvf_lock, RW_WRITER); + nvfd->nvf_flags &= ~NVF_F_FLUSHING; + if (rval == DDI_FAILURE) { + if (NVF_IS_READONLY(nvfd)) { + rval = DDI_SUCCESS; + nvfd->nvf_flags &= ~(NVF_F_ERROR | NVF_F_DIRTY); + } else if ((nvfd->nvf_flags & NVF_F_ERROR) == 0) { + cmn_err(CE_CONT, + "%s: updated failed\n", nvfd->nvf_cache_path); + nvfd->nvf_flags |= NVF_F_ERROR | NVF_F_DIRTY; + } + } else { + if (nvfd->nvf_flags & NVF_F_CREATE_MSG) { + cmn_err(CE_CONT, + "!Creating %s\n", nvfd->nvf_cache_path); + nvfd->nvf_flags &= ~NVF_F_CREATE_MSG; + } + if (nvfd->nvf_flags & NVF_F_REBUILD_MSG) { + cmn_err(CE_CONT, + "!Rebuilding %s\n", nvfd->nvf_cache_path); + nvfd->nvf_flags &= ~NVF_F_REBUILD_MSG; + } + if (nvfd->nvf_flags & NVF_F_ERROR) { + cmn_err(CE_CONT, + "%s: update now ok\n", nvfd->nvf_cache_path); + nvfd->nvf_flags &= ~NVF_F_ERROR; + } + /* + * The file may need to be flushed again if the cached + * data was touched while writing the earlier contents. + */ + if (NVF_IS_DIRTY(nvfd)) + rval = DDI_FAILURE; + } + + rw_exit(&nvfd->nvf_lock); + return (rval); +} + + +static void +nvpflush_daemon(void) +{ + callb_cpr_t cprinfo; + nvfd_t *nvfdp, *nextfdp; + clock_t clk; + int rval; + int want_wakeup; + int is_now_clean; + + ASSERT(modrootloaded); + + nvpflush_thread = curthread; + NVPDAEMON_DEBUG((CE_CONT, "nvpdaemon: init\n")); + + CALLB_CPR_INIT(&cprinfo, &nvpflush_lock, callb_generic_cpr, "nvp"); + mutex_enter(&nvpflush_lock); + for (;;) { + + CALLB_CPR_SAFE_BEGIN(&cprinfo); + while (do_nvpflush == 0) { + clk = cv_timedwait(&nvpflush_cv, &nvpflush_lock, + ddi_get_lbolt() + + (nvpdaemon_idle_time * TICKS_PER_SECOND)); + if (clk == -1 && + do_nvpflush == 0 && nvpflush_timer_busy == 0) { + /* + * Note that CALLB_CPR_EXIT calls mutex_exit() + * on the lock passed in to CALLB_CPR_INIT, + * so the lock must be held when invoking it. + */ + CALLB_CPR_SAFE_END(&cprinfo, &nvpflush_lock); + NVPDAEMON_DEBUG((CE_CONT, "nvpdaemon: exit\n")); + ASSERT(mutex_owned(&nvpflush_lock)); + nvpflush_thr_id = NULL; + nvpflush_daemon_active = 0; + CALLB_CPR_EXIT(&cprinfo); + thread_exit(); + } + } + CALLB_CPR_SAFE_END(&cprinfo, &nvpflush_lock); + + nvpbusy = 1; + want_wakeup = 0; + do_nvpflush = 0; + mutex_exit(&nvpflush_lock); + + /* + * Try flushing what's dirty, reschedule if there's + * a failure or data gets marked as dirty again. + * First move each file marked dirty to the dirty + * list to avoid locking the list across the write. + */ + mutex_enter(&nvf_cache_mutex); + for (nvfdp = list_head(&nvf_cache_files); + nvfdp; nvfdp = nextfdp) { + nextfdp = list_next(&nvf_cache_files, nvfdp); + rw_enter(&nvfdp->nvf_lock, RW_READER); + if (NVF_IS_DIRTY(nvfdp)) { + list_remove(&nvf_cache_files, nvfdp); + list_insert_tail(&nvf_dirty_files, nvfdp); + rw_exit(&nvfdp->nvf_lock); + } else { + NVPDAEMON_DEBUG((CE_CONT, + "nvpdaemon: not dirty %s\n", + nvfdp->nvf_cache_path)); + rw_exit(&nvfdp->nvf_lock); + } + } + mutex_exit(&nvf_cache_mutex); + + /* + * Now go through the dirty list + */ + for (nvfdp = list_head(&nvf_dirty_files); + nvfdp; nvfdp = nextfdp) { + nextfdp = list_next(&nvf_dirty_files, nvfdp); + + is_now_clean = 0; + rw_enter(&nvfdp->nvf_lock, RW_READER); + if (NVF_IS_DIRTY(nvfdp)) { + NVPDAEMON_DEBUG((CE_CONT, + "nvpdaemon: flush %s\n", + nvfdp->nvf_cache_path)); + rw_exit(&nvfdp->nvf_lock); + rval = nvpflush_one(nvfdp); + rw_enter(&nvfdp->nvf_lock, RW_READER); + if (rval != DDI_SUCCESS || + NVF_IS_DIRTY(nvfdp)) { + rw_exit(&nvfdp->nvf_lock); + NVPDAEMON_DEBUG((CE_CONT, + "nvpdaemon: %s dirty again\n", + nvfdp->nvf_cache_path)); + want_wakeup = 1; + } else { + rw_exit(&nvfdp->nvf_lock); + nvf_write_is_complete(nvfdp); + is_now_clean = 1; + } + } else { + NVPDAEMON_DEBUG((CE_CONT, + "nvpdaemon: not dirty %s\n", + nvfdp->nvf_cache_path)); + rw_exit(&nvfdp->nvf_lock); + is_now_clean = 1; + } + + if (is_now_clean) { + mutex_enter(&nvf_cache_mutex); + list_remove(&nvf_dirty_files, nvfdp); + list_insert_tail(&nvf_cache_files, + nvfdp); + mutex_exit(&nvf_cache_mutex); + } + } + + if (want_wakeup) + nvf_wake_daemon(); + + mutex_enter(&nvpflush_lock); + nvpbusy = 0; + } +} diff --git a/usr/src/uts/common/os/devctl.c b/usr/src/uts/common/os/devctl.c deleted file mode 100644 index 90354cde34..0000000000 --- a/usr/src/uts/common/os/devctl.c +++ /dev/null @@ -1,1943 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include <sys/note.h> -#include <sys/t_lock.h> -#include <sys/cmn_err.h> -#include <sys/instance.h> -#include <sys/conf.h> -#include <sys/stat.h> -#include <sys/ddi.h> -#include <sys/hwconf.h> -#include <sys/sunddi.h> -#include <sys/sunndi.h> -#include <sys/ddi_impldefs.h> -#include <sys/ndi_impldefs.h> -#include <sys/modctl.h> -#include <sys/dacf.h> -#include <sys/promif.h> -#include <sys/cpuvar.h> -#include <sys/pathname.h> -#include <sys/taskq.h> -#include <sys/sysevent.h> -#include <sys/sunmdi.h> -#include <sys/stream.h> -#include <sys/strsubr.h> -#include <sys/fs/snode.h> -#include <sys/fs/dv_node.h> -#include <sys/kobj.h> -#include <sys/devctl_impl.h> - - -/* - * Tunables - see devctl_impl.h for more thorough explanation - */ -int devid_discovery_boot = 1; -int devid_discovery_postboot = 1; -int devid_discovery_postboot_always = 0; -int devid_discovery_secs = 0; - -int devid_cache_read_disable = 0; -int devid_cache_write_disable = 0; -int sdev_cache_read_disable = 0; -int sdev_cache_write_disable = 0; - -int kfio_report_error = 0; /* kernel file i/o operations */ -int devid_report_error = 0; /* devid cache operations */ - - -/* - * State to manage discovery - */ -static int devid_discovery_busy = 0; -static kmutex_t devid_discovery_mutex; -static kcondvar_t devid_discovery_cv; -static clock_t devid_last_discovery = 0; - - -static int devid_nvp2nvl(nvfd_t *, nvlist_t **); -static nvp_list_t *devid_nvl2nvp(nvlist_t *, char *); -static void devid_nvp_free(nvp_list_t *); - -static int sdev_nvp2nvl(nvfd_t *, nvlist_t **); -static nvp_list_t *sdev_nvl2nvp(nvlist_t *, char *); -static void sdev_nvp_free(nvp_list_t *); - -/* - * Descriptors for the /etc/devices cache files - */ -static nvfd_t devid_cache_fd = { - "/etc/devices/devid_cache", - devid_nvp2nvl, /* nvf_nvp2nvl */ - devid_nvl2nvp, /* nvf_nvl2nvp */ - devid_nvp_free, /* nvf_nvp_free */ - NULL, /* nvf_write_complete */ - 0, NULL, NULL, 0 - -}; -static nvfd_t sdev_cache_fd = { - "/etc/devices/devname_cache", - sdev_nvp2nvl, /* nvf_nvp2nvl */ - sdev_nvl2nvp, /* nvf_nvl2nvp */ - sdev_nvp_free, /* nvf_nvp_free */ - NULL, /* nvf_write_complete */ - 0, NULL, NULL, 0 -}; - -static nvfd_t *dcfd = &devid_cache_fd; -nvfd_t *sdevfd = &sdev_cache_fd; - -static nvfd_t *cachefds[] = { - &devid_cache_fd, - &sdev_cache_fd -}; - -#define NCACHEFDS ((sizeof (cachefds)) / (sizeof (nvfd_t *))) - - - -extern int modrootloaded; -extern struct bootops *bootops; - -extern void mdi_read_devices_files(void); -extern void mdi_clean_vhcache(void); - -#ifdef DEBUG -int nvp_devid_debug = 0; -int nvpdaemon_debug = 0; -int kfio_debug = 0; -int devid_debug = 0; -int devid_log_registers = 0; -int devid_log_finds = 0; -int devid_log_lookups = 0; -int devid_log_discovery = 0; -int devid_log_matches = 0; -int devid_log_paths = 0; -int devid_log_failures = 0; -int devid_log_hold = 0; -int devid_log_unregisters = 0; -int devid_log_removes = 0; -int devid_register_debug = 0; -int devid_log_stale = 0; -int devid_log_detaches = 0; -#endif /* DEBUG */ - - -void -i_ddi_devices_init(void) -{ - dcfd->nvf_flags = 0; - dcfd->nvf_list = NULL; - dcfd->nvf_tail = NULL; - rw_init(&dcfd->nvf_lock, NULL, RW_DRIVER, NULL); - - sdevfd->nvf_flags = 0; - sdevfd->nvf_list = NULL; - sdevfd->nvf_tail = NULL; - rw_init(&sdevfd->nvf_lock, NULL, RW_DRIVER, NULL); - - mutex_init(&devid_discovery_mutex, NULL, MUTEX_DEFAULT, NULL); - cv_init(&devid_discovery_cv, NULL, CV_DRIVER, NULL); -} - -static uint16_t -nvp_cksum(uchar_t *buf, int64_t buflen) -{ - uint16_t cksum = 0; - uint16_t *p = (uint16_t *)buf; - int64_t n; - - if ((buflen & 0x01) != 0) { - buflen--; - cksum = buf[buflen]; - } - n = buflen / 2; - while (n-- > 0) - cksum ^= *p++; - return (cksum); -} - -int -fread_nvlist(char *filename, nvlist_t **ret_nvlist) -{ - struct _buf *file; - nvpf_hdr_t hdr; - char *buf; - nvlist_t *nvl; - int rval; - uint_t offset; - int n; - char c; - uint16_t cksum, hdrsum; - - *ret_nvlist = NULL; - - file = kobj_open_file(filename); - if (file == (struct _buf *)-1) { - KFDEBUG((CE_CONT, "cannot open file: %s\n", filename)); - return (ENOENT); - } - - offset = 0; - n = kobj_read_file(file, (char *)&hdr, sizeof (hdr), offset); - if (n != sizeof (hdr)) { - kobj_close_file(file); - if (n < 0) { - KFIOERR((CE_CONT, - "error reading header: %s\n", filename)); - return (EIO); - } else if (n == 0) { - KFDEBUG((CE_CONT, "file empty: %s\n", filename)); - } else { - KFIOERR((CE_CONT, - "header size incorrect: %s\n", filename)); - } - return (EINVAL); - } - offset += n; - - KFDEBUG2((CE_CONT, "nvpf_magic: 0x%x\n", hdr.nvpf_magic)); - KFDEBUG2((CE_CONT, "nvpf_version: %d\n", hdr.nvpf_version)); - KFDEBUG2((CE_CONT, "nvpf_size: %lld\n", - (longlong_t)hdr.nvpf_size)); - KFDEBUG2((CE_CONT, "nvpf_hdr_chksum: 0x%x\n", - hdr.nvpf_hdr_chksum)); - KFDEBUG2((CE_CONT, "nvpf_chksum: 0x%x\n", hdr.nvpf_chksum)); - - cksum = hdr.nvpf_hdr_chksum; - hdr.nvpf_hdr_chksum = 0; - hdrsum = nvp_cksum((uchar_t *)&hdr, sizeof (hdr)); - - if (hdr.nvpf_magic != NVPF_HDR_MAGIC || - hdr.nvpf_version != NVPF_HDR_VERSION || hdrsum != cksum) { - kobj_close_file(file); - if (hdrsum != cksum) { - KFIOERR((CE_CONT, - "%s: checksum error " - "(actual 0x%x, expected 0x%x)\n", - filename, hdrsum, cksum)); - } - KFIOERR((CE_CONT, - "%s: header information incorrect", filename)); - return (EINVAL); - } - - ASSERT(hdr.nvpf_size >= 0); - - buf = kmem_alloc(hdr.nvpf_size, KM_SLEEP); - n = kobj_read_file(file, buf, hdr.nvpf_size, offset); - if (n != hdr.nvpf_size) { - kmem_free(buf, hdr.nvpf_size); - kobj_close_file(file); - if (n < 0) { - KFIOERR((CE_CONT, "%s: read error %d", filename, n)); - } else { - KFIOERR((CE_CONT, "%s: incomplete read %d/%lld", - filename, n, (longlong_t)hdr.nvpf_size)); - } - return (EINVAL); - } - offset += n; - - rval = kobj_read_file(file, &c, 1, offset); - kobj_close_file(file); - if (rval > 0) { - KFIOERR((CE_CONT, "%s is larger than %lld\n", - filename, (longlong_t)hdr.nvpf_size)); - kmem_free(buf, hdr.nvpf_size); - return (EINVAL); - } - - cksum = nvp_cksum((uchar_t *)buf, hdr.nvpf_size); - if (hdr.nvpf_chksum != cksum) { - KFIOERR((CE_CONT, - "%s: checksum error (actual 0x%x, expected 0x%x)\n", - filename, hdr.nvpf_chksum, cksum)); - kmem_free(buf, hdr.nvpf_size); - return (EINVAL); - } - - nvl = NULL; - rval = nvlist_unpack(buf, hdr.nvpf_size, &nvl, 0); - if (rval != 0) { - KFIOERR((CE_CONT, "%s: error %d unpacking nvlist\n", - filename, rval)); - kmem_free(buf, hdr.nvpf_size); - return (EINVAL); - } - - kmem_free(buf, hdr.nvpf_size); - *ret_nvlist = nvl; - return (0); -} - -static int -kfcreate(char *filename, kfile_t **kfilep) -{ - kfile_t *fp; - int rval; - - ASSERT(modrootloaded); - - fp = kmem_alloc(sizeof (kfile_t), KM_SLEEP); - - fp->kf_vnflags = FCREAT | FWRITE | FTRUNC; - fp->kf_fname = filename; - fp->kf_fpos = 0; - fp->kf_state = 0; - - KFDEBUG((CE_CONT, "create: %s flags 0x%x\n", - filename, fp->kf_vnflags)); - rval = vn_open(filename, UIO_SYSSPACE, fp->kf_vnflags, - 0444, &fp->kf_vp, CRCREAT, 0); - if (rval != 0) { - kmem_free(fp, sizeof (kfile_t)); - KFDEBUG((CE_CONT, "%s: create error %d\n", - filename, rval)); - return (rval); - } - - *kfilep = fp; - return (0); -} - -static int -kfremove(char *filename) -{ - int rval; - - KFDEBUG((CE_CONT, "remove: %s\n", filename)); - rval = vn_remove(filename, UIO_SYSSPACE, RMFILE); - if (rval != 0) { - KFDEBUG((CE_CONT, "%s: remove error %d\n", - filename, rval)); - } - return (rval); -} - -static int -kfread(kfile_t *fp, char *buf, ssize_t bufsiz, ssize_t *ret_n) -{ - ssize_t resid; - int err; - ssize_t n; - - ASSERT(modrootloaded); - - if (fp->kf_state != 0) - return (fp->kf_state); - - err = vn_rdwr(UIO_READ, fp->kf_vp, buf, bufsiz, fp->kf_fpos, - UIO_SYSSPACE, 0, (rlim64_t)0, kcred, &resid); - if (err != 0) { - KFDEBUG((CE_CONT, "%s: read error %d\n", - fp->kf_fname, err)); - fp->kf_state = err; - return (err); - } - - ASSERT(resid >= 0 && resid <= bufsiz); - n = bufsiz - resid; - - KFDEBUG1((CE_CONT, "%s: read %ld bytes ok %ld bufsiz, %ld resid\n", - fp->kf_fname, n, bufsiz, resid)); - - fp->kf_fpos += n; - *ret_n = n; - return (0); -} - -static int -kfwrite(kfile_t *fp, char *buf, ssize_t bufsiz, ssize_t *ret_n) -{ - rlim64_t rlimit; - ssize_t resid; - int err; - ssize_t len; - ssize_t n = 0; - - ASSERT(modrootloaded); - - if (fp->kf_state != 0) - return (fp->kf_state); - - len = bufsiz; - rlimit = bufsiz + 1; - for (;;) { - err = vn_rdwr(UIO_WRITE, fp->kf_vp, buf, len, fp->kf_fpos, - UIO_SYSSPACE, FSYNC, rlimit, kcred, &resid); - if (err) { - KFDEBUG((CE_CONT, "%s: write error %d\n", - fp->kf_fname, err)); - fp->kf_state = err; - return (err); - } - - KFDEBUG1((CE_CONT, "%s: write %ld bytes ok %ld resid\n", - fp->kf_fname, len-resid, resid)); - - ASSERT(resid >= 0 && resid <= len); - - n += (len - resid); - if (resid == 0) - break; - - if (resid == len) { - KFDEBUG((CE_CONT, "%s: filesystem full?\n", - fp->kf_fname)); - fp->kf_state = ENOSPC; - return (ENOSPC); - } - - len -= resid; - buf += len; - fp->kf_fpos += len; - len = resid; - } - - ASSERT(n == bufsiz); - KFDEBUG1((CE_CONT, "%s: wrote %ld bytes ok\n", fp->kf_fname, n)); - - *ret_n = n; - return (0); -} - - -static int -kfclose(kfile_t *fp) -{ - int rval; - - KFDEBUG((CE_CONT, "close: %s\n", fp->kf_fname)); - - if ((fp->kf_vnflags & FWRITE) && fp->kf_state == 0) { - rval = VOP_FSYNC(fp->kf_vp, FSYNC, kcred); - if (rval != 0) { - KFIOERR((CE_CONT, "%s: sync error %d\n", - fp->kf_fname, rval)); - } - KFDEBUG((CE_CONT, "%s: sync ok\n", fp->kf_fname)); - } - - rval = VOP_CLOSE(fp->kf_vp, fp->kf_vnflags, 1, (offset_t)0, kcred); - if (rval != 0) { - if (fp->kf_state == 0) { - KFIOERR((CE_CONT, "%s: close error %d\n", - fp->kf_fname, rval)); - } - } else { - if (fp->kf_state == 0) - KFDEBUG((CE_CONT, "%s: close ok\n", fp->kf_fname)); - } - - VN_RELE(fp->kf_vp); - kmem_free(fp, sizeof (kfile_t)); - return (rval); -} - -static int -kfrename(char *oldname, char *newname) -{ - int rval; - - ASSERT(modrootloaded); - - KFDEBUG((CE_CONT, "renaming %s to %s\n", oldname, newname)); - - if ((rval = vn_rename(oldname, newname, UIO_SYSSPACE)) != 0) { - KFDEBUG((CE_CONT, "rename %s to %s: %d\n", - oldname, newname, rval)); - } - - return (rval); -} - -int -fwrite_nvlist(char *filename, nvlist_t *nvl) -{ - char *buf; - char *nvbuf; - kfile_t *fp; - char *newname; - int len, err, err1; - size_t buflen; - ssize_t n; - - ASSERT(modrootloaded); - - nvbuf = NULL; - err = nvlist_pack(nvl, &nvbuf, &buflen, NV_ENCODE_NATIVE, 0); - if (err != 0) { - KFIOERR((CE_CONT, "%s: error %d packing nvlist\n", - filename, err)); - return (err); - } - - buf = kmem_alloc(sizeof (nvpf_hdr_t) + buflen, KM_SLEEP); - bzero(buf, sizeof (nvpf_hdr_t)); - - ((nvpf_hdr_t *)buf)->nvpf_magic = NVPF_HDR_MAGIC; - ((nvpf_hdr_t *)buf)->nvpf_version = NVPF_HDR_VERSION; - ((nvpf_hdr_t *)buf)->nvpf_size = buflen; - ((nvpf_hdr_t *)buf)->nvpf_chksum = nvp_cksum((uchar_t *)nvbuf, buflen); - ((nvpf_hdr_t *)buf)->nvpf_hdr_chksum = - nvp_cksum((uchar_t *)buf, sizeof (nvpf_hdr_t)); - - bcopy(nvbuf, buf + sizeof (nvpf_hdr_t), buflen); - kmem_free(nvbuf, buflen); - buflen += sizeof (nvpf_hdr_t); - - len = strlen(filename) + MAX_SUFFIX_LEN + 2; - newname = kmem_alloc(len, KM_SLEEP); - - - (void) sprintf(newname, "%s.%s", - filename, NEW_FILENAME_SUFFIX); - - /* - * To make it unlikely we suffer data loss, write - * data to the new temporary file. Once successful - * complete the transaction by renaming the new file - * to replace the previous. - */ - - if ((err = kfcreate(newname, &fp)) == 0) { - err = kfwrite(fp, buf, buflen, &n); - if (err) { - KFIOERR((CE_CONT, "%s: write error - %d\n", - newname, err)); - } else { - if (n != buflen) { - KFIOERR((CE_CONT, - "%s: partial write %ld of %ld bytes\n", - newname, n, buflen)); - KFIOERR((CE_CONT, - "%s: filesystem may be full?\n", newname)); - err = EIO; - } - } - if ((err1 = kfclose(fp)) != 0) { - KFIOERR((CE_CONT, "%s: close error\n", newname)); - if (err == 0) - err = err1; - } - if (err != 0) { - if (kfremove(newname) != 0) { - KFIOERR((CE_CONT, "%s: remove failed\n", - newname)); - } - } - } else { - KFIOERR((CE_CONT, "%s: create failed - %d\n", - filename, err)); - } - - if (err == 0) { - if ((err = kfrename(newname, filename)) != 0) { - KFIOERR((CE_CONT, "%s: rename from %s failed\n", - newname, filename)); - } - } - - kmem_free(newname, len); - kmem_free(buf, buflen); - - return (err); -} - -static int -e_fwrite_nvlist(nvfd_t *nvfd, nvlist_t *nvl) -{ - int err; - - if ((err = fwrite_nvlist(nvfd->nvf_name, nvl)) == 0) - return (DDI_SUCCESS); - else { - if (err == EROFS) - NVF_MARK_READONLY(nvfd); - return (DDI_FAILURE); - } -} - -static void -devid_nvp_free(nvp_list_t *np) -{ - nvp_devid_t *dp = NVP2DEVID(np); - - if (dp->nvp_devpath) - kmem_free(dp->nvp_devpath, strlen(dp->nvp_devpath)+1); - if (dp->nvp_devid) - kmem_free(dp->nvp_devid, ddi_devid_sizeof(dp->nvp_devid)); - - kmem_free(dp, sizeof (nvp_devid_t)); -} - -static void -sdev_nvp_free(nvp_list_t *np) -{ - nvp_devname_t *dp = NVP2DEVNAME(np); - int i; - char **p; - - if (dp->nvp_npaths > 0) { - p = dp->nvp_paths; - for (i = 0; i < dp->nvp_npaths; i++, p++) { - kmem_free(*p, strlen(*p)+1); - } - kmem_free(dp->nvp_paths, - dp->nvp_npaths * sizeof (char *)); - kmem_free(dp->nvp_expirecnts, - dp->nvp_npaths * sizeof (int)); - } - - kmem_free(dp, sizeof (nvp_devname_t)); -} - -static void -nvp_list_free(nvfd_t *nvf, nvp_list_t *nvp) -{ - nvp_list_t *np; - nvp_list_t *next; - - for (np = nvp; np; np = next) { - next = np->nvp_next; - (nvf->nvf_nvp_free)(np); - } -} - - -/* - * Free an nvp element in a list - */ -void -nfd_nvp_free_and_unlink(nvfd_t *nvf, nvp_list_t *np) -{ - nvp_list_t *pv, *next; - - pv = np->nvp_prev; - next = np->nvp_next; - (nvf->nvf_nvp_free)(np); - - /* remove element at head */ - if (pv == NULL) { - if (next) - next->nvp_prev = NULL; - nvf->nvf_list = next; - } - /* remove element at tail */ - if (next == NULL) { - if (pv) - pv->nvp_next = NULL; - nvf->nvf_tail = pv; - } - /* remove element in the middle, neither head nor tail */ - if (pv && next) { - pv->nvp_next = next; - next->nvp_prev = pv; - } -} - -void -nfd_nvp_link(nvfd_t *nvf, nvp_list_t *np) -{ - if (nvf->nvf_list == NULL) { - nvf->nvf_list = np; - } else { - nvf->nvf_tail->nvp_next = np; - } - np->nvp_next = NULL; - np->nvp_prev = nvf->nvf_tail; - nvf->nvf_tail = np; -} - -/* - * Convert a device path/nvlist pair to an nvp_list_t - * Used to parse the nvlist format when reading - * /etc/devices/devid_cache - */ -static nvp_list_t * -devid_nvl2nvp(nvlist_t *nvl, char *name) -{ - nvp_devid_t *np; - ddi_devid_t devidp; - int rval; - uint_t n; - - np = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP); - np->nvp_devpath = i_ddi_strdup(name, KM_SLEEP); - - NVP_DEVID_DEBUG_PATH((np->nvp_devpath)); - - /* - * check path for a devid - */ - np->nvp_devid = NULL; - rval = nvlist_lookup_byte_array(nvl, - DP_DEVID_ID, (uchar_t **)&devidp, &n); - if (rval == 0) { - if (ddi_devid_valid(devidp) == DDI_SUCCESS) { - ASSERT(n == ddi_devid_sizeof(devidp)); - np->nvp_devid = kmem_alloc(n, KM_SLEEP); - (void) bcopy(devidp, np->nvp_devid, n); - NVP_DEVID_DEBUG_DEVID((np->nvp_devid)); - } else { - DEVIDERR((CE_CONT, - "%s: invalid devid\n", np->nvp_devpath)); - } - } - - return (NVPLIST(np)); -} - -/* - * Convert a device path/nvlist pair to an nvp_list_t - * Used to parse the nvlist format when reading - * /etc/devices/devname_cache - */ -static nvp_list_t * -sdev_nvl2nvp(nvlist_t *nvl, char *name) -{ - nvp_devname_t *np; - char **strs; - int *cnts; - uint_t nstrs, ncnts; - int rval, i; - - /* name of the sublist must match what we created */ - if (strcmp(name, DP_DEVNAME_ID) != 0) { - return (NULL); - } - - np = kmem_zalloc(sizeof (nvp_devname_t), KM_SLEEP); - - rval = nvlist_lookup_string_array(nvl, - DP_DEVNAME_NCACHE_ID, &strs, &nstrs); - if (rval) { - kmem_free(np, sizeof (nvp_devname_t)); - return (NULL); - } - - np->nvp_npaths = nstrs; - np->nvp_paths = kmem_zalloc(nstrs * sizeof (char *), KM_SLEEP); - for (i = 0; i < nstrs; i++) { - np->nvp_paths[i] = i_ddi_strdup(strs[i], KM_SLEEP); - } - np->nvp_expirecnts = kmem_zalloc(nstrs * sizeof (int), KM_SLEEP); - for (i = 0; i < nstrs; i++) { - np->nvp_expirecnts[i] = 4; /* XXX sdev_nc_expirecnt */ - } - - rval = nvlist_lookup_int32_array(nvl, - DP_DEVNAME_NC_EXPIRECNT_ID, &cnts, &ncnts); - if (rval == 0) { - ASSERT(ncnts == nstrs); - ncnts = max(ncnts, nstrs); - for (i = 0; i < nstrs; i++) { - np->nvp_expirecnts[i] = cnts[i]; - } - } - - return (NVPLIST(np)); -} - - -/* - * Convert a list of nvp_list_t's to a single nvlist - * Used when writing the nvlist file. - */ -static int -devid_nvp2nvl(nvfd_t *nvfd, nvlist_t **ret_nvl) -{ - nvlist_t *nvl, *sub_nvl; - nvp_devid_t *np; - int rval; - - ASSERT(modrootloaded); - - rval = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); - if (rval != 0) { - KFIOERR((CE_CONT, "%s: nvlist alloc error %d\n", - nvfd->nvf_name, rval)); - return (DDI_FAILURE); - } - - for (np = NVF_DEVID_LIST(nvfd); np; np = NVP_DEVID_NEXT(np)) { - if (np->nvp_devid == NULL) - continue; - NVP_DEVID_DEBUG_PATH(np->nvp_devpath); - rval = nvlist_alloc(&sub_nvl, NV_UNIQUE_NAME, KM_SLEEP); - if (rval != 0) { - KFIOERR((CE_CONT, "%s: nvlist alloc error %d\n", - nvfd->nvf_name, rval)); - sub_nvl = NULL; - goto err; - } - - rval = nvlist_add_byte_array(sub_nvl, DP_DEVID_ID, - (uchar_t *)np->nvp_devid, - ddi_devid_sizeof(np->nvp_devid)); - if (rval == 0) { - NVP_DEVID_DEBUG_DEVID(np->nvp_devid); - } else { - KFIOERR((CE_CONT, - "%s: nvlist add error %d (devid)\n", - nvfd->nvf_name, rval)); - goto err; - } - - rval = nvlist_add_nvlist(nvl, np->nvp_devpath, sub_nvl); - if (rval != 0) { - KFIOERR((CE_CONT, "%s: nvlist add error %d (sublist)\n", - nvfd->nvf_name, rval)); - goto err; - } - nvlist_free(sub_nvl); - } - - *ret_nvl = nvl; - return (DDI_SUCCESS); - -err: - if (sub_nvl) - nvlist_free(sub_nvl); - nvlist_free(nvl); - *ret_nvl = NULL; - return (DDI_FAILURE); -} - -/* - * Convert a list of nvp_list_t's to a single nvlist - * Used when writing the nvlist file. - */ -static int -sdev_nvp2nvl(nvfd_t *nvfd, nvlist_t **ret_nvl) -{ - nvlist_t *nvl, *sub_nvl; - nvp_devname_t *np; - int rval; - - ASSERT(modrootloaded); - - rval = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); - if (rval != 0) { - KFIOERR((CE_CONT, "%s: nvlist alloc error %d\n", - nvfd->nvf_name, rval)); - return (DDI_FAILURE); - } - - if ((np = NVF_DEVNAME_LIST(nvfd)) != NULL) { - ASSERT(NVP_DEVNAME_NEXT(np) == NULL); - - rval = nvlist_alloc(&sub_nvl, NV_UNIQUE_NAME, KM_SLEEP); - if (rval != 0) { - KFIOERR((CE_CONT, "%s: nvlist alloc error %d\n", - nvfd->nvf_name, rval)); - sub_nvl = NULL; - goto err; - } - - rval = nvlist_add_string_array(sub_nvl, - DP_DEVNAME_NCACHE_ID, np->nvp_paths, np->nvp_npaths); - if (rval != 0) { - KFIOERR((CE_CONT, - "%s: nvlist add error %d (sdev)\n", - nvfd->nvf_name, rval)); - goto err; - } - - rval = nvlist_add_int32_array(sub_nvl, - DP_DEVNAME_NC_EXPIRECNT_ID, - np->nvp_expirecnts, np->nvp_npaths); - if (rval != 0) { - KFIOERR((CE_CONT, - "%s: nvlist add error %d (sdev)\n", - nvfd->nvf_name, rval)); - goto err; - } - - rval = nvlist_add_nvlist(nvl, DP_DEVNAME_ID, sub_nvl); - if (rval != 0) { - KFIOERR((CE_CONT, "%s: nvlist add error %d (sublist)\n", - nvfd->nvf_name, rval)); - goto err; - } - nvlist_free(sub_nvl); - } - - *ret_nvl = nvl; - return (DDI_SUCCESS); - -err: - if (sub_nvl) - nvlist_free(sub_nvl); - nvlist_free(nvl); - *ret_nvl = NULL; - return (DDI_FAILURE); -} - - -/* - * Read a file in the nvlist format - * EIO - i/o error during read - * ENOENT - file not found - * EINVAL - file contents corrupted - */ -static int -fread_nvp_list(nvfd_t *nvfd) -{ - nvlist_t *nvl; - nvpair_t *nvp; - char *name; - nvlist_t *sublist; - int rval; - nvp_list_t *np; - nvp_list_t *nvp_list = NULL; - nvp_list_t *nvp_tail = NULL; - - nvfd->nvf_list = NULL; - nvfd->nvf_tail = NULL; - - rval = fread_nvlist(nvfd->nvf_name, &nvl); - if (rval != 0) - return (rval); - ASSERT(nvl != NULL); - - nvp = NULL; - while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { - name = nvpair_name(nvp); - ASSERT(strlen(name) > 0); - - switch (nvpair_type(nvp)) { - case DATA_TYPE_NVLIST: - rval = nvpair_value_nvlist(nvp, &sublist); - if (rval != 0) { - KFIOERR((CE_CONT, - "nvpair_value_nvlist error %s %d\n", - name, rval)); - goto error; - } - - /* - * convert nvlist for this device to - * an nvp_list_t struct - */ - np = (nvfd->nvf_nvl2nvp)(sublist, name); - if (np) { - np->nvp_next = NULL; - np->nvp_prev = nvp_tail; - - if (nvp_list == NULL) { - nvp_list = np; - } else { - nvp_tail->nvp_next = np; - } - nvp_tail = np; - } - break; - - default: - KFIOERR((CE_CONT, "%s: %s unsupported data type %d\n", - nvfd->nvf_name, name, nvpair_type(nvp))); - rval = EINVAL; - goto error; - } - } - - nvlist_free(nvl); - - nvfd->nvf_list = nvp_list; - nvfd->nvf_tail = nvp_tail; - - return (0); - -error: - nvlist_free(nvl); - if (nvp_list) - nvp_list_free(nvfd, nvp_list); - return (rval); -} - - -static int -i_ddi_read_one_nvfile(nvfd_t *nvfd) -{ - int rval; - - KFDEBUG((CE_CONT, "reading %s\n", nvfd->nvf_name)); - - rval = fread_nvp_list(nvfd); - if (rval) { - switch (rval) { - case EIO: - nvfd->nvf_flags |= NVF_REBUILD_MSG; - cmn_err(CE_WARN, "%s: I/O error", - nvfd->nvf_name); - break; - case ENOENT: - nvfd->nvf_flags |= NVF_CREATE_MSG; - KFIOERR((CE_CONT, "%s: not found\n", - nvfd->nvf_name)); - break; - case EINVAL: - default: - nvfd->nvf_flags |= NVF_REBUILD_MSG; - cmn_err(CE_WARN, "%s: data file corrupted", - nvfd->nvf_name); - break; - } - } - return (rval); -} - -/* for information possibly required to mount root */ -void -i_ddi_read_devices_files(void) -{ - mdi_read_devices_files(); - - if (devid_cache_read_disable == 0) { - ASSERT(dcfd->nvf_list == NULL); - (void) i_ddi_read_one_nvfile(dcfd); - } -} - -/* may be done after root is mounted */ -void -i_ddi_read_devname_file(void) -{ - if (sdev_cache_read_disable == 0) { - ASSERT(sdevfd->nvf_list == NULL); - (void) i_ddi_read_one_nvfile(sdevfd); - } -} - -static int -e_devid_do_discovery(void) -{ - ASSERT(mutex_owned(&devid_discovery_mutex)); - - if (i_ddi_io_initialized() == 0) { - if (devid_discovery_boot > 0) { - devid_discovery_boot--; - return (1); - } - } else { - if (devid_discovery_postboot_always > 0) - return (1); - if (devid_discovery_postboot > 0) { - devid_discovery_postboot--; - return (1); - } - if (devid_discovery_secs > 0) { - if ((ddi_get_lbolt() - devid_last_discovery) > - drv_usectohz(devid_discovery_secs * MICROSEC)) { - return (1); - } - } - } - - DEVID_LOG_DISC((CE_CONT, "devid_discovery: no discovery\n")); - return (0); -} - -static void -e_ddi_devid_hold_by_major(major_t major) -{ - DEVID_LOG_DISC((CE_CONT, - "devid_discovery: ddi_hold_installed_driver %d\n", major)); - - if (ddi_hold_installed_driver(major) == NULL) - return; - - ddi_rele_driver(major); -} - -static char *e_ddi_devid_hold_driver_list[] = { "sd", "ssd", "dad" }; - -#define N_DRIVERS_TO_HOLD \ - (sizeof (e_ddi_devid_hold_driver_list) / sizeof (char *)) - - -static void -e_ddi_devid_hold_installed_driver(ddi_devid_t devid) -{ - impl_devid_t *id = (impl_devid_t *)devid; - major_t major, hint_major; - char hint[DEVID_HINT_SIZE + 1]; - char **drvp; - int i; - - /* Count non-null bytes */ - for (i = 0; i < DEVID_HINT_SIZE; i++) - if (id->did_driver[i] == '\0') - break; - - /* Make a copy of the driver hint */ - bcopy(id->did_driver, hint, i); - hint[i] = '\0'; - - /* search for the devid using the hint driver */ - hint_major = ddi_name_to_major(hint); - if (hint_major != (major_t)-1) { - e_ddi_devid_hold_by_major(hint_major); - } - - drvp = e_ddi_devid_hold_driver_list; - for (i = 0; i < N_DRIVERS_TO_HOLD; i++, drvp++) { - major = ddi_name_to_major(*drvp); - if (major != (major_t)-1 && major != hint_major) { - e_ddi_devid_hold_by_major(major); - } - } -} - - -/* - * Return success if discovery was attempted, to indicate - * that the desired device may now be available. - */ -int -e_ddi_devid_discovery(ddi_devid_t devid) -{ - int flags; - int rval = DDI_SUCCESS; - - mutex_enter(&devid_discovery_mutex); - - if (devid_discovery_busy) { - DEVID_LOG_DISC((CE_CONT, "devid_discovery: busy\n")); - while (devid_discovery_busy) { - cv_wait(&devid_discovery_cv, &devid_discovery_mutex); - } - } else if (e_devid_do_discovery()) { - devid_discovery_busy = 1; - mutex_exit(&devid_discovery_mutex); - - if (i_ddi_io_initialized() == 0) { - e_ddi_devid_hold_installed_driver(devid); - } else { - DEVID_LOG_DISC((CE_CONT, - "devid_discovery: ndi_devi_config\n")); - flags = NDI_DEVI_PERSIST | NDI_CONFIG | NDI_NO_EVENT; - if (i_ddi_io_initialized()) - flags |= NDI_DRV_CONF_REPROBE; - (void) ndi_devi_config(ddi_root_node(), flags); - } - - mutex_enter(&devid_discovery_mutex); - devid_discovery_busy = 0; - cv_broadcast(&devid_discovery_cv); - if (devid_discovery_secs > 0) - devid_last_discovery = ddi_get_lbolt(); - DEVID_LOG_DISC((CE_CONT, "devid_discovery: done\n")); - } else { - rval = DDI_FAILURE; - DEVID_LOG_DISC((CE_CONT, "no devid discovery\n")); - } - - mutex_exit(&devid_discovery_mutex); - - return (rval); -} - -int -e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid) -{ - nvp_devid_t *np; - nvp_devid_t *new_nvp; - ddi_devid_t new_devid; - int new_devid_size; - char *path, *fullpath; - ddi_devid_t free_devid = NULL; - int pathlen; - - ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS); - - fullpath = kmem_alloc(MAXPATHLEN, KM_SLEEP); - (void) ddi_pathname(dip, fullpath); - pathlen = strlen(fullpath) + 1; - path = kmem_alloc(pathlen, KM_SLEEP); - bcopy(fullpath, path, pathlen); - kmem_free(fullpath, MAXPATHLEN); - - DEVID_LOG_REG(("register", devid, path)); - - new_nvp = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP); - new_devid_size = ddi_devid_sizeof(devid); - new_devid = kmem_alloc(new_devid_size, KM_SLEEP); - (void) bcopy(devid, new_devid, new_devid_size); - - rw_enter(&dcfd->nvf_lock, RW_WRITER); - - for (np = NVF_DEVID_LIST(dcfd); np; np = NVP_DEVID_NEXT(np)) { - if (strcmp(path, np->nvp_devpath) == 0) { - DEVID_DEBUG2((CE_CONT, - "register: %s path match\n", path)); - if (np->nvp_devid == NULL) { - replace: - np->nvp_devid = new_devid; - np->nvp_flags |= - NVP_DEVID_DIP | NVP_DEVID_REGISTERED; - np->nvp_dip = dip; - NVF_MARK_DIRTY(dcfd); - rw_exit(&dcfd->nvf_lock); - kmem_free(new_nvp, sizeof (nvp_devid_t)); - kmem_free(path, pathlen); - goto exit; - } - if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) { - /* replace invalid devid */ - free_devid = np->nvp_devid; - goto replace; - } - /* - * We're registering an already-cached path - * Does the device's devid match the cache? - */ - if (ddi_devid_compare(devid, np->nvp_devid) != 0) { - DEVID_DEBUG((CE_CONT, "devid register: " - "devid %s does not match\n", path)); - /* - * Replace cached devid for this path - * with newly registered devid. A devid - * may map to multiple paths but one path - * should only map to one devid. - */ - nfd_nvp_free_and_unlink(dcfd, NVPLIST(np)); - np = NULL; - break; - } else { - DEVID_DEBUG2((CE_CONT, - "devid register: %s devid match\n", path)); - np->nvp_flags |= - NVP_DEVID_DIP | NVP_DEVID_REGISTERED; - np->nvp_dip = dip; - rw_exit(&dcfd->nvf_lock); - kmem_free(new_nvp, sizeof (nvp_devid_t)); - kmem_free(path, pathlen); - kmem_free(new_devid, new_devid_size); - return (DDI_SUCCESS); - } - } - } - - /* - * Add newly registered devid to the cache - */ - ASSERT(np == NULL); - - new_nvp->nvp_devpath = path; - new_nvp->nvp_flags = NVP_DEVID_DIP | NVP_DEVID_REGISTERED; - new_nvp->nvp_dip = dip; - new_nvp->nvp_devid = new_devid; - - NVF_MARK_DIRTY(dcfd); - nfd_nvp_link(dcfd, NVPLIST(new_nvp)); - - rw_exit(&dcfd->nvf_lock); - -exit: - if (free_devid) - kmem_free(free_devid, ddi_devid_sizeof(free_devid)); - - if (!devid_cache_write_disable) - wake_nvpflush_daemon(); - - return (DDI_SUCCESS); -} - -/* - * Unregister a device's devid - * Called as an instance detachs - * Invalidate the devid's devinfo reference - * Devid-path remains in the cache - */ -void -e_devid_cache_unregister(dev_info_t *dip) -{ - nvp_devid_t *np; - - rw_enter(&dcfd->nvf_lock, RW_WRITER); - - for (np = NVF_DEVID_LIST(dcfd); np; np = NVP_DEVID_NEXT(np)) { - if (np->nvp_devid == NULL) - continue; - if ((np->nvp_flags & NVP_DEVID_DIP) && np->nvp_dip == dip) { - DEVID_LOG_UNREG((CE_CONT, - "unregister: %s\n", np->nvp_devpath)); - np->nvp_flags &= ~NVP_DEVID_DIP; - np->nvp_dip = NULL; - break; - } - } - - rw_exit(&dcfd->nvf_lock); -} - - -void -e_devid_cache_cleanup(void) -{ - nvp_devid_t *np, *next; - - rw_enter(&dcfd->nvf_lock, RW_WRITER); - - for (np = NVF_DEVID_LIST(dcfd); np; np = next) { - next = NVP_DEVID_NEXT(np); - if (np->nvp_devid == NULL) - continue; - if ((np->nvp_flags & NVP_DEVID_REGISTERED) == 0) { - DEVID_LOG_REMOVE((CE_CONT, - "cleanup: %s\n", np->nvp_devpath)); - NVF_MARK_DIRTY(dcfd); - nfd_nvp_free_and_unlink(dcfd, NVPLIST(np)); - } - } - - rw_exit(&dcfd->nvf_lock); - - if (NVF_IS_DIRTY(dcfd)) - wake_nvpflush_daemon(); -} - - -/* - * Build a list of dev_t's for a device/devid - * - * The effect of this function is cumulative, adding dev_t's - * for the device to the list of all dev_t's for a given - * devid. - */ -static void -e_devid_minor_to_devlist( - dev_info_t *dip, - char *minor_name, - int ndevts_alloced, - int *devtcntp, - dev_t *devtsp) -{ - struct ddi_minor_data *dmdp; - int minor_all = 0; - int ndevts = *devtcntp; - - ASSERT(i_ddi_devi_attached(dip)); - - /* are we looking for a set of minor nodes? */ - if ((minor_name == DEVID_MINOR_NAME_ALL) || - (minor_name == DEVID_MINOR_NAME_ALL_CHR) || - (minor_name == DEVID_MINOR_NAME_ALL_BLK)) - minor_all = 1; - - mutex_enter(&(DEVI(dip)->devi_lock)); - - /* Find matching minor names */ - for (dmdp = DEVI(dip)->devi_minor; dmdp; dmdp = dmdp->next) { - - /* Skip non-minors, and non matching minor names */ - if ((dmdp->type != DDM_MINOR) || ((minor_all == 0) && - strcmp(dmdp->ddm_name, minor_name))) - continue; - - /* filter out minor_all mismatches */ - if (minor_all && - (((minor_name == DEVID_MINOR_NAME_ALL_CHR) && - (dmdp->ddm_spec_type != S_IFCHR)) || - ((minor_name == DEVID_MINOR_NAME_ALL_BLK) && - (dmdp->ddm_spec_type != S_IFBLK)))) - continue; - - if (ndevts < ndevts_alloced) - devtsp[ndevts] = dmdp->ddm_dev; - ndevts++; - } - - mutex_exit(&(DEVI(dip)->devi_lock)); - - *devtcntp = ndevts; -} - -/* - * Search for cached entries matching a devid - * Return two lists: - * a list of dev_info nodes, for those devices in the attached state - * a list of pathnames whose instances registered the given devid - * If the lists passed in are not sufficient to return the matching - * references, return the size of lists required. - * The dev_info nodes are returned with a hold that the caller must release. - */ -static int -e_devid_cache_devi_path_lists(ddi_devid_t devid, int retmax, - int *retndevis, dev_info_t **retdevis, int *retnpaths, char **retpaths) -{ - nvp_devid_t *np; - int ndevis, npaths; - dev_info_t *dip, *pdip; - int circ; - int maxdevis = 0; - int maxpaths = 0; - - ndevis = 0; - npaths = 0; - for (np = NVF_DEVID_LIST(dcfd); np; np = NVP_DEVID_NEXT(np)) { - if (np->nvp_devid == NULL) - continue; - if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) { - DEVIDERR((CE_CONT, - "find: invalid devid %s\n", - np->nvp_devpath)); - continue; - } - if (ddi_devid_compare(devid, np->nvp_devid) == 0) { - DEVID_DEBUG2((CE_CONT, - "find: devid match: %s 0x%x\n", - np->nvp_devpath, np->nvp_flags)); - DEVID_LOG_MATCH(("find", devid, np->nvp_devpath)); - DEVID_LOG_PATHS((CE_CONT, "%s\n", np->nvp_devpath)); - - /* - * Check if we have a cached devinfo reference for this - * devid. Place a hold on it to prevent detach - * Otherwise, use the path instead. - * Note: returns with a hold on each dev_info - * node in the list. - */ - dip = NULL; - if (np->nvp_flags & NVP_DEVID_DIP) { - pdip = ddi_get_parent(np->nvp_dip); - if (ndi_devi_tryenter(pdip, &circ)) { - dip = np->nvp_dip; - ndi_hold_devi(dip); - ndi_devi_exit(pdip, circ); - ASSERT(!DEVI_IS_ATTACHING(dip)); - ASSERT(!DEVI_IS_DETACHING(dip)); - } else { - DEVID_LOG_DETACH((CE_CONT, - "may be detaching: %s\n", - np->nvp_devpath)); - } - } - - if (dip) { - if (ndevis < retmax) { - retdevis[ndevis++] = dip; - } else { - ndi_rele_devi(dip); - } - maxdevis++; - } else { - if (npaths < retmax) - retpaths[npaths++] = np->nvp_devpath; - maxpaths++; - } - } - } - - *retndevis = ndevis; - *retnpaths = npaths; - return (maxdevis > maxpaths ? maxdevis : maxpaths); -} - - -/* - * Search the devid cache, returning dev_t list for all - * device paths mapping to the device identified by the - * given devid. - * - * Primary interface used by ddi_lyr_devid_to_devlist() - */ -int -e_devid_cache_to_devt_list(ddi_devid_t devid, char *minor_name, - int *retndevts, dev_t **retdevts) -{ - char *path, **paths; - int i, j, n; - dev_t *devts, *udevts; - dev_t tdevt; - int ndevts, undevts, ndevts_alloced; - dev_info_t *devi, **devis; - int ndevis, npaths, nalloced; - ddi_devid_t match_devid; - - DEVID_LOG_FIND(("find", devid, NULL)); - - ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS); - if (ddi_devid_valid(devid) != DDI_SUCCESS) { - DEVID_LOG_ERR(("invalid devid", devid, NULL)); - return (DDI_FAILURE); - } - - nalloced = 128; - - for (;;) { - paths = kmem_zalloc(nalloced * sizeof (char *), KM_SLEEP); - devis = kmem_zalloc(nalloced * sizeof (dev_info_t *), KM_SLEEP); - - rw_enter(&dcfd->nvf_lock, RW_READER); - n = e_devid_cache_devi_path_lists(devid, nalloced, - &ndevis, devis, &npaths, paths); - if (n <= nalloced) - break; - rw_exit(&dcfd->nvf_lock); - for (i = 0; i < ndevis; i++) - ndi_rele_devi(devis[i]); - kmem_free(paths, nalloced * sizeof (char *)); - kmem_free(devis, nalloced * sizeof (dev_info_t *)); - nalloced = n + 128; - } - - for (i = 0; i < npaths; i++) { - path = i_ddi_strdup(paths[i], KM_SLEEP); - paths[i] = path; - } - rw_exit(&dcfd->nvf_lock); - - if (ndevis == 0 && npaths == 0) { - DEVID_LOG_ERR(("no devid found", devid, NULL)); - kmem_free(paths, nalloced * sizeof (char *)); - kmem_free(devis, nalloced * sizeof (dev_info_t *)); - return (DDI_FAILURE); - } - - ndevts_alloced = 128; -restart: - ndevts = 0; - devts = kmem_alloc(ndevts_alloced * sizeof (dev_t), KM_SLEEP); - for (i = 0; i < ndevis; i++) { - ASSERT(!DEVI_IS_ATTACHING(devis[i])); - ASSERT(!DEVI_IS_DETACHING(devis[i])); - e_devid_minor_to_devlist(devis[i], minor_name, - ndevts_alloced, &ndevts, devts); - if (ndevts > ndevts_alloced) { - kmem_free(devts, ndevts_alloced * sizeof (dev_t)); - ndevts_alloced += 128; - goto restart; - } - } - for (i = 0; i < npaths; i++) { - DEVID_LOG_LOOKUP((CE_CONT, "lookup %s\n", paths[i])); - devi = e_ddi_hold_devi_by_path(paths[i], 0); - if (devi == NULL) { - DEVID_LOG_STALE(("stale device reference", - devid, paths[i])); - continue; - } - /* - * Verify the newly attached device registered a matching devid - */ - if (i_ddi_devi_get_devid(DDI_DEV_T_ANY, devi, - &match_devid) != DDI_SUCCESS) { - DEVIDERR((CE_CONT, - "%s: no devid registered on attach\n", - paths[i])); - ddi_release_devi(devi); - continue; - } - - if (ddi_devid_compare(devid, match_devid) != 0) { - DEVID_LOG_STALE(("new devid registered", - devid, paths[i])); - ddi_release_devi(devi); - ddi_devid_free(match_devid); - continue; - } - ddi_devid_free(match_devid); - - e_devid_minor_to_devlist(devi, minor_name, - ndevts_alloced, &ndevts, devts); - ddi_release_devi(devi); - if (ndevts > ndevts_alloced) { - kmem_free(devts, - ndevts_alloced * sizeof (dev_t)); - ndevts_alloced += 128; - goto restart; - } - } - - /* drop hold from e_devid_cache_devi_path_lists */ - for (i = 0; i < ndevis; i++) { - ndi_rele_devi(devis[i]); - } - for (i = 0; i < npaths; i++) { - kmem_free(paths[i], strlen(paths[i]) + 1); - } - kmem_free(paths, nalloced * sizeof (char *)); - kmem_free(devis, nalloced * sizeof (dev_info_t *)); - - if (ndevts == 0) { - DEVID_LOG_ERR(("no devid found", devid, NULL)); - kmem_free(devts, ndevts_alloced * sizeof (dev_t)); - return (DDI_FAILURE); - } - - /* - * Build the final list of sorted dev_t's with duplicates collapsed so - * returned results are consistent. This prevents implementation - * artifacts from causing unnecessary changes in SVM namespace. - */ - /* bubble sort */ - for (i = 0; i < (ndevts - 1); i++) { - for (j = 0; j < ((ndevts - 1) - i); j++) { - if (devts[j + 1] < devts[j]) { - tdevt = devts[j]; - devts[j] = devts[j + 1]; - devts[j + 1] = tdevt; - } - } - } - - /* determine number of unique values */ - for (undevts = ndevts, i = 1; i < ndevts; i++) { - if (devts[i - 1] == devts[i]) - undevts--; - } - - /* allocate unique */ - udevts = kmem_alloc(undevts * sizeof (dev_t), KM_SLEEP); - - /* copy unique */ - udevts[0] = devts[0]; - for (i = 1, j = 1; i < ndevts; i++) { - if (devts[i - 1] != devts[i]) - udevts[j++] = devts[i]; - } - ASSERT(j == undevts); - - kmem_free(devts, ndevts_alloced * sizeof (dev_t)); - - *retndevts = undevts; - *retdevts = udevts; - - return (DDI_SUCCESS); -} - -void -e_devid_cache_free_devt_list(int ndevts, dev_t *devt_list) -{ - kmem_free(devt_list, ndevts * sizeof (dev_t *)); -} - - -#include <sys/callb.h> - -/* - * Allow some delay from an update of the data before flushing - * to permit simultaneous updates of multiple changes. - * Changes in the data are expected to be bursty, ie - * reconfig boot or hot-plug of a new adapter. - * - * nvpflush_delay is in units of seconds. - * The data should be "quiet" for this interval before - * the repository update is triggered. - * - * nvpdaemon_idle_time is the number of seconds the - * daemon will sleep idle before exiting. - */ -#define NVPFLUSH_DELAY 10 -#define NVPDAEMON_IDLE_TIME 60 - -#define TICKS_PER_SECOND (drv_usectohz(1000000)) - -static int nvpflush_delay = NVPFLUSH_DELAY; -static int nvpdaemon_idle_time = NVPDAEMON_IDLE_TIME; - -static timeout_id_t nvpflush_id = 0; -static int nvpflush_timer_busy = 0; -static int nvpflush_daemon_active = 0; -static kthread_t *nvpflush_thr_id = 0; - -static int do_nvpflush = 0; -static int nvpbusy = 0; -static kmutex_t nvpflush_lock; -static kcondvar_t nvpflush_cv; -static kthread_id_t nvpflush_thread; -static clock_t nvpticks; - -static void nvpflush_daemon(void); - -void -nvf_register_write_complete(nvfd_t *fd, void (*f)(nvfd_t *)) -{ - fd->nvf_write_complete = f; -} - -void -nvf_unregister_write_complete(nvfd_t *fd) -{ - fd->nvf_write_complete = NULL; -} - -static void -nvf_write_complete(nvfd_t *fd) -{ - if (fd->nvf_write_complete) { - (*(fd->nvf_write_complete))(fd); - } -} - -void -i_ddi_start_flush_daemon(void) -{ - ASSERT(i_ddi_io_initialized()); - - mutex_init(&nvpflush_lock, NULL, MUTEX_DRIVER, NULL); - cv_init(&nvpflush_cv, NULL, CV_DRIVER, NULL); - - if ((NVF_IS_DIRTY(dcfd) && !devid_cache_write_disable) || - (NVF_IS_DIRTY(sdevfd) && !sdevfd && sdev_cache_write_disable)) { - wake_nvpflush_daemon(); - } -} - -/*ARGSUSED*/ -static void -nvpflush_timeout(void *arg) -{ - clock_t nticks; - - mutex_enter(&nvpflush_lock); - nticks = nvpticks - ddi_get_lbolt(); - if (nticks > 4) { - nvpflush_timer_busy = 1; - mutex_exit(&nvpflush_lock); - nvpflush_id = timeout(nvpflush_timeout, NULL, nticks); - } else { - do_nvpflush = 1; - NVPDAEMON_DEBUG((CE_CONT, "signal nvpdaemon\n")); - cv_signal(&nvpflush_cv); - nvpflush_id = 0; - nvpflush_timer_busy = 0; - mutex_exit(&nvpflush_lock); - } -} - -void -wake_nvpflush_daemon() -{ - clock_t nticks; - - /* - * If the system isn't up yet - * don't even think about starting a flush. - */ - if (!i_ddi_io_initialized()) - return; - - mutex_enter(&nvpflush_lock); - - if (nvpflush_daemon_active == 0) { - nvpflush_daemon_active = 1; - mutex_exit(&nvpflush_lock); - NVPDAEMON_DEBUG((CE_CONT, "starting nvpdaemon thread\n")); - nvpflush_thr_id = thread_create(NULL, 0, - (void (*)())nvpflush_daemon, - NULL, 0, &p0, TS_RUN, minclsyspri); - mutex_enter(&nvpflush_lock); - } - - nticks = nvpflush_delay * TICKS_PER_SECOND; - nvpticks = ddi_get_lbolt() + nticks; - if (nvpflush_timer_busy == 0) { - nvpflush_timer_busy = 1; - mutex_exit(&nvpflush_lock); - nvpflush_id = timeout(nvpflush_timeout, NULL, nticks + 4); - } else - mutex_exit(&nvpflush_lock); -} - -static int -nvpflush_one(nvfd_t *nvfd) -{ - int rval = DDI_SUCCESS; - nvlist_t *nvl; - - rw_enter(&nvfd->nvf_lock, RW_READER); - - if (!NVF_IS_DIRTY(nvfd) || NVF_IS_READONLY(nvfd)) { - rw_exit(&nvfd->nvf_lock); - return (DDI_SUCCESS); - } - - if (rw_tryupgrade(&nvfd->nvf_lock) == 0) { - KFIOERR((CE_CONT, "nvpflush: " - "%s rw upgrade failed\n", nvfd->nvf_name)); - rw_exit(&nvfd->nvf_lock); - return (DDI_FAILURE); - } - if (((nvfd->nvf_nvp2nvl)(nvfd, &nvl)) != DDI_SUCCESS) { - KFIOERR((CE_CONT, "nvpflush: " - "%s nvlist construction failed\n", nvfd->nvf_name)); - rw_exit(&nvfd->nvf_lock); - return (DDI_FAILURE); - } - - NVF_CLEAR_DIRTY(nvfd); - nvfd->nvf_flags |= NVF_FLUSHING; - rw_exit(&nvfd->nvf_lock); - - rval = e_fwrite_nvlist(nvfd, nvl); - nvlist_free(nvl); - - rw_enter(&nvfd->nvf_lock, RW_WRITER); - nvfd->nvf_flags &= ~NVF_FLUSHING; - if (rval == DDI_FAILURE) { - if (NVF_IS_READONLY(nvfd)) { - rval = DDI_SUCCESS; - nvfd->nvf_flags &= ~(NVF_ERROR | NVF_DIRTY); - } else if ((nvfd->nvf_flags & NVF_ERROR) == 0) { - cmn_err(CE_CONT, - "%s: updated failed\n", nvfd->nvf_name); - nvfd->nvf_flags |= NVF_ERROR | NVF_DIRTY; - } - } else { - if (nvfd->nvf_flags & NVF_CREATE_MSG) { - cmn_err(CE_CONT, "!Creating %s\n", nvfd->nvf_name); - nvfd->nvf_flags &= ~NVF_CREATE_MSG; - } - if (nvfd->nvf_flags & NVF_REBUILD_MSG) { - cmn_err(CE_CONT, "!Rebuilding %s\n", nvfd->nvf_name); - nvfd->nvf_flags &= ~NVF_REBUILD_MSG; - } - if (nvfd->nvf_flags & NVF_ERROR) { - cmn_err(CE_CONT, - "%s: update now ok\n", nvfd->nvf_name); - nvfd->nvf_flags &= ~NVF_ERROR; - } - /* - * The file may need to be flushed again if the cached - * data was touched while writing the earlier contents. - */ - if (NVF_IS_DIRTY(nvfd)) - rval = DDI_FAILURE; - } - - rw_exit(&nvfd->nvf_lock); - return (rval); -} - - -static void -nvpflush_daemon(void) -{ - callb_cpr_t cprinfo; - clock_t clk; - int rval; - int i; - - ASSERT(modrootloaded); - - nvpflush_thread = curthread; - NVPDAEMON_DEBUG((CE_CONT, "nvpdaemon: init\n")); - - CALLB_CPR_INIT(&cprinfo, &nvpflush_lock, callb_generic_cpr, "nvp"); - mutex_enter(&nvpflush_lock); - for (;;) { - - CALLB_CPR_SAFE_BEGIN(&cprinfo); - while (do_nvpflush == 0) { - clk = cv_timedwait(&nvpflush_cv, &nvpflush_lock, - ddi_get_lbolt() + - (nvpdaemon_idle_time * TICKS_PER_SECOND)); - if (clk == -1 && - do_nvpflush == 0 && nvpflush_timer_busy == 0) { - /* - * Note that CALLB_CPR_EXIT calls mutex_exit() - * on the lock passed in to CALLB_CPR_INIT, - * so the lock must be held when invoking it. - */ - CALLB_CPR_SAFE_END(&cprinfo, &nvpflush_lock); - NVPDAEMON_DEBUG((CE_CONT, "nvpdaemon: exit\n")); - ASSERT(mutex_owned(&nvpflush_lock)); - nvpflush_thr_id = NULL; - nvpflush_daemon_active = 0; - CALLB_CPR_EXIT(&cprinfo); - thread_exit(); - } - } - CALLB_CPR_SAFE_END(&cprinfo, &nvpflush_lock); - - nvpbusy = 1; - do_nvpflush = 0; - mutex_exit(&nvpflush_lock); - - /* - * Try flushing what's dirty, reschedule if there's - * a failure or data gets marked as dirty again. - */ - for (i = 0; i < NCACHEFDS; i++) { - rw_enter(&cachefds[i]->nvf_lock, RW_READER); - if (NVF_IS_DIRTY(cachefds[i])) { - NVPDAEMON_DEBUG((CE_CONT, - "nvpdaemon: flush %s\n", - cachefds[i]->nvf_name)); - rw_exit(&cachefds[i]->nvf_lock); - rval = nvpflush_one(cachefds[i]); - rw_enter(&cachefds[i]->nvf_lock, RW_READER); - if (rval != DDI_SUCCESS || - NVF_IS_DIRTY(cachefds[i])) { - rw_exit(&cachefds[i]->nvf_lock); - NVPDAEMON_DEBUG((CE_CONT, - "nvpdaemon: %s dirty again\n", - cachefds[i]->nvf_name)); - wake_nvpflush_daemon(); - } else { - rw_exit(&cachefds[i]->nvf_lock); - nvf_write_complete(cachefds[i]); - } - } else { - NVPDAEMON_DEBUG((CE_CONT, - "nvpdaemon: not dirty %s\n", - cachefds[i]->nvf_name)); - rw_exit(&cachefds[i]->nvf_lock); - } - } - - mutex_enter(&nvpflush_lock); - nvpbusy = 0; - } -} - - -void -i_ddi_clean_devices_files(void) -{ - e_devid_cache_cleanup(); - mdi_clean_vhcache(); -} - -#ifdef DEBUG -static void -devid_log(char *fmt, ddi_devid_t devid, char *path) -{ - char *devidstr = ddi_devid_str_encode(devid, NULL); - if (path) { - cmn_err(CE_CONT, "%s: %s %s\n", fmt, path, devidstr); - } else { - cmn_err(CE_CONT, "%s: %s\n", fmt, devidstr); - } - ddi_devid_str_free(devidstr); -} -#endif /* DEBUG */ diff --git a/usr/src/uts/common/os/devid_cache.c b/usr/src/uts/common/os/devid_cache.c new file mode 100644 index 0000000000..77efe41817 --- /dev/null +++ b/usr/src/uts/common/os/devid_cache.c @@ -0,0 +1,998 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/note.h> +#include <sys/t_lock.h> +#include <sys/cmn_err.h> +#include <sys/instance.h> +#include <sys/conf.h> +#include <sys/stat.h> +#include <sys/ddi.h> +#include <sys/hwconf.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/ddi_impldefs.h> +#include <sys/ndi_impldefs.h> +#include <sys/kobj.h> +#include <sys/devcache.h> +#include <sys/devid_cache.h> +#include <sys/sysmacros.h> + +/* + * Discovery refers to the heroic effort made to discover a device which + * cannot be accessed at the physical path where it once resided. Discovery + * involves walking the entire device tree attaching all possible disk + * instances, to search for the device referenced by a devid. Obviously, + * full device discovery is something to be avoided where possible. + * Note that simply invoking devfsadm(1M) is equivalent to running full + * discovery at the devid cache level. + * + * Reasons why a disk may not be accessible: + * disk powered off + * disk removed or cable disconnected + * disk or adapter broken + * + * Note that discovery is not needed and cannot succeed in any of these + * cases. + * + * When discovery may succeed: + * Discovery will result in success when a device has been moved + * to a different address. Note that it's recommended that + * devfsadm(1M) be invoked (no arguments required) whenever a system's + * h/w configuration has been updated. Alternatively, a + * reconfiguration boot can be used to accomplish the same result. + * + * Note that discovery is not necessary to be able to correct an access + * failure for a device which was powered off. Assuming the cache has an + * entry for such a device, simply powering it on should permit the system + * to access it. If problems persist after powering it on, invoke + * devfsadm(1M). + * + * Discovery prior to mounting root is only of interest when booting + * from a filesystem which accesses devices by device id, which of + * not all do. + * + * Tunables + * + * devid_discovery_boot (default 1) + * Number of times discovery will be attempted prior to mounting root. + * Must be done at least once to recover from corrupted or missing + * devid cache backing store. Probably there's no reason to ever + * set this to greater than one as a missing device will remain + * unavailable no matter how often the system searches for it. + * + * devid_discovery_postboot (default 1) + * Number of times discovery will be attempted after mounting root. + * This must be performed at least once to discover any devices + * needed after root is mounted which may have been powered + * off and moved before booting. + * Setting this to a larger positive number will introduce + * some inconsistency in system operation. Searching for a device + * will take an indeterminate amount of time, sometimes slower, + * sometimes faster. In addition, the system will sometimes + * discover a newly powered on device, sometimes it won't. + * Use of this option is not therefore recommended. + * + * devid_discovery_postboot_always (default 0) + * Set to 1, the system will always attempt full discovery. + * + * devid_discovery_secs (default 0) + * Set to a positive value, the system will attempt full discovery + * but with a minimum delay between attempts. A device search + * within the period of time specified will result in failure. + * + * devid_cache_read_disable (default 0) + * Set to 1 to disable reading /etc/devices/devid_cache. + * Devid cache will continue to operate normally but + * at least one discovery attempt will be required. + * + * devid_cache_write_disable (default 0) + * Set to 1 to disable updates to /etc/devices/devid_cache. + * Any updates to the devid cache will not be preserved across a reboot. + * + * devid_report_error (default 0) + * Set to 1 to enable some error messages related to devid + * cache failures. + * + * The devid is packed in the cache file as a byte array. For + * portability, this could be done in the encoded string format. + */ + + +int devid_discovery_boot = 1; +int devid_discovery_postboot = 1; +int devid_discovery_postboot_always = 0; +int devid_discovery_secs = 0; + +int devid_cache_read_disable = 0; +int devid_cache_write_disable = 0; + +int devid_report_error = 0; + + +/* + * State to manage discovery of devices providing a devid + */ +static int devid_discovery_busy = 0; +static kmutex_t devid_discovery_mutex; +static kcondvar_t devid_discovery_cv; +static clock_t devid_last_discovery = 0; + + +#ifdef DEBUG +int nvp_devid_debug = 0; +int devid_debug = 0; +int devid_log_registers = 0; +int devid_log_finds = 0; +int devid_log_lookups = 0; +int devid_log_discovery = 0; +int devid_log_matches = 0; +int devid_log_paths = 0; +int devid_log_failures = 0; +int devid_log_hold = 0; +int devid_log_unregisters = 0; +int devid_log_removes = 0; +int devid_register_debug = 0; +int devid_log_stale = 0; +int devid_log_detaches = 0; +#endif /* DEBUG */ + +/* + * devid cache file registration for cache reads and updates + */ +static nvf_ops_t devid_cache_ops = { + "/etc/devices/devid_cache", /* path to cache */ + devid_cache_unpack_nvlist, /* read: nvlist to nvp */ + devid_cache_pack_list, /* write: nvp to nvlist */ + devid_list_free, /* free data list */ + NULL /* write complete callback */ +}; + +/* + * handle to registered devid cache handlers + */ +nvf_handle_t dcfd_handle; + + +/* + * Initialize devid cache file management + */ +void +devid_cache_init(void) +{ + dcfd_handle = nvf_register_file(&devid_cache_ops); + ASSERT(dcfd_handle); + + list_create(nvf_list(dcfd_handle), sizeof (nvp_devid_t), + offsetof(nvp_devid_t, nvp_link)); + + mutex_init(&devid_discovery_mutex, NULL, MUTEX_DEFAULT, NULL); + cv_init(&devid_discovery_cv, NULL, CV_DRIVER, NULL); +} + +/* + * Read and initialize the devid cache from the persistent store + */ +void +devid_cache_read(void) +{ + if (!devid_cache_read_disable) { + rw_enter(nvf_lock(dcfd_handle), RW_WRITER); + ASSERT(list_head(nvf_list(dcfd_handle)) == NULL); + (void) nvf_read_file(dcfd_handle); + rw_exit(nvf_lock(dcfd_handle)); + } +} + +static void +devid_nvp_free(nvp_devid_t *dp) +{ + if (dp->nvp_devpath) + kmem_free(dp->nvp_devpath, strlen(dp->nvp_devpath)+1); + if (dp->nvp_devid) + kmem_free(dp->nvp_devid, ddi_devid_sizeof(dp->nvp_devid)); + + kmem_free(dp, sizeof (nvp_devid_t)); +} + +static void +devid_list_free(nvf_handle_t fd) +{ + list_t *listp; + nvp_devid_t *np; + + ASSERT(RW_WRITE_HELD(nvf_lock(dcfd_handle))); + + listp = nvf_list(fd); + while (np = list_head(listp)) { + list_remove(listp, np); + devid_nvp_free(np); + } +} + +/* + * Free an nvp element in a list + */ +static void +devid_nvp_unlink_and_free(nvf_handle_t fd, nvp_devid_t *np) +{ + list_remove(nvf_list(fd), np); + devid_nvp_free(np); +} + +/* + * Unpack a device path/nvlist pair to the list of devid cache elements. + * Used to parse the nvlist format when reading + * /etc/devices/devid_cache + */ +static int +devid_cache_unpack_nvlist(nvf_handle_t fd, nvlist_t *nvl, char *name) +{ + nvp_devid_t *np; + ddi_devid_t devidp; + int rval; + uint_t n; + + NVP_DEVID_DEBUG_PATH((name)); + ASSERT(RW_WRITE_HELD(nvf_lock(dcfd_handle))); + + /* + * check path for a devid + */ + rval = nvlist_lookup_byte_array(nvl, + DP_DEVID_ID, (uchar_t **)&devidp, &n); + if (rval == 0) { + if (ddi_devid_valid(devidp) == DDI_SUCCESS) { + ASSERT(n == ddi_devid_sizeof(devidp)); + np = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP); + np->nvp_devpath = i_ddi_strdup(name, KM_SLEEP); + np->nvp_devid = kmem_alloc(n, KM_SLEEP); + (void) bcopy(devidp, np->nvp_devid, n); + list_insert_tail(nvf_list(fd), np); + NVP_DEVID_DEBUG_DEVID((np->nvp_devid)); + } else { + DEVIDERR((CE_CONT, + "%s: invalid devid\n", name)); + } + } else { + DEVIDERR((CE_CONT, + "%s: devid not available\n", name)); + } + + return (0); +} + +/* + * Pack the list of devid cache elements into a single nvlist + * Used when writing the nvlist file. + */ +static int +devid_cache_pack_list(nvf_handle_t fd, nvlist_t **ret_nvl) +{ + nvlist_t *nvl, *sub_nvl; + nvp_devid_t *np; + int rval; + list_t *listp; + + ASSERT(RW_WRITE_HELD(nvf_lock(dcfd_handle))); + + rval = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); + if (rval != 0) { + nvf_error("%s: nvlist alloc error %d\n", + nvf_cache_name(fd), rval); + return (DDI_FAILURE); + } + + listp = nvf_list(fd); + for (np = list_head(listp); np; np = list_next(listp, np)) { + if (np->nvp_devid == NULL) + continue; + NVP_DEVID_DEBUG_PATH(np->nvp_devpath); + rval = nvlist_alloc(&sub_nvl, NV_UNIQUE_NAME, KM_SLEEP); + if (rval != 0) { + nvf_error("%s: nvlist alloc error %d\n", + nvf_cache_name(fd), rval); + sub_nvl = NULL; + goto err; + } + + rval = nvlist_add_byte_array(sub_nvl, DP_DEVID_ID, + (uchar_t *)np->nvp_devid, + ddi_devid_sizeof(np->nvp_devid)); + if (rval == 0) { + NVP_DEVID_DEBUG_DEVID(np->nvp_devid); + } else { + nvf_error( + "%s: nvlist add error %d (devid)\n", + nvf_cache_name(fd), rval); + goto err; + } + + rval = nvlist_add_nvlist(nvl, np->nvp_devpath, sub_nvl); + if (rval != 0) { + nvf_error("%s: nvlist add error %d (sublist)\n", + nvf_cache_name(fd), rval); + goto err; + } + nvlist_free(sub_nvl); + } + + *ret_nvl = nvl; + return (DDI_SUCCESS); + +err: + if (sub_nvl) + nvlist_free(sub_nvl); + nvlist_free(nvl); + *ret_nvl = NULL; + return (DDI_FAILURE); +} + +static int +e_devid_do_discovery(void) +{ + ASSERT(mutex_owned(&devid_discovery_mutex)); + + if (i_ddi_io_initialized() == 0) { + if (devid_discovery_boot > 0) { + devid_discovery_boot--; + return (1); + } + } else { + if (devid_discovery_postboot_always > 0) + return (1); + if (devid_discovery_postboot > 0) { + devid_discovery_postboot--; + return (1); + } + if (devid_discovery_secs > 0) { + if ((ddi_get_lbolt() - devid_last_discovery) > + drv_usectohz(devid_discovery_secs * MICROSEC)) { + return (1); + } + } + } + + DEVID_LOG_DISC((CE_CONT, "devid_discovery: no discovery\n")); + return (0); +} + +static void +e_ddi_devid_hold_by_major(major_t major) +{ + DEVID_LOG_DISC((CE_CONT, + "devid_discovery: ddi_hold_installed_driver %d\n", major)); + + if (ddi_hold_installed_driver(major) == NULL) + return; + + ddi_rele_driver(major); +} + +static char *e_ddi_devid_hold_driver_list[] = { "sd", "ssd", "dad" }; + +#define N_DRIVERS_TO_HOLD \ + (sizeof (e_ddi_devid_hold_driver_list) / sizeof (char *)) + + +static void +e_ddi_devid_hold_installed_driver(ddi_devid_t devid) +{ + impl_devid_t *id = (impl_devid_t *)devid; + major_t major, hint_major; + char hint[DEVID_HINT_SIZE + 1]; + char **drvp; + int i; + + /* Count non-null bytes */ + for (i = 0; i < DEVID_HINT_SIZE; i++) + if (id->did_driver[i] == '\0') + break; + + /* Make a copy of the driver hint */ + bcopy(id->did_driver, hint, i); + hint[i] = '\0'; + + /* search for the devid using the hint driver */ + hint_major = ddi_name_to_major(hint); + if (hint_major != (major_t)-1) { + e_ddi_devid_hold_by_major(hint_major); + } + + drvp = e_ddi_devid_hold_driver_list; + for (i = 0; i < N_DRIVERS_TO_HOLD; i++, drvp++) { + major = ddi_name_to_major(*drvp); + if (major != (major_t)-1 && major != hint_major) { + e_ddi_devid_hold_by_major(major); + } + } +} + + +/* + * Return success if discovery was attempted, to indicate + * that the desired device may now be available. + */ +int +e_ddi_devid_discovery(ddi_devid_t devid) +{ + int flags; + int rval = DDI_SUCCESS; + + mutex_enter(&devid_discovery_mutex); + + if (devid_discovery_busy) { + DEVID_LOG_DISC((CE_CONT, "devid_discovery: busy\n")); + while (devid_discovery_busy) { + cv_wait(&devid_discovery_cv, &devid_discovery_mutex); + } + } else if (e_devid_do_discovery()) { + devid_discovery_busy = 1; + mutex_exit(&devid_discovery_mutex); + + if (i_ddi_io_initialized() == 0) { + e_ddi_devid_hold_installed_driver(devid); + } else { + DEVID_LOG_DISC((CE_CONT, + "devid_discovery: ndi_devi_config\n")); + flags = NDI_DEVI_PERSIST | NDI_CONFIG | NDI_NO_EVENT; + if (i_ddi_io_initialized()) + flags |= NDI_DRV_CONF_REPROBE; + (void) ndi_devi_config(ddi_root_node(), flags); + } + + mutex_enter(&devid_discovery_mutex); + devid_discovery_busy = 0; + cv_broadcast(&devid_discovery_cv); + if (devid_discovery_secs > 0) + devid_last_discovery = ddi_get_lbolt(); + DEVID_LOG_DISC((CE_CONT, "devid_discovery: done\n")); + } else { + rval = DDI_FAILURE; + DEVID_LOG_DISC((CE_CONT, "no devid discovery\n")); + } + + mutex_exit(&devid_discovery_mutex); + + return (rval); +} + +/* + * As part of registering a devid for a device, + * update the devid cache with this device/devid pair + * or note that this combination has registered. + */ +int +e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid) +{ + nvp_devid_t *np; + nvp_devid_t *new_nvp; + ddi_devid_t new_devid; + int new_devid_size; + char *path, *fullpath; + ddi_devid_t free_devid = NULL; + int pathlen; + list_t *listp; + int is_dirty = 0; + + ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS); + + fullpath = kmem_alloc(MAXPATHLEN, KM_SLEEP); + (void) ddi_pathname(dip, fullpath); + pathlen = strlen(fullpath) + 1; + path = kmem_alloc(pathlen, KM_SLEEP); + bcopy(fullpath, path, pathlen); + kmem_free(fullpath, MAXPATHLEN); + + DEVID_LOG_REG(("register", devid, path)); + + new_nvp = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP); + new_devid_size = ddi_devid_sizeof(devid); + new_devid = kmem_alloc(new_devid_size, KM_SLEEP); + (void) bcopy(devid, new_devid, new_devid_size); + + rw_enter(nvf_lock(dcfd_handle), RW_WRITER); + + listp = nvf_list(dcfd_handle); + for (np = list_head(listp); np; np = list_next(listp, np)) { + if (strcmp(path, np->nvp_devpath) == 0) { + DEVID_DEBUG2((CE_CONT, + "register: %s path match\n", path)); + if (np->nvp_devid == NULL) { + replace: + np->nvp_devid = new_devid; + np->nvp_flags |= + NVP_DEVID_DIP | NVP_DEVID_REGISTERED; + np->nvp_dip = dip; + if (!devid_cache_write_disable) { + nvf_mark_dirty(dcfd_handle); + is_dirty = 1; + } + rw_exit(nvf_lock(dcfd_handle)); + kmem_free(new_nvp, sizeof (nvp_devid_t)); + kmem_free(path, pathlen); + goto exit; + } + if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) { + /* replace invalid devid */ + free_devid = np->nvp_devid; + goto replace; + } + /* + * We're registering an already-cached path + * Does the device's devid match the cache? + */ + if (ddi_devid_compare(devid, np->nvp_devid) != 0) { + DEVID_DEBUG((CE_CONT, "devid register: " + "devid %s does not match\n", path)); + /* + * Replace cached devid for this path + * with newly registered devid. A devid + * may map to multiple paths but one path + * should only map to one devid. + */ + devid_nvp_unlink_and_free(dcfd_handle, np); + np = NULL; + break; + } else { + DEVID_DEBUG2((CE_CONT, + "devid register: %s devid match\n", path)); + np->nvp_flags |= + NVP_DEVID_DIP | NVP_DEVID_REGISTERED; + np->nvp_dip = dip; + rw_exit(nvf_lock(dcfd_handle)); + kmem_free(new_nvp, sizeof (nvp_devid_t)); + kmem_free(path, pathlen); + kmem_free(new_devid, new_devid_size); + return (DDI_SUCCESS); + } + } + } + + /* + * Add newly registered devid to the cache + */ + ASSERT(np == NULL); + + new_nvp->nvp_devpath = path; + new_nvp->nvp_flags = NVP_DEVID_DIP | NVP_DEVID_REGISTERED; + new_nvp->nvp_dip = dip; + new_nvp->nvp_devid = new_devid; + + if (!devid_cache_write_disable) { + is_dirty = 1; + nvf_mark_dirty(dcfd_handle); + } + list_insert_tail(nvf_list(dcfd_handle), new_nvp); + + rw_exit(nvf_lock(dcfd_handle)); + +exit: + if (free_devid) + kmem_free(free_devid, ddi_devid_sizeof(free_devid)); + + if (is_dirty) + nvf_wake_daemon(); + + return (DDI_SUCCESS); +} + +/* + * Unregister a device's devid + * Called as an instance detachs + * Invalidate the devid's devinfo reference + * Devid-path remains in the cache + */ +void +e_devid_cache_unregister(dev_info_t *dip) +{ + nvp_devid_t *np; + list_t *listp; + + rw_enter(nvf_lock(dcfd_handle), RW_WRITER); + + listp = nvf_list(dcfd_handle); + for (np = list_head(listp); np; np = list_next(listp, np)) { + if (np->nvp_devid == NULL) + continue; + if ((np->nvp_flags & NVP_DEVID_DIP) && np->nvp_dip == dip) { + DEVID_LOG_UNREG((CE_CONT, + "unregister: %s\n", np->nvp_devpath)); + np->nvp_flags &= ~NVP_DEVID_DIP; + np->nvp_dip = NULL; + break; + } + } + + rw_exit(nvf_lock(dcfd_handle)); +} + +/* + * Purge devid cache of stale devids + */ +void +devid_cache_cleanup(void) +{ + nvp_devid_t *np, *next; + list_t *listp; + int is_dirty = 0; + + rw_enter(nvf_lock(dcfd_handle), RW_WRITER); + + listp = nvf_list(dcfd_handle); + for (np = list_head(listp); np; np = next) { + next = list_next(listp, np); + if (np->nvp_devid == NULL) + continue; + if ((np->nvp_flags & NVP_DEVID_REGISTERED) == 0) { + DEVID_LOG_REMOVE((CE_CONT, + "cleanup: %s\n", np->nvp_devpath)); + if (!devid_cache_write_disable) { + nvf_mark_dirty(dcfd_handle); + is_dirty = 0; + } + devid_nvp_unlink_and_free(dcfd_handle, np); + } + } + + rw_exit(nvf_lock(dcfd_handle)); + + if (is_dirty) + nvf_wake_daemon(); +} + + +/* + * Build a list of dev_t's for a device/devid + * + * The effect of this function is cumulative, adding dev_t's + * for the device to the list of all dev_t's for a given + * devid. + */ +static void +e_devid_minor_to_devlist( + dev_info_t *dip, + char *minor_name, + int ndevts_alloced, + int *devtcntp, + dev_t *devtsp) +{ + struct ddi_minor_data *dmdp; + int minor_all = 0; + int ndevts = *devtcntp; + + ASSERT(i_ddi_devi_attached(dip)); + + /* are we looking for a set of minor nodes? */ + if ((minor_name == DEVID_MINOR_NAME_ALL) || + (minor_name == DEVID_MINOR_NAME_ALL_CHR) || + (minor_name == DEVID_MINOR_NAME_ALL_BLK)) + minor_all = 1; + + mutex_enter(&(DEVI(dip)->devi_lock)); + + /* Find matching minor names */ + for (dmdp = DEVI(dip)->devi_minor; dmdp; dmdp = dmdp->next) { + + /* Skip non-minors, and non matching minor names */ + if ((dmdp->type != DDM_MINOR) || ((minor_all == 0) && + strcmp(dmdp->ddm_name, minor_name))) + continue; + + /* filter out minor_all mismatches */ + if (minor_all && + (((minor_name == DEVID_MINOR_NAME_ALL_CHR) && + (dmdp->ddm_spec_type != S_IFCHR)) || + ((minor_name == DEVID_MINOR_NAME_ALL_BLK) && + (dmdp->ddm_spec_type != S_IFBLK)))) + continue; + + if (ndevts < ndevts_alloced) + devtsp[ndevts] = dmdp->ddm_dev; + ndevts++; + } + + mutex_exit(&(DEVI(dip)->devi_lock)); + + *devtcntp = ndevts; +} + +/* + * Search for cached entries matching a devid + * Return two lists: + * a list of dev_info nodes, for those devices in the attached state + * a list of pathnames whose instances registered the given devid + * If the lists passed in are not sufficient to return the matching + * references, return the size of lists required. + * The dev_info nodes are returned with a hold that the caller must release. + */ +static int +e_devid_cache_devi_path_lists(ddi_devid_t devid, int retmax, + int *retndevis, dev_info_t **retdevis, int *retnpaths, char **retpaths) +{ + nvp_devid_t *np; + int ndevis, npaths; + dev_info_t *dip, *pdip; + int circ; + int maxdevis = 0; + int maxpaths = 0; + list_t *listp; + + ndevis = 0; + npaths = 0; + listp = nvf_list(dcfd_handle); + for (np = list_head(listp); np; np = list_next(listp, np)) { + if (np->nvp_devid == NULL) + continue; + if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) { + DEVIDERR((CE_CONT, + "find: invalid devid %s\n", + np->nvp_devpath)); + continue; + } + if (ddi_devid_compare(devid, np->nvp_devid) == 0) { + DEVID_DEBUG2((CE_CONT, + "find: devid match: %s 0x%x\n", + np->nvp_devpath, np->nvp_flags)); + DEVID_LOG_MATCH(("find", devid, np->nvp_devpath)); + DEVID_LOG_PATHS((CE_CONT, "%s\n", np->nvp_devpath)); + + /* + * Check if we have a cached devinfo reference for this + * devid. Place a hold on it to prevent detach + * Otherwise, use the path instead. + * Note: returns with a hold on each dev_info + * node in the list. + */ + dip = NULL; + if (np->nvp_flags & NVP_DEVID_DIP) { + pdip = ddi_get_parent(np->nvp_dip); + if (ndi_devi_tryenter(pdip, &circ)) { + dip = np->nvp_dip; + ndi_hold_devi(dip); + ndi_devi_exit(pdip, circ); + ASSERT(!DEVI_IS_ATTACHING(dip)); + ASSERT(!DEVI_IS_DETACHING(dip)); + } else { + DEVID_LOG_DETACH((CE_CONT, + "may be detaching: %s\n", + np->nvp_devpath)); + } + } + + if (dip) { + if (ndevis < retmax) { + retdevis[ndevis++] = dip; + } else { + ndi_rele_devi(dip); + } + maxdevis++; + } else { + if (npaths < retmax) + retpaths[npaths++] = np->nvp_devpath; + maxpaths++; + } + } + } + + *retndevis = ndevis; + *retnpaths = npaths; + return (maxdevis > maxpaths ? maxdevis : maxpaths); +} + + +/* + * Search the devid cache, returning dev_t list for all + * device paths mapping to the device identified by the + * given devid. + * + * Primary interface used by ddi_lyr_devid_to_devlist() + */ +int +e_devid_cache_to_devt_list(ddi_devid_t devid, char *minor_name, + int *retndevts, dev_t **retdevts) +{ + char *path, **paths; + int i, j, n; + dev_t *devts, *udevts; + dev_t tdevt; + int ndevts, undevts, ndevts_alloced; + dev_info_t *devi, **devis; + int ndevis, npaths, nalloced; + ddi_devid_t match_devid; + + DEVID_LOG_FIND(("find", devid, NULL)); + + ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS); + if (ddi_devid_valid(devid) != DDI_SUCCESS) { + DEVID_LOG_ERR(("invalid devid", devid, NULL)); + return (DDI_FAILURE); + } + + nalloced = 128; + + for (;;) { + paths = kmem_zalloc(nalloced * sizeof (char *), KM_SLEEP); + devis = kmem_zalloc(nalloced * sizeof (dev_info_t *), KM_SLEEP); + + rw_enter(nvf_lock(dcfd_handle), RW_READER); + n = e_devid_cache_devi_path_lists(devid, nalloced, + &ndevis, devis, &npaths, paths); + if (n <= nalloced) + break; + rw_exit(nvf_lock(dcfd_handle)); + for (i = 0; i < ndevis; i++) + ndi_rele_devi(devis[i]); + kmem_free(paths, nalloced * sizeof (char *)); + kmem_free(devis, nalloced * sizeof (dev_info_t *)); + nalloced = n + 128; + } + + for (i = 0; i < npaths; i++) { + path = i_ddi_strdup(paths[i], KM_SLEEP); + paths[i] = path; + } + rw_exit(nvf_lock(dcfd_handle)); + + if (ndevis == 0 && npaths == 0) { + DEVID_LOG_ERR(("no devid found", devid, NULL)); + kmem_free(paths, nalloced * sizeof (char *)); + kmem_free(devis, nalloced * sizeof (dev_info_t *)); + return (DDI_FAILURE); + } + + ndevts_alloced = 128; +restart: + ndevts = 0; + devts = kmem_alloc(ndevts_alloced * sizeof (dev_t), KM_SLEEP); + for (i = 0; i < ndevis; i++) { + ASSERT(!DEVI_IS_ATTACHING(devis[i])); + ASSERT(!DEVI_IS_DETACHING(devis[i])); + e_devid_minor_to_devlist(devis[i], minor_name, + ndevts_alloced, &ndevts, devts); + if (ndevts > ndevts_alloced) { + kmem_free(devts, ndevts_alloced * sizeof (dev_t)); + ndevts_alloced += 128; + goto restart; + } + } + for (i = 0; i < npaths; i++) { + DEVID_LOG_LOOKUP((CE_CONT, "lookup %s\n", paths[i])); + devi = e_ddi_hold_devi_by_path(paths[i], 0); + if (devi == NULL) { + DEVID_LOG_STALE(("stale device reference", + devid, paths[i])); + continue; + } + /* + * Verify the newly attached device registered a matching devid + */ + if (i_ddi_devi_get_devid(DDI_DEV_T_ANY, devi, + &match_devid) != DDI_SUCCESS) { + DEVIDERR((CE_CONT, + "%s: no devid registered on attach\n", + paths[i])); + ddi_release_devi(devi); + continue; + } + + if (ddi_devid_compare(devid, match_devid) != 0) { + DEVID_LOG_STALE(("new devid registered", + devid, paths[i])); + ddi_release_devi(devi); + ddi_devid_free(match_devid); + continue; + } + ddi_devid_free(match_devid); + + e_devid_minor_to_devlist(devi, minor_name, + ndevts_alloced, &ndevts, devts); + ddi_release_devi(devi); + if (ndevts > ndevts_alloced) { + kmem_free(devts, + ndevts_alloced * sizeof (dev_t)); + ndevts_alloced += 128; + goto restart; + } + } + + /* drop hold from e_devid_cache_devi_path_lists */ + for (i = 0; i < ndevis; i++) { + ndi_rele_devi(devis[i]); + } + for (i = 0; i < npaths; i++) { + kmem_free(paths[i], strlen(paths[i]) + 1); + } + kmem_free(paths, nalloced * sizeof (char *)); + kmem_free(devis, nalloced * sizeof (dev_info_t *)); + + if (ndevts == 0) { + DEVID_LOG_ERR(("no devid found", devid, NULL)); + kmem_free(devts, ndevts_alloced * sizeof (dev_t)); + return (DDI_FAILURE); + } + + /* + * Build the final list of sorted dev_t's with duplicates collapsed so + * returned results are consistent. This prevents implementation + * artifacts from causing unnecessary changes in SVM namespace. + */ + /* bubble sort */ + for (i = 0; i < (ndevts - 1); i++) { + for (j = 0; j < ((ndevts - 1) - i); j++) { + if (devts[j + 1] < devts[j]) { + tdevt = devts[j]; + devts[j] = devts[j + 1]; + devts[j + 1] = tdevt; + } + } + } + + /* determine number of unique values */ + for (undevts = ndevts, i = 1; i < ndevts; i++) { + if (devts[i - 1] == devts[i]) + undevts--; + } + + /* allocate unique */ + udevts = kmem_alloc(undevts * sizeof (dev_t), KM_SLEEP); + + /* copy unique */ + udevts[0] = devts[0]; + for (i = 1, j = 1; i < ndevts; i++) { + if (devts[i - 1] != devts[i]) + udevts[j++] = devts[i]; + } + ASSERT(j == undevts); + + kmem_free(devts, ndevts_alloced * sizeof (dev_t)); + + *retndevts = undevts; + *retdevts = udevts; + + return (DDI_SUCCESS); +} + +void +e_devid_cache_free_devt_list(int ndevts, dev_t *devt_list) +{ + kmem_free(devt_list, ndevts * sizeof (dev_t *)); +} + +#ifdef DEBUG +static void +devid_log(char *fmt, ddi_devid_t devid, char *path) +{ + char *devidstr = ddi_devid_str_encode(devid, NULL); + if (path) { + cmn_err(CE_CONT, "%s: %s %s\n", fmt, path, devidstr); + } else { + cmn_err(CE_CONT, "%s: %s\n", fmt, devidstr); + } + ddi_devid_str_free(devidstr); +} +#endif /* DEBUG */ diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile index df16a73f89..f3683c70c7 100644 --- a/usr/src/uts/common/sys/Makefile +++ b/usr/src/uts/common/sys/Makefile @@ -164,8 +164,9 @@ CHKHDRS= \ dditypes.h \ debug.h \ des.h \ - devctl.h \ - devctl_impl.h \ + devcache.h \ + devcache_impl.h \ + devid_cache.h \ devinfo_impl.h \ devops.h \ devpolicy.h \ diff --git a/usr/src/uts/common/sys/ddi_implfuncs.h b/usr/src/uts/common/sys/ddi_implfuncs.h index ad70123d76..5105c4ce18 100644 --- a/usr/src/uts/common/sys/ddi_implfuncs.h +++ b/usr/src/uts/common/sys/ddi_implfuncs.h @@ -254,19 +254,22 @@ struct dev_ops *ddi_hold_driver(major_t); void ddi_rele_driver(major_t); /* - * devid cache + * /etc/devices cache files management */ void i_ddi_devices_init(void); void i_ddi_read_devices_files(void); void i_ddi_clean_devices_files(void); +/* + * devid cache + */ +void devid_cache_init(void); +void devid_cache_read(void); +void devid_cache_cleanup(void); int i_ddi_devi_get_devid(dev_t, dev_info_t *, ddi_devid_t *); - int e_ddi_devid_discovery(ddi_devid_t); - int e_devid_cache_register(dev_info_t *, ddi_devid_t); void e_devid_cache_unregister(dev_info_t *); - int e_devid_cache_to_devt_list(ddi_devid_t, char *, int *, dev_t **); void e_devid_cache_free_devt_list(int, dev_t *); diff --git a/usr/src/uts/common/sys/devcache.h b/usr/src/uts/common/sys/devcache.h new file mode 100644 index 0000000000..eee6539a85 --- /dev/null +++ b/usr/src/uts/common/sys/devcache.h @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_DEVCACHE_H +#define _SYS_DEVCACHE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/list.h> + +#ifdef _KERNEL + +/* + * Handle reference to a registered file + */ +typedef struct __nvf_handle *nvf_handle_t; + +/* + * Registration descriptor for a cache file within /etc/devices + * + * path - cache file path path + * unpack_list - when reading, called to unpack nvlist + * pack_list - when writing, called to pack nvlist + * list_free - free data contained within list + * write_complete - called when write is completed + */ +typedef struct nvf_ops { + char *nvfr_cache_path; + int (*nvfr_unpack_nvlist)(nvf_handle_t, nvlist_t *, char *); + int (*nvfr_pack_list)(nvf_handle_t, nvlist_t **); + void (*nvfr_list_free)(nvf_handle_t); + void (*nvfr_write_complete)(nvf_handle_t); +} nvf_ops_t; + +/* + * Client interfaces + */ + +nvf_handle_t nvf_register_file(nvf_ops_t *); +int nvf_read_file(nvf_handle_t); +void nvf_wake_daemon(void); +void nvf_error(const char *, ...); +char *nvf_cache_name(nvf_handle_t); +krwlock_t *nvf_lock(nvf_handle_t); +list_t *nvf_list(nvf_handle_t); +void nvf_mark_dirty(nvf_handle_t); +int nvf_is_dirty(nvf_handle_t); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DEVCACHE_H */ diff --git a/usr/src/uts/common/sys/devcache_impl.h b/usr/src/uts/common/sys/devcache_impl.h new file mode 100644 index 0000000000..925da85c28 --- /dev/null +++ b/usr/src/uts/common/sys/devcache_impl.h @@ -0,0 +1,166 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_DEVCACHE_IMPL_H +#define _SYS_DEVCACHE_IMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/list.h> + +/* + * /etc/devices cache files format + * Leave some padding for easy extension in the future + */ + +#define NVPF_HDR_MAGIC 0xdeb1dcac +#define NVPF_HDR_VERSION 1 +#define NVPF_HDR_SIZE 128 + +typedef struct nvpacked_file_hdr { + union { + struct nvfp_hdr { + uint32_t magic; + int32_t version; + int64_t size; + uint16_t hdr_chksum; + uint16_t chksum; + } nvpf; + uchar_t nvpf_pad[NVPF_HDR_SIZE]; + } un; +} nvpf_hdr_t; + +#define nvpf_magic un.nvpf.magic +#define nvpf_version un.nvpf.version +#define nvpf_size un.nvpf.size +#define nvpf_hdr_chksum un.nvpf.hdr_chksum +#define nvpf_chksum un.nvpf.chksum + + +#ifdef _KERNEL + +/* + * Descriptor used for kernel-level file i/o + */ +typedef struct kfile { + struct vnode *kf_vp; + int kf_vnflags; + char *kf_fname; + offset_t kf_fpos; + int kf_state; +} kfile_t; + +/* + * File descriptor for files in the nvlist format + */ +typedef struct nvfiledesc nvfd_t; + +/* + * Descriptor for a file managed as a backing store for some + * kernel-generated device state such as device devids, + * vhci-to-phci mapping, etc. + * Each client can manage the data in any form convenient. + * providing functions to unpack (read) and pack (write) + * the data as an nvlist. + * + * Clients should not reference this structure directly + * but through the handle returned when registering. + */ +struct nvfiledesc { + nvf_ops_t *nvf_ops; /* client ops vectors */ + int nvf_flags; /* flags */ + list_t nvf_data_list; /* data list */ + krwlock_t nvf_lock; /* lock for data list */ + list_node_t nvf_link; /* link to next file desc */ +}; + +/* + * nvf_flags + */ +#define NVF_F_DIRTY 0x01 /* needs to be flushed */ +#define NVF_F_FLUSHING 0x02 /* in process of being flushed */ +#define NVF_F_ERROR 0x04 /* most recent flush failed */ +#define NVF_F_READONLY 0x10 /* file is read-only */ +#define NVF_F_CREATE_MSG 0x20 /* file not found on boot, emit msg */ +#define NVF_F_REBUILD_MSG 0x40 /* file was found corrupted, emit msg */ + +#define NVF_IS_DIRTY(nvfd) ((nvfd)->nvf_flags & NVF_F_DIRTY) +#define NVF_MARK_DIRTY(nvfd) ((nvfd)->nvf_flags |= NVF_F_DIRTY) +#define NVF_CLEAR_DIRTY(nvfd) ((nvfd)->nvf_flags &= ~NVF_F_DIRTY) + +#define NVF_IS_READONLY(nvfd) ((nvfd)->nvf_flags & NVF_F_READONLY) +#define NVF_MARK_READONLY(nvfd) ((nvfd)->nvf_flags |= NVF_F_READONLY) + +/* shorthand to client ops */ +#define nvf_cache_path nvf_ops->nvfr_cache_path +#define nvf_unpack_nvlist nvf_ops->nvfr_unpack_nvlist +#define nvf_pack_list nvf_ops->nvfr_pack_list +#define nvf_list_free nvf_ops->nvfr_list_free +#define nvf_write_complete nvf_ops->nvfr_write_complete + + +/* + * More thorough error reporting available both debug & + * non-debug kernels, but turned off by default. + */ +extern int kfio_report_error; /* kernel file i/o operations */ + +/* + * Suffix of temporary file for updates + */ +#define MAX_SUFFIX_LEN 4 +#define NEW_FILENAME_SUFFIX "new" + + +#ifdef DEBUG + +#define NVPDAEMON_DEBUG(args) { if (nvpdaemon_debug) cmn_err args; } +#define KFDEBUG(args) { if (kfio_debug) cmn_err args; } +#define KFDEBUG1(args) { if (kfio_debug > 1) cmn_err args; } +#define KFDEBUG2(args) { if (kfio_debug > 2) cmn_err args; } +#define KFDUMP(args) { if (kfio_debug > 2) args; } + +#else + +#define NVPDAEMON_DEBUG(args) +#define KFDEBUG(args) +#define KFDEBUG1(args) +#define KFDEBUG2(args) +#define KFDUMP(args) + +#endif /* DEBUG */ + + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DEVCACHE_IMPL_H */ diff --git a/usr/src/uts/common/sys/devctl_impl.h b/usr/src/uts/common/sys/devctl_impl.h deleted file mode 100644 index 09dab4cfe3..0000000000 --- a/usr/src/uts/common/sys/devctl_impl.h +++ /dev/null @@ -1,371 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#ifndef _SYS_DEVCTL_IMPL_H -#define _SYS_DEVCTL_IMPL_H - -#pragma ident "%Z%%M% %I% %E% SMI" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * /etc/devices/devid_cache - * Leave some padding for easy extension in the future - */ - -#define NVPF_HDR_MAGIC 0xdeb1dcac -#define NVPF_HDR_VERSION 1 -#define NVPF_HDR_SIZE 128 - -typedef struct nvpacked_file_hdr { - union { - struct nvfp_hdr { - uint32_t magic; - int32_t version; - int64_t size; - uint16_t hdr_chksum; - uint16_t chksum; - } nvpf; - uchar_t nvpf_pad[NVPF_HDR_SIZE]; - } un; -} nvpf_hdr_t; - -#define nvpf_magic un.nvpf.magic -#define nvpf_version un.nvpf.version -#define nvpf_size un.nvpf.size -#define nvpf_hdr_chksum un.nvpf.hdr_chksum -#define nvpf_chksum un.nvpf.chksum - -/* - * The top-level nvpair identifiers in the - * /etc/devices/devid_cache nvlist format - */ -#define DP_DEVID_ID "devid" -#define DP_DEVNAME_ID "devname" -#define DP_DEVNAME_NCACHE_ID "ncache" -#define DP_DEVNAME_NC_EXPIRECNT_ID "expire-counts" - - -#ifdef _KERNEL - -/* common header for all formats */ -typedef struct nvp_list { - struct nvp_list *nvp_next; - struct nvp_list *nvp_prev; -} nvp_list_t; - -#define NVPLIST(p) (((nvp_list_t *)(p))) - - -/* devid-specific list */ -typedef struct nvp_devid { - nvp_list_t nvp_list; /* must be first element */ - int nvp_flags; - char *nvp_devpath; - dev_info_t *nvp_dip; - ddi_devid_t nvp_devid; -} nvp_devid_t; - -#define NVP2DEVID(p) (((nvp_devid_t *)(p))) -#define NVP_DEVID_NEXT(p) (NVP2DEVID((NVPLIST(p))->nvp_next)) - -/* - * nvp_flags - devid - */ -#define NVP_DEVID_REGISTERED 0x01 /* devid registered on this boot */ -#define NVP_DEVID_DIP 0x02 /* devinfo valid for this devid */ - - -/* devname-specific list */ -typedef struct nvp_devname { - nvp_list_t nvp_list; /* must be first element */ - char **nvp_paths; - int *nvp_expirecnts; - int nvp_npaths; -} nvp_devname_t; - -#define NVP2DEVNAME(p) (((nvp_devname_t *)(p))) -#define NVP_DEVNAME_NEXT(p) (NVP2DEVNAME((NVPLIST(p))->nvp_next)) - -/* - * nvp_flags - devname - */ - - -/* - * Descriptor used for kernel-level file i/o - */ -typedef struct kfile { - struct vnode *kf_vp; - int kf_vnflags; - char *kf_fname; - offset_t kf_fpos; - int kf_state; -} kfile_t; - -/* - * File descriptor for files in the nvlist format - */ -typedef struct nvfiledesc { - char *nvf_name; - int (*nvf_nvp2nvl)(struct nvfiledesc *, nvlist_t **); - nvp_list_t *(*nvf_nvl2nvp)(nvlist_t *, char *name); - void (*nvf_nvp_free)(nvp_list_t *); - void (*nvf_write_complete)(struct nvfiledesc *); - int nvf_flags; - nvp_list_t *nvf_list; - nvp_list_t *nvf_tail; - krwlock_t nvf_lock; -} nvfd_t; - -#define NVF_DEVID_LIST(p) ((nvp_devid_t *)((p)->nvf_list)) -#define NVF_DEVID_TAIL(p) ((nvp_devid_t *)((p)->nvf_tail)) -#define NVF_DEVNAME_LIST(p) ((nvp_devname_t *)((p)->nvf_list)) -#define NVF_DEVNAME_TAIL(p) ((nvp_devname_t *)((p)->nvf_tail)) - -/* - * Discovery refers to the heroic effort made to discover a device which - * cannot be accessed. Discovery involves walking the entire device tree - * attaching all possible disk instances, to search for the device referenced - * by a devid. Obviously, full device discovery is something to be avoided - * where at all possible. Note that simply invoking devfsadm(1M) is - * equivalent to running full discovery at the devid cache level. - * - * Reasons why a disk may not be accessible: - * disk powered off - * disk removed or cable disconnected - * disk or adapter broken - * - * Note that discovery is not needed and cannot succeed in any of these - * cases. - * - * When discovery may succeed: - * Discovery will result in success when a device has been moved - * to a different address. Note that it's recommended that - * devfsadm(1M) be invoked (no arguments required) whenever a system's - * h/w configuration has been updated. Alternatively, a - * reconfiguration boot can be used to accomplish the same result. - * - * Note that discovery is not necessary to be able to correct an access - * failure for a device which was powered off. Assuming the cache has an - * entry for such a device, simply powering it on should permit the system - * to access it. If problems persist after powering it on, invoke devfsadm(1M). - * - * Tunables - * - * devid_discovery_boot (default 1) - * Number of times discovery will be attempted prior to mounting root. - * Must be at least once to recover from corrupted or missing - * devid cache backing store. Probably there's no reason to ever - * set this to greater than one as a missing device will remain - * unavailable no matter how often the system searches for it. - * - * devid_discovery_postboot (default 1) - * Number of times discovery will be attempted after mounting root. - * This must be performed at least once to discover any devices - * needed after root is mounted which may have been powered - * off and moved before booting. - * Setting this to a larger positive number will introduce - * some inconsistency in system operation. Searching for a device - * will take an indeterminate amount of time, sometimes slower, - * sometimes faster. In addition, the system will sometimes - * discover a newly powered on device, sometimes it won't. - * Use of this option is not therefore recommended. - * - * devid_discovery_postboot_always (default 0) - * Set to 1, the system will always attempt full discovery. - * - * devid_discovery_secs (default 0) - * Set to a positive value, the system will attempt full discovery - * but with a minimum delay between attempts. A device search - * within the period of time specified will result in failure. - * - * devid_cache_read_disable (default 0) - * Set to 1 to disable reading /etc/devices/devid_cache. - * Devid cache will continue to operate normally but - * at least one discovery attempt will be required. - * - * devid_cache_write_disable (default 0) - * Set to 1 to disable updates to /etc/devices/devid_cache. - * Any updates to the devid cache will not be preserved across a reboot. - * - * kfio_report_error (default 0) - * Set to 1 to enable some error messages related to low-level - * kernel file i/o operations. - * - * devid_report_error (default 0) - * Set to 1 to enable some error messages related to devid - * cache failures. - * - * nvpflush_delay (default 10) - * The number of seconds after data is marked dirty before the - * flush daemon is triggered to flush the data. A longer period - * of time permits more data updates per write. Note that - * every update resets the timer so no repository write will - * occur while data is being updated continuously. - * - * nvpdaemon_idle_time (default 60) - * The number of seconds the daemon will sleep idle before exiting. - * - */ -extern int devid_discovery_boot; -extern int devid_discovery_postboot; -extern int devid_discovery_postboot_always; -extern int devid_discovery_secs; - -extern int devid_cache_read_disable; -extern int devid_cache_write_disable; - -/* - * More thorough error reporting available both debug & - * non-debug kernels, but turned off by default. - */ -extern int kfio_report_error; /* kernel file i/o operations */ -extern int devid_report_error; /* devid cache operations */ - -/* - * Suffix of temporary file for updates - */ -#define MAX_SUFFIX_LEN 4 -#define NEW_FILENAME_SUFFIX "new" - -/* - * nvf_flags - */ -#define NVF_DIRTY 0x01 /* needs to be flushed */ -#define NVF_FLUSHING 0x02 /* in process of being flushed */ -#define NVF_ERROR 0x04 /* most recent flush failed */ -#define NVF_READONLY 0x10 /* file is read-only */ -#define NVF_CREATE_MSG 0x20 /* file not found on boot, emit msg */ -#define NVF_REBUILD_MSG 0x40 /* file was found corrupted, emit msg */ - -#define NVF_IS_DIRTY(nvfd) ((nvfd)->nvf_flags & NVF_DIRTY) -#define NVF_MARK_DIRTY(nvfd) ((nvfd)->nvf_flags |= NVF_DIRTY) -#define NVF_CLEAR_DIRTY(nvfd) ((nvfd)->nvf_flags &= ~NVF_DIRTY) - -#define NVF_IS_READONLY(nvfd) ((nvfd)->nvf_flags & NVF_READONLY) -#define NVF_MARK_READONLY(nvfd) ((nvfd)->nvf_flags |= NVF_READONLY) -#define NVF_CLR_READONLY(nvfd) ((nvfd)->nvf_flags &= ~NVF_READONLY) - -#ifdef DEBUG - -#define NVPDAEMON_DEBUG(args) { if (nvpdaemon_debug) cmn_err args; } -#define KFDEBUG(args) { if (kfio_debug) cmn_err args; } -#define KFDEBUG1(args) { if (kfio_debug > 1) cmn_err args; } -#define KFDEBUG2(args) { if (kfio_debug > 2) cmn_err args; } -#define KFDUMP(args) { if (kfio_debug > 2) args; } -#define DEVID_DEBUG(args) { if (devid_debug) cmn_err args; } -#define DEVID_DEBUG1(args) { if (devid_debug > 1) cmn_err args; } -#define DEVID_DEBUG2(args) { if (devid_debug > 2) cmn_err args; } -#define DEVID_DUMP(args) { if (devid_debug > 2) args; } -#define DEVID_LOG_REG(args) { if (devid_log_registers) devid_log args; } -#define DEVID_LOG_FIND(args) { if (devid_log_finds) devid_log args; } -#define DEVID_LOG_LOOKUP(args) { if (devid_log_lookups) cmn_err args; } -#define DEVID_LOG_MATCH(args) { if (devid_log_matches) devid_log args; } -#define DEVID_LOG_PATHS(args) { if (devid_log_paths) cmn_err args; } -#define DEVID_LOG_ERR(args) { if (devid_log_failures) devid_log args; } -#define DEVID_LOG_DISC(args) { if (devid_log_discovery) cmn_err args; } -#define DEVID_LOG_HOLD(args) { if (devid_log_hold) cmn_err args; } -#define DEVID_LOG_UNREG(args) { if (devid_log_unregisters) cmn_err args; } -#define DEVID_LOG_REMOVE(args) { if (devid_log_removes) cmn_err args; } -#define DEVID_LOG_STALE(args) { if (devid_log_stale) devid_log args; } -#define DEVID_LOG_DETACH(args) { if (devid_log_detaches) cmn_err args; } - - -#define NVP_DEVID_DEBUG_PATH(arg) { \ - if (nvp_devid_debug) \ - cmn_err(CE_CONT, "%s\n", arg); \ - } - -#define NVP_DEVID_DEBUG_DEVID(arg) { \ - if (nvp_devid_debug) { \ - char *ds = ddi_devid_str_encode(arg, NULL); \ - cmn_err(CE_CONT, "devid: %s\n", ds); \ - ddi_devid_str_free(ds); \ - } \ - } - -static void devid_log(char *, ddi_devid_t, char *); - -#else - -#define NVPDAEMON_DEBUG(args) -#define KFDEBUG(args) -#define KFDEBUG1(args) -#define KFDEBUG2(args) -#define KFDUMP(args) -#define DEVID_DEBUG(args) -#define DEVID_DEBUG1(args) -#define DEVID_DEBUG2(args) -#define DEVID_DUMP(args) -#define DEVID_LOG_REG(args) -#define DEVID_LOG_FIND(args) -#define DEVID_LOG_LOOKUP(args) -#define DEVID_LOG_MATCH(args) -#define DEVID_LOG_PATHS(args) -#define DEVID_LOG_ERR(args) -#define DEVID_LOG_DISC(args) -#define DEVID_LOG_HOLD(args) -#define DEVID_LOG_UNREG(args) -#define DEVID_LOG_REMOVE(args) -#define DEVID_LOG_STALE(args) -#define DEVID_LOG_DETACH(args) -#define NVP_DEVID_DEBUG_PATH(arg) -#define NVP_DEVID_DEBUG_DEVID(arg) - -#endif /* DEBUG */ - -#define KFIOERR(args) { if (kfio_report_error) cmn_err args; } -#define DEVIDERR(args) { if (devid_report_error) cmn_err args; } - - -/* extern data */ - -extern nvfd_t *sdevfd; - -extern int devid_cache_read_disable; -extern int devid_cache_write_disable; -extern int sdev_cache_read_disable; -extern int sdev_cache_write_disable; - -/* extern prototypes */ - -void i_ddi_read_devname_file(void); -void nvf_register_write_complete(nvfd_t *, void (*f)(nvfd_t *)); -void nvf_unregister_write_complete(nvfd_t *); -void nfd_nvp_link(nvfd_t *, nvp_list_t *); -void nfd_nvp_free_and_unlink(nvfd_t *, nvp_list_t *); -void wake_nvpflush_daemon(void); - - -#endif /* _KERNEL */ - -#ifdef __cplusplus -} -#endif - -#endif /* _SYS_DEVCTL_IMPL_H */ diff --git a/usr/src/uts/common/sys/devid_cache.h b/usr/src/uts/common/sys/devid_cache.h new file mode 100644 index 0000000000..7eb70f3e70 --- /dev/null +++ b/usr/src/uts/common/sys/devid_cache.h @@ -0,0 +1,153 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_DEVID_CACHE_H +#define _SYS_DEVID_CACHE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/list.h> + +/* + * The top-level nvpair identifiers in the + * /etc/devices/devid_cache nvlist format + */ +#define DP_DEVID_ID "devid" + +#ifdef _KERNEL + +/* devid-specific list element */ +typedef struct nvp_devid { + int nvp_flags; + char *nvp_devpath; + dev_info_t *nvp_dip; + ddi_devid_t nvp_devid; + list_node_t nvp_link; /* link to next element */ +} nvp_devid_t; + + +/* + * nvp_flags - devid + */ +#define NVP_DEVID_REGISTERED 0x01 /* devid registered on this boot */ +#define NVP_DEVID_DIP 0x02 /* devinfo valid for this devid */ + +/* + * tunables - see devid_cache.c for more details + */ +extern int devid_discovery_boot; +extern int devid_discovery_postboot; +extern int devid_discovery_postboot_always; +extern int devid_discovery_secs; + +extern int devid_cache_read_disable; +extern int devid_cache_write_disable; + +/* + * More thorough error reporting available both debug & + * non-debug kernels, but turned off by default. + */ +extern int devid_report_error; /* devid cache operations */ + + +/* + * function prototypes + */ +static int devid_cache_pack_list(nvf_handle_t, nvlist_t **); +static int devid_cache_unpack_nvlist(nvf_handle_t, nvlist_t *, char *); +static void devid_list_free(nvf_handle_t); + + +#ifdef DEBUG + +#define DEVID_DEBUG(args) { if (devid_debug) cmn_err args; } +#define DEVID_DEBUG1(args) { if (devid_debug > 1) cmn_err args; } +#define DEVID_DEBUG2(args) { if (devid_debug > 2) cmn_err args; } +#define DEVID_DUMP(args) { if (devid_debug > 2) args; } +#define DEVID_LOG_REG(args) { if (devid_log_registers) devid_log args; } +#define DEVID_LOG_FIND(args) { if (devid_log_finds) devid_log args; } +#define DEVID_LOG_LOOKUP(args) { if (devid_log_lookups) cmn_err args; } +#define DEVID_LOG_MATCH(args) { if (devid_log_matches) devid_log args; } +#define DEVID_LOG_PATHS(args) { if (devid_log_paths) cmn_err args; } +#define DEVID_LOG_ERR(args) { if (devid_log_failures) devid_log args; } +#define DEVID_LOG_DISC(args) { if (devid_log_discovery) cmn_err args; } +#define DEVID_LOG_HOLD(args) { if (devid_log_hold) cmn_err args; } +#define DEVID_LOG_UNREG(args) { if (devid_log_unregisters) cmn_err args; } +#define DEVID_LOG_REMOVE(args) { if (devid_log_removes) cmn_err args; } +#define DEVID_LOG_STALE(args) { if (devid_log_stale) devid_log args; } +#define DEVID_LOG_DETACH(args) { if (devid_log_detaches) cmn_err args; } + + +#define NVP_DEVID_DEBUG_PATH(arg) { \ + if (nvp_devid_debug) \ + cmn_err(CE_CONT, "%s\n", arg); \ + } + +#define NVP_DEVID_DEBUG_DEVID(arg) { \ + if (nvp_devid_debug) { \ + char *ds = ddi_devid_str_encode(arg, NULL); \ + cmn_err(CE_CONT, "devid: %s\n", ds); \ + ddi_devid_str_free(ds); \ + } \ + } + +static void devid_log(char *, ddi_devid_t, char *); + +#else + +#define DEVID_DEBUG(args) +#define DEVID_DEBUG1(args) +#define DEVID_DEBUG2(args) +#define DEVID_DUMP(args) +#define DEVID_LOG_REG(args) +#define DEVID_LOG_FIND(args) +#define DEVID_LOG_LOOKUP(args) +#define DEVID_LOG_MATCH(args) +#define DEVID_LOG_PATHS(args) +#define DEVID_LOG_ERR(args) +#define DEVID_LOG_DISC(args) +#define DEVID_LOG_HOLD(args) +#define DEVID_LOG_UNREG(args) +#define DEVID_LOG_REMOVE(args) +#define DEVID_LOG_STALE(args) +#define DEVID_LOG_DETACH(args) +#define NVP_DEVID_DEBUG_PATH(arg) +#define NVP_DEVID_DEBUG_DEVID(arg) + +#endif /* DEBUG */ + +#define DEVIDERR(args) { if (devid_report_error) cmn_err args; } + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DEVID_CACHE_H */ diff --git a/usr/src/uts/common/sys/fs/sdev_impl.h b/usr/src/uts/common/sys/fs/sdev_impl.h index e8f124566d..5b0a8b8426 100644 --- a/usr/src/uts/common/sys/fs/sdev_impl.h +++ b/usr/src/uts/common/sys/fs/sdev_impl.h @@ -520,6 +520,22 @@ typedef struct sdev_nc_node { #define SLF_REBUILT 0x02 /* reconfig performed during lookup attempt */ /* + * The nvlist name and nvpair identifiers in the + * /etc/devices/devname_cache nvlist format + */ +#define DP_DEVNAME_ID "devname" +#define DP_DEVNAME_NCACHE_ID "ncache" +#define DP_DEVNAME_NC_EXPIRECNT_ID "expire-counts" + +/* devname-cache list element */ +typedef struct nvp_devname { + char **nvp_paths; + int *nvp_expirecnts; + int nvp_npaths; + list_node_t nvp_link; +} nvp_devname_t; + +/* * name service globals and prototypes */ |