devcfg.c revision e37c6c376a1a22a828db3bb5ab40c86cb08f9c86
/* Multi-threaded configuration */ int mtc_op;
/* config or unconfig */ /* used to keep track of branch remove events to be generated */ * 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 */ /* bitset of DS_SYSAVAIL & DS_RECONFIG - no races, no lock */ * 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. /* For ddvis, which needs pseudo children under PCI */ /* Allow path-oriented alias driver binding on driver.conf enumerated nodes */ * The following switch is for service people, in case a * 3rd party driver depends on identify(9e) being called. int mtc_off;
/* turn off mt config */ * dev_info cache and node management /* initialize dev_info node cache */ * Allocating a dev_info node, callable from interrupt context with KM_NOSLEEP * The allocated node has a reference count of 0. static char failed[] =
"i_ddi_alloc_node: out of memory";
/* default binding name is node name */ * Make a copy of system property * 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 * Where A = DDI_AUTO_ASSIGNED_NODEID (auto-assign a nodeid) * auto-assigned nodeids are also auto-freed. panic(
"i_ddi_alloc_node: out of nodeids");
* the nodetype is 'prom', try to 'take' the nodeid now. * This requires memory allocation, so check for failure. * 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 * free a dev_info structure. * NB. Not callable from interrupt since impl_ddi_free_nodeid may block. /* free devi_addr_buf allocated by ddi_set_name_addr() */ /* free this last since contract_device_remove_dip() uses it */ * 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," static const char *
fcn =
"i_ddi_remove_devimap";
* The following check is done with dno_lock held * to prevent race between dip removal and * e_ddi_prom_node_to_dip() panic(
"%s: devinfo node(%p) not found",
* Link this node into the devinfo tree and add to orphan list * Not callable from interrupt context * 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. /* Add scsi_vhci to beginning of list */ /* scsi_vhci under rootnex */ * 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 * Hold the global_vhci_lock before linking any direct * children of rootnex driver. * Release the global_vhci_lock before linking any direct * children of rootnex driver. /* Remove node from orphan list */ * 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. * - set the binding name to the the string, * - set major number to driver major * If we don't find a binding, /* find the driver with most specific binding using compatible */ /* Link node to per-driver list */ * reset parent flag so that nexus will merge .conf props * Unbind this devinfo node * Called before the node is destroyed or driver is removed from system * 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 /* should be DS_READY except for pcmcia ... */ * The parent must have a bus_ctl operation. * 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. /* in case nexus driver didn't clear this field */ /* 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(). * 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);
/* Unbind: demote the node back to DS_LINKED. */ "of node %s failed",
path);
/* 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);
* Now that we have taken care of merge, repromote back * Release our initial hold. If ddi_initchild() was * successfull then it will return with the active hold. /* On failure ensure that DEVI_REBIND is cleared */ * 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(). * Don't check for references here or else a ref-counted * dip cannot be downgraded by the framework. * save the @addr prior to DDI_CTLOPS_UNINITCHILD for use in * freeing the instance if it succeeds. /* 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 /* release the init_node hold */ * NOTE: The decision on whether to allow a path-oriented * init_node() based on driver_conf_allow_path_alias. The * rebind code below prevents deletion of system properties * When driver_conf_allow_path_alias is set, property behavior * driver.conf node, the unit-address properties come from * the driver.conf file as system properties. Removing system * 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, * 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. * 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. /* temporarily hold the driver while we probe */ "probe_node: 0x%p(%s%d) cannot load driver\n",
/* release the driver now that probe is complete */ "probe_node: 0x%p(%s%d) no hardware found%s\n",
* Unprobe a node. Simply reset the node state. * Per-driver list must be held busy while calling this function. * Don't check for references here or else a ref-counted * dip cannot be downgraded by the framework. * Per-driver list must be held busy. * Tell mpxio framework that a node is about to online. /* 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. /* ensure that devids are unregistered */ * Cleanup dacf reservations /* release the driver if attach failed */ /* successful attach, return with driver held */ * Per-driver list must be held busy. * 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. */ "detach_node: 0x%p(%s%d) failed\n",
/* Cleanup dacf reservations */ /* Remove properties and minor nodes in case driver forgots */ /* a detached node can't have attached or .conf children */ /* ensure that devids registered during attach are unregistered */ * 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()). /* successful detach, release the driver */ * Run dacf post_attach routines * 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 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 * Run dacf pre-detach routines * Don't auto-detach if DDI_FORCEATTACH or DDI_NO_AUTODETACH /* check for driver global version of DDI_NO_AUTODETACH */ * 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 /* don't allow any more changes to the device tree */ * only caller can reference this node, no external * Three code path may attempt to bind a node: * 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. * The following transitions synchronizes on the * per-driver busy changing flag, since we already /* should never reach here */ ASSERT(
"unknown devinfo state");
* i_ndi_unconfig_node: downgrade dev_info node into a specified state. /* don't allow any more changes to the device tree */ * Persistent nodes are only removed by hotplug code * .conf nodes synchronizes on per-driver list. * The following transitions synchronizes on the * per-driver busy changing flag, since we already ASSERT(
"unknown devinfo state");
* ddi_initchild: transform node to DS_INITIALIZED state * ddi_uninitchild: transform node down to DS_BOUND state * Take it down to DS_INITIALIZED so pm_pre_probe is run * 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. /* allow pm_pre_probe to reestablish pm state */ * Add a child and bind to driver /* allocate a new node */ * ddi_remove_child: remove the dip. The parent must be attached and held * If we still have children, for example SID nodes marked * as persistent but not attached, attempt to remove them. * NDI wrappers for ref counting, node allocation, and transitions * Caller is assumed to prevent the devi from detaching during this call * Hold/release the driver the devinfo node is bound to. * Single thread entry into devinfo node for modifying its children. * To verify in ASSERTS use DEVI_BUSY_OWNED macro. /* for vHCI, enforce (vHCI, pHCI) ndi_deve_enter() order */ * Release ndi_devi_enter or successful ndi_devi_tryenter. * For pHCI exit we issue a broadcast to vHCI for ndi_devi_config_one() * Release ndi_devi_enter and wait for possibility of new children, avoiding * possibility of missing broadcast before getting to cv_timedwait(). * 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 rval =
1;
/* assume we enter */ rval = 0;
/* devi is busy */ * 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 * Allocate and initialize a new dev_info structure * This routine may sleep and should not be called at interrupt time * Remove an initialized (but not yet attached) dev_info * 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. "ndi_devi_bind_driver: %s%d (%p) flags: %x\n",
* ndi_devi_unbind_driver: unbind the dip * Misc. help routines called by framework only * Determine if node is attached. The implementation accommodates transient * DS_READY->DS_ATTACHED->DS_READY state changes. Outside this file, this * 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. * This support is only used/needed during boot to match * a node bound via a path-based driver alias. * If a child is not named (dev_addr == NULL), there are three * (2) FIND_ADDR_BY_INIT: bring child to DS_INITIALIZED state * (3) FIND_ADDR_BY_CALLBACK: use a caller-supplied callback function /* only one way to find a node */ /* only one way to name a node */ /* preallocate buffer of naming node by callback */ * Walk the child list to find a match /* name the child based on the flag */ * Find child of pdip with name: cname@caddr * Called by init_node() to look for duplicate nodes /* search nodes before dip */ * 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. * Find a child of a given name and address, invoking initchild to name * unnamed children. cname is the node name. /* attempt search without changing state of preceding siblings */ * Find a child of a given name and address, invoking initchild to name * unnamed children. cname is the node name. /* attempt search without changing state of preceding siblings */ * 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. /* return NULL if called without a unit-address */ /* attempt search without changing state of preceding siblings */ * Deleting a property list. Take care, since some property structures * may not be fully built. * Duplicate property list * Create a reference property list, currently used only for * driver global properties. Created with ref count of 1. * protected by dn_lock. The only interfaces modifying * dn_global_prop_ptr is in impl_make[free]_parlist(). * Free table of classes by drivers * Get all classes exported by dip return (0);
/* no class exported */ * Helper functions, returns NULL if no memory. * Load driver.conf file for major. Load all if major == -1. * - 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 /* build dn_list from old entries in path_to_inst */ * Don't support unload all because it doesn't make any sense * 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. * Look for the hardware node that is the target of the merge; * return failure if not found. * 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 it is possible that the hardware has already been touched "!Cannot merge .conf node %s with hw node %p " "-- not in proper state",
* 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. /* 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 * Skip nodes not bound to same driver * Make sure the node is uninitialized and has no property. "suitable for merging wildcard conf node %s",
* 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. * 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). * 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 */ * none of the compatible forms have a driver binding, see if * the node name has a driver binding. * lookup the "compatible" property and cache it's contents in the * encode the compatible property data in the dev_info node * Create a composite string from a list of strings. * A composite string consists of a single buffer containing one * or more NULL terminated strings. "?failed to allocate device node compatstr");
* Remove from orphan list * Remove from per-driver list * scan the per-driver list looking for dev_info "dip" * insert devinfo node 'dip' into the per-driver instance list * Nodes on the per-driver list are ordered: HW - SID - PSEUDO. The order is * required for merging of .conf file data to work properly. * Find the first non-prom node or end of list * Find the first non-persistent node * Find the end of the list * add a list of device nodes to the device node list in the * Look to see if node already exists "remove_from_dn_list: node %s not found in list",
* Add and remove reference driver global property list * Set this variable to '0' to disable the optimization, * and to 2 to print debug message. * Don't print unless optimize dtree is set to 2+ * Set the unoptimized values * Log the stack trace in per-devinfo audit structure and also enter * it into a system wide log for recording the time history. * Copy into common log and note the location for tracing history for (i = 0; i <
devcnt; i++) {
* Launch a thread to force attach drivers. This avoids penalty on boot time. * On i386, the USB drivers need to load and take over from the * SMM BIOS drivers ASAP after consconfig(), so make sure they * get loaded right here rather than letting the thread do it. * The order here is important. EHCI must be loaded first, as * we have observed many systems on which hangs occur if the * {U,O}HCI companion controllers take over control from the BIOS * before EHCI does. These hangs are also caused by BIOSes leaving * interrupt-on-port-change enabled in the ehci controller, so that * when uhci/ohci reset themselves, it induces a port change on * the ehci companion controller. Since there's no interrupt handler * installed at the time, the moment that interrupt is unmasked, an * interrupt storm will occur. All this is averted when ehci is * loaded first. And now you know..... the REST of the story. * Regardless of platform, ehci needs to initialize first to avoid * unnecessary connects and disconnects on the companion controller * when ehci sets up the routing. * 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 * This is a private DDI interface for optimizing boot performance. * I/O subsystem initialization is considered complete when devfsadm * 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. * 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. * May be used to determine if boot is a reconfigure boot. * Note system services are up, inform /dev. * Note reconfiguration boot, inform /dev. * The implementation of ddi_walk_devs(). * Do it in two passes. First pass invoke callback on each * dip on the sibling list. Second pass invoke callback on /* ignore sibling by setting dip to NULL */ /* don't worry about children */ * 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 * It does the walk a layer at a time, not depth-first. The given function * must return one of the following values: * 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 * 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 * 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). * This is a general-purpose routine traverses the per-driver list * and calls the given function for each node. must return one of * N.B. The same restrictions from ddi_walk_devs() apply. * argument to i_find_devi, a devinfo node search callback function. char *
nodename;
/* if non-null, nodename must match */ int instance;
/* if != -1, instance must match */ int attached;
/* if != 0, i_ddi_devi_attached() */ * Find dip with a known node name and instance and return with it held * Parse for name, addr, and minor names. Some args may be NULL. * 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 * Get the binding. If there is none, return the child_name * and let the caller deal with it. * Given the pathname of a device, fill in the dev_info_t value and/or the * dev_t value and/or the spectype, depending on which parameters are non-NULL. * 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 * 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. /* remember prev minor (:xxx) in the middle of path */ /* Get component and chop off minorname */ * Find and configure the child * First look for a minor node matching minorname. * Failing that, try to pass minorname to bus_config(). "%s: minor node not found\n",
pathname));
/* search for a default entry */ * No default minor node, try the first one; * else, assume 1-1 instance-minor mapping * If there is no error, return the appropriate parameters * 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. * 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. * 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. * Get in-kernel devfs pathname * 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() */ * Reset all the pure leaf drivers on the system at halt time /* if the device doesn't need to be reset then there's nothing to do */ * if the device isn't a char/block device or doesn't have a * reset entry point then there's nothing to do. * 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. * 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 reset_leaves() during a * normal system shutdown. It attempts to ensure that there are no * outstanding attach or detach operations in progress when 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 /* if we're panicing then the device tree isn't going to be changing */ /* stop all dev_info state changes in the device tree */ * 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. /* do a sleeping wait for one second */ * 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. /* attempt unbind if current driver is incorrect */ /* If unbound, try to bind to a driver */ /* flush devfs so that ndi_devi_unbind_driver will work when possible */ * We are called either from rem_drv or update_drv. * In both cases, 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() * will be invoked later to reenumerate (new) driver.conf * rebind persistent nodes. * create and attach a dev_info node from a .conf file spec "init_spec_child: parent=%s, bad spec (%s)\n",
* 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. * Run initchild on all child nodes such that instance assignment * for multiport network cards are contiguous. * The pdip must be held busy. /* contiguous instance assignment */ * log a notification that a dev_info node has been configured. * Invalidate the devinfo snapshot cache /* do not generate ESC_DEVFS_DEVI_ADD event during boot */ /* add the device class attribute */ * 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. * Instead of logging a separate branch event just add * DEVFS_BRANCH_EVENT attribute. It indicates devfsadmd to * generate a EC_DEV_BRANCH event. * log a notification that a dev_info node has been unconfigured. /* add the device class, driver name and instance attributes */ * 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. * Instead of logging a separate branch event just add * DEVFS_BRANCH_EVENT attribute. It indicates devfsadmd to * generate a EC_DEV_BRANCH event. cmn_err(
CE_WARN,
"failed to log ESC_DEVFS_DEVI_REMOVE event for %s%s",
* log an event that a dev_info branch has been configured or unconfigured. /* do not generate the event during boot */ * log an event that a dev_info tree branch has been configured. * log an event that a dev_info tree branch has been unconfigured. * enqueue the dip's deviname on the branch event queue. * free the memory allocated for the elements on the branch event queue. * log the events queued up on the branch event queue and free the * node_path must have been allocated with at least MAXPATHLEN bytes. * log the events queued up on the branch event queue and free the * associated memory. Same as the previous function but operates on dip. * log the outstanding branch remove events for the grand children of the dip * and free the associated memory. /* now path contains the node path to the dip's child */ * log and cleanup branch remove events for the grand children of the dip. * 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 * Free up the outstanding branch remove events * queued on brn->brn_child since brn->brn_child * itself is eligible for branch remove event. * 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 * 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 * attach a node/branch with parent already held busy * Delete .conf nodes and nodes that are not * log an event, but not during devfs lookups in which case /* internal function to config immediate children */ "config_immediate_children: %s%d (%p), flags=%x\n",
/* NOTE: devi_attach_node() may remove the dip */ * Configure all nexus nodes or leaf nodes with /* internal function to config grand children */ /* 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. /* call bus_config entry point */ * Some callers, notably SCSI, need to mark the devfs cache * to be rebuilt together with the config operation. * Framework entry point for BUS_CONFIG_ALL "ndi_devi_config: par = %s%d (%p), flags = 0x%x\n",
* Framework entry point for BUS_CONFIG_DRIVER, bound to major /* 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. /* 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 * 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 * child - break out of for(;;) loop if child found. * NOTE: Lock order for ndi_devi_enter is (vHCI, pHCI). /* use mdi_devi_enter ordering */ * When not a vHCI or not all pHCI devices are required to * enumerated under the vHCI (NDI_MDI_FALLBACK) search for /* 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. * determine if we should reenumerate .conf nodes * and look for child again. /* break out of for(;;) if time expired */ * 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 * 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",
* 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. /* done with paddr, fixup i_ddi_parse_name '@'->'\0' change */ /* attach and hold the child, returning pointer to child */ * 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 "ndi_devi_config_one: par = %s%d (%p), child = %s\n",
/* call bus_config entry point */ * DR usage (i.e. call with NDI_CONFIG) recursively configures * grandchildren, performing a BUS_CONFIG_ALL from the node attached * Enumerate and attach a child specified by name 'devnm'. * Called during configure the OBP options. This configures /* call bus_config entry point */ * 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 * - 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. * Start with userland constraints first - applied via device contracts * Next, use LDI to impose kernel constraints /* no matching LDI callbacks */ "BLOCKED flag. dip=%p", (
void *)
dip));
"blocked. clearing RCM CONSTRAINT flag. dip=%p",
"CONSTRAINT flag. dip=%p", (
void *)
dip));
/* also allow retire if device is not in use */ "use. Setting CONSTRAINT flag. dip=%p", (
void *)
dip));
* Note: We cannot ASSERT here that DEVI_R_CONSTRAINT is * not set, since other sources (such as RCM) may have "constraint flag. dip=%p", (
void *)
dip));
"result always = DDI_SUCCESS, dip=%p", (
void *)
dip));
"result always = DDI_SUCCESS, dip=%p", (
void *)
dip));
* detach a node with parent already held busy * Invoke notify if offlining " Calling e_ddi_offline_finalize with result=%d. " " Calling e_ddi_offline_finalize with result=%d, " * For DR, even bound nodes may need to have offline * Remove uninitialized pseudo nodes because * system props are lost and the node cannot be * unconfigure immediate children of bus nexus device * 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 vHCI and vHCI is not a sibling of pHCI * then enter in (vHCI, parent(pHCI)) order. /* use mdi_devi_enter ordering */ /* skip nexus nodes during autodetach */ * Continue upon failure--best effort algorithm * unconfigure grand children of bus nexus device /* multi-threaded configuration of child nexus */ * 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. * 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. * 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. * call bus_unconfig entry point * It should reset nexus flags if unconfigure succeeds. * If NDI_AUTODETACH is specified, this is invoked by either the * moduninstall daemon or the modunload -i 0 command. "ndi_devi_unconfig_driver: par = %s%d (%p), flags = 0x%x\n",
"ndi_devi_unconfig: par = %s%d (%p), flags = 0x%x\n",
"e_ddi_devi_unconfig: par = %s%d (%p), flags = 0x%x\n",
* Unconfigure child by name * If child is pHCI and vHCI and pHCI are not siblings then enter vHCI * before parent(pHCI) to avoid deadlock with mpxio Client power /* use mdi_devi_enter ordering */ "devi_unconfig_one: %s not found\n",
devnm));
"ndi_devi_unconfig_one: par = %s%d (%p), child = %s\n",
* If child is pHCI and vHCI and pHCI are not siblings then enter vHCI * before parent(pHCI) to avoid deadlock with mpxio Client power /* use mdi_devi_enter ordering */ /* call bus_config entry point */ * Common async handler for: * ndi_devi_bind_driver_async * place the devinfo in the ONLINE state. /* bind child before merging .conf nodes */ /* merge .conf properties */ * 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. * devi_attach_node() may remove dip on failure * 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 * 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. /* mark child as need config if requested. */ * 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 * 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. * If child is pHCI and vHCI and pHCI are not siblings then enter vHCI * before parent(pHCI) to avoid deadlock with mpxio Client power * 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 we own parent lock, this is part of a branch * operation. We skip the devfs_clean() step. * Find the child dev_info node of parent nexus 'p' whose name * matches "cname@caddr". Recommend use of ndi_devi_findchild() instead. * Find the child dev_info node of parent nexus 'p' whose name * matches devname "name@addr". Permits caller to hold the parent. * Misc. routines called by framework only * if new child spec has been added. /* coordinate child state update */ * Helper functions, returns NULL if no memory. * 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 (major_t)-1. * Used in loadrootmodules() in the swapgeneric module to * associate a given pathname with a given leaf driver. /* check for path-oriented alias */ * Get the nodeid of the given pathname, if such a mapping exists. * 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));
* If we're bound to something other than the nodename, * note that in the message buffer and system log. if (p && q && (
strcmp(p, q) != 0))
* 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 * must ensure that an ndi_devi_enter(parent)/ndi_devi_hold() from a safe * context is already active. The hold_devi() implementation must accommodate /* try to find the instance in the per driver list */ /* skip node if instance field is not valid */ /* look for instance match */ * To accommodate callers that can't block in * ndi_devi_enter() we do an ndi_devi_hold(), 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. return (
dip);
/* fast-path with devi held */ return (
NULL);
/* told not to drive attach */ /* slow-path may block, so it should not occur from interrupt */ /* reconstruct the path and drive attach by path through devfs. */ 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 * 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 */ * For compatibility only. Do not call this function! * 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. /* can't specify NOATTACH by path */ * Associate a streams queue with a devinfo node * NOTE: This function is called by STREAM driver's put procedure. /* 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 * This is called during boot to force attachment order of special dips * dip must be referenced via ndi_hold_devi() * Recurse up until attached parent is found. * Come top-down, expanding .conf nodes under this parent /* keep this function static */ * 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 * 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. * 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 * This facility should only be called only at boot time * 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. /* disallow recursion on the same driver */ /* check for attached instances */ * 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 * NOTE: This facility is for compatibility purposes only and will * eventually go away. Its usage is strongly discouraged. * Return immediately if all the attach operations associated * with a ddi_hold_installed_driver() call have already been done. "ddi_hold_installed_driver: %s\n",
dnp->
dn_name));
* 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. * 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 /* find hw node parents */ * Default bus_config entry point for nexus drivers * 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. "%s%d: bus config all timeout=%ld\n",
* Default busop bus_unconfig handler for nexus drivers * dummy functions to be removed * Determine if a node is a leaf node. If not sure, return false (0). * Multithreaded [un]configuration "config %s%d: total time %d msec, real time %d msec",
"op %d.%d.%x at %s failed %d",
/* Update total_time in handle */ * 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 * 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 * skip leaf nodes and (for configure) nodes not * Switch a 'driver' operation to an 'all' operation below a * node bound to the driver. * The unconfig-driver to unconfig-all conversion above * constitutes an autodetach for NDI_DETACH_DRIVER calls, * Add to end of list to process after ndi_devi_exit to avoid * locking differences depending on value of mtc_off. /* go through the list of held children */ /* Update total_time in handle */ * 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 /* skip leaf nodes and nodes not fully attached */ * Add to end of list to process after UNLOCK_DEV_OPS to avoid * locking differences depending on value of mtc_off. /* go through the list of held children */ * Given the nodeid for a persistent (PROM or SID) node, return * the corresponding devinfo node * NOTE: This function will return NULL for .conf nodeids. * Move to head for faster lookup next time * 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. /* Increment devtree generation number. */ /* Invalidate the in-core cache and dispatch free on valid->invalid */ * scsi_vhci should be kept left most of the device tree. * 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 * 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. /* Make sure we create the VHCI node only once */ /* Allocate the VHCI node */ /* Mark the node as VHCI */ * 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 * ASSERT that constraint flag is not set and then set the "retire attempt" * Walk *every* node in subtree and check if it blocks, allows or has no * comment on a proposed retire. "subtree is not marked: dip = %p", (
void *)
dip));
"dip = %p", (
void *)
dip));
"dip = %p", (
void *)
dip));
"retire: unmarked dip(%p) in retire subtree",
* retire the device if constraints have been applied * or if the device is not in use /* we have already finalized during notify */ * even if no contracts, need to call finalize * to clear the contract barrier on the dip * phci_only variable indicates no client checking, just * offline the PHCI. We set that to 0 to enable client * 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. * 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));
* 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 /* release hold from e_ddi_hold_devi_by_path */ * If it cannot make a determination, is_leaf_node() assumes constraint =
1;
/* assume constraints allow retire */ * Now finalize the retire "device unretire: %s",
path);
* 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; "device unretire: %s",
path);
/* release hold from find_dip_fcn() */ * Called before attach on a dip that has been retired. * 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. * Checks the retire database and: * - if device is present in the retire database, marks the device retired * - 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. * 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. * Mark dips and fence off snodes (if any) * We don't want to check the client. We just want to