sata.c revision 184cd04c26b064536977dfbb913a1240eaf6f708
/*
* 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"
/*
* SATA libtopo enumerator plugin
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
#include <alloca.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <libnvpair.h>
#include <fm/topo_mod.h>
#include <libdevinfo.h>
#include <config_admin.h>
#include <smbios.h>
#include <dirent.h>
#include <libgen.h>
#include <assert.h>
#include <pthread.h>
#include <fm/libdiskstatus.h>
#include <devid.h>
#include "sata.h"
#include "sfx4500_props.h"
#define MAX_ACTION_RULES 10
#define MAX_MACHNAMELEN 256
/*
* Given a /devices path for a whole disk, appending this extension gives the
* path to a raw device that can be opened.
*/
#define PHYS_EXTN ":q,raw"
#define PHYS_EXTN ":c,raw"
#else
#endif
struct sata_machine_specific_properties *machprops[] = {
};
static const topo_pgroup_info_t io_pgroup =
static const topo_pgroup_info_t storage_pgroup = {
1
};
static char *devpath_to_devid(char *devpath);
int *err);
nvlist_t **);
/*
* Methods for SATA disks. This is used by the disk-transport module to
* generate ereports based off SCSI disk status. Unlike the present method,
* this can only function when we have a /devices path (i.e. the disk is
* configured) and so must be a function of the disk itself.
*/
static const topo_method_t sata_disk_methods[] = {
{ NULL }
};
/*
* Since the global data is only initialized ONCE in _topo_init
* and multiple threads that use libtopo can cause _topo_init to
* be called, the flag and lock here enable only the first to enter
* _topo_init to initialize the global data. After that, it's all
* read-only. The global data used here could have been stored in
* the node-private data area, but that's overkill, because all data
* items will not change once they are set -- they depend only on
* the machine on which this module executes.
*/
static char machname[MAX_MACHNAMELEN];
static const char *pgroupname = NULL;
static const topo_modops_t sata_ops =
{ sata_enum, sata_release };
static const topo_modinfo_t sata_info =
static void
{
}
static char *
{
char *rv;
}
return (rv);
}
static char *
{
char *scheme;
char *dpath;
int e;
return (NULL);
}
"Bad or missing %s in ASRU?\n", FM_FMRI_DEV_PATH);
return (NULL);
}
/*
* dup the string before the nvlist_free() to get a copy before it's
* freed
*/
return (dpath);
}
/*
* If add_fru is B_TRUE, and fru is NULL, the node's FMRI will be used as
* the FRU, otherwise the FMRI specified in the fru nvlist is used.
*
* If asru is NULL, no ASRU will be added.
*
* If label is NULL, no label will be added.
*/
static tnode_t *
{
int len = 0;
char *s;
*s = '-';
} else {
len = 0;
}
}
}
/* Set the FRU to the node's FMRI if caller didn't specify it */
if (add_fru)
err);
}
if (len != 0)
if (model)
if (manuf)
if (serial)
if (firm)
if (fmri)
if (auth)
return (cnode);
}
static char *
{
int i;
while (isspace(*s) && *s != 0)
s++;
i = strlen(s) - 1;
while (i >= 0 && isspace(s[i]))
i--;
rs[i + 1] = 0;
return (rs);
}
static boolean_t
{
}
}
return (retval);
}
int
{
int i;
int mnamelen;
char *mname;
if (getenv("SATADBG"))
(void) pthread_mutex_lock(&global_data_mutex);
if (!global_data_initted) {
i = 0;
/* Initialize the sata_dev_props and ruleset globals */
i++;
}
}
}
}
(void) pthread_mutex_unlock(&global_data_mutex);
return (-1); /* mod errno set */
}
return (0);
}
void
{
}
static int
{
int timeout = 0;
int e;
#define TIMEOUT_MAX 60
/*
* This timeout mechanism handles attachment points that are
* temporarily BUSY while the device is initialized.
*/
do {
case CFGA_OK:
return (CFGA_OK);
case CFGA_BUSY:
case CFGA_SYSTEM_BUSY:
if (timeout++ >= TIMEOUT_MAX)
else {
(void) sleep(1);
}
break;
default:
break;
}
return (e);
}
/*
* Translates a device minor name into an attachment point (`sataX/Y`)
* Allocates memory and returns the attachment point name in *ap.
* (Caller is responsible for deallocating *ap.)
*/
static int
{
char *p;
int nlist;
return (-1);
/*
* The logical id is:
* <attachmentpoint>[::<logicaldisknode>],
* so remove the portion we're not interested in.
*/
*p = 0;
return (0);
}
return (-1);
}
static sata_dev_prop_t *
lookup_sdp_by_minor(char *minorpath)
{
int i;
if (sata_dev_props == NULL)
return (NULL);
return (&sata_dev_props[i]);
}
return (NULL);
}
/*
* Look for disk nodes of the form {physpath}/<whatever>@{portnum},0
* and return the path to that node in physpath
*/
static boolean_t
{
char matchstr[32];
char *p;
int dentlen;
return (-1);
/*
* Allocate a dirent structure large enough to hold the longest path
* in the devfs filesystem. The NUL byte is accounted-for in the
* 1 byte already allocated as part of d_name in the dirent structure.
*/
sizeof (struct dirent);
return (-1);
}
errno = 0;
/* Skip entries that we don't care about */
continue;
}
/* Set errno to 0 before next call to readdir_r */
errno = 0;
}
return (found);
}
static char *
devpath_to_devid(char *devpath)
{
return (NULL);
return (NULL);
}
return (devidstr);
}
static int
{
return (-1);
/* basename() may modify its argument, so dup the string first: */
return (-1);
}
/*
* Allocate a dirent structure large enough to hold the longest path
* in the devfs filesystem. The NUL byte is accounted-for in the
* 1 byte already allocated as part of d_name in the dirent structure.
*/
sizeof (struct dirent);
/*
* Open the directory at "/devices" + dpath + "/.." and scan for the
* minor node names (of minor nodes whose name portion is == to the
* last path element of dpath) with the largest number
*/
errno = 0;
/* Skip entries that we don't care about */
continue;
/* Update the largest minor # by pulling it out of the dirent */
}
/* Set errno to 0 before next call to readdir_r */
errno = 0;
}
if (errno != 0)
nodemax = -1;
return (nodemax);
}
static void
{
int i;
#define MAX_PNAME_LEN 128
char pname[MAX_PNAME_LEN];
/*
* Save the attachment point physical path
*/
/*
* The private properties are the core of the configuration
* mechanism for the sfx4500-disk Diagnosis Engine.
*/
}
/* Add the indicators: */
/*
* Since topo node properties can't contain nvlists,
* the indicators and their actions will go into two
* separate properties
*/
i);
err);
i);
err);
}
/* Now, the (global) indicator rules */
i);
err);
SATA_INDRULE_ACTIONS "-%d", i);
err);
}
}
/*
* minorpath is the path to the minor node for this port (/devices/...:<n>)
*/
static void
{
/* Try to match the minorpath to one of the sata_dev_prop_t's */
}
}
static void
{
int infolen;
char *sata_info;
/*
* The information string from the SATA cfgadm plugin has the
* following form:
*
* Mod: <model> FRev: <revision> SN: <serialno>
*/
return;
return;
}
*revp = 0; /* terminate model string */
*snp = 0; /* terminate revision string */
/*
* The model string is broken into 2 fields --
* manufacturer and model string (separated by one space)
* If there is no space, then there's just a model number
* with no manufacturer.
*/
} else {
*modlp = 0;
modlp += 1;
}
}
static void
{
char *ldev, *p;
if (physpath) {
err);
err);
}
ldev = p + 1;
}
}
if (model) {
}
if (manuf) {
}
if (serial) {
}
if (firm) {
}
/*
* Try to get the disk capacity and store it in a property if the
* device is accessible
*/
if (physpath) {
int fd;
char capstr[32];
}
}
if (capacity > 0) {
capacity);
err);
}
}
}
static boolean_t
is_sata_controller(char *dpath)
{
int *sataprops;
/*
* SATA controllers have a `sata' property on their nodes with
* a value of 1.
*/
if (devnode != DI_NODE_NIL) {
}
return (satactrlr);
}
static int
{
int i, nerrs = 0;
char physpath_buf[PATH_MAX];
char *devid;
SATA_DISK " range [%d..%d]: %s\n",
return (-1);
}
for (i = 0; i < ndisks; i++) {
/*
* Check to see if there's a disk inserted in this port
*/
/*
* The physical path to the drive can be derived by
* taking the attachment point physical path, chopping
* off the minor portion, and looking for the target
* node of the form <nodename>@<X>,0 (where <X> is the
* port number)
*/
/* The AP phys path MUST have a colon: */
/*
* Create the ASRU. If the device is attached to the
* system but unconfigured, then it will have no
* associated ASRU.
*/
devpath = physpath_buf +
sizeof ("/devices") - 1;
/*
* Given the /devices path, attempt to find an
* associated devid.
*/
} else {
}
sata_disk_methods) < 0) {
"topo_method_register failed: %s\n",
++nerrs;
}
} else {
"node for port %d: %s\n", portnum,
topo_strerror(*err));
++nerrs;
}
}
}
if (nerrs)
return (-1);
return (0);
}
static int
{
int i;
char *dpath;
int apbuflen;
int dpathlen;
if (min < 0)
min = 0;
return (0);
/* Get the device path from the parent node's properties: */
return (0);
if (!is_sata_controller(dpath)) {
return (0);
}
"on SATA controller at %s\n", dpath);
return (-1);
}
/* The minor node name = "/devices" + dpath + ":" + i */
mod) != 0) {
"into an attachment point: ignoring sata-port=%d\n",
i);
continue;
}
/* Create the ASRU - a dev:// FMRI */
++nerr;
continue;
}
/*
* The ASRU is the devices path
* The FRU is a the component FRMI
*/
++nerr;
continue;
}
/* For now, ignore errors from private property creation */
/* Create the disk node(s) under this sata-port: */
++nerr;
}
}
if (nerr != 0)
return (-1);
else
return (0);
}
/*ARGSUSED*/
static int
{
return (0);
}
/*
* Query the current disk status. If successful, the disk status is returned as
* an nvlist consisting of at least the following members:
*
* protocol string Supported protocol (currently "scsi")
*
* status nvlist Arbitrary protocol-specific information
* about the current state of the disk.
*
* faults nvlist A list of supported faults. Each
* element of this list is a boolean value.
* An element's existence indicates that
* the drive supports detecting this fault,
* and the value indicates the current
* state of the fault.
*
* <fault-name> nvlist For each fault named in 'faults', a
* nvlist describing protocol-specific
* attributes of the fault.
*
* This method relies on the libdiskstatus library to query this information.
*/
static int
{
int err;
if (vers != TOPO_METH_DISK_STATUS_VERSION)
/*
* If the caller specifies the "path" parameter, then this indicates
* that we should use this instead of deriving it from the topo node
* itself.
*/
} else {
/*
* Get the /devices path and attempt to open the disk status
* handle.
*/
/*
* Note that sizeof(string) includes the terminating NULL byte
*/
sizeof (PHYS_EXTN) - 1;
}
}
if (devpath)
}
return (0);
}