/* * 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2023 Racktop Systems, Inc. */ #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 */ extern taskq_t *system_taskq; static void dam_addrset_activate(dam_t *, bitset_t *); static void dam_addrset_deactivate(dam_t *, bitset_t *); static void dam_stabilize_map(void *); static void dam_addr_stable_cb(void *); static void dam_addrset_stable_cb(void *); static void dam_sched_timeout(void (*timeout_cb)(), dam_t *, clock_t); static void dam_addr_report(dam_t *, dam_da_t *, id_t, int); static void dam_addr_release(dam_t *, id_t); static void dam_addr_report_release(dam_t *, id_t); static void dam_addr_deactivate(dam_t *, id_t); static void dam_deact_cleanup(dam_t *, id_t, char *, damap_deact_rsn_t); static id_t dam_get_addrid(dam_t *, char *); static int dam_kstat_create(dam_t *); static int dam_map_alloc(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); \ } /* * increase damap size by 64 entries at a time */ #define DAM_SIZE_BUMP 64 int damap_taskq_dispatch_retry_usec = 1000; /* * config/unconfig taskq data */ typedef struct { dam_t *tqd_mapp; id_t tqd_id; } cfg_tqd_t; extern pri_t maxclsyspri; /* * Create new device address map * * name: map name (kstat unique) * size: max # of map entries * mode: style of address reports: per-address or fullset * 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 *name, damap_rptmode_t mode, int map_opts, int 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; if (configure_cb == NULL || unconfig_cb == NULL || name == NULL) return (DAM_EINVAL); mapp = kmem_zalloc(sizeof (*mapp), KM_SLEEP); mapp->dam_options = map_opts; mapp->dam_stable_ticks = drv_usectohz(stable_usec); mapp->dam_size = 0; mapp->dam_rptmode = mode; 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; mapp->dam_name = i_ddi_strdup(name, KM_SLEEP); mutex_init(&mapp->dam_lock, NULL, MUTEX_DRIVER, NULL); cv_init(&mapp->dam_sync_cv, NULL, CV_DRIVER, NULL); bitset_init(&mapp->dam_active_set); bitset_init(&mapp->dam_stable_set); bitset_init(&mapp->dam_report_set); *damapp = (damap_t *)mapp; DTRACE_PROBE5(damap__create, char *, mapp->dam_name, damap_t *, mapp, damap_rptmode_t, mode, int, map_opts, int, stable_usec); return (DAM_SUCCESS); } /* * Allocate backing resources * * DAMs are lightly backed on create - major allocations occur * at the time a report is made to the map, and are extended on * a demand basis. */ static int dam_map_alloc(dam_t *mapp) { void *softstate_p; ASSERT(mutex_owned(&mapp->dam_lock)); if (mapp->dam_flags & DAM_DESTROYPEND) return (DAM_FAILURE); /* * dam_high > 0 signals map allocation complete */ if (mapp->dam_high) return (DAM_SUCCESS); mapp->dam_size = DAM_SIZE_BUMP; if (ddi_soft_state_init(&softstate_p, sizeof (dam_da_t), mapp->dam_size) != DDI_SUCCESS) return (DAM_FAILURE); if (ddi_strid_init(&mapp->dam_addr_hash, mapp->dam_size) != DDI_SUCCESS) { ddi_soft_state_fini(&softstate_p); return (DAM_FAILURE); } if (dam_kstat_create(mapp) != DDI_SUCCESS) { ddi_soft_state_fini(&softstate_p); ddi_strid_fini(&mapp->dam_addr_hash); return (DAM_FAILURE); } mapp->dam_da = softstate_p; mapp->dam_high = 1; bitset_resize(&mapp->dam_active_set, mapp->dam_size); bitset_resize(&mapp->dam_stable_set, mapp->dam_size); bitset_resize(&mapp->dam_report_set, mapp->dam_size); return (DAM_SUCCESS); } /* * Destroy 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, char *, mapp->dam_name, damap_t *, mapp); mutex_enter(&mapp->dam_lock); /* * prevent new reports from being added to the map */ mapp->dam_flags |= DAM_DESTROYPEND; if (mapp->dam_high) { mutex_exit(&mapp->dam_lock); /* * wait for outstanding reports to stabilize and cancel * the timer for this map */ (void) damap_sync(damapp, 0); mutex_enter(&mapp->dam_lock); dam_sched_timeout(NULL, mapp, 0); /* * map is at full stop * release the contents of the map, invoking the * detactivation protocol as addresses are released */ mutex_exit(&mapp->dam_lock); for (i = 1; i < mapp->dam_high; i++) { if (ddi_get_soft_state(mapp->dam_da, i) == NULL) continue; ASSERT(DAM_IN_REPORT(mapp, i) == 0); if (DAM_IS_STABLE(mapp, i)) { dam_addr_deactivate(mapp, i); } else { 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); kstat_delete(mapp->dam_kstatsp); } else mutex_exit(&mapp->dam_lock); bitset_fini(&mapp->dam_active_set); bitset_fini(&mapp->dam_stable_set); bitset_fini(&mapp->dam_report_set); mutex_destroy(&mapp->dam_lock); cv_destroy(&mapp->dam_sync_cv); if (mapp->dam_name) kmem_free(mapp->dam_name, strlen(mapp->dam_name) + 1); kmem_free(mapp, sizeof (*mapp)); } /* * Wait for map stability. If sync was successfull then return 1. * If called with a non-zero sync_usec, then a return value of 0 means a * timeout occurred prior to sync completion. NOTE: if sync_usec is * non-zero, it should be much longer than dam_stable_ticks. * * damapp: address map * sync_usec: micorseconds until we give up on sync completion. */ #define WAITFOR_FLAGS (DAM_SETADD | DAM_SPEND) int damap_sync(damap_t *damapp, int sync_usec) { dam_t *mapp = (dam_t *)damapp; int rv; ASSERT(mapp); DTRACE_PROBE3(damap__map__sync__start, char *, mapp->dam_name, dam_t *, mapp, int, sync_usec); /* * Block when waiting for * a) stabilization pending or a fullset update pending * b) the report set to finalize (bitset is null) * c) any scheduled timeouts to fire */ rv = 1; /* return synced */ mutex_enter(&mapp->dam_lock); again: while ((mapp->dam_flags & WAITFOR_FLAGS) || (!bitset_is_null(&mapp->dam_report_set)) || (mapp->dam_tid != 0)) { DTRACE_PROBE2(damap__map__sync__waiting, char *, mapp->dam_name, dam_t *, mapp); /* Wait for condition relayed via timeout */ if (sync_usec) { if (cv_reltimedwait(&mapp->dam_sync_cv, &mapp->dam_lock, drv_usectohz(sync_usec), TR_MICROSEC) == -1) { mapp->dam_sync_to_cnt++; rv = 0; /* return timeout */ break; } } else cv_wait(&mapp->dam_sync_cv, &mapp->dam_lock); } if (rv) { /* * Delay one stabilization time after the apparent sync above * and verify accuracy - resync if not accurate. */ (void) cv_reltimedwait(&mapp->dam_sync_cv, &mapp->dam_lock, mapp->dam_stable_ticks, TR_MICROSEC); if (rv && ((mapp->dam_flags & WAITFOR_FLAGS) || (!bitset_is_null(&mapp->dam_report_set)) || (mapp->dam_tid != 0))) goto again; } mutex_exit(&mapp->dam_lock); DTRACE_PROBE3(damap__map__sync__end, char *, mapp->dam_name, dam_t *, mapp, int, rv); return (rv); } /* * Return 1 if active set is empty */ int damap_is_empty(damap_t *damapp) { dam_t *mapp = (dam_t *)damapp; int rv; mutex_enter(&mapp->dam_lock); rv = bitset_is_null(&mapp->dam_active_set); mutex_exit(&mapp->dam_lock); return (rv); } /* * 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"); } /* * Get the current size of the device address map * * damapp: address map * * Returns: size */ int damap_size(damap_t *damapp) { dam_t *mapp = (dam_t *)damapp; return (mapp->dam_size); } /* * Report an address to per-address report * * damapp: address map handle * address: address in ascii string representation * addridp: address ID * 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 *addridp, nvlist_t *nvl, void *addr_priv) { dam_t *mapp = (dam_t *)damapp; id_t addrid; dam_da_t *passp; if (!mapp || !address || (mapp->dam_rptmode != DAMAP_REPORT_PERADDR)) return (DAM_EINVAL); DTRACE_PROBE3(damap__addr__add, char *, mapp->dam_name, dam_t *, mapp, char *, address); mutex_enter(&mapp->dam_lock); if ((dam_map_alloc(mapp) != DAM_SUCCESS) || ((addrid = dam_get_addrid(mapp, address)) == 0)) { mutex_exit(&mapp->dam_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)) { DTRACE_PROBE3(damap__addr__add__jitter, char *, mapp->dam_name, dam_t *, mapp, char *, address); DAM_INCR_STAT(mapp, dam_jitter); dam_addr_report_release(mapp, addrid); passp->da_jitter++; } passp->da_ppriv = addr_priv; if (nvl) (void) nvlist_dup(nvl, &passp->da_nvl_rpt, KM_SLEEP); dam_addr_report(mapp, passp, addrid, RPT_ADDR_ADD); if (addridp != NULL) *addridp = (damap_id_t)addrid; mutex_exit(&mapp->dam_lock); 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; if (!mapp || !address || (mapp->dam_rptmode != DAMAP_REPORT_PERADDR)) return (DAM_EINVAL); DTRACE_PROBE3(damap__addr__del, char *, mapp->dam_name, dam_t *, mapp, char *, address); mutex_enter(&mapp->dam_lock); if (dam_map_alloc(mapp) != DAM_SUCCESS) { mutex_exit(&mapp->dam_lock); return (DAM_MAPFULL); } /* * if reporting the removal of an address which is not in the map * return success */ if (!(addrid = ddi_strid_str2id(mapp->dam_addr_hash, address))) { mutex_exit(&mapp->dam_lock); return (DAM_SUCCESS); } passp = ddi_get_soft_state(mapp->dam_da, addrid); ASSERT(passp); if (DAM_IN_REPORT(mapp, addrid)) { DTRACE_PROBE3(damap__addr__del__jitter, char *, mapp->dam_name, dam_t *, mapp, char *, address); DAM_INCR_STAT(mapp, dam_jitter); dam_addr_report_release(mapp, addrid); passp->da_jitter++; } dam_addr_report(mapp, passp, addrid, RPT_ADDR_DEL); mutex_exit(&mapp->dam_lock); return (DAM_SUCCESS); } static int damap_addrset_flush_locked(damap_t *damapp) { dam_t *mapp = (dam_t *)damapp; int idx; ASSERT(mapp); ASSERT(mutex_owned(&mapp->dam_lock)); if (mapp->dam_rptmode != DAMAP_REPORT_FULLSET) { return (DAM_EINVAL); } DTRACE_PROBE2(damap__addrset__flush__locked__enter, char *, mapp->dam_name, dam_t *, mapp); if (mapp->dam_flags & DAM_SETADD) { DTRACE_PROBE2(damap__addrset__flush__locked__reset, char *, mapp->dam_name, dam_t *, mapp); /* * cancel stabilization timeout */ dam_sched_timeout(NULL, mapp, 0); DAM_INCR_STAT(mapp, dam_jitter); /* * clear pending reports */ for (idx = 1; idx < mapp->dam_high; idx++) { if (DAM_IN_REPORT(mapp, idx)) { dam_addr_report_release(mapp, idx); } } bitset_zero(&mapp->dam_report_set); mapp->dam_flags &= ~DAM_SETADD; cv_signal(&mapp->dam_sync_cv); } 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 rv; if (mapp == NULL) { return (DAM_EINVAL); } DTRACE_PROBE2(damap__addrset__begin, char *, mapp->dam_name, dam_t *, mapp); mutex_enter(&mapp->dam_lock); if (dam_map_alloc(mapp) != DAM_SUCCESS) { mutex_exit(&mapp->dam_lock); return (DAM_MAPFULL); } rv = damap_addrset_flush_locked(damapp); if (rv == DAM_SUCCESS) { mapp->dam_flags |= DAM_SETADD; } mutex_exit(&mapp->dam_lock); return (rv); } /* * Cancel full-set report * * damapp: address map * * Returns: DAM_SUCCESS * DAM_EINVAL Invalid argument(s) */ int damap_addrset_flush(damap_t *damapp) { int rv; dam_t *mapp = (dam_t *)damapp; if (mapp == NULL) { return (DAM_EINVAL); } DTRACE_PROBE2(damap__addrset__flush, char *, mapp->dam_name, dam_t *, mapp); mutex_enter(&mapp->dam_lock); rv = damap_addrset_flush_locked(damapp); mutex_exit(&mapp->dam_lock); return (rv); } /* * 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; if (!mapp || !address || (mapp->dam_rptmode != DAMAP_REPORT_FULLSET)) return (DAM_EINVAL); DTRACE_PROBE3(damap__addrset__add, char *, mapp->dam_name, dam_t *, mapp, char *, address); mutex_enter(&mapp->dam_lock); if (!(mapp->dam_flags & DAM_SETADD)) { mutex_exit(&mapp->dam_lock); return (DAM_FAILURE); } if ((addrid = dam_get_addrid(mapp, address)) == 0) { mutex_exit(&mapp->dam_lock); return (DAM_MAPFULL); } passp = ddi_get_soft_state(mapp->dam_da, addrid); ASSERT(passp); if (DAM_IN_REPORT(mapp, addrid)) { DTRACE_PROBE3(damap__addrset__add__jitter, char *, mapp->dam_name, dam_t *, mapp, char *, address); dam_addr_report_release(mapp, addrid); passp->da_jitter++; } passp->da_ppriv = addr_priv; if (nvl) (void) nvlist_dup(nvl, &passp->da_nvl_rpt, KM_SLEEP); bitset_add(&mapp->dam_report_set, addrid); if (ridx) *ridx = (damap_id_t)addrid; mutex_exit(&mapp->dam_lock); 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; if (!mapp || (mapp->dam_rptmode != DAMAP_REPORT_FULLSET)) return (DAM_EINVAL); DTRACE_PROBE2(damap__addrset__end, char *, mapp->dam_name, dam_t *, mapp); mutex_enter(&mapp->dam_lock); if (!(mapp->dam_flags & DAM_SETADD)) { mutex_exit(&mapp->dam_lock); return (DAM_FAILURE); } if (flags & DAMAP_END_RESET) { DTRACE_PROBE2(damap__addrset__end__reset, char *, mapp->dam_name, dam_t *, mapp); dam_sched_timeout(NULL, mapp, 0); for (i = 1; i < mapp->dam_high; i++) if (DAM_IN_REPORT(mapp, i)) dam_addr_report_release(mapp, i); } else { mapp->dam_last_update = gethrtime(); dam_sched_timeout(dam_addrset_stable_cb, mapp, mapp->dam_stable_ticks); } mutex_exit(&mapp->dam_lock); return (DAM_SUCCESS); } /* * Return nvlist registered with reported address * * damapp: address map handle * addrid: 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; dam_da_t *pass; if (mapp->dam_high && ddi_strid_id2str(mapp->dam_addr_hash, addrid)) { if (pass = ddi_get_soft_state(mapp->dam_da, addrid)) return (pass->da_nvl); } return (NULL); } /* * Return address string * * damapp: address map handle * addrid: address ID * * Returns: char * Address string * NULL */ char * damap_id2addr(damap_t *damapp, damap_id_t addrid) { dam_t *mapp = (dam_t *)damapp; if (mapp->dam_high) return (ddi_strid_id2str(mapp->dam_addr_hash, addrid)); else return (NULL); } /* * Release address reference in map * * damapp: address map handle * addrid: address ID */ void damap_id_rele(damap_t *damapp, damap_id_t addrid) { dam_t *mapp = (dam_t *)damapp; dam_da_t *passp; char *addr; passp = ddi_get_soft_state(mapp->dam_da, (id_t)addrid); ASSERT(passp); addr = damap_id2addr(damapp, addrid); DTRACE_PROBE4(damap__id__rele, char *, mapp->dam_name, dam_t *, mapp, char *, addr, int, passp->da_ref); mutex_enter(&mapp->dam_lock); /* * teardown address if last outstanding reference */ if (--passp->da_ref == 0) dam_addr_release(mapp, (id_t)addrid); mutex_exit(&mapp->dam_lock); } /* * Return current reference count on address reference in map * * damapp: address map handle * addrid: address ID * * Returns: DAM_SUCCESS * DAM_FAILURE */ int damap_id_ref(damap_t *damapp, damap_id_t addrid) { dam_t *mapp = (dam_t *)damapp; dam_da_t *passp; int ref = -1; passp = ddi_get_soft_state(mapp->dam_da, (id_t)addrid); if (passp) ref = passp->da_ref; 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 * addrid: address ID * cfg_priv: configuration private data * */ void damap_id_priv_set(damap_t *damapp, damap_id_t addrid, void *cfg_priv) { dam_t *mapp = (dam_t *)damapp; dam_da_t *passp; mutex_enter(&mapp->dam_lock); passp = ddi_get_soft_state(mapp->dam_da, (id_t)addrid); if (!passp) { mutex_exit(&mapp->dam_lock); return; } passp->da_cfg_priv = cfg_priv; mutex_exit(&mapp->dam_lock); } /* * Get config private data * * damapp: address map handle * addrid: address ID * * Returns: configuration private data */ void * damap_id_priv_get(damap_t *damapp, damap_id_t addrid) { dam_t *mapp = (dam_t *)damapp; dam_da_t *passp; void *rv; mutex_enter(&mapp->dam_lock); passp = ddi_get_soft_state(mapp->dam_da, (id_t)addrid); if (!passp) { mutex_exit(&mapp->dam_lock); return (NULL); } rv = passp->da_cfg_priv; mutex_exit(&mapp->dam_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; DTRACE_PROBE3(damap__lookup, char *, mapp->dam_name, dam_t *, mapp, char *, address); mutex_enter(&mapp->dam_lock); if (!mapp->dam_high) addrid = 0; else addrid = ddi_strid_str2id(mapp->dam_addr_hash, address); if (addrid) { 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; } } mutex_exit(&mapp->dam_lock); DTRACE_PROBE4(damap__lookup__return, char *, mapp->dam_name, dam_t *, mapp, char *, address, int, addrid); 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; char *addrp; dam_da_t *passp; DTRACE_PROBE2(damap__lookup__all, char *, mapp->dam_name, dam_t *, mapp); mutex_enter(&mapp->dam_lock); if (!mapp->dam_high) { *id_listp = (damap_id_list_t)NULL; mutex_exit(&mapp->dam_lock); DTRACE_PROBE2(damap__lookup__all__nomap, char *, mapp->dam_name, dam_t *, mapp); return (0); } bsp = kmem_alloc(sizeof (*bsp), KM_SLEEP); bitset_init(bsp); bitset_resize(bsp, mapsz); bitset_copy(&mapp->dam_active_set, bsp); 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) { addrp = damap_id2addr(damapp, i); DTRACE_PROBE3(damap__lookup__all__item, char *, mapp->dam_name, dam_t *, mapp, char *, addrp); passp->da_ref++; n_ids++; } } } if (n_ids) { *id_listp = (damap_id_list_t)bsp; mutex_exit(&mapp->dam_lock); return (n_ids); } else { *id_listp = (damap_id_list_t)NULL; bitset_fini(bsp); kmem_free(bsp, sizeof (*bsp)); mutex_exit(&mapp->dam_lock); 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; mutex_enter(&mapp->dam_lock); for (i = 1; i < mapp->dam_high; i++) { if (bitset_in_set((bitset_t *)id_list, i)) (void) dam_addr_release(mapp, i); } mutex_exit(&mapp->dam_lock); bitset_fini((bitset_t *)id_list); kmem_free((void *)id_list, sizeof (bitset_t)); } /* * activate an address that has passed the stabilization interval */ static void dam_addr_activate(dam_t *mapp, id_t addrid) { dam_da_t *passp; int config_rv; char *addrstr; mutex_enter(&mapp->dam_lock); bitset_add(&mapp->dam_active_set, addrid); passp = ddi_get_soft_state(mapp->dam_da, addrid); ASSERT(passp); /* * copy the reported nvlist and provider private data */ addrstr = ddi_strid_id2str(mapp->dam_addr_hash, addrid); DTRACE_PROBE3(damap__addr__activate__start, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr); passp->da_nvl = passp->da_nvl_rpt; passp->da_nvl_rpt = NULL; passp->da_last_stable = gethrtime(); passp->da_stable_cnt++; mutex_exit(&mapp->dam_lock); if (mapp->dam_activate_cb) { (*mapp->dam_activate_cb)(mapp->dam_activate_arg, addrstr, addrid, &passp->da_ppriv); } /* * call the address-specific configuration action as part of * activation. */ config_rv = (*mapp->dam_configure_cb)(mapp->dam_config_arg, mapp, addrid); if (config_rv != DAM_SUCCESS) { mutex_enter(&mapp->dam_lock); passp->da_flags |= DA_FAILED_CONFIG; mutex_exit(&mapp->dam_lock); DTRACE_PROBE3(damap__addr__activate__config__failure, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr); dam_deact_cleanup(mapp, addrid, addrstr, DAMAP_DEACT_RSN_CFG_FAIL); } else { DTRACE_PROBE3(damap__addr__activate__end, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr); } } /* * deactivate a previously stable address */ static void dam_addr_deactivate(dam_t *mapp, id_t addrid) { char *addrstr; addrstr = ddi_strid_id2str(mapp->dam_addr_hash, addrid); DTRACE_PROBE3(damap__addr__deactivate__start, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr); /* * call the unconfiguration callback */ (*mapp->dam_unconfig_cb)(mapp->dam_config_arg, mapp, addrid); dam_deact_cleanup(mapp, addrid, addrstr, DAMAP_DEACT_RSN_GONE); } static void dam_deact_cleanup(dam_t *mapp, id_t addrid, char *addrstr, damap_deact_rsn_t deact_rsn) { dam_da_t *passp; passp = ddi_get_soft_state(mapp->dam_da, addrid); ASSERT(passp); 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, deact_rsn); /* * clear the active bit and free the backing info for * this address */ mutex_enter(&mapp->dam_lock); bitset_del(&mapp->dam_active_set, addrid); passp->da_ppriv = NULL; nvlist_free(passp->da_nvl); passp->da_nvl = NULL; nvlist_free(passp->da_nvl_rpt); passp->da_nvl_rpt = NULL; DTRACE_PROBE3(damap__addr__deactivate__end, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr); (void) dam_addr_release(mapp, addrid); mutex_exit(&mapp->dam_lock); } /* * taskq callback for multi-thread activation */ static void dam_tq_config(void *arg) { cfg_tqd_t *tqd = (cfg_tqd_t *)arg; dam_addr_activate(tqd->tqd_mapp, tqd->tqd_id); kmem_free(tqd, sizeof (*tqd)); } /* * taskq callback for multi-thread deactivation */ static void dam_tq_unconfig(void *arg) { cfg_tqd_t *tqd = (cfg_tqd_t *)arg; dam_addr_deactivate(tqd->tqd_mapp, tqd->tqd_id); kmem_free(tqd, sizeof (*tqd)); } /* * Activate a set of stabilized addresses */ static void dam_addrset_activate(dam_t *mapp, bitset_t *activate) { int i, nset; taskq_t *tqp = NULL; cfg_tqd_t *tqd = NULL; char tqn[TASKQ_NAMELEN]; extern pri_t maxclsyspri; if (mapp->dam_options & DAMAP_MTCONFIG) { /* * calculate the # of taskq threads to create */ for (i = 1, nset = 0; i < mapp->dam_high; i++) if (bitset_in_set(activate, i)) nset++; ASSERT(nset); (void) snprintf(tqn, sizeof (tqn), "actv-%s", mapp->dam_name); tqp = taskq_create(tqn, nset, maxclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE); } for (i = 1; i < mapp->dam_high; i++) { if (bitset_in_set(activate, i)) { if (!tqp) dam_addr_activate(mapp, i); else { /* * multi-threaded activation */ tqd = kmem_alloc(sizeof (*tqd), KM_SLEEP); tqd->tqd_mapp = mapp; tqd->tqd_id = i; (void) taskq_dispatch(tqp, dam_tq_config, tqd, TQ_SLEEP); } } } if (tqp) { taskq_wait(tqp); taskq_destroy(tqp); } } /* * Deactivate a set of stabilized addresses */ static void dam_addrset_deactivate(dam_t *mapp, bitset_t *deactivate) { int i, nset; taskq_t *tqp = NULL; cfg_tqd_t *tqd = NULL; char tqn[TASKQ_NAMELEN]; DTRACE_PROBE2(damap__addrset__deactivate, char *, mapp->dam_name, dam_t *, mapp); if (mapp->dam_options & DAMAP_MTCONFIG) { /* * compute the # of taskq threads to dispatch */ for (i = 1, nset = 0; i < mapp->dam_high; i++) if (bitset_in_set(deactivate, i)) nset++; (void) snprintf(tqn, sizeof (tqn), "deactv-%s", mapp->dam_name); tqp = taskq_create(tqn, nset, maxclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE); } for (i = 1; i < mapp->dam_high; i++) { if (bitset_in_set(deactivate, i)) { if (!tqp) { dam_addr_deactivate(mapp, i); } else { tqd = kmem_alloc(sizeof (*tqd), KM_SLEEP); tqd->tqd_mapp = mapp; tqd->tqd_id = i; (void) taskq_dispatch(tqp, dam_tq_unconfig, tqd, TQ_SLEEP); } } } if (tqp) { taskq_wait(tqp); taskq_destroy(tqp); } } /* * Release a previously activated address */ static void dam_addr_release(dam_t *mapp, id_t addrid) { dam_da_t *passp; char *addrstr; ASSERT(mutex_owned(&mapp->dam_lock)); passp = ddi_get_soft_state(mapp->dam_da, addrid); ASSERT(passp); addrstr = ddi_strid_id2str(mapp->dam_addr_hash, addrid); DTRACE_PROBE3(damap__addr__release, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr); /* * defer releasing the address until outstanding references * are released */ if (passp->da_ref > 1) { DTRACE_PROBE4(damap__addr__release__outstanding__refs, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr, int, passp->da_ref); return; } /* * allow pending reports to stabilize */ if (DAM_IN_REPORT(mapp, addrid)) { DTRACE_PROBE3(damap__addr__release__report__pending, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr); return; } ddi_strid_free(mapp->dam_addr_hash, addrid); ddi_soft_state_free(mapp->dam_da, addrid); } /* * process stabilized address reports */ static void dam_stabilize_map(void *arg) { dam_t *mapp = (dam_t *)arg; bitset_t delta; bitset_t cfg; bitset_t uncfg; int has_cfg, has_uncfg; uint32_t i, n_active; DTRACE_PROBE2(damap__stabilize__map, char *, mapp->dam_name, dam_t *, mapp); 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); /* * determine which addresses have changed during * this stabilization cycle */ mutex_enter(&mapp->dam_lock); ASSERT(mapp->dam_flags & DAM_SPEND); if (!bitset_xor(&mapp->dam_active_set, &mapp->dam_stable_set, &delta)) { /* * no difference */ bitset_zero(&mapp->dam_stable_set); mapp->dam_flags &= ~DAM_SPEND; cv_signal(&mapp->dam_sync_cv); mutex_exit(&mapp->dam_lock); bitset_fini(&uncfg); bitset_fini(&cfg); bitset_fini(&delta); DTRACE_PROBE2(damap__stabilize__map__nochange, char *, mapp->dam_name, dam_t *, mapp); return; } /* * compute the sets of addresses to be activated and deactivated */ has_cfg = bitset_and(&delta, &mapp->dam_stable_set, &cfg); has_uncfg = bitset_and(&delta, &mapp->dam_active_set, &uncfg); /* * drop map lock while invoking callouts */ mutex_exit(&mapp->dam_lock); /* * activate all newly stable addresss */ if (has_cfg) dam_addrset_activate(mapp, &cfg); /* * deactivate addresss which are no longer in the map */ if (has_uncfg) dam_addrset_deactivate(mapp, &uncfg); /* * timestamp the last stable time and increment the kstat keeping * the # of of stable cycles for the map */ mutex_enter(&mapp->dam_lock); bitset_zero(&mapp->dam_stable_set); mapp->dam_last_stable = gethrtime(); mapp->dam_stable_cnt++; DAM_INCR_STAT(mapp, dam_cycles); /* * determine the number of stable addresses * and update the n_active kstat for this map */ for (i = 1, n_active = 0; i < mapp->dam_high; i++) if (bitset_in_set(&mapp->dam_active_set, i)) n_active++; DAM_SET_STAT(mapp, dam_active, n_active); DTRACE_PROBE3(damap__map__stable__end, char *, mapp->dam_name, dam_t *, mapp, int, n_active); mapp->dam_flags &= ~DAM_SPEND; cv_signal(&mapp->dam_sync_cv); mutex_exit(&mapp->dam_lock); bitset_fini(&uncfg); bitset_fini(&cfg); bitset_fini(&delta); } /* * 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 ts, next_ticks, delta_ticks; mutex_enter(&mapp->dam_lock); if (mapp->dam_tid == 0) { DTRACE_PROBE2(damap__map__addr__stable__cancelled, char *, mapp->dam_name, dam_t *, mapp); mutex_exit(&mapp->dam_lock); return; } mapp->dam_tid = 0; /* * If still under stabilization, reschedule timeout, * otherwise dispatch the task to activate and deactivate the * new stable address */ if (mapp->dam_flags & DAM_SPEND) { DAM_INCR_STAT(mapp, dam_overrun); mapp->dam_stable_overrun++; DTRACE_PROBE2(damap__map__addr__stable__overrun, char *, mapp->dam_name, dam_t *, mapp); dam_sched_timeout(dam_addr_stable_cb, mapp, mapp->dam_stable_ticks); mutex_exit(&mapp->dam_lock); return; } DAM_SET_STAT(mapp, dam_overrun, 0); mapp->dam_stable_overrun = 0; /* See if any reports stabalized and compute next timeout. */ ts = ddi_get_lbolt64(); next_ticks = mapp->dam_stable_ticks; for (i = 1; i < mapp->dam_high; i++) { if (bitset_in_set(&mapp->dam_report_set, i)) { passp = ddi_get_soft_state(mapp->dam_da, i); ASSERT(passp); if (passp->da_deadline <= ts) spend++; /* report has stabilized */ else { /* not stabilized, determine next map timeout */ tpend++; delta_ticks = passp->da_deadline - ts; if (delta_ticks < next_ticks) next_ticks = delta_ticks; } } } /* * schedule system_taskq activation of stabilized reports */ if (spend) { if (taskq_dispatch(system_taskq, dam_stabilize_map, mapp, TQ_NOSLEEP | TQ_NOQUEUE) != TASKQID_INVALID) { DTRACE_PROBE2(damap__map__addr__stable__start, char *, mapp->dam_name, dam_t *, mapp); /* * The stable_set we compute below stays pending until * processed by dam_stabilize_map. We can't set * DAM_SPEND (or bitset_del things from the * report_set) until we *know* that we can handoff the * result to dam_stabilize_map. If dam_stabilize_map * starts executing before we are complete, it will * block on the dam_lock mutex until we are ready. */ mapp->dam_flags |= DAM_SPEND; /* * Copy the current active_set to the stable_set, then * add or remove stabilized report_set address from * the stable set (and delete them from the report_set). */ 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; passp = ddi_get_soft_state(mapp->dam_da, i); if (passp->da_deadline > ts) continue; /* report not stabilized */ /* report has stabilized */ if (passp->da_flags & DA_RELE) bitset_del(&mapp->dam_stable_set, i); else bitset_add(&mapp->dam_stable_set, i); bitset_del(&mapp->dam_report_set, i); } } else { DTRACE_PROBE2(damap__map__addr__stable__spendfail, char *, mapp->dam_name, dam_t *, mapp); /* * Avoid waiting the entire stabalization * time again if taskq_diskpatch fails. */ tpend++; delta_ticks = drv_usectohz( damap_taskq_dispatch_retry_usec); if (delta_ticks < next_ticks) next_ticks = delta_ticks; } } /* * reschedule the stabilization timer if there are reports * still pending */ if (tpend) { DTRACE_PROBE2(damap__map__addr__stable__tpend, char *, mapp->dam_name, dam_t *, mapp); dam_sched_timeout(dam_addr_stable_cb, mapp, (clock_t)next_ticks); } mutex_exit(&mapp->dam_lock); } /* * fullset stabilization timeout callback */ static void dam_addrset_stable_cb(void *arg) { dam_t *mapp = (dam_t *)arg; mutex_enter(&mapp->dam_lock); if (mapp->dam_tid == 0) { mutex_exit(&mapp->dam_lock); DTRACE_PROBE2(damap__map__addrset__stable__cancelled, char *, mapp->dam_name, dam_t *, mapp); return; } mapp->dam_tid = 0; /* * If map still underoing stabilization reschedule timeout, * else dispatch the task to configure the new stable set of * addresses. */ if ((mapp->dam_flags & DAM_SPEND) || (taskq_dispatch(system_taskq, dam_stabilize_map, mapp, TQ_NOSLEEP | TQ_NOQUEUE) == TASKQID_INVALID)) { DAM_INCR_STAT(mapp, dam_overrun); mapp->dam_stable_overrun++; dam_sched_timeout(dam_addrset_stable_cb, mapp, drv_usectohz(damap_taskq_dispatch_retry_usec)); DTRACE_PROBE2(damap__map__addrset__stable__overrun, char *, mapp->dam_name, dam_t *, mapp); mutex_exit(&mapp->dam_lock); return; } DAM_SET_STAT(mapp, dam_overrun, 0); mapp->dam_stable_overrun = 0; bitset_copy(&mapp->dam_report_set, &mapp->dam_stable_set); bitset_zero(&mapp->dam_report_set); mapp->dam_flags |= DAM_SPEND; mapp->dam_flags &= ~DAM_SETADD; /* NOTE: don't need cv_signal since DAM_SPEND is still set */ DTRACE_PROBE2(damap__map__addrset__stable__start, char *, mapp->dam_name, dam_t *, mapp); mutex_exit(&mapp->dam_lock); } /* * schedule map timeout in 'ticks' ticks * if map timer is currently running, cancel if ticks == 0 */ static void dam_sched_timeout(void (*timeout_cb)(), dam_t *mapp, clock_t ticks) { timeout_id_t tid; DTRACE_PROBE4(damap__sched__timeout, char *, mapp->dam_name, dam_t *, mapp, int, ticks, timeout_id_t, mapp->dam_tid); ASSERT(mutex_owned(&mapp->dam_lock)); if ((tid = mapp->dam_tid) != 0) { if (ticks == 0) { mapp->dam_tid = 0; mutex_exit(&mapp->dam_lock); (void) untimeout(tid); mutex_enter(&mapp->dam_lock); } } else { if (timeout_cb && (ticks != 0)) mapp->dam_tid = timeout(timeout_cb, mapp, ticks); } } /* * report addition or removal of an address */ static void dam_addr_report(dam_t *mapp, dam_da_t *passp, id_t addrid, int rpt_type) { char *addrstr = damap_id2addr((damap_t *)mapp, addrid); DTRACE_PROBE4(damap__addr__report, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr, int, rpt_type); ASSERT(mutex_owned(&mapp->dam_lock)); ASSERT(!DAM_IN_REPORT(mapp, addrid)); passp->da_last_report = gethrtime(); mapp->dam_last_update = gethrtime(); passp->da_report_cnt++; passp->da_deadline = ddi_get_lbolt64() + mapp->dam_stable_ticks; if (rpt_type == RPT_ADDR_DEL) passp->da_flags |= DA_RELE; else if (rpt_type == RPT_ADDR_ADD) passp->da_flags &= ~DA_RELE; bitset_add(&mapp->dam_report_set, addrid); dam_sched_timeout(dam_addr_stable_cb, mapp, mapp->dam_stable_ticks); } /* * release an address report */ static void dam_addr_report_release(dam_t *mapp, id_t addrid) { dam_da_t *passp; char *addrstr = damap_id2addr((damap_t *)mapp, addrid); DTRACE_PROBE3(damap__addr__report__release, char *, mapp->dam_name, dam_t *, mapp, char *, addrstr); ASSERT(mutex_owned(&mapp->dam_lock)); passp = ddi_get_soft_state(mapp->dam_da, addrid); ASSERT(passp); /* * clear the report bit * if the address has a registered deactivation handler and * we are holding a private data pointer and the address has not * stabilized, deactivate the address (private data). */ bitset_del(&mapp->dam_report_set, addrid); if (!DAM_IS_STABLE(mapp, addrid) && mapp->dam_deactivate_cb && passp->da_ppriv) { mutex_exit(&mapp->dam_lock); (*mapp->dam_deactivate_cb)(mapp->dam_activate_arg, ddi_strid_id2str(mapp->dam_addr_hash, addrid), addrid, passp->da_ppriv, DAMAP_DEACT_RSN_UNSTBL); mutex_enter(&mapp->dam_lock); } passp->da_ppriv = NULL; nvlist_free(passp->da_nvl_rpt); } /* * 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; ASSERT(mutex_owned(&mapp->dam_lock)); if ((addrid = ddi_strid_str2id(mapp->dam_addr_hash, address)) == 0) { if ((addrid = ddi_strid_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; /* * expand bitmaps if ID has outgrown old map size */ if (mapp->dam_high > mapp->dam_size) { mapp->dam_size = mapp->dam_size + DAM_SIZE_BUMP; bitset_resize(&mapp->dam_active_set, mapp->dam_size); bitset_resize(&mapp->dam_stable_set, mapp->dam_size); bitset_resize(&mapp->dam_report_set, mapp->dam_size); } passp = ddi_get_soft_state(mapp->dam_da, addrid); passp->da_ref = 1; 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_cycles, "cycles", KSTAT_DATA_UINT32); kstat_named_init(&statsp->dam_overrun, "overrun", KSTAT_DATA_UINT32); kstat_named_init(&statsp->dam_jitter, "jitter", KSTAT_DATA_UINT32); kstat_named_init(&statsp->dam_active, "active", KSTAT_DATA_UINT32); kstat_install(mapsp); mapp->dam_kstatsp = mapsp; return (DDI_SUCCESS); }