disk_common.c revision 4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Functions in this file are shared between the disk and ses enumerators.
*
* A topo_list_t of all disks is returned by a successful disk_list_gather()
* call, and the list is freed by a disk_list_free(). To create a 'disk' topo
* node below a specific 'bay' parent node either disk_declare_path() or
* disk_declare_addr() are called. The caller determines which 'disk' is
* in which 'bay'. A disk's 'label' and 'authority' information come from
* its parent 'bay' node.
*/
#include <ctype.h>
#include <strings.h>
#include <libdevinfo.h>
#include <devid.h>
#include <sys/libdevid.h>
#include <pthread.h>
#include <inttypes.h>
#include <fm/topo_mod.h>
#include <fm/topo_list.h>
#include <fm/libdiskstatus.h>
#include "disk.h"
/*
* disk node information.
*/
typedef struct disk_di_node {
/* the following two fields are always defined */
char *ddn_devid; /* devid of disk */
char *ddn_dpath; /* path to devinfo (may be vhci) */
char **ddn_ppath; /* physical path to device (phci) */
int ddn_ppath_count;
char *ddn_lpath; /* logical path (public /dev name) */
char *ddn_mfg; /* misc information about device */
char *ddn_model;
char *ddn_serial;
char *ddn_firm;
char *ddn_cap;
char **ddn_target_port;
/* common callback information for di_walk_node() and di_devlink_walk */
typedef struct disk_cbdata {
/*
* 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
/*
* Methods for disks. This is used by the disk-transport module to
* generate ereports based off SCSI disk status.
*/
static const topo_method_t disk_methods[] = {
disk_status },
{ NULL }
};
static const topo_pgroup_info_t io_pgroup = {
1
};
static const topo_pgroup_info_t disk_auth_pgroup = {
1
};
static const topo_pgroup_info_t storage_pgroup = {
1
};
/*
* Set the properties of the disk node, from disk_di_node_t data.
* Properties include:
* group: protocol properties: resource, asru, label, fru
* group: authority properties: product-id, chasis-id, server-id
* group: io properties: devfs-path, devid
* group: storage properties:
* - logical-disk, disk-model, disk-manufacturer, serial-number
* - firmware-revision, capacity-in-bytes
*/
static int
{
int err;
/* form and set the asru */
goto error;
}
goto error;
}
/* pull the label property down from our parent 'bay' node */
goto error;
}
goto error;
}
/* get the resource fmri, and use it as the fru */
goto error;
}
goto error;
}
(err != ETOPO_PROP_DEFD)) {
goto error;
}
goto error;
}
goto error;
}
goto error;
}
if (dnode->ddn_ppath_count != 0 &&
goto error;
}
/* create the storage group */
goto error;
}
/* set the storage group public /dev name */
goto error;
}
/* populate other misc storage group properties */
goto error;
}
goto error;
}
goto error;
}
goto error;
}
goto error;
}
err = 0;
if (label)
if (asru)
return (err);
goto out;
}
/*
* Trim leading and trailing whitespace from the string.
*/
static char *
{
const char *end;
char *buf;
return (NULL);
begin++;
end--;
return (NULL);
return (buf);
}
/*
* Manufacturing strings can contain characters that are invalid for use in hc
* authority names. This trims leading and trailing whitespace, and
* substitutes any characters known to be bad.
*/
char *
{
char *buf, *p;
return (NULL);
return (NULL);
*p = '-';
return (buf);
}
/* create the disk topo node */
static tnode_t *
{
int len;
/* form 'part=' of fmri as "<mfg>-<model>" */
}
"hcfmri (%s%d/%s%d) error %s\n",
return (NULL);
}
"bind (%s%d/%s%d) error %s\n",
return (NULL);
}
/* add the properties of the disk */
"disk_set_props (%s%d/%s%d) error %s\n",
return (NULL);
}
return (dtn);
}
static int
{
/* create the disk topo node: one disk per 'bay' */
"disk_tnode_create error %s\n",
return (-1);
}
/* register disk_methods against the disk topo node */
"topo_method_register error %s\n",
return (-1);
}
return (0);
}
int
const char *path)
{
int i;
/*
* Check for match using physical phci (ddn_ppath). Use
* di_devfs_path_match so generic.vs.non-generic names match.
*/
continue;
for (i = 0; i < dnode->ddn_ppath_count; i++) {
}
}
"failed to find disk matching path %s", path);
return (0);
}
int
const char *addr)
{
int i;
/* Check for match using addr. */
continue;
for (i = 0; i < dnode->ddn_target_port_count; i++) {
}
}
"failed to find disk matching addr %s", addr);
return (0);
}
/* di_devlink callback for disk_di_node_add */
static int
{
const char *devpath;
return (DI_WALK_TERMINATE);
/* trim the slice off the public name */
*slice = '\0';
/* Establish the public /dev name (no slice) */
*slice = 's';
return (DI_WALK_TERMINATE);
}
static void
{
int i;
/* free the stuff we point to */
for (i = 0; i < dnode->ddn_ppath_count; i++)
for (i = 0; i < dnode->ddn_target_port_count; i++)
/* free self */
}
static int
{
char *path;
int mlen;
char *minorpath;
char *extn = ":a";
char *s;
int *dblksizep;
char lentry[MAXPATHLEN];
int ret, i;
/* check for list duplicate using devid search */
"already there %s\n", devid);
return (0);
}
}
return (-1);
/* Establish the devid. */
goto error;
/* Establish the devinfo dpath */
goto error;
}
goto error;
/*
* Establish the physical ppath and target ports. If the device is
* non-mpxio then dpath and ppath are the same, and the target port is a
* property of the device node.
*
* If dpath is a client node under scsi_vhci, then iterate over all
* paths and get their physical paths and target port properrties.
* di_path_client_next_path call below will
* return non-NULL, and ppath is set to the physical path to the first
* pathinfo node.
*
* NOTE: It is possible to get a generic.vs.non-generic path
* for di_devfs_path.vs.di_path_devfs_path like:
* xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
* pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
* To resolve this issue disk_declare_path() needs to use the
* special di_devfs_path_match() interface.
*/
SCSI_ADDR_PROP_TARGET_PORT, &s)) > 0)
pathcount++;
}
if (pathcount == 0) {
goto error;
goto error;
SCSI_ADDR_PROP_TARGET_PORT, &s)) > 0) {
goto error;
for (i = 0; i < ret; i++) {
if ((dnode->ddn_target_port[i] =
scsi_wwnstr_skip_ua_prefix(s))) ==
NULL)
goto error;
s += strlen(s) + 1;
}
}
} else {
goto error;
if (portcount != 0 &&
goto error;
goto error;
}
goto error;
SCSI_ADDR_PROP_TARGET_PORT, &s)) > 0) {
for (i = 0; i < ret; i++) {
scsi_wwnstr_skip_ua_prefix(s))) ==
NULL)
goto error;
portcount++;
s += strlen(s) + 1;
}
}
pathcount++;
}
}
/*
* Find the public /dev name by adding a minor name and using
* di_devlink interface for reverse translation (use devinfo path).
*/
goto error;
"failed to determine logical path");
goto error;
}
/* cache various bits of optional information about the disk */
INQUIRY_VENDOR_ID, &s) > 0) {
goto error;
}
INQUIRY_PRODUCT_ID, &s) > 0) {
goto error;
}
INQUIRY_REVISION_ID, &s) > 0) {
goto error;
}
INQUIRY_SERIAL_NO, &s) > 0) {
goto error;
}
"device-nblocks", &nblocksp) > 0) {
/*
* To save kernel memory, the driver may not define
* "device-dblksize" when its value is default DEV_BSIZE.
*/
"device-dblksize", &dblksizep) > 0)
else
goto error;
}
for (i = 0; i < dnode->ddn_ppath_count; i++) {
}
return (0);
return (-1);
}
/* di_walk_node callback for disk_list_gather */
static int
{
/* only interested in nodes that have devids */
DEVID_PROP_NAME, &devidstr) < 0) {
return (DI_WALK_CONTINUE);
}
return (DI_WALK_CONTINUE);
}
int
{
"topo_mod_devinfo() failed");
return (-1);
}
"di_devlink_init() failed");
return (-1);
}
/* walk the devinfo snapshot looking for nodes with devids */
(void) di_devlink_fini(&devhdl);
return (0);
}
void
{
}
}
/*
* 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)
}
if (devpath)
}
return (0);
}