ses.c revision f76de7499b0498d120c0c5ba970d061ccb587552
/*
* 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
*/
/*
*/
#include <alloca.h>
#include <dirent.h>
#include <devid.h>
#include <fm/libdiskstatus.h>
#include <inttypes.h>
#include <pthread.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <sys/libdevid.h>
#include <sys/byteorder.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <libcontract.h>
#include <poll.h>
#include <libsysevent.h>
#include "disk.h"
#include "ses.h"
#define SES_VERSION 1
#define SES_STATUS_UNAVAIL(s) \
((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
/*
* Because multiple SES targets can be part of a single chassis, we construct
* our own hierarchy that takes this into account. These SES targets may refer
* to the same devices (multiple paths) or to different devices (managing
* different portions of the space). We arrange things into a
* ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
* nodes found so far.
*/
typedef struct ses_alt_node {
typedef struct ses_enum_node {
typedef struct ses_enum_chassis {
const char *sec_csn;
typedef struct ses_enum_data {
int sed_errno;
char *sed_name;
typedef struct sas_connector_phy_data {
typedef struct sas_connector_type {
char *name;
static const sas_connector_type_t sas_connector_type_list[] = {
{ 0x0, "Information unknown" },
{ 0x1, "External SAS 4x receptacle (see SAS-2 and SFF-8470)" },
{ 0x2, "Exteranl Mini SAS 4x receptacle (see SAS-2 and SFF-8088)" },
{ 0xF, "Vendor-specific external connector" },
{ 0x10, "Internal wide SAS 4i plug (see SAS-2 and SFF-8484)" },
{ 0x11,
"Internal wide Mini SAS 4i receptacle (see SAS-2 and SFF-8087)" },
{ 0x20, "Internal SAS Drive receptacle (see SAS-2 and SFF-8482)" },
{ 0x21, "Internal SATA host plug (see SAS-2 and SATA-2)" },
{ 0x22, "Internal SAS Drive plug (see SAS-2 and SFF-8482)" },
{ 0x23, "Internal SATA device plug (see SAS-2 and SATA-2)" },
{ 0x2F, "Internal SAS virtual connector" },
{ 0x3F, "Vendor-specific internal connector" },
{ 0x70, "Other Vendor-specific connector" },
{ 0x71, "Other Vendor-specific connector" },
{ 0x72, "Other Vendor-specific connector" },
{ 0x73, "Other Vendor-specific connector" },
{ 0x74, "Other Vendor-specific connector" },
{ 0x75, "Other Vendor-specific connector" },
{ 0x76, "Other Vendor-specific connector" },
{ 0x77, "Other Vendor-specific connector" },
{ 0x78, "Other Vendor-specific connector" },
{ 0x79, "Other Vendor-specific connector" },
{ 0x7A, "Other Vendor-specific connector" },
{ 0x7B, "Other Vendor-specific connector" },
{ 0x7C, "Other Vendor-specific connector" },
{ 0x7D, "Other Vendor-specific connector" },
{ 0x7E, "Other Vendor-specific connector" },
{ 0x7F, "Other Vendor-specific connector" },
{ 0x80, "Not Defined" }
};
#define SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED 0x80
#define SAS_CONNECTOR_TYPE_NOT_DEFINED \
"Connector type not definedi by SES-2 standard"
#define SAS_CONNECTOR_TYPE_RESERVED \
"Connector type reserved by SES-2 standard"
typedef enum {
SES_NEW_CHASSIS = 0x1,
SES_NEW_SUBCHASSIS = 0x2,
SES_DUP_CHASSIS = 0x4,
SES_DUP_SUBCHASSIS = 0x8
static const topo_pgroup_info_t io_pgroup = {
1
};
static const topo_pgroup_info_t storage_pgroup = {
1
};
nvlist_t **);
nvlist_t **);
static const topo_method_t ses_component_methods[] = {
{ NULL }
};
static const topo_method_t ses_bay_methods[] = {
{ NULL }
};
static const topo_method_t ses_enclosure_methods[] = {
{ NULL }
};
/*
* Functions for tracking ses devices which we were unable to open. We retry
* these at regular intervals using ses_recheck_dir() and if we find that we
* can now open any of them then we send a sysevent to indicate that a new topo
* snapshot should be taken.
*/
typedef struct ses_open_fail_list {
struct ses_open_fail_list *sof_next;
char *sof_path;
static ses_open_fail_list_t *ses_sofh;
static pthread_mutex_t ses_sofmt;
static void
{
/*
* check list of "unable to open" devices
*/
(void) pthread_mutex_lock(&ses_sofmt);
/*
* see if we can open it now
*/
continue;
}
/*
* ok - better force a new snapshot
*/
break;
}
(void) pthread_mutex_unlock(&ses_sofmt);
}
static void
{
(void) pthread_mutex_lock(&ses_sofmt);
(void) pthread_mutex_unlock(&ses_sofmt);
}
static void
{
(void) pthread_mutex_lock(&ses_sofmt);
}
(void) pthread_mutex_unlock(&ses_sofmt);
}
/*
* functions for verifying that the ses_enum_target_t held in a device
* contract's cookie field is still valid (it may have been freed by
* ses_release()).
*/
typedef struct ses_stp_list {
struct ses_stp_list *ssl_next;
static ses_stp_list_t *ses_sslh;
static pthread_mutex_t ses_sslmt;
static void
{
(void) pthread_mutex_lock(&ses_sslmt);
(void) pthread_mutex_unlock(&ses_sslmt);
}
static void
{
(void) pthread_mutex_lock(&ses_sslmt);
else
break;
}
}
(void) pthread_mutex_unlock(&ses_sslmt);
}
static int
{
return (1);
return (0);
}
/*
* Functions for creating and destroying a background thread
* (ses_contract_thread) used for detecting when ses devices have been
*/
static struct ses_thread_s {
int thr_sig;
int doexit;
int count;
} sesthread = {
0,
0,
0
};
static void *
ses_contract_thread(void *arg)
{
int pollret;
for (;;) {
/* check if we've been asked to exit */
break;
}
/* poll until an event arrives */
if (pollret == 0)
continue;
}
/* read the event */
(void) pthread_mutex_lock(&ses_sslmt);
(void) pthread_mutex_unlock(&ses_sslmt);
continue;
}
/* see if it is an event we are expecting */
(void) pthread_mutex_unlock(&ses_sslmt);
continue;
}
/* find target pointer saved in cookie */
ctid);
/* check if target pointer is still valid */
if (ses_ssl_valid(stp) == 0) {
event);
if (event != CT_EV_NEGEND)
else
(void) pthread_mutex_unlock(&ses_sslmt);
continue;
}
ctid);
if (event != CT_EV_NEGEND) {
/* if this is an offline event, do the offline */
if (stp->set_target) {
}
} else {
/* if this is the negend, then abandon the contract */
}
}
(void) pthread_mutex_unlock(&ses_sslmt);
}
return (NULL);
}
int
find_thr_sig(void)
{
int i;
/* prefered set of signals that are likely used to terminate threads */
(void) sigemptyset(&oset);
for (i = 0; i < sig_sz; i++) {
return (sig[i]);
}
}
/* reserved set of signals that are not allowed to terminate thread */
(void) sigemptyset(&rset);
/* Find signal that is not masked and not in the reserved list. */
for (i = 1; i < MAXSIG; i++) {
continue;
}
if (sigismember(&oset, i) == 0) {
return (i);
}
}
return (rc);
}
/*ARGSUSED*/
static void
ses_handler(int sig)
{
}
static void
ses_thread_init(void *arg)
{
/* find a suitable signal to use for killing the thread below */
/* if don't have a handler for this signal, create one */
/* create a thread to listen for offline events */
}
}
static void
{
return;
}
}
static void
{
/* convert "/dev" path into "/devices" path */
return;
}
/* set up template to create new contract */
/* strip "../../devices" off the front and create the contract */
else
}
static void
{
if (--stp->set_refcount == 0) {
/* check if already closed due to contract offline request */
if (stp->set_target) {
}
int ctlfd;
}
}
}
static void
{
else
NULL) {
}
}
}
}
}
}
/*
* For enclosure nodes, we have a special contains method. By default, the hc
* walker will compare the node name and instance number to determine if an
* FMRI matches. For enclosures where the enumeration order is impossible to
* predict, we instead use the chassis-id as a unique identifier, and ignore
* the instance number.
*/
static int
{
int err, i;
int mindepth;
if (err != 0)
/*
* If the chassis-id doesn't match, then these FMRIs are not
* equivalent. If one of the FMRIs doesn't have a chassis ID, then we
* have no choice but to fall back to the instance ID.
*/
return (0);
mindepth = 1;
} else {
mindepth = 0;
}
return (0);
for (i = 0; i < nhcp1; i++) {
continue;
return (0);
}
return (1);
}
/*ARGSUSED*/
static int
{
int ret;
if (ret < 0)
return (-1);
ret) == 0)
return (0);
else
nvlist_free(*out);
}
return (-1);
}
/*
* Return a current instance of the node. This is somewhat complicated because
* we need to take a new snapshot in order to get the new data, but we don't
* want to be constantly taking SES snapshots if the consumer is going to do a
* series of queries. So we adopt the strategy of assuming that the SES state
* is not going to be rapidly changing, and limit our snapshot frequency to
* some defined bounds.
*/
{
int err;
ses_node_t *np;
return (NULL);
}
/*
* Determine if we need to take a new snapshot.
*/
/*
* We may have closed the device but not yet abandoned the
* contract (ie we've had the offline event but not yet the
* negend). If so, just return failure.
*/
return (NULL);
}
/*
* The device has been closed due to a contract offline
* request, then we need to reopen it and create a new contract.
*/
if ((tp->set_target =
(void) sysevent_post_event(EC_PLATFORM,
&eid);
return (NULL);
}
if (ses_snap_generation(snap) !=
/*
* If we find ourselves in this situation, we're in
* trouble. The generation count has changed, which
* indicates that our current topology is out of date.
* But we need to consult the new topology in order to
* determine presence at this moment in time. We can't
* go back and change the topo snapshot in situ, so
* we'll just have to fail the call in this unlikely
* scenario.
*/
return (NULL);
} else {
}
}
return (np);
}
/*ARGSUSED*/
void
{
}
/*
* Determine if the element is present.
*/
/*ARGSUSED*/
static int
{
ses_node_t *np;
return (-1);
SES_PROP_STATUS_CODE, &status) == 0);
present) != 0) {
}
return (0);
}
/*
* Sets standard properties for a ses node (enclosure, bay, controller
* or expander).
* This includes setting the FRU, as well as setting the
* authority information. When the fru topo node(frutn) is not NULL
* its resouce should be used as FRU.
*/
static int
{
int err;
/*
* Set the authority explicitly if specified.
*/
if (auth) {
&product) == 0);
&chassis) == 0);
&err) != 0 ||
&err) != 0 ||
&err) != 0) {
}
}
/*
* Copy the resource and set that as the FRU.
*/
"topo_node_resource() failed : %s\n",
topo_strerror(err));
}
} else {
"topo_node_resource() failed : %s\n",
topo_strerror(err));
}
}
"topo_node_fru_set() failed : %s\n",
topo_strerror(err));
}
/*
* Set the SES-specific properties so that consumers can query
* additional information about the particular SES element.
*/
return (-1);
}
"failed to create property %s: %s\n",
return (-1);
}
"failed to create property %s: %s\n",
return (-1);
}
return (0);
}
/*
* Callback to add a disk to a given bay. We first check the status-code to
* determine if a disk is present, ignoring those that aren't in an appropriate
* state. We then scan the parent bay node's SAS address array to determine
* possible attached SAS addresses. We create a disk node if the disk is not
* SAS or the SES target does not support the necessary pages for this; if we
* find the SAS address, we create a disk node and also correlate it with
* the corresponding Solaris device node to fill in the rest of the data.
*/
static int
{
char **paths;
/*
* Skip devices that are not in a present (and possibly damaged) state.
*/
return (0);
if (status != SES_ESC_UNSUPPORTED &&
status != SES_ESC_OK &&
status != SES_ESC_CRITICAL &&
status != SES_ESC_NONCRITICAL &&
status != SES_ESC_UNRECOVERABLE &&
return (0);
/*
* Create the disk range.
*/
"topo_node_create_range() failed: %s",
return (-1);
}
/*
* Look through all SAS addresses and attempt to correlate them to a
* known Solaris device. If we don't find a matching node, then we
* don't enumerate the disk node.
*/
return (0);
return (0);
err = 0;
for (s = 0; s < nsas; s++) {
&child);
if (ret == 0) {
break;
} else if (ret < 0) {
err = -1;
break;
}
}
if (s == nsas)
/* copy sas_addresses (target-ports) from parent (with 'w'added) */
int i;
char **tports;
for (i = 0; i < nsas; i++) {
break;
break;
}
/* if they all worked then create the property */
if (i == nsas)
(void) topo_prop_set_string_array(child,
TOPO_PROP_IMMUTABLE, (const char **)tports,
for (i = 0; i < nsas; i++)
scsi_free_wwnstr(tports[i]);
}
}
for (s = 0; s < nsas; s++)
return (err);
}
static int
{
ses_node_t *np;
char **paths;
continue;
}
if (all_phys == 0)
return (0);
return (-1);
continue;
for (j = 0; j < n_phys; j++) {
&addr) != 0)
continue;
goto error;
++i;
}
}
if (err != 0)
return (err);
}
/*
* Callback to create a basic node (bay, psu, fan, or controller and expander).
*/
static int
{
char label[128];
int err;
char *desc;
/*
* Create the node. The interesting information is all copied from the
* parent enclosure node, so there is not much to do.
*/
goto error;
/*
* We want to report revision information for the controller nodes, but
* we do not get per-element revision information. However, we do have
* revision information for the entire enclosure, and we can use the
* 'reported-via' property to know that this controller corresponds to
* the given revision information. This means we cannot get revision
* information for targets we are not explicitly connected to, but
* there is little we can do about the situation.
*/
report) {
(void) nvlist_lookup_string(
break;
}
}
}
goto error;
}
goto error;
}
/*
* For the node label, we look for the following in order:
*
* <ses-description>
* <ses-class-description> <instance>
* <default-type-label> <instance>
*/
desc[0] == '\0') {
instance);
}
goto error;
/*
* For an expander node, set the FRU to its parent(controller).
* For a connector node, set the FRU to its grand parent(controller).
*/
}
goto error;
goto error;
goto error;
"topo_method_register() failed: %s",
goto error;
}
/*
* Only fan, psu, and controller nodes have a 'present' method.
* Bay nodes are always present, and disk nodes are present by
* virtue of being enumerated and SAS expander nodes and
* SAS connector nodes are also always present once
* the parent controller is found.
*/
"topo_method_register() failed: %s",
goto error;
}
}
return (0);
return (-1);
}
/*
* Create SAS expander specific props.
*/
/*ARGSUSED*/
static int
{
int err, i;
char sasaddr_str[17];
/*
* the uninstalled expander is not enumerated by checking
* the element status code. No present present' method provided.
*/
/*
* Get the Expander SAS address. It should exist.
*/
&sasaddr) != 0) {
"Failed to get prop %s.", SES_EXP_PROP_SAS_ADDR);
goto error;
}
/* search matching dev_di_node. */
break;
}
}
if (!found) {
"ses_set_expander_props: Failed to find matching "
"devinfo node for Exapnder SAS address %s",
/* continue on to get storage group props. */
} else {
goto error;
} else {
}
}
if (dnode->ddn_ppath_count != 0 &&
"set phys-path error %s\n",
topo_strerror(err));
}
}
}
/* create the storage group */
goto error;
} else {
/* set the SAS address prop of the expander. */
&err) != 0) {
"set %S error %s\n", TOPO_PROP_SAS_ADDR,
topo_strerror(err));
}
/* Get the phy information for the expander */
"Failed to get prop %s.", SES_SAS_PROP_PHYS);
} else {
/*
* For each phy, get the connector element index and
* stores into connector element index array.
*/
for (i = 0; i < pcount; i++) {
if (nvlist_lookup_uint64(phylist[i],
SES_PROP_CE_IDX, &connidx) == 0) {
if (connidx != 0xff) {
} else {
connlist[i] = -1;
}
} else {
/* Fail to get the index. set to -1. */
connlist[i] = -1;
}
}
/* set the phy count prop of the expander. */
&err) != 0) {
"set %S error %s\n", TOPO_PROP_PHY_COUNT,
topo_strerror(err));
}
/*
* set the connector element index of
* the expander phys.
*/
}
/* populate other misc storage group properties */
if (found) {
}
}
"set serial error %s\n",
topo_strerror(err));
}
}
}
}
return (0);
return (-1);
}
/*
* Create SAS expander specific props.
*/
/*ARGSUSED*/
static int
{
int err, i;
/*
* convert phy mask to string.
*/
/* create the storage group */
return (-1);
} else {
/* set the SAS address prop of the expander. */
phymask_str, &err) != 0) {
"set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK,
topo_strerror(err));
}
/* Get the connector type information for the expander */
SES_SC_PROP_CONNECTOR_TYPE, &conntype) != 0) {
} else {
for (i = 0; ; i++) {
if (sas_connector_type_list[i].type ==
break;
}
if (sas_connector_type_list[i].type ==
conntype) {
break;
}
}
if (!found) {
if (conntype <
} else {
}
}
/* set the phy count prop of the expander. */
"set %S error %s\n", TOPO_PROP_PHY_COUNT,
topo_strerror(err));
}
}
}
return (0);
}
/*
* Instantiate SAS expander nodes for a given ESC Electronics node(controller)
* nodes.
*/
/*ARGSUSED*/
static int
{
int phycount;
&index) != 0)
return (-1);
/*
* For SES constroller node, check to see if there are
* associated SAS expanders.
*/
max = 0;
}
}
/*
* No SAS expander found notthing to process.
*/
if (!found)
return (0);
/*
* The max number represent the number of elements
* deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX
* of SET_ET_SAS_EXPANDER type element.
*
* There may be multiple ESC Electronics element(controllers)
* within JBOD(typicall two for redundancy) and SAS expander
* elements are associated with only one of them. We are
* still creating the range based max number here.
* That will cover the case that all expanders are associated
* with one SES controller.
*/
SASEXPANDER, 0, max) != 0) {
"topo_node_create_range() failed: %s",
return (-1);
}
/*
* Search exapnders with the parent index matching with
* ESC Electronics element index.
* Note the index used here is a global index across
* SES elements.
*/
/*
* get the parent ESC controller.
*/
SES_PROP_STATUS_CODE, &psstatus) == 0) {
if (psstatus == SES_ESC_NOT_INSTALLED) {
/*
* Not installed.
* Don't create a ndoe.
*/
continue;
}
} else {
/*
* The element should have status code.
* If not there is no way to find
* out if the expander element exist or
* not.
*/
continue;
}
/* Get the physical parent index to compare. */
LIBSES_PROP_PHYS_PARENT, &psindex) == 0) {
/* indentation moved forward */
/*
* Handle basic node information of SAS expander
* element - binding to parent node and
* allocating FMRI...
*/
"SAS-EXPANDER", &exptn) != 0)
continue;
/*
* Now handle SAS expander unique portion of node creation.
* The max nubmer of the phy count is 256 since SES-2
* defines as 1 byte field. The cidxlist has the same
* number of elements.
*
* We use size 64 array to store the connectors.
* Typically a connectors associated with 4 phys so that
* matches with the max number of connecters associated
* with an expander.
* The phy count goes up to 38 for Sun supported
* JBOD.
*/
cidxlist) != 0) {
/*
* error on getting specific prop failed.
* continue on. Note that the node is
* left bound.
*/
continue;
}
/*
* count represetns the number of connectors discovered so far.
*/
count = 0;
for (i = 0; i < phycount; i++) {
if (cidxlist[i] != -1) {
/* connector index is valid. */
for (j = 0; j < count; j++) {
if (connectors[j].index ==
cidxlist[i]) {
/*
* Just update phy mask.
* The postion for connector
* index lists(cidxlist index)
* is set.
*/
connectors[j].phy_mask =
connectors[j].phy_mask |
(1ULL << i);
break;
}
}
/*
* If j and count matche a new connector
* index is found.
*/
if (j == count) {
/* add a new index and phy mask. */
(1ULL << i);
count++;
}
}
}
/*
* create range for the connector nodes.
* The class index of the ses connector element
* is set as the instance nubmer for the node.
* Even though one expander may not have all connectors
* are associated with we are creating the range with
* max possible instance number.
*/
max = 0;
}
}
/*
* No SAS connector found nothing to process.
*/
if (!found)
return (0);
RECEPTACLE, 0, max) != 0) {
"topo_node_create_range() failed: %s",
return (-1);
}
/* search matching connector element using the index. */
for (i = 0; i < count; i++) {
/*
* Get the physical parent index to
* compare.
* The connector elements are children
* of ESC Electronics element even
* though we enumerate them under
* an expander in libtopo.
*/
&conindex) == 0) {
if (conindex ==
connectors[i].index) {
break;
}
}
}
}
/* now create a libtopo node. */
if (found) {
/* Create generic props. */
0) {
continue;
}
/* Create connector specific props. */
continue;
}
}
}
/* end indentation change */
}
}
}
}
return (0);
}
/*
* Instantiate any protocol specific portion of a node.
*/
/*ARGSUSED*/
static int
{
if (type == SES_ET_ESC_ELECTRONICS) {
/* create SAS specific children(expanders and connectors. */
dorange));
}
return (0);
}
/*
* Instantiate any children of a given type.
*/
static int
{
/*
* First go through and count how many matching nodes we have.
*/
max = 0;
}
}
/*
* No enclosure should export both DEVICE and ARRAY_DEVICE elements.
* Since we map both of these to 'disk', if an enclosure does this, we
* just ignore the array elements.
*/
if (!found ||
return (0);
"topo_node_create_range() failed: %s",
return (-1);
}
return (-1);
/*
* For some SES element there may be protocol specific
* information to process. Here we are processing
* the association between enclosure controller and
* SAS expanders.
*/
if (type == SES_ET_ESC_ELECTRONICS) {
/* create SAS expander node */
return (-1);
}
}
}
}
return (0);
}
/*
* Instantiate a new subchassis instance in the topology.
*/
static int
{
char *desc;
char label[128];
char **paths;
int i, err;
int ret = -1;
/*
* Copy authority information from parent enclosure node
*/
goto error;
/*
* Record the subchassis serial number in the FMRI.
* For now, we assume that logical id is the subchassis serial number.
* If this assumption changes in future, then the following
* piece of code will need to be updated via an RFE.
*/
goto error;
}
goto error;
}
/*
* Look for the subchassis label in the following order:
* <ses-description>
* <ses-class-description> <instance>
* <default-type-label> <instance>
*
* For subchassis, the default label is "SUBCHASSIS"
*/
desc[0] == '\0') {
instance);
else
"SUBCHASSIS %llu", instance);
}
goto error;
goto error;
/*
* Set the 'chassis-type' property for this subchassis. This is either
* 'ses-class-description' or 'subchassis'.
*/
desc = "subchassis";
goto error;
}
/*
* For enclosures, we want to include all possible targets (for upgrade
* purposes).
*/
;
verify(i != 0);
i, &err) != 0) {
goto error;
}
goto error;
}
/*
* Create the nodes for controllers and bays.
*/
goto error;
ret = 0;
return (ret);
}
/*
* Instantiate a new chassis instance in the topology.
*/
static int
{
char *serial;
char **paths;
int ret = -1;
int i, err;
/*
* Ignore any internal enclosures.
*/
if (cp->sec_internal)
return (0);
/*
* Check to see if there are any devices presennt in the chassis. If
* not, ignore the chassis alltogether. This is most useful for
* ignoring internal HBAs that present a SES target but don't actually
* manage any of the devices.
*/
break;
}
return (0);
/*
* We use the following property mappings:
*
* manufacturer vendor-id
* model product-id
* serial-number libses-chassis-serial
*/
&raw_manufacturer) == 0);
&raw_revision) == 0);
/*
* To construct the authority information, we 'clean' each string by
* removing any offensive characters and trimmming whitespace. For the
* 'product-id', we use a concatenation of 'manufacturer-model'. We
* also take the numerical serial number and convert it to a string.
*/
goto error;
}
goto error;
/*
* Construct the topo node and bind it to our parent.
*/
goto error;
goto error;
}
/*
* We pass NULL for the parent FMRI because there is no resource
* associated with it. For the toplevel enclosure, we leave the
* individual components within the chassis.
*/
goto error;
}
goto error;
}
"topo_method_register() failed: %s",
goto error;
}
goto error;
/*
* For enclosures, we want to include all possible targets (for upgrade
* purposes).
*/
;
verify(i != 0);
i, &err) != 0) {
"failed to create property %s: %s\n",
goto error;
}
/*
* Create the nodes for power supplies, fans, controllers and devices.
* Note that SAS exopander nodes and connector nodes are handled
* through protocol specific processing of controllers.
*/
goto error;
if (cp->sec_maxinstance >= 0 &&
cp->sec_maxinstance) != 0)) {
goto error;
}
goto error;
"instance %u\nand target (%s) under Chassis with CSN %s",
sc_count++;
}
ret = 0;
return (ret);
}
/*
* Create a bay node explicitly enumerated via XML.
*/
static int
{
/*
* Iterate over chassis looking for an internal enclosure. This
* property is set via a vendor-specific plugin, and there should only
* ever be a single internal chassis in a system.
*/
if (cp->sec_internal)
break;
}
return (-1);
}
return (-1);
return (0);
}
/*
* Initialize chassis or subchassis.
*/
static int
{
SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0);
LIBSES_EN_PROP_INTERNAL, &internal) == 0)
if (flags & SES_NEW_CHASSIS) {
if (!cp->sec_internal)
} else {
if (subchassis != NO_SUBCHASSIS)
else
}
} else {
SES_PROP_IDENT, &ident) == 0) {
}
}
return (0);
}
/*
* Gather nodes from the current SES target into our chassis list, merging the
* results if necessary.
*/
static ses_walk_action_t
{
char *csn;
/*
* If we have already identified the chassis for this target,
* then this is a secondary enclosure and we should ignore it,
* along with the rest of the tree (since this is depth-first).
*/
return (SES_WALK_ACTION_TERMINATE);
/*
* Go through the list of chassis we have seen so far and see
* if this serial number matches one of the known values.
* If so, check whether this enclosure is a subchassis.
*/
&csn) != 0)
return (SES_WALK_ACTION_TERMINATE);
&subchassis);
/*
* We need to determine whether this enclosure node
* represents a chassis or a subchassis. Since we may
* receive the enclosure nodes in a non-deterministic
* manner, we need to account for all possible combinations:
* 1. Chassis for the current CSN has not yet been
* allocated
* 1.1 This is a new chassis:
* allocate and instantiate the chassis
* 1.2 This is a new subchassis:
* allocate a placeholder chassis
* allocate and instantiate the subchassis
* link the subchassis to the chassis
* 2. Chassis for the current CSN has been allocated
* 2.1 This is a duplicate chassis enclosure
* check whether to override old chassis
* append to chassis' target list
* 2.2 Only placeholder chassis exists
* fill in the chassis fields
* 2.3 This is a new subchassis
* allocate and instantiate the subchassis
* link the subchassis to the chassis
* 2.4 This is a duplicate subchassis enclosure
* check whether to override old chassis
* append to chassis' target list
*/
break;
/* 1. Haven't seen a chassis with this CSN before */
sizeof (ses_enum_chassis_t))) == NULL)
goto error;
if (subchassis == NO_SUBCHASSIS) {
/* 1.1 This is a new chassis */
SES_NEW_CHASSIS) < 0)
goto error;
} else {
/* 1.2 This is a new subchassis */
"subchassis with CSN %s and index %llu",
sizeof (ses_enum_chassis_t))) == NULL)
goto error;
goto error;
}
} else {
/*
* We have a chassis or subchassis with this CSN. If
* it's a chassis, we must check to see whether it is
* a placeholder previously created because we found a
* subchassis with this CSN. We will know that because
* the sec_target value will not be set; it is set only
* in ses_init_chassis(). In that case, initialise it
* as a new chassis; otherwise, it's a duplicate and we
* need to append only.
*/
if (subchassis == NO_SUBCHASSIS) {
/* 2.1 This is a duplicate chassis */
"duplicate chassis with CSN (%s)",
SES_DUP_CHASSIS) < 0)
goto error;
} else {
/* Placeholder chassis - init it up */
"placeholder chassis with CSN %s",
SES_NEW_CHASSIS) < 0)
goto error;
}
} else {
/* This is a subchassis */
break;
/* 2.3 This is a new subchassis */
"new subchassis with CSN (%s) "
"and LID (%s)",
sizeof (ses_enum_chassis_t)))
== NULL)
goto error;
SES_NEW_SUBCHASSIS) < 0)
goto error;
} else {
/* 2.4 This is a duplicate subchassis */
"duplicate subchassis with "
SES_DUP_SUBCHASSIS) < 0)
goto error;
}
}
}
/*
* If we haven't yet seen an enclosure node and identified the
* current chassis, something is very wrong; bail out.
*/
return (SES_WALK_ACTION_TERMINATE);
/*
* If this isn't one of the element types we care about, then
* ignore it.
*/
&type) == 0);
if (type != SES_ET_DEVICE &&
type != SES_ET_ARRAY_DEVICE &&
type != SES_ET_COOLING &&
type != SES_ET_POWER_SUPPLY &&
type != SES_ET_ESC_ELECTRONICS &&
type != SES_ET_SAS_EXPANDER &&
return (SES_WALK_ACTION_CONTINUE);
/*
* Get the current instance number and see if we already know
* about this element. If so, it means we have multiple paths
* to the same elements, and we should ignore the current path.
*/
&instance) == 0);
&instance);
break;
}
/*
* We prefer the new element under the following circumstances:
*
* - The currently known element's status is unknown or not
* available, but the new element has a known status. This
* occurs if a given element is only available through a
* particular target.
*
* - This is an ESC_ELECTRONICS element, and the 'reported-via'
* property is set. This allows us to get reliable firmware
* revision information from the enclosure node.
*/
if (nvlist_lookup_uint64(
SES_PROP_STATUS_CODE, &prevstatus) != 0)
if (nvlist_lookup_uint64(
if ((SES_STATUS_UNAVAIL(prevstatus) &&
!SES_STATUS_UNAVAIL(status)) ||
(type == SES_ET_ESC_ELECTRONICS &&
report)) {
}
sizeof (ses_alt_node_t))) == NULL)
goto error;
return (SES_WALK_ACTION_CONTINUE);
}
sizeof (ses_enum_node_t))) == NULL)
goto error;
sizeof (ses_alt_node_t))) == NULL) {
goto error;
}
if (type == SES_ET_DEVICE)
}
return (SES_WALK_ACTION_CONTINUE);
return (SES_WALK_ACTION_TERMINATE);
}
static int
{
int err = -1;
/*
* Open the SES target directory and iterate over any available
* targets.
*/
/*
* If the SES target directory does not exist, then return as if
* there are no active targets.
*/
"directory '%s'", dirpath);
return (0);
}
continue;
/*
* Create a new target instance and take a snapshot.
*/
sizeof (ses_enum_target_t))) == NULL)
goto error;
/*
* We keep track of the SES device path and export it on a
* per-node basis to allow higher level software to get to the
* corresponding SES state.
*/
goto error;
}
if ((stp->set_target =
continue;
}
/*
* Enumerate over all SES elements and merge them into the
* correct ses_enum_chassis_t.
*/
goto error;
}
err = 0;
return (err);
}
static void
{
}
}
/*ARGSUSED*/
static int
{
/*
* Check to make sure we're being invoked sensibly, and that we're not
* being invoked as part of a post-processing step.
*/
return (0);
/*
* If this is the first time we've called our enumeration method, then
* gather information about any available enclosures.
*/
NULL)
return (-1);
goto error;
/*
* We search both the ses(7D) and sgen(7D) locations, so we are
* independent of any particular driver class bindings.
*/
goto error;
}
/*
* This is a request to enumerate external enclosures. Go
* through all the targets and create chassis nodes where
* necessary.
*/
goto error;
}
} else {
/*
* This is a request to enumerate a specific bay underneath the
* root chassis (for internal disks).
*/
goto error;
}
/*
* This is a bit of a kludge. In order to allow internal disks to be
* enumerated and share snapshot-specific information with the external
* enclosure enumeration, we rely on the fact that we will be invoked
* for the 'ses-enclosure' node last.
*/
}
return (0);
return (-1);
}
static const topo_modops_t ses_ops =
{ ses_enum, ses_release };
static topo_modinfo_t ses_info =
/*ARGSUSED*/
int
{
int rval;
return (rval);
}
void
{
}