/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }