cfg_link.c revision 3ebafc43f8c876353693c0e5a9e759edd91165b6
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <devfsadm.h>
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <config_admin.h>
#include <cfg_link.h>
#ifdef DEBUG
/*
* for use in print routine arg list as a shorthand way to locate node via
* "prtconf -D" to avoid messy and cluttered debugging code
* don't forget the corresponding "%s%d" format
*/
#else
#endif
char *, int, int);
char *, int);
char *, int, char **);
static char *pci_cfg_info_data(char *);
char *, int);
static void pci_cfg_rm_invalid_links(char *, char *);
static void pci_cfg_rm_link(char *);
static void pci_cfg_rm_all(char *);
di_node_t *, di_minor_t *);
/* flag definitions for di_propall_*(); value "0" is always the default flag */
#define DIPROP_PRI_NODE 0x0
#define DIPROP_PRI_PROM 0x1
static int di_propall_lookup_ints(di_prom_handle_t, int,
static int di_propall_lookup_strings(di_prom_handle_t, int,
static int di_propall_lookup_slot_names(di_prom_handle_t, int,
/*
* NOTE: The CREATE_DEFER flag is private to this module.
* NOT to be used by other modules
*/
static devfsadm_create_t cfg_create_cbt[] = {
},
},
},
},
},
},
}
};
static devfsadm_remove_t cfg_remove_cbt[] = {
},
},
},
},
},
},
},
}
};
static int
{
};
return (DEVFSADM_CONTINUE);
}
== DEVFSADM_FAILURE) {
/*
* Unlike the disks module we don't retry on failure.
* If we have multiple "c" numbers for a single physical
* controller due to bug 4045879, we will not assign a
*/
return (DEVFSADM_CONTINUE);
}
return (DEVFSADM_CONTINUE);
}
static int
{
return (DEVFSADM_CONTINUE);
}
static int
{
return (DEVFSADM_CONTINUE);
}
return (DEVFSADM_CONTINUE);
}
/* create usbN and the symlink */
return (DEVFSADM_CONTINUE);
}
static int
{
char *minor_nm;
return (DEVFSADM_CONTINUE);
return (DEVFSADM_CONTINUE);
/* build the physical path from the components */
return (DEVFSADM_CONTINUE);
}
return (DEVFSADM_CONTINUE);
}
/*
* get_roothub:
*/
/* ARGSUSED */
static char *
{
int i, count = 0;
/* make a copy */
return (NULL);
}
/*
* physpath must always have a minor name component
*/
return (NULL);
}
*cp++ = '\0';
/*
* No '.' in the minor name indicates a roothub port.
*/
/* roothub device */
return (physpath);
}
while (*cp) {
if (*cp == '.')
count++;
cp++;
}
/* Remove as many trailing path components as there are '.'s */
for (i = 0; i < count; i++) {
return (NULL);
}
*cp = '\0';
}
return (physpath);
}
/*
* returns an allocted string containing the device path for <node> and
* <minor>
*/
static char *
{
char *path;
char *bufp;
char *minor_nm;
int buflen;
return (bufp);
}
static int
{
int rv;
if (flags & DIPROP_PRI_PROM) {
if (rv < 0)
} else {
if (rv < 0)
}
return (rv);
}
static int
{
int rv;
if (flags & DIPROP_PRI_PROM) {
if (rv < 0)
} else {
if (rv < 0)
}
return (rv);
}
static di_node_t
{
int *firstchas;
do {
DI_PROP_FIRST_CHAS, &firstchas) >= 0)
return (curnode);
return (DI_NODE_NIL);
}
static int
{
int rv;
if (flags & DIPROP_PRI_PROM) {
if (rv < 0)
} else {
if (rv < 0)
}
return (rv);
}
/*
* returns an allocated string containing the slot name for the slot with
* device number <pci_dev> on bus <node>
*/
static char *
{
#ifdef DEBUG
char *fnm = "pci_cfg_slotname";
#endif
int i, count;
&slot_names);
if (count < 0)
return (NULL);
for (i = 0; i < count; i++) {
break;
}
}
#ifdef DEBUG
dprint(("%s: slot w/ pci_dev %d not found in %s for %s%d\n",
#endif
if (count > 0)
return (name);
}
/*
* returns non-zero if we can return a valid attachment point name for <node>,
* for its slot identified by child pci device number <pci_dev>, through <buf>
*
* prioritized naming scheme:
* 1) <DI_PROP_SLOT_NAMES property> (see pci_cfg_slotname())
* 2) <device-type><DI_PROP_PHYS_SLOT property>
* 3) <drv name><drv inst>.<device-type><pci_dev>
*
* where <device-type> is derived from the DI_PROP_DEV_TYPE property:
* if its value is "pciex" then <device-type> is "pcie"
* else the raw value is used
*
* if <flags> contains APNODE_DEFNAME, then scheme (3) is used
*/
static int
{
int *nump;
int rv;
if (rv < 1)
return (0);
if (flags & APNODE_DEFNAME)
goto DEF;
return (1);
}
DI_PROP_PHYS_SLOT, &nump) > 0) {
if (*nump > 0) {
return (1);
}
}
DEF:
return (1);
}
/*
* returns non-zero if we can return a valid expansion chassis name for <node>
* through <buf>
*
* prioritized naming scheme:
* 1) <IOB_PRE string><DI_PROP_SERID property: sun specific portion>
* 2) <IOB_PRE string><full DI_PROP_SERID property in hex>
* 3) <IOB_PRE string>
*
* DI_PROP_SERID encoding <64-bit int: msb ... lsb>:
* <24 bits: IEEE company id><40 bits: serial number>
*
* sun encoding of 40 bit serial number:
* first byte = device type indicator
* next 4 bytes = 4 ascii characters
*
* In the unlikely event that serial id contains non-printable characters
* the full 64 bit raw hex string will be used for the attachment point.
*/
/*ARGSUSED*/
static int
{
char *idstr;
&seridp) < 1) {
return (1);
}
!serid_printable(&serid)) {
return (1);
}
/*
* the serial id is constructed from lower 40 bits of the serialid
* property and is represented by 5 ascii characters. The first
* character indicates if the IO Box is PCIe or PCI-X.
*/
serid <<= 24;
return (1);
}
/*
* returns the pci device number for <node> if found, else returns PCIDEV_NIL
*/
static minor_t
{
int rv;
int *regp;
®p);
if (rv < 1) {
dprint(("pci_cfg_pcidev: property %s not found "
return (PCIDEV_NIL);
}
return (REG_PCIDEV(regp));
}
/*
* returns non-zero when it can successfully return an attachment point
* through <ap_path> whose length is less than <ap_pathsz>; returns the full
* path of the AP through <pathret> which may be larger than <ap_pathsz>.
* Callers need to free <pathret>. If it cannot return the full path through
* <pathret> it will be set to NULL
*
* The ap path reflects a subset of the device path from an onboard host slot
* up to <node>. We traverse up the device tree starting from <node>, naming
* each component using pci_cfg_ap_node(). If we detect that a certain
* segment is contained within an expansion chassis, then we skip any bus
* nodes in between our current node and the topmost node of the chassis,
* which is identified by the DI_PROP_FIRST_CHAS property, and prepend the name
* of the expansion chassis as given by pci_cfg_iob_name()
*
* This scheme is always used for <pathret>. If however, the size of
* <pathret> is greater than <ap_pathsz> then only the default name as given
* by pci_cfg_ap_node() for <node> will be used
*/
static int
{
#ifdef DEBUG
char *fnm = "pci_cfg_ap_path";
#endif
char *bufptr;
char buf[MAXPATHLEN];
char pathbuf[MAXPATHLEN];
int bufsz;
char *pathptr;
int len;
int rv = 0;
int chasflag = 0;
buf[0] = '\0';
pathbuf[0] = '\0';
*pathptr = '\0';
/*
* as we traverse up the device tree, we prepend components of our
* path inside pathbuf, using pathptr and decrementing
*/
do {
if (chasnode != DI_NODE_NIL) {
if (rv == 0) {
dprint(("%s: cannot create iob name "
*pathptr = '\0';
goto OUT;
}
/* set chasflag when the leaf node is within an iob */
chasflag = 1;
}
if (rv == 0) {
dprint(("%s: cannot create ap node name "
*pathptr = '\0';
goto OUT;
}
/*
* if we can't fit the entire path in our pathbuf, then use
* the default short name and nullify pathptr; also, since
* we prepend in the buffer, we must avoid adding a null char
*/
*pathptr = '\0';
goto DEF;
}
}
*pathptr = '\0';
goto DEF;
}
/* remember the leaf component */
/*
* go no further than the hosts' onboard slots
*/
if (chasnode == DI_NODE_NIL)
break;
/*
* the pci device number of the current node is used to
* identify which slot of the parent's bus (next iteration)
* the current node is on
*/
if (pci_dev == PCIDEV_NIL) {
dprint(("%s: cannot obtain pci device number "
*pathptr = '\0';
goto OUT;
}
rv = 1;
goto OUT;
}
DEF:
/*
* <node>'s name ONLY IF it has a serialid# which will make the apid
* globally unique
*/
ap_path_iob_sep_len) != 0) &&
rv = 1;
goto OUT;
}
}
/*
* if our name still won't fit <ap_pathsz>, then use the leaf <node>'s
* default name
*/
if (rv == 0) {
dprint(("%s: cannot create default ap node name for %s%d\n",
*pathptr = '\0';
goto OUT;
}
rv = 1;
goto OUT;
}
/*
* in this case, cfgadm goes through an expensive process to generate
* a purely dynamic logical apid: the framework will look through
* the device tree for attachment point minor nodes and will invoke
* each plugin responsible for that attachment point class, and if
* the plugin returns a logical apid that matches the queried apid
* or matches the default apid generated by the cfgadm framework for
* then that is what it will use
*
* it is doubly expensive because the cfgadm pci plugin itself will
* also search the entire device tree in the absence of a link
*/
rv = 0;
dprint(("%s: cannot create apid for %s%d within length of %d\n",
OUT:
return (rv);
}
/*
* the DI_PROP_AP_NAMES property contains the first integer section of the
* ieee1275 "slot-names" property and functions as a bitmask; see comment for
* pci_cfg_slotname()
*
* we use the name of the attachment point minor node if its pci device
* number (encoded in the minor number) is allowed by DI_PROP_AP_NAMES
*
* returns non-zero if we return a valid attachment point through <path>
*/
static int
{
int *anp;
&anp) < 1)
return (0);
return (0);
return (1);
}
/*
* determine if <node> qualifies for a path style apid
*/
static int
{
char *devtype;
do {
DI_PROP_DEV_TYPE, &devtype) > 0)
return (1);
return (0);
}
/*
* takes a full path as returned by <pathret> from pci_cfg_ap_path() and
* returns an allocated string intendend to be stored in a devlink info (dli)
* file
*
* data format: "Location: <transformed path>"
* where <transformed path> is <path> with occurrances of AP_PATH_SEP
* replaced by "/"
*/
static char *
pci_cfg_info_data(char *path)
{
#define head "Location: "
char *newpath;
int len;
*np++ = '/';
}
return (newpath);
}
static void
pci_cfg_rm_link(char *file)
{
char *dlipath;
}
/*
* removes all registered devlinks to physical path <physpath> except for
* the devlink <valid> if not NULL;
* <physpath> must include the minor node
*/
static void
{
char **dnp;
int i, dnlen;
return;
else
}
for (i = 0; i < dnlen; i++) {
else
continue;
}
}
}
/*
* takes a complete devinfo snapshot and returns the root node;
* callers must do a di_fini() on the returned node;
* if the snapshot failed, DI_NODE_NIL is returned instead
*
* if <pci_node> is not DI_NODE_NIL, it will search for the same devinfo node
* in the new snapshot and return it through <ret_node> if it is found,
* else DI_NODE_NIL is returned instead
*
* in addition, if <pci_minor> is not DI_MINOR_NIL, it will also return
* the matching minor in the new snapshot through <ret_minor> if it is found,
* else DI_MINOR_NIL is returned instead
*/
static di_node_t
{
int pci_inst;
*ret_node = DI_NODE_NIL;
if (root_node == DI_NODE_NIL)
return (DI_NODE_NIL);
/*
* narrow down search by driver, then instance, then minor
*/
if (pci_node == DI_NODE_NIL)
return (root_node);
do {
break;
}
if (node == DI_NODE_NIL)
return (root_node);
/*
* found node, now search minors
*/
if (pci_minor == DI_MINOR_NIL)
return (root_node);
break;
}
}
return (root_node);
}
static int
{
#ifdef DEBUG
char *fnm = "pci_cfg_creat_cb";
#endif
char ap_path[CFGA_LOG_EXT_LEN];
char linkbuf[MAXPATHLEN];
ph = di_prom_init();
if (ph == DI_PROM_HANDLE_NIL) {
dprint(("%s: di_prom_init() failed for %s%d\n",
goto OUT;
}
/*
* Since incoming nodes from hotplug events are from snapshots that
* snapshot and search for the target node
*/
minor == DI_MINOR_NIL) {
dprint(("%s: devinfo snapshot or search failed for %s%d\n",
goto OUT;
}
&fullpath);
if (rv == 0)
goto OUT;
/*
* We must remove existing links because we may have invalid
* apids that are valid links. Since these are not dangling,
* devfsadm will not invoke the remove callback on them.
*
* What are "invalid apids with valid links"? Consider swapping
* an attachment point bus with another while the system is
* down, on the same device path bound to the same drivers
* but with the new AP bus having different properties
* (e.g. serialid#). If the previous apid is not removed,
* there will now be two different links pointing to the same
* attachment point, but only one reflects the correct
* logical apid
*/
goto OUT;
/*
* we store the full logical path of the attachment point for
* cfgadm to display in its info field which is useful when
* the full logical path exceeds the size limit for logical
* apids (CFGA_LOG_EXT_LEN)
*
* for the cfgadm pci plugin to do the same would be expensive
* (i.e. devinfo snapshot + top down exhaustive minor search +
* equivalent of pci_cfg_ap_path() on every invocation)
*
* note that if we do not create a link (pci_cfg_ap_path() is
* not successful), that is what cfgadm will do anyways to
* create a purely dynamic apid
*/
if (fd < 0)
goto OUT;
dprint(("%s: could not write full pathinfo to dli "
goto OUT;
}
} else {
if (rv == 0)
goto OUT;
}
OUT:
if (fd >= 0)
if (ph != DI_PROM_HANDLE_NIL)
if (root_node != DI_NODE_NIL)
return (DEVFSADM_CONTINUE);
}
static void
pci_cfg_rm_all(char *file)
{
}
/*
* ib_cfg_creat_cb() creates two types of links
*/
static int
{
char *cp;
return (DEVFSADM_CONTINUE);
}
/* create fabric or hca:GUID and the symlink */
} else {
}
return (DEVFSADM_CONTINUE);
}
/*
* This function verifies if the serial id is printable.
*/
static int
{
char *ptr;
int i = 0;
return (0);
return (1);
}