summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4/os/ddi_impl.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4/os/ddi_impl.c')
-rw-r--r--usr/src/uts/sun4/os/ddi_impl.c938
1 files changed, 0 insertions, 938 deletions
diff --git a/usr/src/uts/sun4/os/ddi_impl.c b/usr/src/uts/sun4/os/ddi_impl.c
index d01eabae19..f3efc433c5 100644
--- a/usr/src/uts/sun4/os/ddi_impl.c
+++ b/usr/src/uts/sun4/os/ddi_impl.c
@@ -48,12 +48,8 @@
#include <sys/systeminfo.h>
#include <sys/fpu/fpusystm.h>
#include <sys/vm.h>
-#include <sys/fs/dv_node.h>
-#include <sys/fs/snode.h>
#include <sys/ddi_isa.h>
-#include <sys/modhash.h>
#include <sys/modctl.h>
-#include <sys/sunldi_impl.h>
dev_info_t *get_intr_parent(dev_info_t *, dev_info_t *,
ddi_intr_handle_impl_t *);
@@ -1954,937 +1950,3 @@ peekpoke_mem(ddi_ctl_enum_t cmd, peekpoke_ctlops_t *in_args)
return (err);
}
-
-/*
- * Platform independent DR routines
- */
-
-static int
-ndi2errno(int n)
-{
- int err = 0;
-
- switch (n) {
- case NDI_NOMEM:
- err = ENOMEM;
- break;
- case NDI_BUSY:
- err = EBUSY;
- break;
- case NDI_FAULT:
- err = EFAULT;
- break;
- case NDI_FAILURE:
- err = EIO;
- break;
- case NDI_SUCCESS:
- break;
- case NDI_BADHANDLE:
- default:
- err = EINVAL;
- break;
- }
- return (err);
-}
-
-/*
- * Prom tree node list
- */
-struct ptnode {
- pnode_t nodeid;
- struct ptnode *next;
-};
-
-/*
- * Prom tree walk arg
- */
-struct pta {
- dev_info_t *pdip;
- devi_branch_t *bp;
- uint_t flags;
- dev_info_t *fdip;
- struct ptnode *head;
-};
-
-static void
-visit_node(pnode_t nodeid, struct pta *ap)
-{
- struct ptnode **nextp;
- int (*select)(pnode_t, void *, uint_t);
-
- ASSERT(nodeid != OBP_NONODE && nodeid != OBP_BADNODE);
-
- select = ap->bp->create.prom_branch_select;
-
- ASSERT(select);
-
- if (select(nodeid, ap->bp->arg, 0) == DDI_SUCCESS) {
-
- for (nextp = &ap->head; *nextp; nextp = &(*nextp)->next)
- ;
-
- *nextp = kmem_zalloc(sizeof (struct ptnode), KM_SLEEP);
-
- (*nextp)->nodeid = nodeid;
- }
-
- if ((ap->flags & DEVI_BRANCH_CHILD) == DEVI_BRANCH_CHILD)
- return;
-
- nodeid = prom_childnode(nodeid);
- while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) {
- visit_node(nodeid, ap);
- nodeid = prom_nextnode(nodeid);
- }
-}
-
-/*
- * NOTE: The caller of this function must check for device contracts
- * or LDI callbacks against this dip before setting the dip offline.
- */
-static int
-set_infant_dip_offline(dev_info_t *dip, void *arg)
-{
- char *path = (char *)arg;
-
- ASSERT(dip);
- ASSERT(arg);
-
- if (i_ddi_node_state(dip) >= DS_ATTACHED) {
- (void) ddi_pathname(dip, path);
- cmn_err(CE_WARN, "Attempt to set offline flag on attached "
- "node: %s", path);
- return (DDI_FAILURE);
- }
-
- mutex_enter(&(DEVI(dip)->devi_lock));
- if (!DEVI_IS_DEVICE_OFFLINE(dip))
- DEVI_SET_DEVICE_OFFLINE(dip);
- mutex_exit(&(DEVI(dip)->devi_lock));
-
- return (DDI_SUCCESS);
-}
-
-typedef struct result {
- char *path;
- int result;
-} result_t;
-
-static int
-dip_set_offline(dev_info_t *dip, void *arg)
-{
- int end;
- result_t *resp = (result_t *)arg;
-
- ASSERT(dip);
- ASSERT(resp);
-
- /*
- * We stop the walk if e_ddi_offline_notify() returns
- * failure, because this implies that one or more consumers
- * (either LDI or contract based) has blocked the offline.
- * So there is no point in conitnuing the walk
- */
- if (e_ddi_offline_notify(dip) == DDI_FAILURE) {
- resp->result = DDI_FAILURE;
- return (DDI_WALK_TERMINATE);
- }
-
- /*
- * If set_infant_dip_offline() returns failure, it implies
- * that we failed to set a particular dip offline. This
- * does not imply that the offline as a whole should fail.
- * We want to do the best we can, so we continue the walk.
- */
- if (set_infant_dip_offline(dip, resp->path) == DDI_SUCCESS)
- end = DDI_SUCCESS;
- else
- end = DDI_FAILURE;
-
- e_ddi_offline_finalize(dip, end);
-
- return (DDI_WALK_CONTINUE);
-}
-
-/*
- * The call to e_ddi_offline_notify() exists for the
- * unlikely error case that a branch we are trying to
- * create already exists and has device contracts or LDI
- * event callbacks against it.
- *
- * We allow create to succeed for such branches only if
- * no constraints block the offline.
- */
-static int
-branch_set_offline(dev_info_t *dip, char *path)
-{
- int circ;
- int end;
- result_t res;
-
-
- if (e_ddi_offline_notify(dip) == DDI_FAILURE) {
- return (DDI_FAILURE);
- }
-
- if (set_infant_dip_offline(dip, path) == DDI_SUCCESS)
- end = DDI_SUCCESS;
- else
- end = DDI_FAILURE;
-
- e_ddi_offline_finalize(dip, end);
-
- if (end == DDI_FAILURE)
- return (DDI_FAILURE);
-
- res.result = DDI_SUCCESS;
- res.path = path;
-
- ndi_devi_enter(dip, &circ);
- ddi_walk_devs(ddi_get_child(dip), dip_set_offline, &res);
- ndi_devi_exit(dip, circ);
-
- return (res.result);
-}
-
-/*ARGSUSED*/
-static int
-create_prom_branch(void *arg, int has_changed)
-{
- int circ;
- int exists, rv;
- pnode_t nodeid;
- struct ptnode *tnp;
- dev_info_t *dip;
- struct pta *ap = arg;
- devi_branch_t *bp;
- char *path;
-
- ASSERT(ap);
- ASSERT(ap->fdip == NULL);
- ASSERT(ap->pdip && ndi_dev_is_prom_node(ap->pdip));
-
- bp = ap->bp;
-
- nodeid = ddi_get_nodeid(ap->pdip);
- if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) {
- cmn_err(CE_WARN, "create_prom_branch: invalid "
- "nodeid: 0x%x", nodeid);
- return (EINVAL);
- }
-
- ap->head = NULL;
-
- nodeid = prom_childnode(nodeid);
- while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) {
- visit_node(nodeid, ap);
- nodeid = prom_nextnode(nodeid);
- }
-
- if (ap->head == NULL)
- return (ENODEV);
-
- path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
- rv = 0;
- while ((tnp = ap->head) != NULL) {
- ap->head = tnp->next;
-
- ndi_devi_enter(ap->pdip, &circ);
-
- /*
- * Check if the branch already exists.
- */
- exists = 0;
- dip = e_ddi_nodeid_to_dip(tnp->nodeid);
- if (dip != NULL) {
- exists = 1;
-
- /* Parent is held busy, so release hold */
- ndi_rele_devi(dip);
-#ifdef DEBUG
- cmn_err(CE_WARN, "create_prom_branch: dip(%p) exists"
- " for nodeid 0x%x", (void *)dip, tnp->nodeid);
-#endif
- } else {
- dip = i_ddi_create_branch(ap->pdip, tnp->nodeid);
- }
-
- kmem_free(tnp, sizeof (struct ptnode));
-
- /*
- * Hold the branch if it is not already held
- */
- if (dip && !exists) {
- e_ddi_branch_hold(dip);
- }
-
- ASSERT(dip == NULL || e_ddi_branch_held(dip));
-
- /*
- * Set all dips in the newly created branch offline so that
- * only a "configure" operation can attach
- * the branch
- */
- if (dip == NULL || branch_set_offline(dip, path)
- == DDI_FAILURE) {
- ndi_devi_exit(ap->pdip, circ);
- rv = EIO;
- continue;
- }
-
- ASSERT(ddi_get_parent(dip) == ap->pdip);
-
- ndi_devi_exit(ap->pdip, circ);
-
- if (ap->flags & DEVI_BRANCH_CONFIGURE) {
- int error = e_ddi_branch_configure(dip, &ap->fdip, 0);
- if (error && rv == 0)
- rv = error;
- }
-
- /*
- * Invoke devi_branch_callback() (if it exists) only for
- * newly created branches
- */
- if (bp->devi_branch_callback && !exists)
- bp->devi_branch_callback(dip, bp->arg, 0);
- }
-
- kmem_free(path, MAXPATHLEN);
-
- return (rv);
-}
-
-static int
-sid_node_create(dev_info_t *pdip, devi_branch_t *bp, dev_info_t **rdipp)
-{
- int rv, circ, len;
- int i, flags, ret;
- dev_info_t *dip;
- char *nbuf;
- char *path;
- static const char *noname = "<none>";
-
- ASSERT(pdip);
- ASSERT(DEVI_BUSY_OWNED(pdip));
-
- flags = 0;
-
- /*
- * Creating the root of a branch ?
- */
- if (rdipp) {
- *rdipp = NULL;
- flags = DEVI_BRANCH_ROOT;
- }
-
- ndi_devi_alloc_sleep(pdip, (char *)noname, DEVI_SID_NODEID, &dip);
- rv = bp->create.sid_branch_create(dip, bp->arg, flags);
-
- nbuf = kmem_alloc(OBP_MAXDRVNAME, KM_SLEEP);
-
- if (rv == DDI_WALK_ERROR) {
- cmn_err(CE_WARN, "e_ddi_branch_create: Error setting"
- " properties on devinfo node %p", (void *)dip);
- goto fail;
- }
-
- len = OBP_MAXDRVNAME;
- if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "name", nbuf, &len)
- != DDI_PROP_SUCCESS) {
- cmn_err(CE_WARN, "e_ddi_branch_create: devinfo node %p has"
- "no name property", (void *)dip);
- goto fail;
- }
-
- ASSERT(i_ddi_node_state(dip) == DS_PROTO);
- if (ndi_devi_set_nodename(dip, nbuf, 0) != NDI_SUCCESS) {
- cmn_err(CE_WARN, "e_ddi_branch_create: cannot set name (%s)"
- " for devinfo node %p", nbuf, (void *)dip);
- goto fail;
- }
-
- kmem_free(nbuf, OBP_MAXDRVNAME);
-
- /*
- * Ignore bind failures just like boot does
- */
- (void) ndi_devi_bind_driver(dip, 0);
-
- switch (rv) {
- case DDI_WALK_CONTINUE:
- case DDI_WALK_PRUNESIB:
- ndi_devi_enter(dip, &circ);
-
- i = DDI_WALK_CONTINUE;
- for (; i == DDI_WALK_CONTINUE; ) {
- i = sid_node_create(dip, bp, NULL);
- }
-
- ASSERT(i == DDI_WALK_ERROR || i == DDI_WALK_PRUNESIB);
- if (i == DDI_WALK_ERROR)
- rv = i;
- /*
- * If PRUNESIB stop creating siblings
- * of dip's child. Subsequent walk behavior
- * is determined by rv returned by dip.
- */
-
- ndi_devi_exit(dip, circ);
- break;
- case DDI_WALK_TERMINATE:
- /*
- * Don't create children and ask our parent
- * to not create siblings either.
- */
- rv = DDI_WALK_PRUNESIB;
- break;
- case DDI_WALK_PRUNECHILD:
- /*
- * Don't create children, but ask parent to continue
- * with siblings.
- */
- rv = DDI_WALK_CONTINUE;
- break;
- default:
- ASSERT(0);
- break;
- }
-
- if (rdipp)
- *rdipp = dip;
-
- /*
- * Set device offline - only the "configure" op should cause an attach.
- * Note that it is safe to set the dip offline without checking
- * for either device contract or layered driver (LDI) based constraints
- * since there cannot be any contracts or LDI opens of this device.
- * This is because this node is a newly created dip with the parent busy
- * held, so no other thread can come in and attach this dip. A dip that
- * has never been attached cannot have contracts since by definition
- * a device contract (an agreement between a process and a device minor
- * node) can only be created against a device that has minor nodes
- * i.e is attached. Similarly an LDI open will only succeed if the
- * dip is attached. We assert below that the dip is not attached.
- */
- ASSERT(i_ddi_node_state(dip) < DS_ATTACHED);
- path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
- ret = set_infant_dip_offline(dip, path);
- ASSERT(ret == DDI_SUCCESS);
- kmem_free(path, MAXPATHLEN);
-
- return (rv);
-fail:
- (void) ndi_devi_free(dip);
- kmem_free(nbuf, OBP_MAXDRVNAME);
- return (DDI_WALK_ERROR);
-}
-
-static int
-create_sid_branch(
- dev_info_t *pdip,
- devi_branch_t *bp,
- dev_info_t **dipp,
- uint_t flags)
-{
- int rv = 0, state = DDI_WALK_CONTINUE;
- dev_info_t *rdip;
-
- while (state == DDI_WALK_CONTINUE) {
- int circ;
-
- ndi_devi_enter(pdip, &circ);
-
- state = sid_node_create(pdip, bp, &rdip);
- if (rdip == NULL) {
- ndi_devi_exit(pdip, circ);
- ASSERT(state == DDI_WALK_ERROR);
- break;
- }
-
- e_ddi_branch_hold(rdip);
-
- ndi_devi_exit(pdip, circ);
-
- if (flags & DEVI_BRANCH_CONFIGURE) {
- int error = e_ddi_branch_configure(rdip, dipp, 0);
- if (error && rv == 0)
- rv = error;
- }
-
- /*
- * devi_branch_callback() is optional
- */
- if (bp->devi_branch_callback)
- bp->devi_branch_callback(rdip, bp->arg, 0);
- }
-
- ASSERT(state == DDI_WALK_ERROR || state == DDI_WALK_PRUNESIB);
-
- return (state == DDI_WALK_ERROR ? EIO : rv);
-}
-
-int
-e_ddi_branch_create(
- dev_info_t *pdip,
- devi_branch_t *bp,
- dev_info_t **dipp,
- uint_t flags)
-{
- int prom_devi, sid_devi, error;
-
- if (pdip == NULL || bp == NULL || bp->type == 0)
- return (EINVAL);
-
- prom_devi = (bp->type == DEVI_BRANCH_PROM) ? 1 : 0;
- sid_devi = (bp->type == DEVI_BRANCH_SID) ? 1 : 0;
-
- if (prom_devi && bp->create.prom_branch_select == NULL)
- return (EINVAL);
- else if (sid_devi && bp->create.sid_branch_create == NULL)
- return (EINVAL);
- else if (!prom_devi && !sid_devi)
- return (EINVAL);
-
- if (flags & DEVI_BRANCH_EVENT)
- return (EINVAL);
-
- if (prom_devi) {
- struct pta pta = {0};
-
- pta.pdip = pdip;
- pta.bp = bp;
- pta.flags = flags;
-
- error = prom_tree_access(create_prom_branch, &pta, NULL);
-
- if (dipp)
- *dipp = pta.fdip;
- else if (pta.fdip)
- ndi_rele_devi(pta.fdip);
- } else {
- error = create_sid_branch(pdip, bp, dipp, flags);
- }
-
- return (error);
-}
-
-int
-e_ddi_branch_configure(dev_info_t *rdip, dev_info_t **dipp, uint_t flags)
-{
- int rv;
- char *devnm;
- dev_info_t *pdip;
-
- if (dipp)
- *dipp = NULL;
-
- if (rdip == NULL || flags != 0 || (flags & DEVI_BRANCH_EVENT))
- return (EINVAL);
-
- pdip = ddi_get_parent(rdip);
-
- ndi_hold_devi(pdip);
-
- if (!e_ddi_branch_held(rdip)) {
- ndi_rele_devi(pdip);
- cmn_err(CE_WARN, "e_ddi_branch_configure: "
- "dip(%p) not held", (void *)rdip);
- return (EINVAL);
- }
-
- if (i_ddi_node_state(rdip) < DS_INITIALIZED) {
- /*
- * First attempt to bind a driver. If we fail, return
- * success (On some platforms, dips for some device
- * types (CPUs) may not have a driver)
- */
- if (ndi_devi_bind_driver(rdip, 0) != NDI_SUCCESS) {
- ndi_rele_devi(pdip);
- return (0);
- }
-
- if (ddi_initchild(pdip, rdip) != DDI_SUCCESS) {
- rv = NDI_FAILURE;
- goto out;
- }
- }
-
- ASSERT(i_ddi_node_state(rdip) >= DS_INITIALIZED);
-
- devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
-
- (void) ddi_deviname(rdip, devnm);
-
- if ((rv = ndi_devi_config_one(pdip, devnm+1, &rdip,
- NDI_DEVI_ONLINE | NDI_CONFIG)) == NDI_SUCCESS) {
- /* release hold from ndi_devi_config_one() */
- ndi_rele_devi(rdip);
- }
-
- kmem_free(devnm, MAXNAMELEN + 1);
-out:
- if (rv != NDI_SUCCESS && dipp && rdip) {
- ndi_hold_devi(rdip);
- *dipp = rdip;
- }
- ndi_rele_devi(pdip);
- return (ndi2errno(rv));
-}
-
-void
-e_ddi_branch_hold(dev_info_t *rdip)
-{
- if (e_ddi_branch_held(rdip)) {
- cmn_err(CE_WARN, "e_ddi_branch_hold: branch already held");
- return;
- }
-
- mutex_enter(&DEVI(rdip)->devi_lock);
- if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) == 0) {
- DEVI(rdip)->devi_flags |= DEVI_BRANCH_HELD;
- DEVI(rdip)->devi_ref++;
- }
- ASSERT(DEVI(rdip)->devi_ref > 0);
- mutex_exit(&DEVI(rdip)->devi_lock);
-}
-
-int
-e_ddi_branch_held(dev_info_t *rdip)
-{
- int rv = 0;
-
- mutex_enter(&DEVI(rdip)->devi_lock);
- if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) &&
- DEVI(rdip)->devi_ref > 0) {
- rv = 1;
- }
- mutex_exit(&DEVI(rdip)->devi_lock);
-
- return (rv);
-}
-void
-e_ddi_branch_rele(dev_info_t *rdip)
-{
- mutex_enter(&DEVI(rdip)->devi_lock);
- DEVI(rdip)->devi_flags &= ~DEVI_BRANCH_HELD;
- DEVI(rdip)->devi_ref--;
- mutex_exit(&DEVI(rdip)->devi_lock);
-}
-
-int
-e_ddi_branch_unconfigure(
- dev_info_t *rdip,
- dev_info_t **dipp,
- uint_t flags)
-{
- int circ, rv;
- int destroy;
- char *devnm;
- uint_t nflags;
- dev_info_t *pdip;
-
- if (dipp)
- *dipp = NULL;
-
- if (rdip == NULL)
- return (EINVAL);
-
- pdip = ddi_get_parent(rdip);
-
- ASSERT(pdip);
-
- /*
- * Check if caller holds pdip busy - can cause deadlocks during
- * devfs_clean()
- */
- if (DEVI_BUSY_OWNED(pdip)) {
- cmn_err(CE_WARN, "e_ddi_branch_unconfigure: failed: parent"
- " devinfo node(%p) is busy held", (void *)pdip);
- return (EINVAL);
- }
-
- destroy = (flags & DEVI_BRANCH_DESTROY) ? 1 : 0;
-
- devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
-
- ndi_devi_enter(pdip, &circ);
- (void) ddi_deviname(rdip, devnm);
- ndi_devi_exit(pdip, circ);
-
- /*
- * ddi_deviname() returns a component name with / prepended.
- */
- (void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE);
-
- ndi_devi_enter(pdip, &circ);
-
- /*
- * Recreate device name as it may have changed state (init/uninit)
- * when parent busy lock was dropped for devfs_clean()
- */
- (void) ddi_deviname(rdip, devnm);
-
- if (!e_ddi_branch_held(rdip)) {
- kmem_free(devnm, MAXNAMELEN + 1);
- ndi_devi_exit(pdip, circ);
- cmn_err(CE_WARN, "e_ddi_%s_branch: dip(%p) not held",
- destroy ? "destroy" : "unconfigure", (void *)rdip);
- return (EINVAL);
- }
-
- /*
- * Release hold on the branch. This is ok since we are holding the
- * parent busy. If rdip is not removed, we must do a hold on the
- * branch before returning.
- */
- e_ddi_branch_rele(rdip);
-
- nflags = NDI_DEVI_OFFLINE;
- if (destroy || (flags & DEVI_BRANCH_DESTROY)) {
- nflags |= NDI_DEVI_REMOVE;
- destroy = 1;
- } else {
- nflags |= NDI_UNCONFIG; /* uninit but don't remove */
- }
-
- if (flags & DEVI_BRANCH_EVENT)
- nflags |= NDI_POST_EVENT;
-
- if (i_ddi_devi_attached(pdip) &&
- (i_ddi_node_state(rdip) >= DS_INITIALIZED)) {
- rv = ndi_devi_unconfig_one(pdip, devnm+1, dipp, nflags);
- } else {
- rv = e_ddi_devi_unconfig(rdip, dipp, nflags);
- if (rv == NDI_SUCCESS) {
- ASSERT(!destroy || ddi_get_child(rdip) == NULL);
- rv = ndi_devi_offline(rdip, nflags);
- }
- }
-
- if (!destroy || rv != NDI_SUCCESS) {
- /* The dip still exists, so do a hold */
- e_ddi_branch_hold(rdip);
- }
-out:
- kmem_free(devnm, MAXNAMELEN + 1);
- ndi_devi_exit(pdip, circ);
- return (ndi2errno(rv));
-}
-
-int
-e_ddi_branch_destroy(dev_info_t *rdip, dev_info_t **dipp, uint_t flag)
-{
- return (e_ddi_branch_unconfigure(rdip, dipp,
- flag|DEVI_BRANCH_DESTROY));
-}
-
-/*
- * Number of chains for hash table
- */
-#define NUMCHAINS 17
-
-/*
- * Devinfo busy arg
- */
-struct devi_busy {
- int dv_total;
- int s_total;
- mod_hash_t *dv_hash;
- mod_hash_t *s_hash;
- int (*callback)(dev_info_t *, void *, uint_t);
- void *arg;
-};
-
-static int
-visit_dip(dev_info_t *dip, void *arg)
-{
- uintptr_t sbusy, dvbusy, ref;
- struct devi_busy *bsp = arg;
-
- ASSERT(bsp->callback);
-
- /*
- * A dip cannot be busy if its reference count is 0
- */
- if ((ref = e_ddi_devi_holdcnt(dip)) == 0) {
- return (bsp->callback(dip, bsp->arg, 0));
- }
-
- if (mod_hash_find(bsp->dv_hash, dip, (mod_hash_val_t *)&dvbusy))
- dvbusy = 0;
-
- /*
- * To catch device opens currently maintained on specfs common snodes.
- */
- if (mod_hash_find(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy))
- sbusy = 0;
-
-#ifdef DEBUG
- if (ref < sbusy || ref < dvbusy) {
- cmn_err(CE_WARN, "dip(%p): sopen = %lu, dvopen = %lu "
- "dip ref = %lu\n", (void *)dip, sbusy, dvbusy, ref);
- }
-#endif
-
- dvbusy = (sbusy > dvbusy) ? sbusy : dvbusy;
-
- return (bsp->callback(dip, bsp->arg, dvbusy));
-}
-
-static int
-visit_snode(struct snode *sp, void *arg)
-{
- uintptr_t sbusy;
- dev_info_t *dip;
- int count;
- struct devi_busy *bsp = arg;
-
- ASSERT(sp);
-
- /*
- * The stable lock is held. This prevents
- * the snode and its associated dip from
- * going away.
- */
- dip = NULL;
- count = spec_devi_open_count(sp, &dip);
-
- if (count <= 0)
- return (DDI_WALK_CONTINUE);
-
- ASSERT(dip);
-
- if (mod_hash_remove(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy))
- sbusy = count;
- else
- sbusy += count;
-
- if (mod_hash_insert(bsp->s_hash, dip, (mod_hash_val_t)sbusy)) {
- cmn_err(CE_WARN, "%s: s_hash insert failed: dip=0x%p, "
- "sbusy = %lu", "e_ddi_branch_referenced",
- (void *)dip, sbusy);
- }
-
- bsp->s_total += count;
-
- return (DDI_WALK_CONTINUE);
-}
-
-static void
-visit_dvnode(struct dv_node *dv, void *arg)
-{
- uintptr_t dvbusy;
- uint_t count;
- struct vnode *vp;
- struct devi_busy *bsp = arg;
-
- ASSERT(dv && dv->dv_devi);
-
- vp = DVTOV(dv);
-
- mutex_enter(&vp->v_lock);
- count = vp->v_count;
- mutex_exit(&vp->v_lock);
-
- if (!count)
- return;
-
- if (mod_hash_remove(bsp->dv_hash, dv->dv_devi,
- (mod_hash_val_t *)&dvbusy))
- dvbusy = count;
- else
- dvbusy += count;
-
- if (mod_hash_insert(bsp->dv_hash, dv->dv_devi,
- (mod_hash_val_t)dvbusy)) {
- cmn_err(CE_WARN, "%s: dv_hash insert failed: dip=0x%p, "
- "dvbusy=%lu", "e_ddi_branch_referenced",
- (void *)dv->dv_devi, dvbusy);
- }
-
- bsp->dv_total += count;
-}
-
-/*
- * Returns reference count on success or -1 on failure.
- */
-int
-e_ddi_branch_referenced(
- dev_info_t *rdip,
- int (*callback)(dev_info_t *dip, void *arg, uint_t ref),
- void *arg)
-{
- int circ;
- char *path;
- dev_info_t *pdip;
- struct devi_busy bsa = {0};
-
- ASSERT(rdip);
-
- path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-
- ndi_hold_devi(rdip);
-
- pdip = ddi_get_parent(rdip);
-
- ASSERT(pdip);
-
- /*
- * Check if caller holds pdip busy - can cause deadlocks during
- * devfs_walk()
- */
- if (!e_ddi_branch_held(rdip) || DEVI_BUSY_OWNED(pdip)) {
- cmn_err(CE_WARN, "e_ddi_branch_referenced: failed: "
- "devinfo branch(%p) not held or parent busy held",
- (void *)rdip);
- ndi_rele_devi(rdip);
- kmem_free(path, MAXPATHLEN);
- return (-1);
- }
-
- ndi_devi_enter(pdip, &circ);
- (void) ddi_pathname(rdip, path);
- ndi_devi_exit(pdip, circ);
-
- bsa.dv_hash = mod_hash_create_ptrhash("dv_node busy hash", NUMCHAINS,
- mod_hash_null_valdtor, sizeof (struct dev_info));
-
- bsa.s_hash = mod_hash_create_ptrhash("snode busy hash", NUMCHAINS,
- mod_hash_null_valdtor, sizeof (struct snode));
-
- if (devfs_walk(path, visit_dvnode, &bsa)) {
- cmn_err(CE_WARN, "e_ddi_branch_referenced: "
- "devfs walk failed for: %s", path);
- kmem_free(path, MAXPATHLEN);
- bsa.s_total = bsa.dv_total = -1;
- goto out;
- }
-
- kmem_free(path, MAXPATHLEN);
-
- /*
- * Walk the snode table to detect device opens, which are currently
- * maintained on specfs common snodes.
- */
- spec_snode_walk(visit_snode, &bsa);
-
- if (callback == NULL)
- goto out;
-
- bsa.callback = callback;
- bsa.arg = arg;
-
- if (visit_dip(rdip, &bsa) == DDI_WALK_CONTINUE) {
- ndi_devi_enter(rdip, &circ);
- ddi_walk_devs(ddi_get_child(rdip), visit_dip, &bsa);
- ndi_devi_exit(rdip, circ);
- }
-
-out:
- ndi_rele_devi(rdip);
- mod_hash_destroy_ptrhash(bsa.s_hash);
- mod_hash_destroy_ptrhash(bsa.dv_hash);
- return (bsa.s_total > bsa.dv_total ? bsa.s_total : bsa.dv_total);
-}