summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/damap.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/damap.c')
-rw-r--r--usr/src/uts/common/os/damap.c1258
1 files changed, 1258 insertions, 0 deletions
diff --git a/usr/src/uts/common/os/damap.c b/usr/src/uts/common/os/damap.c
new file mode 100644
index 0000000000..b25860e20e
--- /dev/null
+++ b/usr/src/uts/common/os/damap.c
@@ -0,0 +1,1258 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/note.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/sunndi.h>
+#include <sys/kstat.h>
+#include <sys/conf.h>
+#include <sys/ddi_timer.h>
+#include <sys/devctl.h>
+#include <sys/callb.h>
+#include <sys/sysevent.h>
+#include <sys/taskq.h>
+#include <sys/ddi.h>
+#include <sys/bitset.h>
+#include <sys/damap.h>
+#include <sys/damap_impl.h>
+
+#ifdef DEBUG
+static int damap_debug = 0;
+#endif /* DEBUG */
+
+static void dam_addrset_activate(dam_t *, bitset_t *);
+static void dam_addrset_release(dam_t *, bitset_t *);
+static void dam_activate_taskq(void *);
+static void dam_addr_stable_cb(void *);
+static void dam_set_stable_cb(void *);
+static void dam_sched_tmo(dam_t *, clock_t, void (*tmo_cb)());
+static void dam_add_report(dam_t *, dam_da_t *, id_t, int);
+static void dam_release(dam_t *, id_t);
+static void dam_release_report(dam_t *, id_t);
+static void dam_deactivate_addr(dam_t *, id_t);
+static id_t dam_get_addrid(dam_t *, char *);
+static int dam_kstat_create(dam_t *);
+static void dam_kstat_destroy(dam_t *);
+
+#define DAM_INCR_STAT(mapp, stat) \
+ if ((mapp)->dam_kstatsp) { \
+ struct dam_kstats *stp = (mapp)->dam_kstatsp->ks_data; \
+ stp->stat.value.ui32++; \
+ }
+
+#define DAM_SET_STAT(mapp, stat, val) \
+ if ((mapp)->dam_kstatsp) { \
+ struct dam_kstats *stp = (mapp)->dam_kstatsp->ks_data; \
+ stp->stat.value.ui32 = (val); \
+ }
+
+/*
+ * Create new device address map
+ *
+ * ident: map name (kstat)
+ * size: max # of map entries
+ * rptmode: type or mode of reporting
+ * stable_usec: # of quiescent microseconds before report/map is stable
+ *
+ * activate_arg: address provider activation-callout private
+ * activate_cb: address provider activation callback handler
+ * deactivate_cb: address provider deactivation callback handler
+ *
+ * config_arg: configuration-callout private
+ * config_cb: class configuration callout
+ * unconfig_cb: class unconfiguration callout
+ *
+ * damapp: pointer to map handle (return)
+ *
+ * Returns: DAM_SUCCESS
+ * DAM_EINVAL Invalid argument(s)
+ * DAM_FAILURE General failure
+ */
+int
+damap_create(char *ident, size_t size, damap_rptmode_t rptmode,
+ clock_t stable_usec,
+ void *activate_arg, damap_activate_cb_t activate_cb,
+ damap_deactivate_cb_t deactivate_cb,
+ void *config_arg, damap_configure_cb_t configure_cb,
+ damap_unconfig_cb_t unconfig_cb,
+ damap_t **damapp)
+{
+ dam_t *mapp;
+ void *softstate_p;
+
+ DTRACE_PROBE1(damap__create__entry, char *, ident);
+ if ((configure_cb == NULL) || (unconfig_cb == NULL))
+ return (DAM_EINVAL);
+
+ if (ddi_soft_state_init(&softstate_p, sizeof (dam_da_t), size) !=
+ DDI_SUCCESS)
+ return (DAM_FAILURE);
+
+ mapp = kmem_zalloc(sizeof (*mapp), KM_SLEEP);
+ if (ddi_strid_init(&mapp->dam_addr_hash, size) != DDI_SUCCESS) {
+ ddi_soft_state_fini(&softstate_p);
+ kmem_free(mapp, sizeof (*mapp));
+ return (DAM_FAILURE);
+ }
+
+ mapp->dam_da = softstate_p;
+ mapp->dam_stabletmo = drv_usectohz(stable_usec);
+ mapp->dam_size = size;
+ mapp->dam_high = 1;
+ mapp->dam_rptmode = rptmode;
+
+ mapp->dam_activate_arg = activate_arg;
+ mapp->dam_activate_cb = (activate_cb_t)activate_cb;
+ mapp->dam_deactivate_cb = (deactivate_cb_t)deactivate_cb;
+
+ mapp->dam_config_arg = config_arg;
+ mapp->dam_configure_cb = (configure_cb_t)configure_cb;
+ mapp->dam_unconfig_cb = (unconfig_cb_t)unconfig_cb;
+
+ if (ident)
+ mapp->dam_name = i_ddi_strdup(ident, KM_SLEEP);
+
+ bitset_init(&mapp->dam_active_set);
+ bitset_resize(&mapp->dam_active_set, size);
+ bitset_init(&mapp->dam_stable_set);
+ bitset_resize(&mapp->dam_stable_set, size);
+ bitset_init(&mapp->dam_report_set);
+ bitset_resize(&mapp->dam_report_set, size);
+ mutex_init(&mapp->dam_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&mapp->dam_cv, NULL, CV_DRIVER, NULL);
+ mapp->dam_taskqp = ddi_taskq_create(NULL, ident, 1, TASKQ_DEFAULTPRI,
+ 0);
+ *damapp = (damap_t *)mapp;
+ if (dam_kstat_create(mapp) != DDI_SUCCESS) {
+ damap_destroy((damap_t *)mapp);
+ return (DAM_FAILURE);
+ }
+
+ DTRACE_PROBE1(damap__create__exit, dam_t *, mapp);
+ return (DAM_SUCCESS);
+}
+
+/*
+ * Destroy device address map
+ *
+ * damapp: address map
+ *
+ * Returns: DAM_SUCCESS
+ * DAM_EINVAL Invalid argument(s)
+ * DAM_FAILURE General failure
+ */
+void
+damap_destroy(damap_t *damapp)
+{
+ int i;
+ dam_t *mapp = (dam_t *)damapp;
+
+ ASSERT(mapp);
+
+ DTRACE_PROBE2(damap__destroy__entry, dam_t *, mapp, char *,
+ mapp->dam_name);
+
+ DAM_FLAG_SET(mapp, DAM_DESTROYPEND);
+ (void) damap_sync(damapp);
+
+ /*
+ * cancel pending timeouts and kill off the taskq
+ */
+ dam_sched_tmo(mapp, 0, NULL);
+ ddi_taskq_wait(mapp->dam_taskqp);
+ ddi_taskq_destroy(mapp->dam_taskqp);
+
+ for (i = 1; i < mapp->dam_high; i++) {
+ if (ddi_get_soft_state(mapp->dam_da, i) == NULL)
+ continue;
+ if (DAM_IN_REPORT(mapp, i))
+ dam_release_report(mapp, i);
+ if (DAM_IS_STABLE(mapp, i))
+ dam_deactivate_addr(mapp, i);
+ ddi_strid_free(mapp->dam_addr_hash, i);
+ ddi_soft_state_free(mapp->dam_da, i);
+ }
+ ddi_strid_fini(&mapp->dam_addr_hash);
+ ddi_soft_state_fini(&mapp->dam_da);
+ bitset_fini(&mapp->dam_active_set);
+ bitset_fini(&mapp->dam_stable_set);
+ bitset_fini(&mapp->dam_report_set);
+ dam_kstat_destroy(mapp);
+ mutex_destroy(&mapp->dam_lock);
+ cv_destroy(&mapp->dam_cv);
+ if (mapp->dam_name)
+ kmem_free(mapp->dam_name, strlen(mapp->dam_name) + 1);
+ kmem_free(mapp, sizeof (*mapp));
+ DTRACE_PROBE(damap__destroy__exit);
+}
+
+/*
+ * Wait for map stability.
+ *
+ * damapp: address map
+ */
+int
+damap_sync(damap_t *damapp)
+{
+
+#define WAITFOR_FLAGS (DAM_SETADD | DAM_SPEND | MAP_LOCK)
+
+ dam_t *mapp = (dam_t *)damapp;
+ int none_active;
+
+ ASSERT(mapp);
+
+ DTRACE_PROBE1(damap__sync__entry, dam_t *, mapp);
+
+ mutex_enter(&mapp->dam_lock);
+ while ((mapp->dam_flags & WAITFOR_FLAGS) ||
+ (!bitset_is_null(&mapp->dam_report_set)) || (mapp->dam_tid != 0)) {
+ cv_wait(&mapp->dam_cv, &mapp->dam_lock);
+ }
+
+ none_active = bitset_is_null(&mapp->dam_active_set);
+
+ mutex_exit(&mapp->dam_lock);
+ DTRACE_PROBE2(damap__sync__exit, dam_t *, mapp, int, none_active);
+
+ return (none_active);
+}
+
+/*
+ * Get the name of a device address map
+ *
+ * damapp: address map
+ *
+ * Returns: name
+ */
+char *
+damap_name(damap_t *damapp)
+{
+ dam_t *mapp = (dam_t *)damapp;
+
+ return (mapp ? mapp->dam_name : "UNKNOWN_damap");
+}
+
+/*
+ * Report an address to per-address report
+ *
+ * damapp: address map handle
+ * address: address in ascii string representation
+ * rindx: index if address stabilizes
+ * nvl: optional nvlist of configuration-private data
+ * addr_priv: optional provider-private (passed to activate/deactivate cb)
+ *
+ * Returns: DAM_SUCCESS
+ * DAM_EINVAL Invalid argument(s)
+ * DAM_MAPFULL address map exhausted
+ */
+int
+damap_addr_add(damap_t *damapp, char *address, damap_id_t *ridx, nvlist_t *nvl,
+ void *addr_priv)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ id_t addrid;
+ dam_da_t *passp;
+
+ DTRACE_PROBE2(damap__addr__add__entry, dam_t *, mapp,
+ char *, address);
+ if (!mapp || !address || (mapp->dam_rptmode != DAMAP_REPORT_PERADDR) ||
+ (mapp->dam_flags & DAM_DESTROYPEND))
+ return (DAM_EINVAL);
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ if ((addrid = dam_get_addrid(mapp, address)) == 0) {
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return (DAM_MAPFULL);
+ }
+
+ passp = ddi_get_soft_state(mapp->dam_da, addrid);
+ ASSERT(passp != NULL);
+
+ /*
+ * If re-reporting the same address (add or remove) clear
+ * the existing report
+ */
+ if (DAM_IN_REPORT(mapp, addrid)) {
+ DAM_INCR_STAT(mapp, dam_rereport);
+ dam_release_report(mapp, addrid);
+ passp->da_jitter++;
+ }
+ passp->da_ppriv_rpt = addr_priv;
+ if (nvl)
+ (void) nvlist_dup(nvl, &passp->da_nvl_rpt, KM_SLEEP);
+
+ dam_add_report(mapp, passp, addrid, RPT_ADDR_ADD);
+ if (ridx != NULL)
+ *ridx = (damap_id_t)addrid;
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ DTRACE_PROBE3(damap__addr__add__exit, dam_t *, mapp, char *,
+ address, int, addrid);
+ return (DAM_SUCCESS);
+}
+
+/*
+ * Report removal of address from per-address report
+ *
+ * damapp: address map
+ * address: address in ascii string representation
+ *
+ * Returns: DAM_SUCCESS
+ * DAM_EINVAL Invalid argument(s)
+ * DAM_FAILURE General failure
+ */
+int
+damap_addr_del(damap_t *damapp, char *address)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ id_t addrid;
+ dam_da_t *passp;
+
+ DTRACE_PROBE2(damap__addr__del__entry, dam_t *, mapp,
+ char *, address);
+ if (!mapp || !address || (mapp->dam_rptmode != DAMAP_REPORT_PERADDR) ||
+ (mapp->dam_flags & DAM_DESTROYPEND))
+ return (DAM_EINVAL);
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ if (!(addrid = ddi_strid_str2id(mapp->dam_addr_hash, address))) {
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return (DAM_SUCCESS);
+ }
+ passp = ddi_get_soft_state(mapp->dam_da, addrid);
+ ASSERT(passp);
+ if (DAM_IN_REPORT(mapp, addrid)) {
+ DAM_INCR_STAT(mapp, dam_rereport);
+ dam_release_report(mapp, addrid);
+ passp->da_jitter++;
+ }
+ dam_add_report(mapp, passp, addrid, RPT_ADDR_DEL);
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ DTRACE_PROBE3(damap__addr__del__exit, dam_t *, mapp,
+ char *, address, int, addrid);
+ return (DAM_SUCCESS);
+}
+
+/*
+ * Initiate full-set report
+ *
+ * damapp: address map
+ *
+ * Returns: DAM_SUCCESS
+ * DAM_EINVAL Invalid argument(s)
+ */
+int
+damap_addrset_begin(damap_t *damapp)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ int i;
+
+ DTRACE_PROBE1(damap__addrset__begin__entry, dam_t *, mapp);
+
+ if ((mapp->dam_rptmode != DAMAP_REPORT_FULLSET) ||
+ (mapp->dam_flags & DAM_DESTROYPEND))
+ return (DAM_EINVAL);
+
+ DAM_LOCK(mapp, MAP_LOCK);
+ /*
+ * reset any pending reports
+ */
+ if (mapp->dam_flags & DAM_SETADD) {
+ /*
+ * cancel stabilization timeout
+ */
+ dam_sched_tmo(mapp, 0, NULL);
+ DAM_INCR_STAT(mapp, dam_rereport);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ DAM_LOCK(mapp, ADDR_LOCK);
+ for (i = 1; i < mapp->dam_high; i++) {
+ if (DAM_IN_REPORT(mapp, i))
+ dam_release_report(mapp, i);
+ }
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ DAM_LOCK(mapp, MAP_LOCK);
+ }
+ DAM_FLAG_SET(mapp, DAM_SETADD);
+ bitset_zero(&mapp->dam_report_set);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ DTRACE_PROBE(damap__addrset__begin__exit);
+ return (DAM_SUCCESS);
+}
+
+/*
+ * Report address to full-set report
+ *
+ * damapp: address map handle
+ * address: address in ascii string representation
+ * rindx: index if address stabilizes
+ * nvl: optional nvlist of configuration-private data
+ * addr_priv: optional provider-private data (passed to activate/release cb)
+ *
+ * Returns: DAM_SUCCESS
+ * DAM_EINVAL Invalid argument(s)
+ * DAM_MAPFULL address map exhausted
+ * DAM_FAILURE General failure
+ */
+int
+damap_addrset_add(damap_t *damapp, char *address, damap_id_t *ridx,
+ nvlist_t *nvl, void *addr_priv)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ id_t addrid;
+ dam_da_t *passp;
+
+ DTRACE_PROBE2(damap__addrset__add__entry, dam_t *, mapp,
+ char *, address);
+
+ if (!mapp || !address || (mapp->dam_rptmode != DAMAP_REPORT_FULLSET) ||
+ (mapp->dam_flags & DAM_DESTROYPEND))
+ return (DAM_EINVAL);
+
+ if (!(mapp->dam_flags & DAM_SETADD))
+ return (DAM_FAILURE);
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ if ((addrid = dam_get_addrid(mapp, address)) == 0) {
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return (DAM_MAPFULL);
+ }
+
+ passp = ddi_get_soft_state(mapp->dam_da, addrid);
+ ASSERT(passp);
+ if (DAM_IN_REPORT(mapp, addrid)) {
+ dam_release_report(mapp, addrid);
+ passp->da_jitter++;
+ }
+ passp->da_ppriv_rpt = addr_priv;
+ if (nvl)
+ (void) nvlist_dup(nvl, &passp->da_nvl_rpt, KM_SLEEP);
+ DAM_LOCK(mapp, MAP_LOCK);
+ bitset_add(&mapp->dam_report_set, addrid);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ if (ridx)
+ *ridx = (damap_id_t)addrid;
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ DTRACE_PROBE3(damap__addr__addset__exit, dam_t *, mapp, char *,
+ address, int, addrid);
+ return (DAM_SUCCESS);
+}
+
+/*
+ * Commit full-set report for stabilization
+ *
+ * damapp: address map handle
+ * flags: (currently 0)
+ *
+ * Returns: DAM_SUCCESS
+ * DAM_EINVAL Invalid argument(s)
+ * DAM_FAILURE General failure
+ */
+int
+damap_addrset_end(damap_t *damapp, int flags)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ int i;
+
+ DTRACE_PROBE1(damap__addrset__end__entry, dam_t *, mapp);
+
+ if (!mapp || (mapp->dam_rptmode != DAMAP_REPORT_FULLSET) ||
+ (mapp->dam_flags & DAM_DESTROYPEND))
+ return (DAM_EINVAL);
+
+ if (!(mapp->dam_flags & DAM_SETADD))
+ return (DAM_FAILURE);
+
+ if (flags & DAMAP_RESET) {
+ DAM_LOCK(mapp, MAP_LOCK);
+ dam_sched_tmo(mapp, 0, NULL);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ DAM_LOCK(mapp, ADDR_LOCK);
+ for (i = 1; i < mapp->dam_high; i++)
+ if (DAM_IN_REPORT(mapp, i))
+ dam_release_report(mapp, i);
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ } else {
+ mapp->dam_last_update = gethrtime();
+ DAM_LOCK(mapp, MAP_LOCK);
+ dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_set_stable_cb);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ }
+ DTRACE_PROBE(damap__addrset__end__exit);
+ return (DAM_SUCCESS);
+}
+
+/*
+ * Return nvlist registered with reported address
+ *
+ * damapp: address map handle
+ * aid: address ID
+ *
+ * Returns: nvlist_t * provider supplied via damap_addr{set}_add())
+ * NULL
+ */
+nvlist_t *
+damap_id2nvlist(damap_t *damapp, damap_id_t addrid)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ id_t aid = (id_t)addrid;
+ dam_da_t *pass;
+
+ if (ddi_strid_id2str(mapp->dam_addr_hash, aid)) {
+ if (pass = ddi_get_soft_state(mapp->dam_da, aid))
+ return (pass->da_nvl);
+ }
+ return (NULL);
+}
+
+/*
+ * Return address string
+ *
+ * damapp: address map handle
+ * aid: address ID
+ *
+ * Returns: char * Address string
+ * NULL
+ */
+char *
+damap_id2addr(damap_t *damapp, damap_id_t aid)
+{
+ dam_t *mapp = (dam_t *)damapp;
+
+ return (ddi_strid_id2str(mapp->dam_addr_hash, (id_t)aid));
+}
+
+/*
+ * Hold address reference in map
+ *
+ * damapp: address map handle
+ * aid: address ID
+ *
+ * Returns: DAM_SUCCESS
+ * DAM_FAILURE
+ */
+int
+damap_id_hold(damap_t *damapp, damap_id_t aid)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ dam_da_t *passp;
+
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ passp = ddi_get_soft_state(mapp->dam_da, (id_t)aid);
+ if (!passp) {
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return (DAM_FAILURE);
+ }
+ passp->da_ref++;
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return (DAM_SUCCESS);
+}
+
+/*
+ * Release address reference in map
+ *
+ * damapp: address map handle
+ * aid: address ID
+ */
+void
+damap_id_rele(damap_t *damapp, damap_id_t addrid)
+{
+ dam_t *mapp = (dam_t *)damapp;
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ dam_release(mapp, (id_t)addrid);
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+}
+
+/*
+ * Return current reference count on address reference in map
+ *
+ * damapp: address map handle
+ * aid: address ID
+ *
+ * Returns: DAM_SUCCESS
+ * DAM_FAILURE
+ */
+int
+damap_id_ref(damap_t *damapp, damap_id_t aid)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ dam_da_t *passp;
+ int ref = -1;
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ passp = ddi_get_soft_state(mapp->dam_da, (id_t)aid);
+ if (passp)
+ ref = passp->da_ref;
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return (ref);
+}
+
+/*
+ * Return next address ID in list
+ *
+ * damapp: address map handle
+ * damap_list: address ID list passed to config|unconfig
+ * returned by look by lookup_all
+ * last: last ID returned, 0 is start of list
+ *
+ * Returns: addrid Next ID from the list
+ * 0 End of the list
+ */
+damap_id_t
+damap_id_next(damap_t *damapp, damap_id_list_t damap_list, damap_id_t last)
+{
+ int i, start;
+ dam_t *mapp = (dam_t *)damapp;
+ bitset_t *dam_list = (bitset_t *)damap_list;
+
+ if (!mapp || !dam_list)
+ return ((damap_id_t)0);
+
+ start = (int)last + 1;
+ for (i = start; i < mapp->dam_high; i++)
+ if (bitset_in_set(dam_list, i))
+ return ((damap_id_t)i);
+ return ((damap_id_t)0);
+}
+
+/*
+ * Set config private data
+ *
+ * damapp: address map handle
+ * aid: address ID
+ * cfg_priv: configuration private data
+ *
+ */
+void
+damap_id_priv_set(damap_t *damapp, damap_id_t aid, void *cfg_priv)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ dam_da_t *passp;
+
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ passp = ddi_get_soft_state(mapp->dam_da, (id_t)aid);
+ if (!passp) {
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return;
+ }
+ passp->da_cfg_priv = cfg_priv;
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+}
+
+/*
+ * Get config private data
+ *
+ * damapp: address map handle
+ * aid: address ID
+ *
+ * Returns: configuration private data
+ */
+void *
+damap_id_priv_get(damap_t *damapp, damap_id_t aid)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ dam_da_t *passp;
+ void *rv;
+
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ passp = ddi_get_soft_state(mapp->dam_da, (id_t)aid);
+ if (!passp) {
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return (NULL);
+ }
+ rv = passp->da_cfg_priv;
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return (rv);
+}
+
+/*
+ * Lookup a single address in the active address map
+ *
+ * damapp: address map handle
+ * address: address string
+ *
+ * Returns: ID of active/stable address
+ * 0 Address not in stable set
+ *
+ * Future: Allow the caller to wait for stabilize before returning not found.
+ */
+damap_id_t
+damap_lookup(damap_t *damapp, char *address)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ id_t addrid = 0;
+ dam_da_t *passp = NULL;
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ addrid = ddi_strid_str2id(mapp->dam_addr_hash, address);
+ if (addrid) {
+ DAM_LOCK(mapp, MAP_LOCK);
+ if (DAM_IS_STABLE(mapp, addrid)) {
+ passp = ddi_get_soft_state(mapp->dam_da, addrid);
+ ASSERT(passp);
+ if (passp) {
+ passp->da_ref++;
+ } else {
+ addrid = 0;
+ }
+ } else {
+ addrid = 0;
+ }
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ }
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ return ((damap_id_t)addrid);
+}
+
+
+/*
+ * Return the list of stable addresses in the map
+ *
+ * damapp: address map handle
+ * id_listp: pointer to list of address IDs in stable map (returned)
+ *
+ * Returns: # of entries returned in alist
+ */
+int
+damap_lookup_all(damap_t *damapp, damap_id_list_t *id_listp)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ int mapsz = mapp->dam_size;
+ int n_ids, i;
+ bitset_t *bsp;
+ dam_da_t *passp;
+
+ bsp = kmem_alloc(sizeof (*bsp), KM_SLEEP);
+ bitset_init(bsp);
+ bitset_resize(bsp, mapsz);
+ DAM_LOCK(mapp, MAP_LOCK);
+ bitset_copy(&mapp->dam_active_set, bsp);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ DAM_LOCK(mapp, ADDR_LOCK);
+ for (n_ids = 0, i = 1; i < mapsz; i++) {
+ if (bitset_in_set(bsp, i)) {
+ passp = ddi_get_soft_state(mapp->dam_da, i);
+ ASSERT(passp);
+ if (passp) {
+ passp->da_ref++;
+ n_ids++;
+ }
+ }
+ }
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ if (n_ids) {
+ *id_listp = (damap_id_list_t)bsp;
+ return (n_ids);
+ } else {
+ *id_listp = (damap_id_list_t)NULL;
+ bitset_fini(bsp);
+ kmem_free(bsp, sizeof (*bsp));
+ return (0);
+ }
+}
+
+/*
+ * Release the address list returned by damap_lookup_all()
+ *
+ * mapp: address map handle
+ * id_list: list of address IDs returned in damap_lookup_all()
+ */
+void
+damap_id_list_rele(damap_t *damapp, damap_id_list_t id_list)
+{
+ dam_t *mapp = (dam_t *)damapp;
+ int i;
+
+ if (id_list == NULL)
+ return;
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ for (i = 1; i < mapp->dam_high; i++) {
+ if (bitset_in_set((bitset_t *)id_list, i))
+ (void) dam_release(mapp, i);
+ }
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ bitset_fini((bitset_t *)id_list);
+ kmem_free((void *)id_list, sizeof (bitset_t));
+}
+
+/*
+ * Activate a set of stabilized addresses
+ */
+static void
+dam_addrset_activate(dam_t *mapp, bitset_t *active_set)
+{
+ dam_da_t *passp;
+ char *addrstr;
+ int i;
+ uint32_t n_active = 0;
+
+ for (i = 1; i < mapp->dam_high; i++) {
+ if (bitset_in_set(&mapp->dam_active_set, i))
+ n_active++;
+ if (!bitset_in_set(active_set, i))
+ continue;
+ n_active++;
+ passp = ddi_get_soft_state(mapp->dam_da, i);
+ ASSERT(passp);
+ if (mapp->dam_activate_cb) {
+ addrstr = ddi_strid_id2str(mapp->dam_addr_hash, i);
+ (*mapp->dam_activate_cb)(
+ mapp->dam_activate_arg, addrstr, i,
+ &passp->da_ppriv_rpt);
+ }
+ DTRACE_PROBE2(damap__addrset__activate, dam_t *, mapp, int, i);
+ DAM_LOCK(mapp, MAP_LOCK);
+ bitset_add(&mapp->dam_active_set, i);
+ /*
+ * copy the reported nvlist and provider private data
+ */
+ passp->da_nvl = passp->da_nvl_rpt;
+ passp->da_ppriv = passp->da_ppriv_rpt;
+ passp->da_ppriv_rpt = NULL;
+ passp->da_nvl_rpt = NULL;
+ passp->da_last_stable = gethrtime();
+ passp->da_stable_cnt++;
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ DAM_SET_STAT(mapp, dam_numstable, n_active);
+ }
+}
+
+/*
+ * Release a set of stabilized addresses
+ */
+static void
+dam_addrset_release(dam_t *mapp, bitset_t *release_set)
+{
+ int i;
+
+ DAM_LOCK(mapp, ADDR_LOCK);
+ for (i = 1; i < mapp->dam_high; i++) {
+ if (bitset_in_set(release_set, i)) {
+ DTRACE_PROBE2(damap__addrset__release, dam_t *, mapp,
+ int, i);
+ DAM_LOCK(mapp, MAP_LOCK);
+ bitset_del(&mapp->dam_active_set, i);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ (void) dam_release(mapp, i);
+ }
+ }
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+}
+
+/*
+ * release a previously activated address
+ */
+static void
+dam_release(dam_t *mapp, id_t addrid)
+{
+ dam_da_t *passp;
+
+ DAM_ASSERT_LOCKED(mapp, ADDR_LOCK);
+ passp = ddi_get_soft_state(mapp->dam_da, addrid);
+ ASSERT(passp);
+
+ /*
+ * invoke the deactivation callback to notify
+ * this address is no longer active
+ */
+ dam_deactivate_addr(mapp, addrid);
+
+ /*
+ * allow pending reports for this address to stabilize
+ */
+ if (DAM_IN_REPORT(mapp, addrid))
+ return;
+
+ /*
+ * defer teardown until outstanding references are released
+ */
+ if (--passp->da_ref) {
+ passp->da_flags |= DA_RELE;
+ return;
+ }
+ ddi_strid_free(mapp->dam_addr_hash, addrid);
+ ddi_soft_state_free(mapp->dam_da, addrid);
+}
+
+/*
+ * process stabilized address reports
+ */
+static void
+dam_activate_taskq(void *arg)
+{
+ dam_t *mapp = (dam_t *)arg;
+ bitset_t delta;
+ bitset_t cfg;
+ bitset_t uncfg;
+ int has_cfg, has_uncfg;
+
+ bitset_init(&delta);
+ bitset_resize(&delta, mapp->dam_size);
+ bitset_init(&cfg);
+ bitset_resize(&cfg, mapp->dam_size);
+ bitset_init(&uncfg);
+ bitset_resize(&uncfg, mapp->dam_size);
+
+ DTRACE_PROBE1(damap__activate__taskq__entry, dam_t, mapp);
+ DAM_LOCK(mapp, MAP_LOCK);
+ if (!bitset_xor(&mapp->dam_active_set, &mapp->dam_stable_set,
+ &delta)) {
+ bitset_zero(&mapp->dam_stable_set);
+ DAM_FLAG_CLR(mapp, DAM_SPEND);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ bitset_fini(&uncfg);
+ bitset_fini(&cfg);
+ bitset_fini(&delta);
+ return;
+ }
+ has_cfg = bitset_and(&delta, &mapp->dam_stable_set, &cfg);
+ has_uncfg = bitset_and(&delta, &mapp->dam_active_set, &uncfg);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ if (has_cfg) {
+ dam_addrset_activate(mapp, &cfg);
+ (*mapp->dam_configure_cb)(mapp->dam_config_arg, mapp, &cfg);
+ }
+ if (has_uncfg) {
+ (*mapp->dam_unconfig_cb)(mapp->dam_config_arg, mapp, &uncfg);
+ dam_addrset_release(mapp, &uncfg);
+ }
+ DAM_LOCK(mapp, MAP_LOCK);
+ bitset_zero(&mapp->dam_stable_set);
+ DAM_FLAG_CLR(mapp, DAM_SPEND);
+ mapp->dam_last_stable = gethrtime();
+ mapp->dam_stable_cnt++;
+ DAM_INCR_STAT(mapp, dam_stable);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ bitset_fini(&uncfg);
+ bitset_fini(&cfg);
+ bitset_fini(&delta);
+ DTRACE_PROBE1(damap__activate__taskq__exit, dam_t, mapp);
+}
+
+/*
+ * per-address stabilization timeout
+ */
+static void
+dam_addr_stable_cb(void *arg)
+{
+ dam_t *mapp = (dam_t *)arg;
+ int i;
+ dam_da_t *passp;
+ int spend = 0;
+ int tpend = 0;
+ int64_t next_tmov = mapp->dam_stabletmo;
+ int64_t tmo_delta;
+ int64_t ts = lbolt64;
+
+ DTRACE_PROBE1(damap__addr__stable__cb__entry, dam_t *, mapp);
+ DAM_LOCK(mapp, MAP_LOCK);
+ if (mapp->dam_tid == 0) {
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ return;
+ }
+ mapp->dam_tid = 0;
+ /*
+ * If still under stabilization, reschedule timeout,
+ * else dispatch the task to activate & deactivate the stable
+ * set.
+ */
+ if (mapp->dam_flags & DAM_SPEND) {
+ DAM_INCR_STAT(mapp, dam_stable_blocked);
+ mapp->dam_stable_overrun++;
+ dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_addr_stable_cb);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ DTRACE_PROBE1(damap__addr__stable__cb__overrun,
+ dam_t *, mapp);
+ return;
+ }
+
+ bitset_copy(&mapp->dam_active_set, &mapp->dam_stable_set);
+ for (i = 1; i < mapp->dam_high; i++) {
+ if (!bitset_in_set(&mapp->dam_report_set, i))
+ continue;
+ /*
+ * Stabilize each address
+ */
+ passp = ddi_get_soft_state(mapp->dam_da, i);
+ ASSERT(passp);
+ if (!passp) {
+ cmn_err(CE_WARN, "Clearing report no softstate %d", i);
+ bitset_del(&mapp->dam_report_set, i);
+ continue;
+ }
+
+ /* report has stabilized */
+ if (passp->da_deadline <= ts) {
+ bitset_del(&mapp->dam_report_set, i);
+ if (passp->da_flags & DA_RELE) {
+ DTRACE_PROBE2(damap__addr__stable__del,
+ dam_t *, mapp, int, i);
+ bitset_del(&mapp->dam_stable_set, i);
+ } else {
+ DTRACE_PROBE2(damap__addr__stable__add,
+ dam_t *, mapp, int, i);
+ bitset_add(&mapp->dam_stable_set, i);
+ }
+ spend++;
+ continue;
+ }
+
+ /*
+ * not stabilized, determine next (future) map timeout
+ */
+ tpend++;
+ tmo_delta = passp->da_deadline - ts;
+ if (tmo_delta < next_tmov)
+ next_tmov = tmo_delta;
+ }
+
+ /*
+ * schedule taskq activation of stabilized reports
+ */
+ if (spend) {
+ if (ddi_taskq_dispatch(mapp->dam_taskqp, dam_activate_taskq,
+ mapp, DDI_NOSLEEP) == DDI_SUCCESS) {
+ DAM_FLAG_SET(mapp, DAM_SPEND);
+ } else
+ tpend++;
+ }
+
+ /*
+ * schedule timeout to handle future stabalization of active reports
+ */
+ if (tpend)
+ dam_sched_tmo(mapp, (clock_t)next_tmov, dam_addr_stable_cb);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ DTRACE_PROBE1(damap__addr__stable__cb__exit, dam_t *, mapp);
+}
+
+/*
+ * fullset stabilization timeout
+ */
+static void
+dam_set_stable_cb(void *arg)
+{
+ dam_t *mapp = (dam_t *)arg;
+
+ DTRACE_PROBE1(damap__set__stable__cb__enter, dam_t *, mapp);
+
+ DAM_LOCK(mapp, MAP_LOCK);
+ if (mapp->dam_tid == 0) {
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ return;
+ }
+ mapp->dam_tid = 0;
+
+ /*
+ * If still under stabilization, reschedule timeout,
+ * else dispatch the task to activate & deactivate the stable
+ * set.
+ */
+ if (mapp->dam_flags & DAM_SPEND) {
+ DAM_INCR_STAT(mapp, dam_stable_blocked);
+ mapp->dam_stable_overrun++;
+ dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_set_stable_cb);
+ DTRACE_PROBE1(damap__set__stable__cb__overrun,
+ dam_t *, mapp);
+ } else if (ddi_taskq_dispatch(mapp->dam_taskqp, dam_activate_taskq,
+ mapp, DDI_NOSLEEP) == DDI_FAILURE) {
+ dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_set_stable_cb);
+ } else {
+ bitset_copy(&mapp->dam_report_set, &mapp->dam_stable_set);
+ bitset_zero(&mapp->dam_report_set);
+ DAM_FLAG_CLR(mapp, DAM_SETADD);
+ DAM_FLAG_SET(mapp, DAM_SPEND);
+ }
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ DTRACE_PROBE1(damap__set__stable__cb__exit, dam_t *, mapp);
+}
+
+/*
+ * reschedule map timeout 'tmo_ms' ticks
+ */
+static void
+dam_sched_tmo(dam_t *mapp, clock_t tmo_ms, void (*tmo_cb)())
+{
+ timeout_id_t tid;
+
+ if ((tid = mapp->dam_tid) != 0) {
+ mapp->dam_tid = 0;
+ DAM_UNLOCK(mapp, MAP_LOCK);
+ (void) untimeout(tid);
+ DAM_LOCK(mapp, MAP_LOCK);
+ }
+
+ if (tmo_cb && (tmo_ms != 0))
+ mapp->dam_tid = timeout(tmo_cb, mapp, tmo_ms);
+}
+
+/*
+ * record report addition or removal of an address
+ */
+static void
+dam_add_report(dam_t *mapp, dam_da_t *passp, id_t addrid, int report)
+{
+ ASSERT(!DAM_IN_REPORT(mapp, addrid));
+ passp->da_last_report = gethrtime();
+ mapp->dam_last_update = gethrtime();
+ passp->da_report_cnt++;
+ passp->da_deadline = lbolt64 + mapp->dam_stabletmo;
+ if (report == RPT_ADDR_DEL)
+ passp->da_flags |= DA_RELE;
+ else if (report == RPT_ADDR_ADD)
+ passp->da_flags &= ~DA_RELE;
+ DAM_LOCK(mapp, MAP_LOCK);
+ bitset_add(&mapp->dam_report_set, addrid);
+ dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_addr_stable_cb);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+
+}
+
+/*
+ * release an address report
+ */
+static void
+dam_release_report(dam_t *mapp, id_t addrid)
+{
+ dam_da_t *passp;
+
+ passp = ddi_get_soft_state(mapp->dam_da, addrid);
+ ASSERT(passp);
+ passp->da_ppriv_rpt = NULL;
+ if (passp->da_nvl_rpt)
+ nvlist_free(passp->da_nvl_rpt);
+ passp->da_nvl_rpt = NULL;
+ DAM_LOCK(mapp, MAP_LOCK);
+ bitset_del(&mapp->dam_report_set, addrid);
+ DAM_UNLOCK(mapp, MAP_LOCK);
+}
+
+/*
+ * deactivate a previously stable address
+ */
+static void
+dam_deactivate_addr(dam_t *mapp, id_t addrid)
+{
+ dam_da_t *passp;
+
+ passp = ddi_get_soft_state(mapp->dam_da, addrid);
+ ASSERT(passp);
+ if (passp == NULL)
+ return;
+ DAM_UNLOCK(mapp, ADDR_LOCK);
+ if (mapp->dam_deactivate_cb)
+ (*mapp->dam_deactivate_cb)(
+ mapp->dam_activate_arg,
+ ddi_strid_id2str(mapp->dam_addr_hash,
+ addrid), addrid, passp->da_ppriv);
+ DAM_LOCK(mapp, ADDR_LOCK);
+ passp->da_ppriv = NULL;
+ if (passp->da_nvl)
+ nvlist_free(passp->da_nvl);
+ passp->da_nvl = NULL;
+}
+
+/*
+ * return the map ID of an address
+ */
+static id_t
+dam_get_addrid(dam_t *mapp, char *address)
+{
+ damap_id_t addrid;
+ dam_da_t *passp;
+
+ if ((addrid = ddi_strid_str2id(mapp->dam_addr_hash, address)) == 0) {
+ if ((addrid = ddi_strid_fixed_alloc(mapp->dam_addr_hash,
+ address)) == (damap_id_t)0) {
+ return (0);
+ }
+ if (ddi_soft_state_zalloc(mapp->dam_da, addrid) !=
+ DDI_SUCCESS) {
+ ddi_strid_free(mapp->dam_addr_hash, addrid);
+ return (0);
+ }
+ if (addrid >= mapp->dam_high)
+ mapp->dam_high = addrid + 1;
+ }
+ passp = ddi_get_soft_state(mapp->dam_da, addrid);
+ if (passp == NULL)
+ return (0);
+ passp->da_ref++;
+ if (passp->da_addr == NULL)
+ passp->da_addr = ddi_strid_id2str(
+ mapp->dam_addr_hash, addrid); /* for mdb */
+ return (addrid);
+}
+
+/*
+ * create and install map statistics
+ */
+static int
+dam_kstat_create(dam_t *mapp)
+{
+ kstat_t *mapsp;
+ struct dam_kstats *statsp;
+
+ mapsp = kstat_create("dam", 0, mapp->dam_name, "damap",
+ KSTAT_TYPE_NAMED,
+ sizeof (struct dam_kstats) / sizeof (kstat_named_t), 0);
+ if (mapsp == NULL) {
+ return (DDI_FAILURE);
+ }
+
+ statsp = (struct dam_kstats *)mapsp->ks_data;
+ kstat_named_init(&statsp->dam_stable, "stable cycles",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&statsp->dam_stable_blocked,
+ "stable cycle overrun", KSTAT_DATA_UINT32);
+ kstat_named_init(&statsp->dam_rereport,
+ "restarted reports", KSTAT_DATA_UINT32);
+ kstat_named_init(&statsp->dam_numstable,
+ "# of stable map entries", KSTAT_DATA_UINT32);
+ kstat_install(mapsp);
+ mapp->dam_kstatsp = mapsp;
+ return (DDI_SUCCESS);
+}
+
+/*
+ * destroy map stats
+ */
+static void
+dam_kstat_destroy(dam_t *mapp)
+{
+
+ kstat_delete(mapp->dam_kstatsp);
+}