ses.c revision 940d71d237794874e18a0eb72f6564821a823517
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <dirent.h>
#include <devid.h>
#include <fm/topo_mod.h>
#include <fm/topo_list.h>
#include <fm/topo_method.h>
#include <fm/libdiskstatus.h>
#include <inttypes.h>
#include <pthread.h>
#include <strings.h>
#include <unistd.h>
#include "disk.h"
#define SES_VERSION 1
#define TOPO_PGROUP_SES "ses"
#define TOPO_PROP_NODE_ID "node-id"
#define TOPO_PROP_TARGET_PATH "target-path"
#ifndef NDEBUG
#else
#define verify(x) ((void)(x))
#endif
/*
* 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_enum_target {
struct timeval set_snaptime;
char *set_devpath;
int set_refcount;
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;
nvlist_t **);
nvlist_t **);
static const topo_method_t ses_component_methods[] = {
{ NULL }
};
static const topo_method_t ses_enclosure_methods[] = {
{ NULL }
};
static void
{
if (--stp->set_refcount == 0) {
}
}
static void
{
}
}
}
}
/*
* 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);
}
/*
* Determine if the element is present. This is somewhat complicated because
* we need to take a new snapshot in order to determine presence, 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.
*/
/*ARGSUSED*/
static int
{
int err;
ses_node_t *np;
/*
* Determine if we need to take a new snapshot.
*/
}
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 pretend like the device is present in
* this scenario.
*/
goto out;
} else {
}
}
SES_PROP_STATUS_CODE, &status) == 0);
out:
present) != 0) {
}
return (0);
}
/*
* Sets standard properties for a ses node (enclosure or bay). This includes
* setting the FRU to be the same as the resource, as well as setting the
* authority information.
*/
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));
}
"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 sas-phys array to determine the attached SAS
* address. We create a disk node regardless of whether the SES target is SAS
* and supports the necessary pages. If we do find a SAS address, we correlate
* this to the corresponding Solaris device node to fill in the rest of the
* data.
*/
static int
{
char buf[17];
/*
* Skip devices that are not in a present (and possibly damaged) state.
*/
return (0);
if (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);
for (s = 0; s < nsas; s++) {
SES_SAS_PROP_ADDR, &addr) == 0);
if (addr == 0)
continue;
buf) != 0)
return (-1);
}
return (0);
}
/*
* Callback to create a basic node (bay, psu, fan, or controller).
*/
static int
{
char label[128];
int err;
char *classdesc;
/*
* Create the node. The interesting information is all copied from the
* parent enclosure node, so there is not much to do.
*/
goto error;
goto error;
}
goto error;
}
/*
* If the aggregate gives us class description, then use that instead
* of the default label name.
*/
goto error;
goto error;
goto error;
} else {
/*
* always present, and disk nodes are present by virtue of being
* enumerated.
*/
"topo_method_register() failed: %s",
goto error;
}
}
return (0);
return (-1);
}
/*
* 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);
}
nodename, defaultlabel) != 0)
return (-1);
}
}
return (0);
}
/*
* Instantiate a new chassis instance in the topology.
*/
static int
{
char *serial;
int ret = -1;
/*
* 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;
/*
* Create the nodes for power supplies, fans, and devices.
*/
goto error;
ret = 0;
return (ret);
}
/*
* 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.
*/
&csn) != 0)
return (SES_WALK_ACTION_TERMINATE);
break;
}
}
sizeof (ses_enum_chassis_t))) == NULL)
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 &&
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);
return (SES_WALK_ACTION_CONTINUE);
}
sizeof (ses_enum_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);
return (-1);
/*
* We search both the ses(7D) and sgen(7D) locations, so we are
* independent of any particular driver class bindings.
*/
goto error;
/*
* Iterate over known chassis and create the necessary nodes.
*/
goto error;
}
return (0);
return (-1);
}
static const topo_modops_t ses_ops =
{ ses_enum, ses_release };
static topo_modinfo_t ses_info =
/*ARGSUSED*/
int
{
}
void
{
}