devcfg.c revision 39cddb10a31c1c2e66aed69e6871d09caa4c8147
/*
* 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
* 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 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
#include <sys/instance.h>
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/pathname.h>
#include <sys/sysevent.h>
#include <sys/sysmacros.h>
#include <sys/sunldi_impl.h>
#include <sys/bootprops.h>
#include <sys/instance.h>
#include <sys/iommulib.h>
#endif
#ifdef DEBUG
#else
int ddidebug = 0;
#endif
#define MT_CONFIG_OP 0
#define MT_UNCONFIG_OP 1
/* Multi-threaded configuration */
struct mt_config_handle {
int mtc_thr_count;
int mtc_flags;
int mtc_op; /* config or unconfig */
int mtc_error; /* operation error */
#ifdef DEBUG
int total_time;
#endif /* DEBUG */
};
struct devi_nodeid {
struct devi_nodeid *next;
};
struct devi_nodeid_list {
};
/* used to keep track of branch remove events to be generated */
struct brevq_node {
char *brn_deviname;
struct brevq_node *brn_sibling;
struct brevq_node *brn_child;
};
static struct devi_nodeid_list devi_nodeid_list;
/*
* Well known nodes which are attached first at boot time.
*/
/*
* A non-global zone's /dev is derived from the device tree.
* This generation number serves to indicate when a zone's
* /dev may need to be updated.
*/
/* block all future dev_info state changes */
hrtime_t volatile devinfo_freeze = 0;
static ulong_t devinfo_attach_detach = 0;
extern int sys_shutdown;
extern kmutex_t global_vhci_lock;
/* bitset of DS_SYSAVAIL & DS_RECONFIG - no races, no lock */
static int devname_state = 0;
/*
* The devinfo snapshot cache and related variables.
* The only field in the di_cache structure that needs initialization
* is the mutex (cache_lock). However, since this is an adaptive mutex
* (MUTEX_DEFAULT) - it is automatically initialized by being allocated
* in zeroed memory (static storage class). Therefore no explicit
* initialization of the di_cache structure is needed.
*/
int di_cache_debug = 0;
/* For ddvis, which needs pseudo children under PCI */
int pci_allow_pseudo_children = 0;
/* Allow path-oriented alias driver binding on driver.conf enumerated nodes */
int driver_conf_allow_path_alias = 1;
/*
* The following switch is for service people, in case a
* 3rd party driver depends on identify(9e) being called.
*/
int identify_9e = 0;
/*
* Add flag so behaviour of preventing attach for retired persistant nodes
* can be disabled.
*/
int retire_prevents_attach = 1;
int mtc_off; /* turn off mt config */
int quiesce_debug = 0;
#define DDI_ALIAS_HASH_SIZE (2700)
static int devinfo_log_size; /* size in pages */
static void link_to_driver_list(dev_info_t *);
static void unlink_from_driver_list(dev_info_t *);
static dev_info_t *find_duplicate_child();
static void add_global_props(dev_info_t *);
static void remove_global_props(dev_info_t *);
static int uninit_node(dev_info_t *);
static void da_log_init(void);
static void da_log_enter(dev_info_t *);
static int reset_nexus_flags(dev_info_t *, void *);
static void ddi_optimize_dtree(dev_info_t *);
static int is_leaf_node(dev_info_t *);
int, major_t, int, struct brevq_node **);
static void mt_config_children(struct mt_config_handle *);
static void mt_config_driver(struct mt_config_handle *);
static int mt_config_fini(struct mt_config_handle *);
struct brevq_node **);
static int
static void i_link_vhci_node(dev_info_t *);
static void quiesce_one_device(dev_info_t *, void *);
char *ddi_curr_redirect(char *currpath);
/*
* dev_info cache and node management
*/
/* initialize dev_info node cache */
void
{
da_log_init();
}
/*
* Allocating a dev_info node, callable from interrupt context with KM_NOSLEEP
* The allocated node has a reference count of 0.
*/
{
struct devi_nodeid *elem;
static char failed[] = "i_ddi_alloc_node: out of memory";
return (NULL);
}
if (devinfo_audit_log) {
goto fail;
}
goto fail;
/* default binding name is node name */
/*
* Make a copy of system property
*/
if (sys_prop &&
== NULL)
goto fail;
/*
* Assign devi_nodeid, devi_node_class, devi_node_attributes
* according to the following algorithm:
*
* nodeid arg node class node attributes
*
* DEVI_PSEUDO_NODEID DDI_NC_PSEUDO A
* DEVI_SID_NODEID DDI_NC_PSEUDO A,P
* DEVI_SID_HIDDEN_NODEID DDI_NC_PSEUDO A,P,H
* DEVI_SID_HP_NODEID DDI_NC_PSEUDO A,P,h
* DEVI_SID_HP_HIDDEN_NODEID DDI_NC_PSEUDO A,P,H,h
* other DDI_NC_PROM P
*
* Where A = DDI_AUTO_ASSIGNED_NODEID (auto-assign a nodeid)
* and P = DDI_PERSISTENT
* and H = DDI_HIDDEN_NODE
* and h = DDI_HOTPLUG_NODE
*
* auto-assigned nodeids are also auto-freed.
*/
devi->devi_node_attributes = 0;
switch (nodeid) {
case DEVI_SID_HIDDEN_NODEID:
goto sid;
case DEVI_SID_HP_NODEID:
goto sid;
goto sid;
case DEVI_SID_NODEID:
goto fail;
/*FALLTHROUGH*/
case DEVI_PSEUDO_NODEID:
panic("i_ddi_alloc_node: out of nodeids");
/*NOTREACHED*/
}
break;
default:
goto fail;
/*
* the nodetype is 'prom', try to 'take' the nodeid now.
* This requires memory allocation, so check for failure.
*/
goto fail;
}
break;
}
}
/*
* Instance is normally initialized to -1. In a few special
* cases, the caller may specify an instance (e.g. CPU nodes).
*/
/*
* set parent and bus_ctl parent
*/
return ((dev_info_t *)devi);
fail:
if (devi->devi_sys_prop_ptr)
if (devi->devi_node_name)
if (devi->devi_audit)
return (NULL);
}
/*
* free a dev_info structure.
* NB. Not callable from interrupt since impl_ddi_free_nodeid may block.
*/
void
{
struct devi_nodeid *elem;
/* free devi_addr_buf allocated by ddi_set_name_addr() */
if (devi->devi_addr_buf)
if (ndi_dev_is_persistent_node(dip)) {
}
if (devi->devi_sys_prop_ptr)
if (devi->devi_hw_prop_ptr)
if (devi->devi_audit) {
}
if (devi->devi_device_class)
"dip=%p", (void *)dip));
/* free this last since contract_device_remove_dip() uses it */
/* free event data */
if (devi->devi_ev_path)
}
/*
* Node state transitions
*/
/*
* Change the node name
*/
int
{
return (DDI_SUCCESS);
/*
* pcicfg_fix_ethernet requires a name change after node
* is linked into the tree. When pcicfg is fixed, we
* should only allow name change in DS_PROTO state.
*/
/*
* Don't allow name change once node is bound
*/
"ndi_devi_set_nodename: node already bound dip = %p,"
return (NDI_FAILURE);
}
return (NDI_SUCCESS);
}
void
{
struct devi_nodeid *elem;
if (!ndi_dev_is_persistent_node(dip))
return;
}
static int
{
static const char *fcn = "i_ddi_remove_devimap";
if (!ndi_dev_is_persistent_node(dip))
return (DDI_SUCCESS);
/*
* The following check is done with dno_lock held
* to prevent race between dip removal and
* e_ddi_prom_node_to_dip()
*/
if (e_ddi_devi_holdcnt(dip)) {
return (DDI_FAILURE);
}
break;
}
}
else if (elem)
else
panic("%s: devinfo node(%p) not found",
return (DDI_SUCCESS);
}
/*
* Link this node into the devinfo tree and add to orphan list
* Not callable from interrupt context
*/
static void
{
dev_info_t **dipp;
/*
* Hold the global_vhci_lock before linking any direct
* children of rootnex driver. This special lock protects
* linking and unlinking for rootnext direct children.
*/
/*
* attach the node to end of the list unless the node is already there
*/
}
/*
* Now that we are in the tree, update the devi-nodeid map.
*/
/*
* This is a temporary workaround for Bug 4618861.
* We keep the scsi_vhci nexus node on the left side of the devinfo
* tree (under the root nexus driver), so that virtual nodes under
* scsi_vhci will be SUSPENDed first and RESUMEd last. This ensures
* that the pHCI nodes are active during times when their clients
* may be depending on them. This workaround embodies the knowledge
* that system PM and CPR both traverse the tree left-to-right during
* SUSPEND and right-to-left during RESUME.
* driver also.
*/
/* Add scsi_vhci to beginning of list */
/* scsi_vhci under rootnex */
} else {
/* Add to end of list */
}
/*
* Release the global_vhci_lock before linking any direct
* children of rootnex driver.
*/
/* persistent nodes go on orphan list */
}
/*
* Unlink this node from the devinfo tree
*/
static int
{
dev_info_t **dipp;
ddi_node_name(dip)));
/* check references */
return (DDI_FAILURE);
/*
* Hold the global_vhci_lock before linking any direct
* children of rootnex driver.
*/
}
if (*dipp) {
} else {
devi->devi_node_name));
}
/*
* Release the global_vhci_lock before linking any direct
* children of rootnex driver.
*/
/* Remove node from orphan list */
if (ndi_dev_is_persistent_node(dip)) {
}
/* Update parent's hotplug handle list */
}
return (DDI_SUCCESS);
}
/*
* Bind this devinfo node to a driver. If compat is NON-NULL, try that first.
* Else, use the node-name.
*
* NOTE: IEEE1275 specifies that nodename should be tried before compatible.
* Solaris implementation binds nodename after compatible.
*
* If we find a binding,
* - set the binding name to the string,
* - set major number to driver major
*
* If we don't find a binding,
* - return failure
*/
static int
{
char *p = NULL;
return (DDI_FAILURE);
}
/* find the driver with most specific binding using compatible */
if (major == DDI_MAJOR_T_NONE)
return (DDI_FAILURE);
if (p != NULL) {
devi->devi_node_name, p));
}
/* Link node to per-driver list */
/*
* reset parent flag so that nexus will merge .conf props
*/
if (ndi_dev_is_persistent_node(dip)) {
}
return (DDI_SUCCESS);
}
/*
* Unbind this devinfo node
* Called before the node is destroyed or driver is removed from system
*/
static int
{
/* check references */
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* Initialize a node: calls the parent nexus' bus_ctl ops to do the operation.
* Must hold parent and per-driver list while calling this function.
* A successful init_node() returns with an active ndi_hold_devi() hold on
* the parent.
*/
static int
{
int error;
char *path;
/* should be DS_READY except for pcmcia ... */
/*
* The parent must have a bus_ctl operation.
*/
error = DDI_FAILURE;
goto out;
}
/*
* Invoke the parent's bus_ctl operation with the DDI_CTLOPS_INITCHILD
* command to transform the child to canonical form 1. If there
* is an error, ddi_remove_child should be called, to clean up.
*/
if (error != DDI_SUCCESS) {
/*
* If a nexus INITCHILD implementation calls ddi_devid_regster()
* prior to setting devi_addr, the devid is not recorded in
* the devid cache (i.e. DEVI_CACHED_DEVID is not set).
* With mpxio, while the vhci client path may be missing
* from the cache, phci pathinfo paths may have already be
* added to the cache, against the client dip, by use of
* e_devid_cache_pathinfo(). Because of this, when INITCHILD
* of the client fails, we need to purge the client dip from
* the cache even if DEVI_CACHED_DEVID is not set - if only
* devi_devid_str is set.
*/
} else
/* in case nexus driver didn't clear this field */
error = DDI_FAILURE;
goto out;
}
/* recompute path after initchild for @addr information */
/* Check for duplicate nodes */
/*
* uninit_node() the duplicate - a successful uninit_node()
* will release inital hold of parent using ndi_rele_devi().
*/
"node %s failed", path);
}
error = DDI_FAILURE;
goto out;
}
/*
* If a devid was registered for a DS_BOUND node then the devid_cache
* may not have captured the path. Detect this situation and ensure that
* the path enters the cache now that devi_addr is established.
*/
}
}
/*
* Check to see if we have a path-oriented driver alias that overrides
* the current driver binding. If so, we need to rebind. This check
* needs to be delayed until after a successful DDI_CTLOPS_INITCHILD,
* so the unit-address is established on the last component of the path.
*
* NOTE: Allowing a path-oriented alias to change the driver binding
* of a driver.conf node results in non-intuitive property behavior.
* We provide a tunable (driver_conf_allow_path_alias) to control
* this behavior. See uninit_node() for more details.
*
* NOTE: If you are adding a path-oriented alias for the boot device,
* and there is mismatch between OBP and the kernel in regard to
* generic name use, like "disk" .vs. "ssd", then you will need
* to add a path-oriented alias for both paths.
*/
/* Mark node for rebind processing. */
/*
* Add an extra hold on the parent to prevent it from ever
* having a zero devi_ref during the child rebind process.
* This is necessary to ensure that the parent will never
* detach(9E) during the rebind.
*/
/*
* uninit_node() current binding - a successful uninit_node()
* will release extra hold of parent using ndi_rele_devi().
*/
"of node %s failed", path);
goto out;
}
/* Unbind: demote the node back to DS_LINKED. */
"of node %s failed", path);
goto out;
}
/* establish rebinding name */
/*
* Now that we are demoted and marked for rebind, repromote.
* We need to do this in steps, instead of just calling
* ddi_initchild, so that we can redo the merge operation
* after we are rebound to the path-bound driver.
*
* Start by rebinding node to the path-bound driver.
*/
"of node %s failed", path);
goto out;
}
/*
* If the node is not a driver.conf node then merge
* driver.conf properties from new path-bound driver.conf.
*/
(void) i_ndi_make_spec_children(pdip, 0);
/*
* Now that we have taken care of merge, repromote back
* to DS_INITIALIZED.
*/
/*
* Release our initial hold. If ddi_initchild() was
* successful then it will return with the active hold.
*/
goto out;
}
/*
* Apply multi-parent/deep-nexus optimization to the new node
*/
/* On failure ensure that DEVI_REBIND is cleared */
}
return (error);
}
/*
* Uninitialize node
* The per-driver list must be held busy during the call.
* A successful uninit_node() releases the init_node() hold on
* the parent by calling ndi_rele_devi().
*/
static int
{
int node_state_entry;
int (*f)();
int error;
char *addr;
/*
* Don't check for references here or else a ref-counted
* dip cannot be downgraded by the framework.
*/
(node_state_entry == DS_INITIALIZED));
return (DDI_FAILURE);
}
/*
* save the @addr prior to DDI_CTLOPS_UNINITCHILD for use in
* freeing the instance if it succeeds.
*/
if (node_state_entry == DS_INITIALIZED) {
if (addr)
} else {
}
if (error == DDI_SUCCESS) {
/* ensure that devids are unregistered */
} else
/* if uninitchild forgot to set devi_addr to NULL do it now */
/*
* Free instance number. This is a no-op if instance has
* been kept by probe_node(). Avoid free when we are called
* from init_node (DS_BOUND) because the instance has not yet
* been assigned.
*/
if (node_state_entry == DS_INITIALIZED) {
}
/* release the init_node hold */
/*
* NOTE: The decision on whether to allow a path-oriented
* rebind of a driver.conf enumerated node is made by
* init_node() based on driver_conf_allow_path_alias. The
* rebind code below prevents deletion of system properties
* on driver.conf nodes.
*
* When driver_conf_allow_path_alias is set, property behavior
* on rebound driver.conf file is non-intuitive. For a
* driver.conf node, the unit-address properties come from
* the driver.conf file as system properties. Removing system
* properties from a driver.conf node makes the node
* useless (we get node without unit-address properties) - so
* we leave system properties in place. The result is a node
* where system properties come from the node being rebound,
* and global properties come from the driver.conf file
* of the driver we are rebinding to. If we could determine
* that the path-oriented alias driver.conf file defined a
* node at the same unit address, it would be best to use
* that node and avoid the non-intuitive property behavior.
* Unfortunately, the current "merge" code does not support
* this, so we live with the non-intuitive property behavior.
*/
if (!((ndi_dev_is_persistent_node(dip) == 0) &&
} else {
}
if (addr)
return (error);
}
/*
* Invoke driver's probe entry point to probe for existence of hardware.
* Keep instance permanent for successful probe and leaf nodes.
*
* Per-driver list must be held busy while calling this function.
*/
static int
{
int rv;
/* temporarily hold the driver while we probe */
"probe_node: 0x%p(%s%d) cannot load driver\n",
return (DDI_FAILURE);
}
if (identify_9e != 0)
(void) devi_identify(dip);
/* release the driver now that probe is complete */
switch (rv) {
case DDI_PROBE_SUCCESS: /* found */
case DDI_PROBE_DONTCARE: /* ddi_dev_is_sid */
rv = DDI_SUCCESS;
break;
case DDI_PROBE_PARTIAL: /* maybe later */
case DDI_PROBE_FAILURE: /* not found */
"probe_node: 0x%p(%s%d) no hardware found%s\n",
rv = DDI_FAILURE;
break;
default:
#ifdef DEBUG
#endif /* DEBUG */
rv = DDI_FAILURE;
break;
}
return (rv);
}
/*
* Unprobe a node. Simply reset the node state.
* Per-driver list must be held busy while calling this function.
*/
static int
{
/*
* Don't check for references here or else a ref-counted
* dip cannot be downgraded by the framework.
*/
return (DDI_SUCCESS);
}
/*
* Attach devinfo node.
* Per-driver list must be held busy.
*/
static int
{
int rv;
/*
* Tell mpxio framework that a node is about to online.
*/
return (DDI_FAILURE);
}
/* no recursive attachment */
/*
* Hold driver the node is bound to.
*/
/*
* We were able to load driver for probing, so we should
* not get here unless something really bad happened.
*/
return (DDI_FAILURE);
}
"nexus_enum_tq", 1,
TASKQ_DEFAULTPRI, 0);
if (rv != DDI_SUCCESS) {
/*
* Cleanup dacf reservations
*/
/* release the driver if attach failed */
return (DDI_FAILURE);
} else
/* successful attach, return with driver held */
return (DDI_SUCCESS);
}
/*
* Detach devinfo node.
* Per-driver list must be held busy.
*/
static int
{
int rv;
/* check references */
return (DDI_FAILURE);
/*
* NOTE: If we are processing a pHCI node then the calling code
* must detect this and ndi_devi_enter() in (vHCI, parent(pHCI))
* order unless pHCI and vHCI are siblings. Code paths leading
* here that must ensure this ordering include:
* unconfig_immediate_children(), devi_unconfig_one(),
* ndi_devi_unconfig_one(), ndi_devi_offline().
*/
/* Offline the device node with the mpxio framework. */
return (DDI_FAILURE);
}
/* drain the taskq */
if (rv != DDI_SUCCESS) {
"detach_node: 0x%p(%s%d) failed\n",
return (DDI_FAILURE);
}
/*
* Close any iommulib mediated linkage to an IOMMU
*/
if (IOMMU_USED(dip))
#endif
/* destroy the taskq */
}
/* Cleanup dacf reservations */
/* remove any additional flavors that were added */
}
/* Remove properties and minor nodes in case driver forgots */
/* a detached node can't have attached or .conf children */
/*
* If the instance has successfully detached in detach_driver() context,
* clear DN_DRIVER_HELD for correct ddi_hold_installed_driver()
* behavior. Consumers like qassociate() depend on this (via clnopen()).
*/
if (flag & NDI_DETACH_DRIVER) {
}
/* successful detach, release the driver */
return (DDI_SUCCESS);
}
/*
* Run dacf post_attach routines
*/
static int
{
int rval;
/*
* For hotplug busses like USB, it's possible that devices
* are removed but dip is still around. We don't want to
* run dacf routines as part of detach failure recovery.
*
* Pretend success until we figure out how to prevent
* access to such devinfo nodes.
*/
if (DEVI_IS_DEVICE_REMOVED(dip))
return (DDI_SUCCESS);
/*
* if dacf_postattach failed, report it to the framework
* so that it can be retried later at the open time.
*/
/*
* Plumbing during postattach may fail because of the
* underlying device is not ready. This will fail ndi_devi_config()
* in dv_filldir() and a warning message is issued. The message
* from here will explain what happened
*/
if (rval != DACF_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Run dacf pre-detach routines
*/
static int
{
int ret;
/*
* Don't auto-detach if DDI_FORCEATTACH or DDI_NO_AUTODETACH
* properties are set.
*/
if (flag & NDI_AUTODETACH) {
return (DDI_FAILURE);
/* check for driver global version of DDI_NO_AUTODETACH */
return (DDI_FAILURE);
}
}
return (ret);
}
/*
* Wrapper for making multiple state transitions
*/
/*
* i_ndi_config_node: upgrade dev_info node into a specified state.
* It is a bit tricky because the locking protocol changes before and
* after a node is bound to a driver. All locks are held external to
* this function.
*/
int
{
int rv = DDI_SUCCESS;
/* don't allow any more changes to the device tree */
if (devinfo_freeze) {
rv = DDI_FAILURE;
break;
}
switch (i_ddi_node_state(dip)) {
case DS_PROTO:
/*
* only caller can reference this node, no external
* locking needed.
*/
break;
case DS_LINKED:
/*
* Three code path may attempt to bind a node:
* - boot code
* - add_drv
* - hotplug thread
* Boot code is single threaded, add_drv synchronize
* on a userland lock, and hotplug synchronize on
* hotplug_lk. There could be a race between add_drv
* and hotplug thread. We'll live with this until the
* conversion to top-down loading.
*/
break;
case DS_BOUND:
/*
* The following transitions synchronizes on the
* per-driver busy changing flag, since we already
* have a driver.
*/
break;
case DS_INITIALIZED:
break;
case DS_PROBED:
/*
* If node is retired and persistent, then prevent
* attach. We can't do this for non-persistent nodes
* as we would lose evidence that the node existed.
*/
retire_prevents_attach == 1) {
rv = DDI_FAILURE;
break;
}
break;
case DS_ATTACHED:
break;
case DS_READY:
break;
default:
/* should never reach here */
ASSERT("unknown devinfo state");
}
}
return (rv);
}
/*
* i_ndi_unconfig_node: downgrade dev_info node into a specified state.
*/
int
{
int rv = DDI_SUCCESS;
/* don't allow any more changes to the device tree */
if (devinfo_freeze) {
rv = DDI_FAILURE;
break;
}
switch (i_ddi_node_state(dip)) {
case DS_PROTO:
break;
case DS_LINKED:
/*
* Persistent nodes are only removed by hotplug code
* .conf nodes synchronizes on per-driver list.
*/
break;
case DS_BOUND:
/*
* The following transitions synchronizes on the
* per-driver busy changing flag, since we already
* have a driver.
*/
break;
case DS_INITIALIZED:
break;
case DS_PROBED:
break;
case DS_ATTACHED:
membar_enter(); /* ensure visibility for hold_devi */
break;
case DS_READY:
break;
default:
ASSERT("unknown devinfo state");
}
}
return (rv);
}
/*
* ddi_initchild: transform node to DS_INITIALIZED state
*/
int
{
return (ret);
}
/*
* ddi_uninitchild: transform node down to DS_BOUND state
*/
int
{
return (ret);
}
/*
* i_ddi_attachchild: transform node to DS_READY/i_ddi_devi_attached() state
*/
static int
{
int ret;
return (DDI_FAILURE);
if (ret == NDI_SUCCESS) {
ret = DDI_SUCCESS;
} else {
/*
* Take it down to DS_INITIALIZED so pm_pre_probe is run
* on the next attach
*/
ret = DDI_FAILURE;
}
return (ret);
}
/*
* i_ddi_detachchild: transform node down to DS_PROBED state
* If it fails, put it back to DS_READY state.
* NOTE: A node that fails detach may be at DS_ATTACHED instead
* of DS_READY for a small amount of time - this is the source of
* transient DS_READY->DS_ATTACHED->DS_READY state changes.
*/
static int
{
int ret;
if (ret != DDI_SUCCESS)
else
/* allow pm_pre_probe to reestablish pm state */
return (ret);
}
/*
* Add a child and bind to driver
*/
{
int circ;
/* allocate a new node */
return (dip);
}
/*
* ddi_remove_child: remove the dip. The parent must be attached and held
*/
int
{
/*
* If we still have children, for example SID nodes marked
* as persistent but not attached, attempt to remove them.
*/
if (ret != NDI_SUCCESS) {
return (DDI_FAILURE);
}
}
if (ret != DDI_SUCCESS)
return (ret);
return (DDI_SUCCESS);
}
/*
* NDI wrappers for ref counting, node allocation, and transitions
*/
/*
* Caller is assumed to prevent the devi from detaching during this call
*/
void
{
membar_enter(); /* make sure stores are flushed */
}
void
{
membar_enter(); /* make sure stores are flushed */
}
int
{
}
/*
*/
struct dev_ops *
{
return (NULL);
}
void
{
}
/*
* Single thread entry into devinfo node for modifying its children (devinfo,
* pathinfo, and minor). To verify in ASSERTS use DEVI_BUSY_OWNED macro.
*/
void
{
/* for vHCI, enforce (vHCI, pHCI) ndi_deve_enter() order */
devi->devi_circular++;
} else {
if (panicstr) {
return;
}
}
}
/*
* Release ndi_devi_enter or successful ndi_devi_tryenter.
*/
void
{
if (panicstr)
return;
if (circular != 0) {
devi->devi_circular--;
} else {
}
/*
* For pHCI exit we issue a broadcast to vHCI for ndi_devi_config_one()
* doing cv_wait on vHCI.
*/
if (vdevi) {
}
}
}
}
/*
* Release ndi_devi_enter and wait for possibility of new children, avoiding
* possibility of missing broadcast before getting to cv_timedwait().
*/
static void
{
if (panicstr)
return;
/*
* We are called to wait for of a new child, and new child can
* only be added if circular is zero.
*/
/* like ndi_devi_exit with circular of zero */
/* now wait for new children while still holding devi_lock */
}
/*
* Attempt to single thread entry into devinfo node for modifying its children.
*/
int
{
devi->devi_circular++;
} else {
if (!DEVI_BUSY_CHANGING(devi)) {
} else {
rval = 0; /* devi is busy */
}
}
return (rval);
}
/*
* Allocate and initialize a new dev_info structure.
*
* This routine may be called at interrupt time by a nexus in
* response to a hotplug event, therefore memory allocations are
* not allowed to sleep.
*/
int
{
return (NDI_NOMEM);
}
return (NDI_SUCCESS);
}
/*
* Allocate and initialize a new dev_info structure
* This routine may sleep and should not be called at interrupt time
*/
void
{
KM_SLEEP);
}
/*
* Remove an initialized (but not yet attached) dev_info
* node from it's parent.
*/
int
{
return (DDI_FAILURE);
(void) ddi_remove_child(dip, 0);
return (NDI_SUCCESS);
}
/*
* ndi_devi_bind_driver() binds a driver to a given device. If it fails
* to bind the driver, it returns an appropriate error back. Some drivers
* may want to know if the actually failed to bind.
*/
int
{
int ret = NDI_FAILURE;
int circ;
"ndi_devi_bind_driver: %s%d (%p) flags: %x\n",
ret = NDI_SUCCESS;
return (ret);
}
/*
* ndi_devi_unbind_driver: unbind the dip
*/
static int
{
}
/*
* Misc. help routines called by framework only
*/
/*
* Get the state of node
*/
{
}
/*
* Set the state of node
*/
void
{
membar_enter(); /* make sure stores are flushed */
}
/*
* Determine if node is attached. The implementation accommodates transient
* DS_READY->DS_ATTACHED->DS_READY state changes. Outside this file, this
* function should be instead of i_ddi_node_state() DS_ATTACHED/DS_READY
* state checks.
*/
int
{
}
/*
* Common function for finding a node in a sibling list given name and addr.
*
* By default, name is matched with devi_node_name. The following
* alternative match strategies are supported:
*
* FIND_NODE_BY_NODENAME: Match on node name - typical use.
*
* FIND_NODE_BY_DRIVER: A match on driver name bound to node is conducted.
* This support is used for support of OBP generic names and
* for the conversion from driver names to generic names. When
* more consistency in the generic name environment is achieved
* (and not needed for upgrade) this support can be removed.
*
* FIND_NODE_BY_ADDR: Match on just the addr.
* a node bound via a path-based driver alias.
*
* If a child is not named (dev_addr == NULL), there are three
* possible actions:
*
* (1) skip it
* (2) FIND_ADDR_BY_INIT: bring child to DS_INITIALIZED state
* (3) FIND_ADDR_BY_CALLBACK: use a caller-supplied callback function
*/
#define FIND_NODE_BY_NODENAME 0x01
#define FIND_NODE_BY_DRIVER 0x02
#define FIND_NODE_BY_ADDR 0x04
#define FIND_ADDR_BY_INIT 0x10
#define FIND_ADDR_BY_CALLBACK 0x20
static dev_info_t *
int (*callback)(dev_info_t *, char *, int))
{
/* only one way to find a node */
/* only one way to name a node */
((flag & FIND_ADDR_BY_CALLBACK) == 0));
if (by == FIND_NODE_BY_DRIVER) {
if (major == DDI_MAJOR_T_NONE)
return (NULL);
}
/* preallocate buffer of naming node by callback */
if (flag & FIND_ADDR_BY_CALLBACK)
/*
* Walk the child list to find a match
*/
return (NULL);
if (by == FIND_NODE_BY_NODENAME) {
/* match node name */
continue;
} else if (by == FIND_NODE_BY_DRIVER) {
/* match driver major */
continue;
}
/* name the child based on the flag */
if (flag & FIND_ADDR_BY_INIT) {
!= DDI_SUCCESS)
continue;
} else if (flag & FIND_ADDR_BY_CALLBACK) {
continue;
} else {
continue; /* skip */
}
}
/* match addr */
break; /* node found */
}
if (flag & FIND_ADDR_BY_CALLBACK)
return (dip);
}
/*
* Find child of pdip with name: cname@caddr
* Called by init_node() to look for duplicate nodes
*/
static dev_info_t *
{
/* search nodes before dip */
return (dup);
/*
* search nodes after dip; normally this is not needed,
*/
}
/*
* Find a child of a given name and address, using a callback to name
* unnamed children. cname is the binding name.
*/
int (*make_ua)(dev_info_t *, char *, int))
{
int by = FIND_ADDR_BY_CALLBACK;
}
/*
* Find a child of a given name and address, invoking initchild to name
* unnamed children. cname is the node name.
*/
static dev_info_t *
{
/* attempt search without changing state of preceding siblings */
if (dip)
return (dip);
}
/*
* Find a child of a given name and address, invoking initchild to name
* unnamed children. cname is the node name.
*/
static dev_info_t *
{
/* attempt search without changing state of preceding siblings */
if (dip)
return (dip);
}
/*
* Find a child of a given address, invoking initchild to name
* unnamed children. cname is the node name.
*
* NOTE: This function is only used during boot. One would hope that
* unique sibling unit-addresses on hardware branches of the tree would
* be a requirement to avoid two drivers trying to control the same
* piece of hardware. Unfortunately there are some cases where this
* situation exists (/ssm@0,0/pci@1c,700000 /ssm@0,0/sghsc@1c,700000).
* Until unit-address uniqueness of siblings is guaranteed, use of this
* interface for purposes other than boot should be avoided.
*/
static dev_info_t *
{
/* return NULL if called without a unit-address */
return (NULL);
/* attempt search without changing state of preceding siblings */
if (dip)
return (dip);
}
/*
* Deleting a property list. Take care, since some property structures
* may not be fully built.
*/
void
{
while (prop) {
}
}
/*
* Duplicate property list
*/
{
return (NULL);
goto fail;
goto fail;
goto fail;
}
else
}
return (result);
fail:
return (NULL);
}
/*
* Create a reference property list, currently used only for
* driver global properties. Created with ref count of 1.
*/
{
return (list);
}
/*
* protected by dn_lock. The only interfaces modifying
* dn_global_prop_ptr is in impl_make[free]_parlist().
*/
void
{
}
void
{
}
}
/*
* Free table of classes by drivers
*/
void
i_ddi_free_exported_classes(char **classes, int n)
{
return;
}
/*
* Get all classes exported by dip
*/
int
{
extern void lock_hw_class_list();
extern void unlock_hw_class_list();
extern int get_class(const char *, char **);
static char *rootclass = "root";
int n = 0, nclass = 0;
char **buf;
nclass = 1;
if (nclass == 0) {
return (0); /* no class exported */
}
if (dip == ddi_root_node()) {
n = 1;
}
return (nclass);
}
/*
* Helper functions, returns NULL if no memory.
*/
char *
{
char *copy;
return (NULL);
return (NULL);
return (copy);
}
/*
* Load driver.conf file for major. Load all if major == -1.
*
* This is called
* - early in boot after devnames array is initialized
* - from vfs code when certain file systems are mounted
* - from add_drv when a new driver is added
*/
int
{
extern int modrootloaded;
if (major == DDI_MAJOR_T_NONE) {
low = 0;
} else {
return (EINVAL);
}
(void) impl_make_parlist(m);
}
if (modrootloaded) {
}
/* build dn_list from old entries in path_to_inst */
return (0);
}
/*
* Unload a specific driver.conf.
* Don't support unload all because it doesn't make any sense
*/
int
{
int error;
return (EINVAL);
/*
* Take the per-driver lock while unloading driver.conf
*/
return (error);
}
/*
* Merge a .conf node. This is called by nexus drivers to augment
* hw node with properties specified in driver.conf file. This function
* takes a callback routine to name nexus children.
* The parent node must be held busy.
*
* It returns DDI_SUCCESS if the node is merged and DDI_FAILURE otherwise.
*/
int
{
/*
* Look for the hardware node that is the target of the merge;
* return failure if not found.
*/
return (DDI_FAILURE);
}
/*
* Make sure the hardware node is uninitialized and has no property.
* This may not be the case if new .conf files are load after some
* hardware nodes have already been initialized and attached.
*
* N.B. We return success here because the node was *intended*
* to be a merge node because there is a hw node with the name.
*/
if (ndi_dev_is_persistent_node(hwdip) == 0) {
char *buf;
return (DDI_SUCCESS);
}
/*
* If it is possible that the hardware has already been touched
* then don't merge.
*/
char *buf;
"!Cannot merge .conf node %s with hw node %p "
"-- not in proper state",
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
/*
* Merge a "wildcard" .conf node. This is called by nexus drivers to
* augment a set of hw node with properties specified in driver.conf file.
* The parent node must be held busy.
*
* There is no failure mode, since the nexus may or may not have child
* node bound the driver specified by the wildcard node.
*/
void
{
/* never attempt to merge a hw node */
/* must be bound to a driver major number */
/*
* Walk the child list to find all nodes bound to major
* and copy properties.
*/
/*
* Skip nodes not bound to same driver
*/
continue;
/*
* Skip .conf nodes
*/
if (ndi_dev_is_persistent_node(hwdip) == 0)
continue;
/*
* Make sure the node is uninitialized and has no property.
*/
"suitable for merging wildcard conf node %s",
continue;
}
}
}
/*
* Return the major number based on the compatible property. This interface
* may be used in situations where we are trying to detect if a better driver
* now exists for a device, so it must use the 'compatible' property. If
* a non-NULL formp is specified and the binding was based on compatible then
* return the pointer to the form used in *formp.
*/
{
void *compat;
char *p = NULL;
if (formp)
"ddi-assigned")) {
return (major);
}
/*
* Highest precedence binding is a path-oriented alias. Since this
* requires a 'path', this type of binding occurs via more obtuse
* 'rebind'. The need for a path-oriented alias 'rebind' is detected
* after a successful DDI_CTLOPS_INITCHILD to another driver: this is
* is the first point at which the unit-address (or instance) of the
* last component of the path is available (even though the path is
* bound to the wrong driver at this point).
*/
p = devi->devi_rebinding_name;
major = ddi_name_to_major(p);
if (driver_active(major)) {
if (formp)
*formp = p;
return (major);
}
/*
* If for some reason devi_rebinding_name no longer resolves
* to a proper driver then clear DEVI_REBIND.
*/
}
/* look up compatible property */
/* find the highest precedence compatible form with a driver binding */
major = ddi_name_to_major(p);
if (driver_active(major)) {
if (formp)
*formp = p;
return (major);
}
}
/*
* none of the compatible forms have a driver binding, see if
* the node name has a driver binding.
*/
if (driver_active(major))
return (major);
/* no driver */
return (DDI_MAJOR_T_NONE);
}
/*
* Static help functions
*/
/*
* lookup the "compatible" property and cache it's contents in the
* device node.
*/
static int
{
int rv;
int prop_flags;
char **compatstrpp;
char *di_compat_strp;
return (DDI_SUCCESS);
}
if (flag & KM_NOSLEEP) {
}
if (ndi_dev_is_prom_node(dip) == 0) {
}
if (rv == DDI_PROP_NOT_FOUND) {
return (DDI_SUCCESS);
}
if (rv != DDI_PROP_SUCCESS) {
return (DDI_FAILURE);
}
/*
* encode the compatible property data in the dev_info node
*/
rv = DDI_SUCCESS;
if (ncompatstrs != 0) {
if (di_compat_strp != NULL) {
} else {
rv = DDI_FAILURE;
}
}
return (rv);
}
/*
* Create a composite string from a list of strings.
*
* A composite string consists of a single buffer containing one
* or more NULL terminated strings.
*/
static char *
{
char **strpp;
char *cbuf_p;
char *cbuf_ip;
return (NULL);
}
"?failed to allocate device node compatstr");
return (NULL);
}
*(cbuf_ip++) = '\0';
}
return (cbuf_p);
}
static void
{
/*
* Remove from orphan list
*/
if (ndi_dev_is_persistent_node(dip)) {
dnp = &orphanlist;
}
/*
* Add to per driver list
*/
}
static void
{
/*
* Remove from per-driver list
*/
/*
* Add to orphan list
*/
if (ndi_dev_is_persistent_node(dip)) {
dnp = &orphanlist;
}
}
/*
* scan the per-driver list looking for dev_info "dip"
*/
static dev_info_t *
{
return (NULL);
while (idevi) {
return (dip);
}
return (NULL);
}
/*
* insert devinfo node 'dip' into the per-driver instance list
* headed by 'dnp'
*
* Nodes on the per-driver list are ordered: HW - SID - PSEUDO. The order is
* required for merging of .conf file data to work properly.
*/
static void
{
dev_info_t **dipp;
if (ndi_dev_is_prom_node(dip)) {
/*
* Find the first non-prom node or end of list
*/
}
} else if (ndi_dev_is_persistent_node(dip)) {
/*
* Find the first non-persistent node
*/
}
} else {
/*
* Find the end of the list
*/
while (*dipp) {
}
}
}
/*
* add a list of device nodes to the device node list in the
* devnames structure
*/
static void
{
/*
* Look to see if node already exists
*/
} else {
}
}
static void
{
dev_info_t **plist;
}
} else {
"remove_from_dn_list: node %s not found in list",
}
}
/*
* Add and remove reference driver global property list
*/
static void
{
return;
}
}
static void
{
if (proplist) {
}
}
#ifdef DEBUG
/*
* Set this variable to '0' to disable the optimization,
* and to 2 to print debug message.
*/
static int optimize_dtree = 1;
static void
{
/*
* Don't print unless optimize dtree is set to 2+
*/
if (optimize_dtree <= 1)
return;
if (*adeviname == '\0')
adeviname = "root";
}
#else /* DEBUG */
#endif /* DEBUG */
static void
{
struct bus_ops *b;
/*
* Set the unoptimized values
*/
#ifdef DEBUG
if (optimize_dtree == 0)
return;
#endif /* DEBUG */
if (i_ddi_map_fault == b->bus_map_fault) {
"bus_map_fault");
}
if (ddi_dma_allochdl == b->bus_dma_allochdl) {
"bus_dma_allochdl");
}
if (ddi_dma_freehdl == b->bus_dma_freehdl) {
"bus_dma_freehdl");
}
if (ddi_dma_bindhdl == b->bus_dma_bindhdl) {
"bus_dma_bindhdl");
}
if (ddi_dma_unbindhdl == b->bus_dma_unbindhdl) {
"bus_dma_unbindhdl");
}
if (ddi_dma_flush == b->bus_dma_flush) {
"bus_dma_flush");
}
if (ddi_dma_win == b->bus_dma_win) {
"bus_dma_win");
}
if (ddi_dma_mctl == b->bus_dma_ctl) {
}
if (ddi_ctlops == b->bus_ctl) {
}
}
#define MIN_DEVINFO_LOG_SIZE max_ncpus
static void
{
int logsize = devinfo_log_size;
if (logsize == 0)
else if (logsize > MAX_DEVINFO_LOG_SIZE)
sizeof (devinfo_audit_t) + 1;
}
/*
* Log the stack trace in per-devinfo audit structure and also enter
* it into a system wide log for recording the time history.
*/
static void
{
if (devinfo_audit_log == NULL)
return;
/*
* Copy into common log and note the location for tracing history
*/
}
static void
{
int i;
for (i = 0; i < devcnt; i++) {
ddi_rele_driver((major_t)i);
}
}
/*
* Launch a thread to force attach drivers. This avoids penalty on boot time.
*/
void
{
/*
* Attach IB VHCI driver before the force-attach thread attaches the
* IB HCA driver. IB HCA driver will fail if IB Nexus has not yet
* been attached.
*/
}
/*
* This is a private DDI interface for optimizing boot performance.
* I/O subsystem initialization is considered complete when devfsadm
* is executed.
*
* NOTE: The start of syseventd happens to be a convenient indicator
* of the completion of I/O initialization during boot.
* The implementation should be replaced by something more robust.
*/
int
{
extern int sysevent_daemon_init;
return (sysevent_daemon_init);
}
/*
* May be used to determine system boot state
* "Available" means the system is for the most part up
* and initialized, with all system services either up or
* capable of being started. This state is set by devfsadm
* during the boot process. The /dev filesystem infers
* from this when implicit reconfig can be performed,
* ie, devfsadm can be invoked. Please avoid making
* further use of this unless it's really necessary.
*/
int
{
return (devname_state & DS_SYSAVAIL);
}
/*
* May be used to determine if boot is a reconfigure boot.
*/
int
{
return (devname_state & DS_RECONFIG);
}
/*
* Note system services are up, inform /dev.
*/
void
{
if ((devname_state & DS_SYSAVAIL) == 0) {
}
}
/*
* Note reconfiguration boot, inform /dev.
*/
void
{
if ((devname_state & DS_RECONFIG) == 0) {
}
}
/*
* device tree walking
*/
struct walk_elem {
};
static void
{
while (list) {
}
}
static void
{
return;
}
}
/*
* The implementation of ddi_walk_devs().
*/
static int
int do_locking)
{
/*
* Do it in two passes. First pass invoke callback on each
* dip on the sibling list. Second pass invoke callback on
* children of each dip.
*/
while (dip) {
case DDI_WALK_TERMINATE:
return (DDI_WALK_TERMINATE);
case DDI_WALK_PRUNESIB:
/* ignore sibling by setting dip to NULL */
break;
case DDI_WALK_PRUNECHILD:
/* don't worry about children */
break;
case DDI_WALK_CONTINUE:
default:
break;
}
}
/* second pass */
while (head) {
int circ;
if (do_locking)
if (do_locking)
return (DDI_WALK_TERMINATE);
}
if (do_locking)
}
return (DDI_WALK_CONTINUE);
}
/*
* This general-purpose routine traverses the tree of dev_info nodes,
* starting from the given node, and calls the given function for each
* node that it finds with the current node and the pointer arg (which
* can point to a structure of information that the function
* needs) as arguments.
*
* It does the walk a layer at a time, not depth-first. The given function
* must return one of the following values:
* DDI_WALK_CONTINUE
* DDI_WALK_PRUNESIB
* DDI_WALK_PRUNECHILD
* DDI_WALK_TERMINATE
*
* N.B. Since we walk the sibling list, the caller must ensure that
* the parent of dip is held against changes, unless the parent
* is rootnode. ndi_devi_enter() on the parent is sufficient.
*
* To avoid deadlock situations, caller must not attempt to
* configure/unconfigure/remove device node in (*f)(), nor should
* it attempt to recurse on other nodes in the system. Any
* ndi_devi_enter() done by (*f)() must occur 'at-or-below' the
* node entered prior to ddi_walk_devs(). Furthermore, if (*f)()
* does any multi-threading (in framework *or* in driver) then the
* ndi_devi_enter() calls done by dependent threads must be
* 'strictly-below'.
*
* This is not callable from device autoconfiguration routines.
* They include, but not limited to, _init(9e), _fini(9e), probe(9e),
* attach(9e), and detach(9e).
*/
void
{
}
/*
* This is a general-purpose routine traverses the per-driver list
* and calls the given function for each node. must return one of
* the following values:
* DDI_WALK_CONTINUE
* DDI_WALK_TERMINATE
*
* N.B. The same restrictions from ddi_walk_devs() apply.
*/
void
{
if (major == DDI_MAJOR_T_NONE)
return;
while (dip) {
return;
}
}
}
/*
* argument to i_find_devi, a devinfo node search callback function.
*/
struct match_info {
char *nodename; /* if non-null, nodename must match */
int instance; /* if != -1, instance must match */
int attached; /* if != 0, i_ddi_devi_attached() */
};
static int
{
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
/*
* Find dip with a known node name and instance and return with it held
*/
{
struct match_info info;
}
extern ib_boot_prop_t *iscsiboot_prop;
static void
char **minorname)
{
static char nulladdrname[] = "";
/* default values */
if (nodename)
if (addrname)
*addrname = nulladdrname;
if (minorname)
while (*cp != '\0') {
*cp = '\0';
}
++cp;
}
*colon = '\0';
}
}
/*
* Parse for name, addr, and minor names. Some args may be NULL.
*/
void
{
char *cp;
static char nulladdrname[] = "";
/* default values */
if (nodename)
if (addrname)
*addrname = nulladdrname;
if (minorname)
while (*cp != '\0') {
*cp = '\0';
*cp = '\0';
}
++cp;
}
}
static char *
{
/*
* Construct the pathname and ask the implementation
* if it can do a driver = f(pathname) for us, if not
* we'll just default to using the node-name that
* was given to us. We want to do this first to
* allow the platform to use 'generic' names for
* legacy device drivers.
*/
(void) ddi_pathname(parent, p);
(void) strcat(p, "/");
(void) strcat(p, child_name);
if (unit_address && *unit_address) {
(void) strcat(p, "@");
(void) strcat(p, unit_address);
}
/*
* Get the binding. If there is none, return the child_name
* and let the caller deal with it.
*/
maj = path_to_major(p);
kmem_free(p, MAXPATHLEN);
if (maj != DDI_MAJOR_T_NONE)
return (drvname);
}
#define PCI_EX_CLASS "pciexclass"
#define PCI_EX "pciex"
#define PCI_CLASS "pciclass"
#define PCI "pci"
int
{
== 0 ||
== 0 ||
== 0 ||
== 0) {
return (1);
}
}
}
return (0);
}
/*
* If there is an error, this function returns -1.
*
* NOTE: If this function returns the dev_info_t structure, then it
* does so with a hold on the devi. Caller should ensure that they get
* decremented via ddi_release_devi() or ndi_rele_devi();
*
* This function can be invoked in the boot case for a pathname without
* device argument (:xxxx), traditionally treated as a minor name.
* In this case, we do the following
* (1) search the minor node of type DDM_DEFAULT.
* (2) if no DDM_DEFAULT minor exists, then the first non-alias minor is chosen.
* (3) if neither exists, a dev_t is faked with minor number = instance.
* As of S9 FCS, no instance of #1 exists. #2 is used by several platforms
* to default the boot partition to :a possibly by other OBP definitions.
* #3 is used for booting off network interfaces, most SPARC network
* drivers support Style-2 only, so only DDM_ALIAS minor exists.
*
* It is possible for OBP to present device args at the end of the path as
* well as in the middle. For example, with IB the following strings are
* valid boot paths.
* a /pci@8,700000/ib@1,2:port=1,pkey=ff,dhcp,...
* b /pci@8,700000/ib@1,1:port=1/ioc@xxxxxx,yyyyyyy:dhcp
* Case (a), we first look for minor node "port=1,pkey...".
* Failing that, we will pass "port=1,pkey..." to the bus_config
* entry point of ib (HCA) driver.
* Case (b), configure ib@1,1 as usual. Then invoke ib's bus_config
* with argument "ioc@xxxxxxx,yyyyyyy:port=1". After configuring
* the ioc, look for minor node dhcp. If not found, pass ":dhcp"
* to ioc's bus_config entry point.
*/
int
resolve_pathname(char *pathname,
{
int error;
char *component, *config_name;
char *prev_minor = NULL;
int spectype;
struct ddi_minor_data *dmn;
int circ;
if (*pathname != '/')
return (EINVAL);
return (error);
pn_skipslash(&pn);
while (pn_pathleft(&pn)) {
/* remember prev minor (:xxx) in the middle of path */
if (minorname)
/* Get component and chop off minorname */
if ((iscsiboot_prop != NULL) &&
&minorname);
} else {
}
if (prev_minor == NULL) {
} else {
prev_minor = NULL;
}
/*
* Find and configure the child
*/
return (-1);
}
pn_skipslash(&pn);
}
/*
* First look for a minor node matching minorname.
* Failing that, try to pass minorname to bus_config().
*/
"%s: minor node not found\n", pathname));
return (-1);
}
}
/*
* Search for a default entry with an active
* ndi_devi_enter to protect the devi_minor list.
*/
break;
}
}
/*
* No default minor node, try the first one;
* else, assume 1-1 instance-minor mapping
*/
} else {
devt = makedevice(
}
}
}
if (devtp)
if (spectypep)
}
/*
* If there is no error, return the appropriate parameters
*/
else {
/*
* We should really keep the ref count to keep the node from
* detaching but ddi_pathname_to_dev_t() specifies a NULL dipp,
* so we have no way of passing back the held dip. Not holding
* the dip allows detaches to occur - which can cause problems
* for subsystems which call ddi_pathname_to_dev_t (console).
*
* Instead of holding the dip, we place a ddi-no-autodetach
* property on the node to prevent auto detaching.
*
* The right fix is to remove ddi_pathname_to_dev_t and replace
* it, and all references, with a call that specifies a dipp.
* In addition, the callers of this new interfaces would then
* need to call ndi_rele_devi when the reference is complete.
*
*/
DDI_NO_AUTODETACH, 1);
}
return (0);
}
/*
* Given the pathname of a device, return the dev_t of the corresponding
* device. Returns NODEV on failure.
*
* Note that this call sets the DDI_NO_AUTODETACH property on the devinfo node.
*/
ddi_pathname_to_dev_t(char *pathname)
{
int error;
}
/*
* Translate a prom pathname to kernel devfs pathname.
* Caller is assumed to allocate devfspath memory of
* size at least MAXPATHLEN
*
* The prom pathname may not include minor name, but
* devfs pathname has a minor name portion.
*/
int
{
char *minor_name = NULL;
int spectype;
int error;
int circ;
if (error)
return (DDI_FAILURE);
/*
* Get in-kernel devfs pathname
*/
if (minor_name) {
} else {
/*
* If minor_name is NULL, we have an alias minor node.
* So manufacture a path to the corresponding clone minor.
*/
}
/* release hold from resolve_pathname() */
return (0);
}
/*
* This function is intended to identify drivers that must quiesce for fast
* reboot to succeed. It does not claim to have more knowledge about the device
* than its driver. If a driver has implemented quiesce(), it will be invoked;
* if a so identified driver does not manage any device that needs to be
* quiesced, it must explicitly set its devo_quiesce dev_op to
* ddi_quiesce_not_needed.
*/
static int
{
/*
* If dip is pseudo and skip_pseudo is set, driver doesn't have to
* implement quiesce().
*/
if (skip_pseudo &&
return (0);
/*
* If parent dip is pseudo and skip_pseudo is set, driver doesn't have
* to implement quiesce().
*/
return (0);
/*
* If not attached, driver doesn't have to implement quiesce().
*/
if (!i_ddi_devi_attached(dip))
return (0);
/*
* If dip has no hardware property and skip_non_hw is set,
* driver doesn't have to implement quiesce().
*/
return (0);
return (1);
}
static int
{
return (1);
else
return (0);
}
/*
* Check to see if a driver has implemented the quiesce() DDI function.
*/
int
{
if (!should_implement_quiesce(dip))
return (DDI_WALK_CONTINUE);
return (DDI_WALK_CONTINUE);
if (driver_has_quiesce(ops)) {
else
}
} else {
*((int *)arg) = -1;
}
return (DDI_WALK_CONTINUE);
}
/*
* Quiesce device.
*/
static void
{
int should_quiesce = 0;
/*
* If the device is not attached it doesn't need to be quiesced.
*/
if (!i_ddi_devi_attached(dip))
return;
return;
/*
* If there's an implementation of quiesce(), always call it even if
* some of the drivers don't have quiesce() or quiesce() have failed
* so we can do force fast reboot. The implementation of quiesce()
* should not negatively affect a regular reboot.
*/
if (driver_has_quiesce(ops)) {
int rc = DDI_SUCCESS;
return;
#ifdef DEBUG
#endif /* DEBUG */
*((int *)arg) = -1;
}
*((int *)arg) = -1;
}
}
/*
* Traverse the dev info tree in a breadth-first manner so that we quiesce
* children first. All subtrees under the parent of dip will be quiesced.
*/
void
{
/*
* if we're reached here, the device tree better not be changing.
* so either devinfo_freeze better be set or we better be panicing.
*/
}
}
/*
* Reset all the pure leaf drivers on the system at halt time
*/
static int
{
/* if the device doesn't need to be reset then there's nothing to do */
if (!DEVI_NEED_RESET(dip))
return (DDI_WALK_CONTINUE);
/*
* reset entry point then there's nothing to do.
*/
return (DDI_WALK_CONTINUE);
static char path[MAXPATHLEN];
/*
* bad news, this device has blocked in it's attach or
* detach routine, which means it not safe to call it's
* devo_reset() entry point.
*/
return (DDI_WALK_CONTINUE);
}
return (DDI_WALK_CONTINUE);
}
void
reset_leaves(void)
{
/*
* if we're reached here, the device tree better not be changing.
* so either devinfo_freeze better be set or we better be panicing.
*/
}
/*
* devtree_freeze() must be called before quiesce_devices() and reset_leaves()
* during a normal system shutdown. It attempts to ensure that there are no
* outstanding attach or detach operations in progress when quiesce_devices() or
* reset_leaves()is invoked. It must be called before the system becomes
* single-threaded because device attach and detach are multi-threaded
* operations. (note that during system shutdown the system doesn't actually
* become single-thread since other threads still exist, but the shutdown thread
* will disable preemption for itself, raise it's pil, and stop all the other
* cpus in the system there by effectively making the system single-threaded.)
*/
void
devtree_freeze(void)
{
int delayed = 0;
/* if we're panicing then the device tree isn't going to be changing */
if (panicstr)
return;
/* stop all dev_info state changes in the device tree */
devinfo_freeze = gethrtime();
/*
* if we're not panicing and there are on-going attach or detach
* operations, wait for up to 3 seconds for them to finish. This
* is a randomly chosen interval but this should be ok because:
* - 3 seconds is very small relative to the deadman timer.
* - normal attach and detach operations should be very quick.
* - attach and detach operations are fairly rare.
*/
(delayed < 3)) {
delayed += 1;
/* do a sleeping wait for one second */
}
}
static int
{
char *path;
/*
* If the node is currently bound to the wrong driver, try to unbind
* so that we can rebind to the correct driver.
*/
/*
* Check for a path-oriented driver alias that
* takes precedence over current driver binding.
*/
if (driver_active(pmajor))
}
/* attempt unbind if current driver is incorrect */
if (driver_active(major) &&
(void) ndi_devi_unbind_driver(dip);
}
/* If unbound, try to bind to a driver */
(void) ndi_devi_bind_driver(dip, 0);
return (DDI_WALK_CONTINUE);
}
void
i_ddi_bind_devs(void)
{
/* flush devfs so that ndi_devi_unbind_driver will work when possible */
}
/* callback data for unbind_children_by_alias() */
typedef struct unbind_data {
char *drv_alias;
int ndevs_bound;
int unbind_errors;
/*
* A utility function provided for testing and support convenience
* Called for each device during an upgrade_drv -d bound to the alias
* that cannot be unbound due to device in use.
*/
static void
{
if (moddebug & MODDEBUG_BINDING) {
}
}
/*
* walkdevs callback for unbind devices bound to specific driver
* and alias. Invoked within the context of update_drv -d <alias>.
*/
static int
{
int circ;
int rv;
/*
* We are called from update_drv to try to unbind a specific
* set of aliases for a driver. Unbind what persistent nodes
* we can, and return the number of nodes which cannot be unbound.
* If not all nodes can be unbound, update_drv leaves the
* state of the driver binding files unchanged, except in
* the case of -f.
*/
continue;
if (rv != DDI_SUCCESS ||
ub->ndevs_bound++;
continue;
}
if (ndi_dev_is_persistent_node(cdip) == 0)
(void) ddi_remove_child(cdip, 0);
}
}
return (DDI_WALK_CONTINUE);
}
/*
* Unbind devices by driver & alias
* Context: update_drv [-f] -d -i <alias> <driver>
*/
int
{
int rv;
ub->ndevs_bound = 0;
ub->unbind_errors = 0;
/* flush devfs so that ndi_devi_unbind_driver will work when possible */
/* return the number of devices remaining bound to the alias */
return (rv);
}
/*
* walkdevs callback for unbind devices by driver
*/
static int
{
int circ;
int rv;
/*
* We are called either from rem_drv or update_drv when reloading
* a driver.conf file. In either case, we unbind persistent nodes
* and destroy .conf nodes. In the case of rem_drv, this will be
* the final state. In the case of update_drv, i_ddi_bind_devs()
* may be invoked later to re-enumerate (new) driver.conf rebind
* persistent nodes.
*/
continue;
if (rv == DDI_FAILURE ||
continue;
if (ndi_dev_is_persistent_node(cdip) == 0)
(void) ddi_remove_child(cdip, 0);
}
}
return (DDI_WALK_CONTINUE);
}
/*
* Unbind devices by driver
* Context: rem_drv or unload driver.conf
*/
void
{
/* flush devfs so that ndi_devi_unbind_driver will work when possible */
}
/*
* I/O Hotplug control
*/
/*
* create and attach a dev_info node from a .conf file spec
*/
static void
{
char *node_name;
tmp = "<none>";
"init_spec_child: parent=%s, bad spec (%s)\n",
return;
}
return;
(void) ddi_remove_child(dip, 0);
}
/*
* Lookup hwc specs from hash tables and make children from the spec
* Because some .conf children are "merge" nodes, we also initialize
* .conf children to merge properties onto hardware nodes.
*
* The pdip must be held busy.
*/
int
{
int circ;
return (DDI_SUCCESS);
}
}
return (DDI_SUCCESS);
}
/*
* Run initchild on all child nodes such that instance assignment
* for multiport network cards are contiguous.
*
* The pdip must be held busy.
*/
static void
{
/* contiguous instance assignment */
while (dip) {
}
}
/*
* report device status
*/
static void
{
char *status;
if (!DEVI_NEED_REPORT(dip) ||
return;
}
/* Invalidate the devinfo snapshot cache */
if (DEVI_IS_DEVICE_REMOVED(dip)) {
status = "removed";
} else if (DEVI_IS_DEVICE_OFFLINE(dip)) {
status = "offline";
} else if (DEVI_IS_DEVICE_DOWN(dip)) {
status = "down";
} else if (DEVI_IS_BUS_QUIESCED(dip)) {
status = "quiesced";
} else if (DEVI_IS_BUS_DOWN(dip)) {
status = "down";
} else if (i_ddi_devi_attached(dip)) {
status = "online";
} else {
status = "unknown";
}
} else {
}
}
/*
* log a notification that a dev_info node has been configured.
*/
static int
{
int se_err;
char *pathname;
sysevent_t *ev;
char *class_name;
int no_transport = 0;
/* do not generate ESC_DEVFS_DEVI_ADD event during boot */
if (!i_ddi_io_initialized())
return (DDI_SUCCESS);
/* Invalidate the devinfo snapshot cache */
goto fail;
}
/* add the device class attribute */
goto fail;
}
}
/*
* must log a branch event too unless NDI_BRANCH_EVENT_OP is set,
* in which case the branch event will be logged by the caller
* after the entire branch has been configured.
*/
if ((flags & NDI_BRANCH_EVENT_OP) == 0) {
/*
* Instead of logging a separate branch event just add
* DEVFS_BRANCH_EVENT attribute. It indicates devfsadmd to
* generate a EC_DEV_BRANCH event.
*/
goto fail;
}
}
goto fail;
}
if (se_err == SE_NO_TRANSPORT)
no_transport = 1;
goto fail;
}
return (DDI_SUCCESS);
fail:
"Run devfsadm -i %s",
return (DDI_SUCCESS);
}
/*
* log a notification that a dev_info node has been unconfigured.
*/
static int
{
sysevent_t *ev;
int se_err;
int no_transport = 0;
if (!i_ddi_io_initialized())
return (DDI_SUCCESS);
/* Invalidate the devinfo snapshot cache */
goto fail;
}
if (class_name) {
/* add the device class, driver name and instance attributes */
goto fail;
}
goto fail;
}
goto fail;
}
}
/*
* must log a branch event too unless NDI_BRANCH_EVENT_OP is set,
* in which case the branch event will be logged by the caller
* after the entire branch has been unconfigured.
*/
if ((flags & NDI_BRANCH_EVENT_OP) == 0) {
/*
* Instead of logging a separate branch event just add
* DEVFS_BRANCH_EVENT attribute. It indicates devfsadmd to
* generate a EC_DEV_BRANCH event.
*/
goto fail;
}
}
goto fail;
}
if (se_err == SE_NO_TRANSPORT)
no_transport = 1;
goto fail;
}
return (DDI_SUCCESS);
fail:
return (DDI_SUCCESS);
}
static void
{
char *path;
return;
/* Inform LDI_EV_DEVICE_REMOVE callbacks. */
/* Generate EC_DEVFS_DEVI_REMOVE sysevent. */
ddi_get_instance(dip), 0);
}
static void
{
(void) i_log_devfs_add_devinfo(dip, 0);
}
/*
* log an event that a dev_info branch has been configured or unconfigured.
*/
static int
{
int se_err;
sysevent_t *ev;
int no_transport = 0;
/* do not generate the event during boot */
if (!i_ddi_io_initialized())
return (DDI_SUCCESS);
/* Invalidate the devinfo snapshot cache */
goto fail;
}
goto fail;
}
if (se_err == SE_NO_TRANSPORT)
no_transport = 1;
goto fail;
}
return (DDI_SUCCESS);
fail:
return (DDI_FAILURE);
}
/*
* log an event that a dev_info tree branch has been configured.
*/
static int
{
char *node_path;
int rv;
return (rv);
}
/*
* log an event that a dev_info tree branch has been unconfigured.
*/
static int
{
}
/*
* enqueue the dip's deviname on the branch event queue.
*/
static struct brevq_node *
struct brevq_node *child)
{
struct brevq_node *brn;
char *deviname;
return (brn);
}
/*
* free the memory allocated for the elements on the branch event queue.
*/
static void
{
}
}
/*
* log the events queued up on the branch event queue and free the
* associated memory.
*
* node_path must have been allocated with at least MAXPATHLEN bytes.
*/
static void
{
struct brevq_node *brn;
char *p;
(void) i_log_devfs_branch_remove(node_path);
}
*p = '\0';
}
/*
* log the events queued up on the branch event queue and free the
* associated memory. Same as the previous function but operates on dip.
*/
static void
{
char *path;
}
/*
* log the outstanding branch remove events for the grand children of the dip
* and free the associated memory.
*/
static void
struct brevq_node *brevq)
{
struct brevq_node *brn;
char *path;
char *p;
/* now path contains the node path to the dip's child */
}
}
}
/*
* log and cleanup branch remove events for the grand children of the dip.
*/
static void
{
char *path;
int circ;
break;
}
}
/*
* Event state is not REMOVE. So branch remove event
* is not going be generated on brn->brn_child.
* If any branch remove events were queued up on
* brn->brn_child log them and remove the brn
* from the queue.
*/
}
if (prev_brn)
else
} else {
/*
* Free up the outstanding branch remove events
* queued on brn->brn_child since brn->brn_child
* itself is eligible for branch remove event.
*/
}
}
}
}
static int
{
!(DEVI_EVREMOVE(dip)))
return (1);
else
return (0);
}
/*
* Unconfigure children/descendants of the dip.
*
* If the operation involves a branch event NDI_BRANCH_EVENT_OP is set
* through out the unconfiguration. On successful return *brevqp is set to
* a queue of dip's child devinames for which branch remove events need
* to be generated.
*/
static int
struct brevq_node **brevqp)
{
int rval;
if (flags & NDI_BRANCH_EVENT_OP) {
brevqp);
}
} else
NULL);
return (rval);
}
/*
* If the dip is already bound to a driver transition to DS_INITIALIZED
* in order to generate an event in the case where the node was left in
* DS_BOUND state since boot (never got attached) and the node is now
* being offlined.
*/
static void
{
}
/*
*/
static int
{
if (flags & NDI_DEVI_ONLINE) {
if (!i_ddi_devi_attached(dip))
}
if (DEVI_IS_DEVICE_OFFLINE(dip)) {
return (NDI_FAILURE);
}
(void) ddi_uninitchild(dip);
else {
/*
* Delete .conf nodes and nodes that are not
* well formed.
*/
(void) ddi_remove_child(dip, 0);
}
return (NDI_FAILURE);
}
/*
* log an event, but not during devfs lookups in which case
* NDI_NO_EVENT is set.
*/
} else if (!(flags & NDI_NO_EVENT_STATE_CHNG)) {
}
return (NDI_SUCCESS);
}
/* internal function to config immediate children */
static int
{
int circ;
return (NDI_SUCCESS);
"config_immediate_children: %s%d (%p), flags=%x\n",
if (flags & NDI_CONFIG_REPROBE) {
}
while (child) {
/* NOTE: devi_attach_node() may remove the dip */
/*
* Configure all nexus nodes or leaf nodes with
* matching driver major
*/
if ((major == DDI_MAJOR_T_NONE) ||
}
return (NDI_SUCCESS);
}
/* internal function to config grand children */
static int
{
struct mt_config_handle *hdl;
/* multi-threaded configuration of child nexus */
}
/*
* Common function for device tree configuration,
* either BUS_CONFIG_ALL or BUS_CONFIG_DRIVER.
* The NDI_CONFIG flag causes recursive configuration of
* grandchildren, devfs usage should not recurse.
*/
static int
{
int error;
int (*f)();
if (!i_ddi_devi_attached(dip))
return (NDI_FAILURE);
return (NDI_FAILURE);
} else {
/* call bus_config entry point */
}
if (error) {
return (error);
}
/*
* Some callers, notably SCSI, need to mark the devfs cache
* to be rebuilt together with the config operation.
*/
if (flags & NDI_DEVFS_CLEAN)
if (flags & NDI_CONFIG)
return (NDI_SUCCESS);
}
/*
* Framework entry point for BUS_CONFIG_ALL
*/
int
{
"ndi_devi_config: par = %s%d (%p), flags = 0x%x\n",
}
/*
* Framework entry point for BUS_CONFIG_DRIVER, bound to major
*/
int
{
/* don't abuse this function */
"ndi_devi_config_driver: par = %s%d (%p), flags = 0x%x\n",
}
/*
* Called by nexus drivers to configure its children.
*/
static int
{
char *drivername = NULL;
int find_by_addr = 0;
int probed;
return (NDI_FAILURE);
/* split name into "name@addr" parts */
/*
* If the nexus is a pHCI and we are not processing a pHCI from
* mdi bus_config code then we need to know the vHCI.
*/
/*
* We may have a genericname on a system that creates drivername
* nodes (from .conf files). Find the drivername by nodeid. If we
* can't find a node with devnm as the node name then we search by
* drivername. This allows an implementation to supply a genericly
* named boot path (disk) and locate drivename nodes (sd). The
*/
find_by_addr = 1;
}
/*
* Determine end_time: This routine should *not* be called with a
* constant non-zero timeout argument, the caller should be adjusting
* the timeout argument relative to when it *started* its asynchronous
* enumeration.
*/
if (timeout > 0)
for (;;) {
/*
* child - break out of for(;;) loop if child found.
* NOTE: Lock order for ndi_devi_enter is (vHCI, pHCI).
*/
if (vdip) {
/* use mdi_devi_enter ordering */
if (cdip)
break;
} else
/*
* When not a vHCI or not all pHCI devices are required to
* enumerated under the vHCI (NDI_MDI_FALLBACK) search for
* devinfo child.
*/
/* determine if .conf nodes already built */
/*
* Search for child by name, if not found then search
* for a node bound to the drivername driver with the
* specified "@addr". Break out of for(;;) loop if
* child found. To support path-oriented aliases
* binding on boot-device, we do a search_by_addr too.
*/
drivername, addr);
if (cdip)
break;
/*
* determine if we should reenumerate .conf nodes
* and look for child again.
*/
if (probed &&
i_ddi_io_initialized() &&
(flags & NDI_CONFIG_REPROBE) &&
probed = 0;
goto again;
}
}
/* break out of for(;;) if time expired */
break;
/*
* Child not found, exit and wait for asynchronous enumeration
* to add child (or timeout). The addition of a new child (vhci
* or phci) requires the asynchronous enumeration thread to
* ndi_devi_enter/ndi_devi_exit. This exit will signal devi_cv
* and cause us to return from ndi_devi_exit_and_wait, after
* which we loop and search for the requested child again.
*/
"%s%d: waiting for child %s@%s, timeout %ld",
if (vdip) {
/*
* Mark vHCI for pHCI ndi_devi_exit broadcast.
*/
/*
* NB: There is a small race window from above
* ndi_devi_exit() of pdip to cv_wait() in
* ndi_devi_exit_and_wait() which can result in
* not immediately finding a new pHCI child
* of a pHCI that uses NDI_MDI_FAILBACK.
*/
} else {
}
}
/* done with paddr, fixup i_ddi_parse_name '@'->'\0' change */
/* attach and hold the child, returning pointer to child */
}
if (vdip)
}
/*
* Enumerate and attach a child specified by name 'devnm'.
* Called by devfs lookup and DR to perform a BUS_CONFIG_ONE.
* Note: devfs does not make use of NDI_CONFIG to configure
* an entire branch.
*/
int
{
int error;
int (*f)();
char *nmdup;
int duplen;
int branch_event = 0;
"ndi_devi_config_one: par = %s%d (%p), child = %s\n",
return (NDI_FAILURE);
}
(flags & NDI_CONFIG)) {
branch_event = 1;
}
} else {
/* call bus_config entry point */
}
if (error) {
}
/*
* if we fail to lookup and this could be an alias, lookup currdip
* To prevent recursive lookups into the same hash table, only
* do the currdip lookups once the hash table init is complete.
* Use tsd so that redirection doesn't recurse
*/
if (error) {
nmdup);
}
}
return (error);
}
/*
* DR usage (i.e. call with NDI_CONFIG) recursively configures
* grandchildren, performing a BUS_CONFIG_ALL from the node attached
* by the BUS_CONFIG_ONE.
*/
if (branch_event)
(void) i_log_devfs_branch_add(*dipp);
return (error);
}
/*
* Enumerate and attach a child specified by name 'devnm'.
* Called during configure the OBP options. This configures
* only one node.
*/
static int
{
int error;
int (*f)();
error = NDI_FAILURE;
} else {
/* call bus_config entry point */
}
return (error);
}
/*
* Pay attention, the following is a bit tricky:
* There are three possible cases when constraints are applied
*
* - A constraint is applied and the offline is disallowed.
* Simply return failure and block the offline
*
* - A constraint is applied and the offline is allowed.
* Mark the dip as having passed the constraint and allow
* offline to proceed.
*
* - A constraint is not applied. Allow the offline to proceed for now.
*
* In the latter two cases we allow the offline to proceed. If the
* offline succeeds (no users) everything is fine. It is ok for an unused
* device to be offlined even if no constraints were imposed on the offline.
* If the offline fails because there are users, we look at the constraint
* flag on the dip. If the constraint flag is set (implying that it passed
* a constraint) we allow the dip to be retired. If not, we don't allow
* the retire. This ensures that we don't allow unconstrained retire.
*/
int
{
int retval;
int constraint;
int failure;
(void *) dip));
constraint = 0;
failure = 0;
/*
* Start with userland constraints first - applied via device contracts
*/
switch (retval) {
case CT_NACK:
failure = 1;
goto out;
case CT_ACK:
constraint = 1;
break;
case CT_NONE:
/* no contracts */
break;
default:
}
/*
* Next, use LDI to impose kernel constraints
*/
switch (retval) {
case LDI_EV_FAILURE:
(void *)dip));
failure = 1;
goto out;
case LDI_EV_SUCCESS:
constraint = 1;
(void *)dip));
break;
case LDI_EV_NONE:
/* no matching LDI callbacks */
(void *)dip));
break;
default:
}
out:
"BLOCKED flag. dip=%p", (void *)dip));
"blocked. clearing RCM CONSTRAINT flag. dip=%p",
(void *)dip));
}
"CONSTRAINT flag. dip=%p", (void *)dip));
/* also allow retire if nexus or if device is not in use */
"use. Setting CONSTRAINT flag. dip=%p", (void *)dip));
} else {
/*
* Note: We cannot ASSERT here that DEVI_R_CONSTRAINT is
* not set, since other sources (such as RCM) may have
* set the flag.
*/
"constraint flag. dip=%p", (void *)dip));
}
(void *) dip));
}
void
{
(void *)dip));
(void *)dip));
}
void
{
"result always = DDI_SUCCESS, dip=%p", (void *)dip));
(void *)dip));
}
void
{
"result always = DDI_SUCCESS, dip=%p", (void *)dip));
(void *)dip));
}
/*
* detach a node with parent already held busy
*/
static int
{
int ret = NDI_SUCCESS;
int instance = -1;
int post_event = 0;
/*
* Invoke notify if offlining
*/
if (flags & NDI_DEVI_OFFLINE) {
(void *)dip));
"dip=%p", (void *)dip));
return (NDI_FAILURE);
}
}
if (flags & NDI_POST_EVENT) {
if (i_ddi_devi_attached(pdip)) {
&cookie) == NDI_SUCCESS)
}
}
/*
* dv_mknod places a hold on the dev_info_t for each devfs node
* created. If we're to succeed in detaching this device, we must
* first release all outstanding references held by devfs.
*/
if (flags & NDI_DEVI_OFFLINE) {
" Calling e_ddi_offline_finalize with result=%d. "
}
return (NDI_FAILURE);
}
if (flags & NDI_DEVI_OFFLINE) {
" Calling e_ddi_offline_finalize with result=%d, "
}
if (flags & NDI_AUTODETACH)
return (NDI_SUCCESS);
/*
* For DR, even bound nodes may need to have offline
* flag set.
*/
if (flags & NDI_DEVI_OFFLINE) {
}
}
if (flags & NDI_DEVI_OFFLINE)
devi->devi_ev_path);
/*
* instance and path data are lost in call to
* ddi_uninitchild
*/
}
}
if (ret == NDI_SUCCESS) {
/*
* Remove uninitialized pseudo nodes because
* system props are lost and the node cannot be
* reattached.
*/
if (!ndi_dev_is_persistent_node(dip))
flags |= NDI_DEVI_REMOVE;
if (flags & NDI_DEVI_REMOVE) {
/*
* NOTE: If there is a consumer of LDI events,
* ddi_uninitchild above would have failed
* because of active devi_ref from ldi_open().
*/
if (DEVI_EVREMOVE(dip)) {
path = i_ddi_strdup(
KM_SLEEP);
class =
KM_SLEEP);
driver =
(char *)ddi_driver_name(dip),
KM_SLEEP);
post_event = 1;
}
/* Generate EC_DEVFS_DEVI_REMOVE */
(void) i_log_devfs_remove_devinfo(path,
}
}
}
}
if (path)
if (class)
if (driver)
return (ret);
}
/*
* unconfigure immediate children of bus nexus device
*/
static int
dev_info_t **dipp,
int flags,
{
int rv = NDI_SUCCESS;
/*
* Scan forward to see if we will be processing a pHCI child. If we
* have a child that is a pHCI and vHCI and pHCI are not siblings then
* enter vHCI before parent(pHCI) to prevent deadlock with mpxio
* Client power management operations.
*/
/* skip same nodes we skip below */
if (((major != DDI_MAJOR_T_NONE) &&
continue;
/*
* If vHCI and vHCI is not a sibling of pHCI
* then enter in (vHCI, parent(pHCI)) order.
*/
/* use mdi_devi_enter ordering */
break;
} else
}
}
while (child) {
if ((major != DDI_MAJOR_T_NONE) &&
continue;
}
/* skip nexus nodes during autodetach */
continue;
}
}
rv = NDI_FAILURE;
}
/*
* Continue upon failure--best effort algorithm
*/
}
if (vdip)
return (rv);
}
/*
* unconfigure grand children of bus nexus device
*/
static int
dev_info_t **dipp,
int flags,
struct brevq_node **brevqp)
{
struct mt_config_handle *hdl;
if (brevqp)
/* multi-threaded configuration of child nexus */
}
/*
* Unconfigure children/descendants of the dip.
*
* If brevqp is not NULL, on return *brevqp is set to a queue of dip's
* child devinames for which branch remove events need to be generated.
*/
static int
dev_info_t **dipp,
int flags,
struct brevq_node **brevqp)
{
int rv;
int pm_cookie;
int (*f)();
if (dipp)
if (brevqp)
/*
* Power up the dip if it is powered off. If the flag bit
* NDI_AUTODETACH is set and the dip is not at its full power,
* skip the rest of the branch.
*/
/*
* Some callers, notably SCSI, need to clear out the devfs
* cache together with the unconfig to prevent stale entries.
*/
if (flags & NDI_DEVFS_CLEAN)
free_brevq(*brevqp);
}
return (rv);
}
}
/*
* It is possible to have a detached nexus with children
* and grandchildren (for example: a branch consisting
* entirely of bound nodes.) Since the nexus is detached
* the bus_unconfig entry point cannot be used to remove
* or unconfigure the descendants.
*/
if (!i_ddi_devi_attached(dip) ||
} else {
/*
* call bus_unconfig entry point
* It should reset nexus flags if unconfigure succeeds.
*/
}
return (rv);
}
/*
* If NDI_AUTODETACH is specified, this is invoked by either the
* moduninstall daemon or the modunload -i 0 command.
*/
int
{
"ndi_devi_unconfig_driver: par = %s%d (%p), flags = 0x%x\n",
}
int
{
"ndi_devi_unconfig: par = %s%d (%p), flags = 0x%x\n",
}
int
{
"e_ddi_devi_unconfig: par = %s%d (%p), flags = 0x%x\n",
}
/*
* Unconfigure child by name
*/
static int
{
int v_circ;
/*
* If child is pHCI and vHCI and pHCI are not siblings then enter vHCI
* before parent(pHCI) to avoid deadlock with mpxio Client power
* management operations.
*/
/* use mdi_devi_enter ordering */
} else
}
if (child) {
} else {
"devi_unconfig_one: %s not found\n", devnm));
rv = NDI_SUCCESS;
}
if (vdip)
return (rv);
}
int
char *devnm,
dev_info_t **dipp,
int flags)
{
int (*f)();
int pm_cookie;
int v_circ;
"ndi_devi_unconfig_one: par = %s%d (%p), child = %s\n",
return (NDI_FAILURE);
if (dipp)
/*
* If child is pHCI and vHCI and pHCI are not siblings then enter vHCI
* before parent(pHCI) to avoid deadlock with mpxio Client power
* management operations.
*/
/* use mdi_devi_enter ordering */
} else
}
" not found\n", devnm));
rv = NDI_SUCCESS;
goto out;
}
/*
* Unconfigure children/descendants of named child
*/
if (rv != NDI_SUCCESS)
goto out;
} else {
/* call bus_config entry point */
}
if (brevq) {
if (rv != NDI_SUCCESS)
else
}
}
out:
if (vdip)
return (rv);
}
struct async_arg {
};
/*
* Common async handler for:
* ndi_devi_bind_driver_async
* ndi_devi_online_async
*/
static int
{
int tqflag;
int kmflag;
if (flags & NDI_NOSLEEP) {
kmflag = KM_NOSLEEP;
tqflag = TQ_NOSLEEP;
} else {
}
goto fail;
DDI_SUCCESS) {
return (NDI_SUCCESS);
}
fail:
if (arg)
return (NDI_FAILURE);
}
static void
{
}
int
{
(void (*)())i_ndi_devi_bind_driver_cb));
}
/*
* place the devinfo in the ONLINE state.
*/
int
{
int branch_event = 0;
/* bind child before merging .conf nodes */
if (rv != NDI_SUCCESS) {
return (rv);
}
/* merge .conf properties */
if (flags & NDI_NO_EVENT) {
/*
* Caller is specifically asking for not to generate an event.
* Set the following flag so that devi_attach_node() don't
* change the event state.
*/
}
branch_event = 1;
}
/*
* devi_attach_node() may remove dip on failure
*/
/*
* Hold the attached dip, and exit the parent while
* we drive configuration of children below the
* attached dip.
*/
}
if (branch_event)
(void) i_log_devfs_branch_add(dip);
}
/*
* Notify devfs that we have a new node. Devfs needs to invalidate
* cached directory contents.
*
* For PCMCIA devices, it is possible the pdip is not fully
* attached. In this case, calling back into devfs will
* result in a loop or assertion error. Hence, the check
* on node state.
*
* If we own parent lock, this is part of a branch operation.
* We skip the devfs_clean() step because the cache invalidation
* is done higher up in the device tree.
*/
return (rv);
}
static void
{
}
int
{
/* mark child as need config if requested. */
if (flags & NDI_CONFIG) {
}
(void (*)())i_ndi_devi_online_cb));
}
/*
* Take a device node Offline
* To take a device Offline means to detach the device instance from
* the driver and prevent devfs requests from re-attaching the device
* instance.
*
* The flag NDI_DEVI_REMOVE causes removes the device node from
* the driver list and the device tree. In this case, the device
* is assumed to be removed from the system.
*/
int
{
int v_circ;
/*
* If child is pHCI and vHCI and pHCI are not siblings then enter vHCI
* before parent(pHCI) to avoid deadlock with mpxio Client power
* management operations.
*/
else
}
if (i_ddi_devi_attached(dip)) {
/*
* If dip is in DS_READY state, there may be cached dv_nodes
* referencing this dip, so we invoke devfs code path.
* Note that we must release busy changing on pdip to
* avoid deadlock against devfs.
*/
if (vdip)
/*
* If we are explictly told to clean, then clean. If we own the
* parent lock then this is part of a branch operation, and we
* skip the devfs_clean() step.
*
* NOTE: A thread performing a devfs file system lookup/
* bus_config can't call devfs_clean to unconfig without
* causing rwlock problems in devfs. For ndi_devi_offline, this
* means that the NDI_DEVFS_CLEAN flag is safe from ioctl code
* or from an async hotplug thread, but is not safe from a
* nexus driver's bus_config implementation.
*/
if ((flags & NDI_DEVFS_CLEAN) ||
(!DEVI_BUSY_OWNED(pdip)))
&brevq);
if (rval)
return (NDI_FAILURE);
if (vdip)
}
if (brevq) {
if (rval != NDI_SUCCESS)
else
}
if (vdip)
return (rval);
}
/*
* Find the child dev_info node of parent nexus 'p' whose unit address
* matches "cname@caddr". Recommend use of ndi_devi_findchild() instead.
*/
{
int circ;
return ((dev_info_t *)NULL);
return (child);
}
/*
* Find the child dev_info node of parent nexus 'p' whose unit address
* matches devname "name@addr". Permits caller to hold the parent.
*/
{
char *devstr;
return ((dev_info_t *)NULL);
}
return (child);
}
/*
* Misc. routines called by framework only
*/
/*
* Clear the DEVI_MADE_CHILDREN/DEVI_ATTACHED_CHILDREN flags
* if new child spec has been added.
*/
static int
{
int circ;
return (DDI_WALK_CONTINUE);
/* coordinate child state update */
return (DDI_WALK_CONTINUE);
}
/*
* Helper functions, returns NULL if no memory.
*/
/*
* path_to_major:
*
* Return an alternate driver name binding for the leaf device
* of the given pathname, if there is one. The purpose of this
* function is to deal with generic pathnames. The default action
* for platforms that can't do this (ie: x86 or any platform that
* does not have prom_finddevice functionality, which matches
* nodenames and unit-addresses without the drivers participation)
* is to return DDI_MAJOR_T_NONE.
*
* Used in loadrootmodules() in the swapgeneric module to
* associate a given pathname with a given leaf driver.
*
*/
path_to_major(char *path)
{
char *p, *q;
/* check for path-oriented alias */
if (driver_active(major)) {
return (major);
}
/*
* Get the nodeid of the given pathname, if such a mapping exists.
*/
if (nodeid != OBP_BADNODE) {
/*
* Find the nodeid in our copy of the device tree and return
* whatever name we used to bind this node to a driver.
*/
}
"path_to_major: can't bind <%s>\n", path));
return (DDI_MAJOR_T_NONE);
}
/*
* If we're bound to something other than the nodename,
* note that in the message buffer and system log.
*/
p = ddi_binding_name(dip);
q = ddi_node_name(dip);
if (p && q && (strcmp(p, q) != 0))
path, p));
major = ddi_name_to_major(p);
return (major);
}
/*
* Return the held dip for the specified major and instance, attempting to do
* an attach if specified. Return NULL if the devi can't be found or put in
* the proper state. The caller must release the hold via ddi_release_devi if
* a non-NULL value is returned.
*
* Some callers expect to be able to perform a hold_devi() while in a context
* where using ndi_devi_enter() to ensure the hold might cause deadlock (see
* open-from-attach code in consconfig_dacf.c). Such special-case callers
* must ensure that an ndi_devi_enter(parent)/ndi_hold_devi() from a safe
* context is already active. The hold_devi() implementation must accommodate
* these callers.
*/
static dev_info_t *
{
char *path;
char *vpath;
return (NULL);
/* try to find the instance in the per driver list */
/* skip node if instance field is not valid */
continue;
/* look for instance match */
/*
* To accommodate callers that can't block in
* ndi_devi_enter() we do an ndi_hold_devi(), and
* afterwards check that the node is in a state where
* the hold prevents detach(). If we did not manage to
* prevent detach then we ndi_rele_devi() and perform
* the slow path below (which can result in a blocking
* ndi_devi_enter() while driving attach top-down).
* This code depends on the ordering of
* DEVI_SET_DETACHING and the devi_ref check in the
* detach_node() code path.
*/
if (i_ddi_devi_attached(dip) &&
!DEVI_IS_DETACHING(dip)) {
return (dip); /* fast-path with devi held */
}
/* try slow-path */
break;
}
}
if (flags & E_DDI_HOLD_DEVI_NOATTACH)
return (NULL); /* told not to drive attach */
/* slow-path may block, so it should not occur from interrupt */
if (servicing_interrupt())
return (NULL);
/* reconstruct the path and drive attach by path through devfs. */
/*
* Verify that we got the correct device - a path_to_inst file
* unit-address format) could result in an incorrect answer
*
* Verify major, instance, and path.
*/
if (dip &&
}
}
return (dip); /* with devi held */
}
/*
* The {e_}ddi_hold_devi{_by_{instance|dev|path}} hold the devinfo node
* associated with the specified arguments. This hold should be released
* by calling ddi_release_devi.
*
* The E_DDI_HOLD_DEVI_NOATTACH flag argument allows the caller to to specify
* a failure return if the node is not already attached.
*
* NOTE: by the time we make e_ddi_hold_devi public, we should be able to reuse
* ddi_hold_devi again.
*/
{
}
{
/*
* The rest of this routine is legacy support for drivers that
* have broken DDI_INFO_DEVT2INSTANCE implementations but may have
* functional DDI_INFO_DEVT2DEVINFO implementations. This code will
* diagnose inconsistency and, for maximum compatibility with legacy
* drivers, give preference to the drivers DDI_INFO_DEVT2DEVINFO
* implementation over the above derived dip based the driver's
* DDI_INFO_DEVT2INSTANCE implementation. This legacy support should
* be removed when DDI_INFO_DEVT2DEVINFO is deprecated.
*
* NOTE: The following code has a race condition. DEVT2DEVINFO
* returns a dip which is not held. By the time we ref ddip,
* it could have been freed. The saving grace is that for
* most drivers, the dip returned from hold_devi() is the
* same one as the one returned by DEVT2DEVINFO, so we are
* safe for drivers with the correct getinfo(9e) impl.
*/
}
/* give preference to the driver returned DEVT2DEVINFO dip */
#ifdef DEBUG
#endif /* DEBUG */
if (dip)
}
if (ops)
return (dip);
}
/*
* For compatibility only. Do not call this function!
*/
{
return (NULL);
switch (type) {
case VCHR:
case VBLK:
default:
break;
}
/*
* For compatibility reasons, we can only return the dip with
* the driver ref count held. This is not a safe thing to do.
* For certain broken third-party software, we are willing
* to venture into unknown territory.
*/
if (dip) {
(void) ndi_hold_driver(dip);
}
return (dip);
}
{
/* can't specify NOATTACH by path */
}
void
{
}
void
{
}
/*
* Associate a streams queue with a devinfo node
* NOTE: This function is called by STREAM driver's put procedure.
* It cannot block.
*/
void
{
/* set flag indicating that ddi_assoc_queue_with_devi was called */
/* get the vnode associated with the queue */
/* change the hardware association of the vnode */
}
/*
* ddi_install_driver(name)
*
* Driver installation is currently a byproduct of driver loading. This
* may change.
*/
int
ddi_install_driver(char *name)
{
if ((major == DDI_MAJOR_T_NONE) ||
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
struct dev_ops *
{
return (mod_hold_dev_by_major(major));
}
void
{
}
/*
* This is called during boot to force attachment order of special dips
* dip must be referenced via ndi_hold_devi()
*/
int
{
/*
* Recurse up until attached parent is found.
*/
if (i_ddi_devi_attached(dip))
return (DDI_SUCCESS);
return (DDI_FAILURE);
/*
* Come top-down, expanding .conf nodes under this parent
* and driving attach.
*/
(void) i_ndi_make_spec_children(parent, 0);
return (ret);
}
/* keep this function static */
static int
{
int error = DDI_FAILURE;
while (dip) {
error = DDI_SUCCESS;
/*
* Set the 'ddi-config-driver-node' property on a nexus
* node to cause attach_driver_nodes() to configure all
* immediate children of the nexus. This property should
* be set on nodes with immediate children that bind to
* the same driver as parent.
*/
}
}
if (error == DDI_SUCCESS)
return (error);
}
/*
* i_ddi_attach_hw_nodes configures and attaches all hw nodes
* bound to a specific driver. This function replaces calls to
* ddi_hold_installed_driver() for drivers with no .conf
* enumerated nodes.
*
* This facility is typically called at boot time to attach
* platform-specific hardware nodes, such as ppm nodes on xcal
* and grover and keyswitch nodes on cherrystone. It does not
* deal with .conf enumerated node. Calling it beyond the boot
* process is strongly discouraged.
*/
int
i_ddi_attach_hw_nodes(char *driver)
{
if (major == DDI_MAJOR_T_NONE)
return (DDI_FAILURE);
return (attach_driver_nodes(major));
}
/*
* i_ddi_attach_pseudo_node configures pseudo drivers which
* has a single node. The .conf nodes must be enumerated
* before calling this interface. The dip is held attached
* upon returning.
*
* This facility should only be called only at boot time
* by the I/O framework.
*/
i_ddi_attach_pseudo_node(char *driver)
{
if (major == DDI_MAJOR_T_NONE)
return (NULL);
return (NULL);
return (dip);
}
static void
{
}
}
/*
* Call ddi_hold_installed_driver() on each parent major
* and invoke mt_config_driver() to attach child major.
* This is part of the implementation of ddi_hold_installed_driver.
*/
static int
{
struct mt_config_handle *hdl;
NULL);
/* disallow recursion on the same driver */
continue;
continue;
}
(void) mt_config_fini(hdl);
return (i_ddi_devs_attached(child_major));
}
int
{
int error = DDI_FAILURE;
/* check for attached instances */
if (i_ddi_devi_attached(dip)) {
error = DDI_SUCCESS;
break;
}
}
return (error);
}
int
{
int circ;
struct ddi_minor_data *dp;
int count = 0;
count++;
}
return (count);
}
/*
* ddi_hold_installed_driver configures and attaches all
* instances of the specified driver. To accomplish this
* it configures and attaches all possible parents of
* the driver, enumerated both in h/w nodes and in the
* driver's .conf file.
*
* NOTE: This facility is for compatibility purposes only and will
* eventually go away. Its usage is strongly discouraged.
*/
static void
{
}
static void
{
}
struct dev_ops *
{
char *parents;
int error;
return (NULL);
/*
* Return immediately if all the attach operations associated
* with a ddi_hold_installed_driver() call have already been done.
*/
return (ops);
return (NULL);
}
/*
* When the driver has no .conf children, it is sufficient
* to attach existing nodes in the device tree. Nodes not
* enumerated by the OBP are not attached.
*/
return (ops);
}
return (NULL);
}
/*
* Driver has .conf nodes. We find all possible parents
* and recursively all ddi_hold_installed_driver on the
* parent driver; then we invoke ndi_config_driver()
* on all possible parent node in parallel to speed up
* performance.
*/
/* find .conf parents */
/* find hw node parents */
if (error == DDI_SUCCESS) {
return (ops);
}
return (NULL);
}
/*
* Default bus_config entry point for nexus drivers
*/
int
{
/*
* A timeout of 30 minutes or more is probably a mistake
* This is intended to catch uses where timeout is in
* the wrong units. timeout must be in units of ticks.
*/
switch (op) {
case BUS_CONFIG_ONE:
timeout));
case BUS_CONFIG_DRIVER:
/*FALLTHROUGH*/
case BUS_CONFIG_ALL:
timeout));
if (timeout > 0) {
"%s%d: bus config all timeout=%ld\n",
timeout));
}
default:
return (NDI_FAILURE);
}
/*NOTREACHED*/
}
/*
* Default busop bus_unconfig handler for nexus drivers
*/
int
void *arg)
{
switch (op) {
case BUS_UNCONFIG_ONE:
(char *)arg));
case BUS_UNCONFIG_DRIVER:
/*FALLTHROUGH*/
case BUS_UNCONFIG_ALL:
default:
return (NDI_FAILURE);
}
/*NOTREACHED*/
}
/*
* dummy functions to be removed
*/
void
{
/* do nothing */
}
/*
* Determine if a node is a leaf node. If not sure, return false (0).
*/
static int
{
if (major == DDI_MAJOR_T_NONE)
return (0);
}
/*
* Multithreaded [un]configuration
*/
static struct mt_config_handle *
{
hdl->mtc_thr_count = 0;
#ifdef DEBUG
hdl->total_time = 0;
#endif /* DEBUG */
return (hdl);
}
#ifdef DEBUG
static int
{
if (nsec < 0) {
sec -= 1;
}
}
#endif /* DEBUG */
static int
{
int rv;
#ifdef DEBUG
int real_time;
#endif /* DEBUG */
while (hdl->mtc_thr_count > 0)
#ifdef DEBUG
"config %s%d: total time %d msec, real time %d msec",
#endif /* DEBUG */
return (rv);
}
struct mt_config_data {
struct mt_config_handle *mtc_hdl;
int mtc_flags;
struct brevq_node *mtc_brn;
struct mt_config_data *mtc_next;
};
static void
mt_config_thread(void *arg)
{
int rv = 0;
#ifdef DEBUG
#endif /* DEBUG */
case MT_CONFIG_OP:
break;
case MT_UNCONFIG_OP:
&brevq);
} else
NULL);
break;
}
#ifdef DEBUG
#endif /* DEBUG */
#ifdef DEBUG
"op %d.%d.%x at %s failed %d",
}
#endif /* DEBUG */
}
}
if (rdip) {
}
if (--hdl->mtc_thr_count == 0)
}
/*
*/
static void
{
int circ;
struct brevq_node *brn;
struct mt_config_data *mcd;
#ifdef DEBUG
/* Update total_time in handle */
#endif
while (dip) {
!(DEVI_EVREMOVE(dip)) &&
/*
* Enqueue this dip's deviname.
* No need to hold a lock while enqueuing since this
* is the only thread doing the enqueue and no one
* walks the queue while we are in multithreaded
* unconfiguration.
*/
} else
/*
* Hold the child that we are processing so he does not get
* removed. The corrisponding ndi_rele_devi() for children
* that are not being skipped is done at the end of
* mt_config_thread().
*/
/*
* skip leaf nodes and (for configure) nodes not
* fully attached.
*/
if (is_leaf_node(dip) ||
continue;
}
/*
* Switch a 'driver' operation to an 'all' operation below a
* node bound to the driver.
*/
if ((major == DDI_MAJOR_T_NONE) ||
else
/*
* The unconfig-driver to unconfig-all conversion above
* constitutes an autodetach for NDI_DETACH_DRIVER calls,
* set NDI_AUTODETACH.
*/
hdl->mtc_thr_count++;
/*
* Add to end of list to process after ndi_devi_exit to avoid
* locking differences depending on value of mtc_off.
*/
else
}
/* go through the list of held children */
else
}
}
static void
{
struct mt_config_data *mcd;
#ifdef DEBUG
/* Update total_time in handle */
#endif
while (dip) {
/*
* Hold the child that we are processing so he does not get
* removed. The corrisponding ndi_rele_devi() for children
* that are not being skipped is done at the end of
* mt_config_thread().
*/
/* skip leaf nodes and nodes not fully attached */
continue;
}
hdl->mtc_thr_count++;
/*
* Add to end of list to process after UNLOCK_DEV_OPS to avoid
* locking differences depending on value of mtc_off.
*/
else
}
/* go through the list of held children */
else
}
}
/*
* Given the nodeid for a persistent (PROM or SID) node, return
* the corresponding devinfo node
* NOTE: This function will return NULL for .conf nodeids.
*/
{
break;
}
}
/*
* Move to head for faster lookup next time
*/
}
return (dip);
}
static void
free_cache_task(void *arg)
{
/*
* The cache can be invalidated without holding the lock
* but it can be made valid again only while the lock is held.
* So if the cache is invalid when the lock is held, it will
* stay invalid until lock is released.
*/
if (!di_cache.cache_valid)
if (di_cache_debug)
}
extern int modrootloaded;
void
{
int error;
extern int sys_shutdown;
if (cache->cache_size) {
cache->cache_size = 0;
if (di_cache_debug)
} else {
if (di_cache_debug)
}
if (di_cache_debug) {
}
return;
}
} else if (di_cache_debug && !error) {
}
}
void
{
int cache_valid;
if (!modrootloaded || !i_ddi_io_initialized()) {
if (di_cache_debug)
return;
}
/* Increment devtree generation number. */
/* Invalidate the in-core cache and dispatch free on valid->invalid */
if (cache_valid) {
/*
* This is an optimization to start cleaning up a cached
* snapshot early. For this reason, it is OK for
* taskq_dispatach to fail (and it is OK to not track calling
* context relative to sleep, and assume NOSLEEP).
*/
}
if (di_cache_debug) {
}
}
static void
{
}
static char vhci_node_addr[2];
static int
{
return (-1);
vhci_node_addr[0] = '\0';
return (0);
}
static void
{
/*
* scsi_vhci should be kept left most of the device tree.
*/
if (scsi_vhci_dip) {
} else {
}
}
/*
* This a special routine to enumerate vhci node (child of rootnex
* node) without holding the ndi_devi_enter() lock. The device node
* is allocated, initialized and brought into DS_READY state before
* inserting into the device tree. The VHCI node is handcrafted
* here to bring the node to DS_READY, similar to rootnex node.
*
* The global_vhci_lock protects linking the node into the device
* of rootnex children.
*
* This routine is a workaround to handle a possible deadlock
* that occurs while trying to enumerate node in a different sub-tree
* during _init/_attach entry points.
*/
/*ARGSUSED*/
{
if (major == -1)
return (NULL);
/* Make sure we create the VHCI node only once */
return (dip);
}
/* Allocate the VHCI node */
/* Mark the node as VHCI */
(void) ndi_devi_free(dip);
return (NULL);
}
(void) ndi_devi_free(dip);
return (NULL);
}
return (dip);
}
/*
* of open devices. Currently, because of tight coupling between the devfs file
* system and the Solaris device tree, a driver can't always make the device
* tree state (esp devi_node_state) match device hardware hotplug state. Until
* resolved, to overcome this deficiency we use the following interfaces that
* maintain the DEVI_DEVICE_REMOVED devi_state status bit. These interface
* report current state, and drive operation (like events and cache
*
* The ndi_devi_device_isremoved() returns 1 if the device is currently removed.
*
* The ndi_devi_device_remove() interface declares the device as removed, and
* returns 1 if there was a state change associated with this declaration.
*
* The ndi_devi_device_insert() declares the device as inserted, and returns 1
* if there was a state change associated with this declaration.
*/
int
{
return (DEVI_IS_DEVICE_REMOVED(dip));
}
int
{
/* Return if already marked removed. */
if (ndi_devi_device_isremoved(dip))
return (0);
/* Mark the device as having been physically removed. */
/* report remove (as 'removed') */
/*
* Invalidate the cache to ensure accurate
* (di_state() & DI_DEVICE_REMOVED).
*/
/*
* Generate sysevent for those interested in removal (either
* directly via private EC_DEVFS or indirectly via devfsadmd
* generated EC_DEV). This will generate LDI DEVICE_REMOVE
* event too.
*/
return (1); /* DEVICE_REMOVED state changed */
}
int
{
/* Return if not marked removed. */
if (!ndi_devi_device_isremoved(dip))
return (0);
/* Mark the device as having been physically reinserted. */
/* report insert (as 'online') */
/*
* Invalidate the cache to ensure accurate
* (di_state() & DI_DEVICE_REMOVED).
*/
/*
* Generate sysevent for those interested in removal (either directly
* via EC_DEVFS or indirectly via devfsadmd generated EC_DEV).
*/
return (1); /* DEVICE_REMOVED state changed */
}
/*
* ibt_hw_is_present() returns 0 when there is no IB hardware actively
* running. This is primarily useful for modules like rpcmod which
* needs a quick check to decide whether or not it should try to use
* InfiniBand
*/
int ib_hw_status = 0;
int
{
return (ib_hw_status);
}
/*
* ASSERT that constraint flag is not set and then set the "retire attempt"
* flag.
*/
int
{
char **cons_array = (char **)arg;
char *path;
int constraint;
int i;
constraint = 0;
if (cons_array) {
for (i = 0; cons_array[i] != NULL; i++) {
constraint = 1;
break;
}
}
}
if (constraint)
(void *)dip));
if (constraint)
(void *)dip));
return (DDI_WALK_CONTINUE);
}
static void
free_array(char **cons_array)
{
int i;
if (cons_array == NULL)
return;
for (i = 0; cons_array[i] != NULL; i++) {
}
}
/*
* Walk *every* node in subtree and check if it blocks, allows or has no
* comment on a proposed retire.
*/
int
{
int *constraint = (int *)arg;
(void) e_ddi_offline_notify(dip);
"subtree is not marked: dip = %p", (void *)dip));
*constraint = 0;
(void *)dip));
*constraint = 0;
"dip = %p", (void *)dip));
*constraint = 0;
} else {
"dip = %p", (void *)dip));
}
return (DDI_WALK_CONTINUE);
}
int
{
int constraint = *(int *)arg;
int finalize;
int phci_only;
"retire: unmarked dip(%p) in retire subtree",
(void *)dip));
return (DDI_WALK_CONTINUE);
}
/*
* retire the device if constraints have been applied
* or if the device is not in use
*/
finalize = 0;
if (constraint) {
} else {
/* we have already finalized during notify */
finalize = 1;
} else {
/*
* even if no contracts, need to call finalize
* to clear the contract barrier on the dip
*/
finalize = 1;
}
(void *)dip));
if (finalize)
}
/*
* phci_only variable indicates no client checking, just
* offline the PHCI. We set that to 0 to enable client
* checking
*/
phci_only = 0;
return (DDI_WALK_CONTINUE);
}
/*
* Returns
* DDI_SUCCESS if constraints allow retire
* DDI_FAILURE if constraints don't allow retire.
* cons_array is a NULL terminated array of node paths for
* which constraints have already been applied.
*/
int
{
int circ;
int circ2;
int constraint;
char *devnm;
/*
* First, lookup the device
*/
/*
* device does not exist. This device cannot be
* a critical device since it is not in use. Thus
* this device is always retireable. Return DDI_SUCCESS
* to indicate this. If this device is ever
* instantiated, I/O framework will consult the
* the persistent retire store, mark it as
* retired and fence it off.
*/
" NOP. Just returning SUCCESS. path=%s", path));
return (DDI_SUCCESS);
}
/*
* Run devfs_clean() in case dip has no constraints and is
* not in use, so is retireable but there are dv_nodes holding
* ref-count on the dip. Note that devfs_clean() always returns
* success.
*/
/* release hold from e_ddi_hold_devi_by_path */
/*
* If it cannot make a determination, is_leaf_node() assumes
* dip is a nexus.
*/
if (!is_leaf_node(dip)) {
}
/*
* apply constraints
*/
if (!is_leaf_node(dip)) {
&constraint);
}
/*
* Now finalize the retire
*/
if (!is_leaf_node(dip)) {
&constraint);
}
if (!constraint) {
} else {
}
}
static int
{
(void) spec_unfence_snode(dip);
return (DDI_WALK_CONTINUE);
}
struct find_dip {
char *fd_buf;
char *fd_path;
};
static int
{
return (DDI_WALK_CONTINUE);
return (DDI_WALK_TERMINATE);
}
int
e_ddi_unretire_device(char *path)
{
int circ;
int circ2;
char *path2;
"device unretire: %s", path);
return (0);
}
/*
* We can't lookup the dip (corresponding to path) via
* e_ddi_hold_devi_by_path() because the dip may be offline
* and may not attach. Use ddi_walk_devs() instead;
*/
pdip = ddi_root_node();
"device unretire: %s", path);
return (0);
}
if (!is_leaf_node(dip)) {
}
/* release hold from find_dip_fcn() */
return (0);
}
/*
* Called before attach on a dip that has been retired.
*/
static int
{
/*
* We have already decided to retire this device. The various
* constraint checking should not be set.
* NOTE that the retire flag may already be set due to
* fenced -> detach -> fenced transitions.
*/
if (fencepath) {
}
return (DDI_WALK_CONTINUE);
}
/*
* Checks the retire database and:
*
* - if device is present in the retire database, marks the device retired
* and fences it off.
* - if device is not in retire database, allows the device to attach normally
*
* To be called only by framework attach code on first attach attempt.
*
*/
static int
{
char *path;
int circ;
int phci_only;
int constraint;
/*
* Root dip is treated special and doesn't take this code path.
* Also root can never be retired.
*/
/*
* Check if this device is in the "retired" store i.e. should
* be retired. If not, we have nothing to do.
*/
if (e_ddi_device_retired(path) == 0) {
(void) e_ddi_unretire_device(path);
return (0);
}
/*
* Mark dips and fence off snodes (if any)
*/
if (!is_leaf_node(dip)) {
}
/*
* We don't want to check the client. We just want to
* offline the PHCI
*/
phci_only = 1;
constraint = 1;
return (1);
}
{ \
}
static int
{
int i = start - 1;
int j = end + 1;
for (;;) {
do {
j--;
do {
i++;
if (i < j)
else
return (j);
}
}
static int
{
int i = start - 1;
int j = end + 1;
for (;;) {
do {
j--;
do {
i++;
if (i < j)
else
return (j);
}
}
static void
{
int mid;
}
}
static void
{
int mid;
}
}
static void
{
int i;
int j;
int k;
int count;
count = 0;
for (i = 0; i < npali; i++) {
}
/*NOTREACHED*/
}
/*NOTREACHED*/
}
for (i = 0, k = 0; i < npali; i++) {
for (j = 0; j < pali[i].pali_naliases; j++, k++) {
pali[i].pali_current;
pali[i].pali_aliases[j];
}
}
/* Now sort the array based on length of pair_alias */
}
void
{
if (npali == 0) {
/*NOTREACHED*/
}
if (ddi_aliases_present == B_TRUE) {
/*NOTREACHED*/
}
/*NOTREACHED*/
}
/*NOTREACHED*/
}
}
static dev_info_t *
path_to_dip(char *path)
{
int error;
char *pdup;
/*NOTREACHED*/
}
}
ddi_alias_to_currdip(char *alias, int i)
{
char *curr;
char *aliasdup;
/*NOTREACHED*/
}
goto out;
goto out;
/*NOTREACHED*/
}
}
out:
if (currdip) {
if (rv != 0) {
}
} else {
if (rv != 0) {
}
if (curr)
}
return (currdip);
}
char *
ddi_curr_to_alias(char *curr, int i)
{
char *alias;
char *currdup;
int len;
int rv;
/*NOTREACHED*/
}
goto out;
goto out;
/*NOTREACHED*/
}
}
}
out:
if (rv != 0) {
}
return (alias);
}
ddi_alias_redirect(char *alias)
{
char *curr;
int i;
if (ddi_aliases_present == B_FALSE)
return (NULL);
if (tsd_get(tsd_ddi_redirect))
return (NULL);
goto out;
}
/* The TLB has no translation, do it the hard way */
if (currdip)
break;
}
out:
return (currdip);
}
char *
ddi_curr_redirect(char *curr)
{
char *alias;
int i;
if (ddi_aliases_present == B_FALSE)
return (NULL);
if (tsd_get(tsd_ddi_redirect))
return (NULL);
goto out;
}
/* The TLB has no translation, do it the slow way */
if (alias)
break;
}
out:
return (alias);
}
void
{
char strbuf[256];
char *buf;
int ce;
int de;
const char *fmtbad = "Invalid arguments to ddi_err()";
switch (ade) {
case DER_CONS:
strbuf[0] = '^';
break;
case DER_LOG:
strbuf[0] = '!';
break;
case DER_VERB:
strbuf[0] = '?';
break;
default:
strbuf[0] = '\0';
break;
}
} else if (rdip) {
}
switch (de) {
case DER_CONT:
}
break;
case DER_NOTE:
break;
case DER_WARN:
break;
case DER_MODE:
if (ddi_err_panic == B_TRUE) {
} else {
}
break;
case DER_DEBUG:
break;
case DER_PANIC:
break;
case DER_INVALID:
default:
break;
}
}
/*ARGSUSED*/
void
{
#else
/*LINTED*/
;
#endif
}