summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjg <none@none>2006-09-25 08:38:36 -0700
committerjg <none@none>2006-09-25 08:38:36 -0700
commit83c4dfe9546fd839e7a52bca7e9920da918f916e (patch)
treeb20d087d1ba86d49a6059c9bf59daeda3cc69db0
parentf92daba9919b6e68875ccdc9a5532cadf37959f1 (diff)
downloadillumos-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_com5
-rw-r--r--usr/src/uts/common/Makefile.files3
-rw-r--r--usr/src/uts/common/fs/dev/sdev_ncache.c265
-rw-r--r--usr/src/uts/common/os/devcache.c1146
-rw-r--r--usr/src/uts/common/os/devctl.c1943
-rw-r--r--usr/src/uts/common/os/devid_cache.c998
-rw-r--r--usr/src/uts/common/sys/Makefile5
-rw-r--r--usr/src/uts/common/sys/ddi_implfuncs.h11
-rw-r--r--usr/src/uts/common/sys/devcache.h81
-rw-r--r--usr/src/uts/common/sys/devcache_impl.h166
-rw-r--r--usr/src/uts/common/sys/devctl_impl.h371
-rw-r--r--usr/src/uts/common/sys/devid_cache.h153
-rw-r--r--usr/src/uts/common/sys/fs/sdev_impl.h16
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
*/