#include <ctype.h>
#include <strings.h>
#include <libdevinfo.h>
#include <libdiskmgt.h>
#include <devid.h>
#include <sys/libdevid.h>
#include <pthread.h>
#include <inttypes.h>
#include <sys/dkio.h>
#include <sys/scsi/scsi_types.h>
#include <fm/topo_mod.h>
#include <fm/topo_list.h>
#include <fm/libdiskstatus.h>
#include <sys/fm/protocol.h>
#include <sys/scsi/generic/inquiry.h>
#include "disk.h"
typedef struct disk_cbdata {
topo_mod_t *dcb_mod;
topo_list_t *dcb_list;
di_devlink_handle_t dcb_devhdl;
dev_di_node_t *dcb_dnode;
} disk_cbdata_t;
static int disk_status(topo_mod_t *, tnode_t *, topo_version_t,
nvlist_t *, nvlist_t **);
static const topo_method_t disk_methods[] = {
{ TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC,
TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL,
disk_status },
{ NULL }
};
static int disk_temp_reading(topo_mod_t *, tnode_t *, topo_version_t,
nvlist_t *, nvlist_t **);
#define TOPO_METH_DISK_TEMP "disk_temp_reading"
#define TOPO_METH_DISK_TEMP_DESC "Disk Temperature Reading"
#define TOPO_METH_DISK_TEMP_VERSION 0
static const topo_method_t disk_fac_methods[] = {
{ TOPO_METH_DISK_TEMP, TOPO_METH_DISK_TEMP_DESC,
TOPO_METH_DISK_TEMP_VERSION, TOPO_STABILITY_INTERNAL,
disk_temp_reading },
{ NULL }
};
static int
disk_set_props(topo_mod_t *mod, tnode_t *parent,
tnode_t *dtn, dev_di_node_t *dnode)
{
nvlist_t *asru = NULL, *drive_attrs;
char *label = NULL;
nvlist_t *fmri = NULL;
dm_descriptor_t drive_descr = 0;
uint32_t rpm;
int err;
if (topo_node_label(parent, &label, &err) != 0) {
if (err != ETOPO_PROP_NOENT) {
topo_mod_dprintf(mod, "disk_set_props: "
"label error %s\n", topo_strerror(err));
goto error;
}
} else if (topo_prop_set_string(dtn, TOPO_PGROUP_PROTOCOL,
TOPO_PROP_LABEL, TOPO_PROP_MUTABLE, label, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"label_set error %s\n", topo_strerror(err));
goto error;
}
if (topo_node_resource(dtn, &fmri, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"resource error: %s\n", topo_strerror(err));
goto error;
}
if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"fru_set error: %s\n", topo_strerror(err));
goto error;
}
if ((topo_pgroup_create(dtn, &disk_auth_pgroup, &err) != 0) &&
(err != ETOPO_PROP_DEFD)) {
topo_mod_dprintf(mod, "disk_set_props: "
"create disk_auth error %s\n", topo_strerror(err));
goto error;
}
if (topo_pgroup_create(dtn, &storage_pgroup, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"create storage error %s\n", topo_strerror(err));
goto error;
}
if (dnode == NULL) {
err = 0;
goto out;
}
if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
dnode->ddn_dpath, dnode->ddn_devid)) == NULL) {
err = ETOPO_FMRI_UNKNOWN;
topo_mod_dprintf(mod, "disk_set_props: "
"asru error %s\n", topo_strerror(err));
goto error;
}
if (topo_node_asru_set(dtn, asru, 0, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"asru_set error %s\n", topo_strerror(err));
goto error;
}
if (topo_pgroup_create(dtn, &io_pgroup, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"create io error %s\n", topo_strerror(err));
goto error;
}
if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH,
TOPO_PROP_IMMUTABLE, dnode->ddn_dpath, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"set dev error %s\n", topo_strerror(err));
goto error;
}
if (dnode->ddn_devid && topo_prop_set_string(dtn, TOPO_PGROUP_IO,
TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, dnode->ddn_devid, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"set devid error %s\n", topo_strerror(err));
goto error;
}
if (dnode->ddn_ppath_count != 0 &&
topo_prop_set_string_array(dtn, TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH,
TOPO_PROP_IMMUTABLE, (const char **)dnode->ddn_ppath,
dnode->ddn_ppath_count, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"set phys-path error %s\n", topo_strerror(err));
goto error;
}
if (dnode->ddn_lpath != NULL &&
topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
dnode->ddn_lpath, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"set disk_name error %s\n", topo_strerror(err));
goto error;
}
if (dnode->ddn_mfg && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE,
dnode->ddn_mfg, &err) != 0)) {
topo_mod_dprintf(mod, "disk_set_props: "
"set mfg error %s\n", topo_strerror(err));
goto error;
}
if (dnode->ddn_model && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE,
dnode->ddn_model, &err) != 0)) {
topo_mod_dprintf(mod, "disk_set_props: "
"set model error %s\n", topo_strerror(err));
goto error;
}
if (dnode->ddn_serial && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE,
dnode->ddn_serial, &err) != 0)) {
topo_mod_dprintf(mod, "disk_set_props: "
"set serial error %s\n", topo_strerror(err));
goto error;
}
if (dnode->ddn_firm && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
dnode->ddn_firm, &err) != 0)) {
topo_mod_dprintf(mod, "disk_set_props: "
"set firm error %s\n", topo_strerror(err));
goto error;
}
if (dnode->ddn_cap && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE,
dnode->ddn_cap, &err) != 0)) {
topo_mod_dprintf(mod, "disk_set_props: "
"set cap error %s\n", topo_strerror(err));
goto error;
}
if (dnode->ddn_devid == NULL ||
(drive_descr = dm_get_descriptor_by_name(DM_DRIVE,
dnode->ddn_devid, &err)) == 0 ||
(drive_attrs = dm_get_attributes(drive_descr, &err)) == NULL)
goto out;
if (nvlist_lookup_boolean(drive_attrs, DM_SOLIDSTATE) == 0 ||
nvlist_lookup_uint32(drive_attrs, DM_RPM, &rpm) != 0)
goto out;
if (topo_prop_set_uint32(dtn, TOPO_PGROUP_STORAGE, TOPO_STORAGE_RPM,
TOPO_PROP_IMMUTABLE, rpm, &err) != 0) {
topo_mod_dprintf(mod, "disk_set_props: "
"set rpm error %s\n", topo_strerror(err));
dm_free_descriptor(drive_descr);
goto error;
}
err = 0;
if (dnode->ddn_firm != NULL) {
topo_ufm_slot_info_t slotinfo = { 0 };
slotinfo.usi_version = dnode->ddn_firm;
slotinfo.usi_active = B_TRUE;
if (strcmp(topo_node_name(parent), USB_DEVICE) == 0)
slotinfo.usi_mode = TOPO_UFM_SLOT_MODE_NONE;
else
slotinfo.usi_mode = TOPO_UFM_SLOT_MODE_WO;
if (topo_node_range_create(mod, dtn, UFM, 0, 0) != 0 ||
topo_mod_create_ufm(mod, dtn, 0, "drive firmware",
&slotinfo) == NULL) {
topo_mod_dprintf(mod, "failed to create %s node", UFM);
goto out;
}
}
out:
if (drive_descr != 0)
dm_free_descriptor(drive_descr);
nvlist_free(fmri);
if (label)
topo_mod_strfree(mod, label);
nvlist_free(asru);
return (err);
error: err = topo_mod_seterrno(mod, err);
goto out;
}
static char *
disk_trim_whitespace(topo_mod_t *mod, const char *begin)
{
const char *end;
char *buf;
size_t count;
if (begin == NULL)
return (NULL);
end = begin + strlen(begin);
while (begin < end && isspace(*begin))
begin++;
while (begin < end && isspace(*(end - 1)))
end--;
count = end - begin;
if ((buf = topo_mod_alloc(mod, count + 1)) == NULL)
return (NULL);
(void) strlcpy(buf, begin, count + 1);
return (buf);
}
static int
disk_temp_reading(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
nvlist_t *in, nvlist_t **out)
{
char *devid;
uint32_t temp;
dm_descriptor_t drive_descr = 0;
nvlist_t *drive_stats, *pargs, *nvl;
int err;
if (vers > TOPO_METH_DISK_TEMP_VERSION)
return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &pargs) != 0 ||
nvlist_lookup_string(pargs, TOPO_IO_DEVID, &devid) != 0) {
topo_mod_dprintf(mod, "Failed to lookup %s arg",
TOPO_IO_DEVID);
return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
}
if ((drive_descr = dm_get_descriptor_by_name(DM_DRIVE, devid,
&err)) == 0) {
topo_mod_dprintf(mod, "failed to get drive decriptor for %s",
devid);
return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
}
if ((drive_stats = dm_get_stats(drive_descr, DM_DRV_STAT_TEMPERATURE,
&err)) == NULL ||
nvlist_lookup_uint32(drive_stats, DM_TEMPERATURE, &temp) != 0) {
topo_mod_dprintf(mod, "failed to read disk temp for %s",
devid);
dm_free_descriptor(drive_descr);
return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
}
dm_free_descriptor(drive_descr);
if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
TOPO_SENSOR_READING) != 0 ||
nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) !=
0 || nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, (double)temp) != 0) {
topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
nvlist_free(nvl);
return (topo_mod_seterrno(mod, EMOD_NOMEM));
}
*out = nvl;
return (0);
}
static int
disk_add_temp_sensor(topo_mod_t *mod, tnode_t *pnode, const char *devid)
{
tnode_t *fnode;
topo_pgroup_info_t pgi;
nvlist_t *arg_nvl = NULL;
int err;
if ((fnode = topo_node_facbind(mod, pnode, "temp",
TOPO_FAC_TYPE_SENSOR)) == NULL) {
topo_mod_dprintf(mod, "failed to bind facility node");
return (-1);
}
pgi.tpi_name = TOPO_PGROUP_FACILITY;
pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
pgi.tpi_version = 1;
if (topo_pgroup_create(fnode, &pgi, &err) != 0) {
if (err != ETOPO_PROP_DEFD) {
topo_mod_dprintf(mod, "pgroups create failure (%s)\n",
topo_strerror(err));
goto err;
}
}
if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY,
TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE,
TOPO_SENSOR_CLASS_THRESHOLD, &err) != 0 ||
topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, TOPO_SENSOR_TYPE_TEMP,
&err) != 0 ||
topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE,
TOPO_SENSOR_UNITS_DEGREES_C, &err) != 0) {
topo_mod_dprintf(mod, "Failed to set props on facnode (%s)",
topo_strerror(err));
goto err;
}
if (topo_method_register(mod, fnode, disk_fac_methods) < 0) {
topo_mod_dprintf(mod, "failed to register facility methods");
goto err;
}
if (topo_mod_nvalloc(mod, &arg_nvl, NV_UNIQUE_NAME) < 0 ||
nvlist_add_string(arg_nvl, TOPO_IO_DEVID, devid) != 0) {
topo_mod_dprintf(mod, "Failed build arg nvlist\n");
(void) topo_mod_seterrno(mod, EMOD_NOMEM);
goto err;
}
if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, "disk_temp_reading",
arg_nvl, &err) != 0) {
topo_mod_dprintf(mod, "Failed to register %s propmeth "
"on fac node %s (%s)\n", TOPO_SENSOR_READING,
topo_node_name(fnode), topo_strerror(err));
goto err;
}
nvlist_free(arg_nvl);
return (0);
err:
topo_node_unbind(fnode);
nvlist_free(arg_nvl);
return (-1);
}
static int
disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
dev_di_node_t *dnode, const char *name, topo_instance_t i, tnode_t **rval)
{
int len;
nvlist_t *fmri;
tnode_t *dtn;
char *part = NULL;
nvlist_t *auth;
char *mfg, *model, *firm, *serial;
*rval = NULL;
if (dnode != NULL) {
mfg = topo_mod_clean_str(mod, dnode->ddn_mfg);
model = topo_mod_clean_str(mod, dnode->ddn_model);
firm = topo_mod_clean_str(mod, dnode->ddn_firm);
serial = topo_mod_clean_str(mod, dnode->ddn_serial);
} else {
mfg = model = firm = serial = NULL;
}
if (mfg != NULL && model != NULL) {
len = strlen(mfg) + 1 + strlen(model) + 1;
if ((part = topo_mod_alloc(mod, len)) != NULL)
(void) snprintf(part, len, "%s-%s",
mfg, model);
}
auth = topo_mod_auth(mod, parent);
fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, NULL,
auth, part ? part : model, firm, serial);
nvlist_free(auth);
topo_mod_strfree(mod, part);
topo_mod_strfree(mod, mfg);
topo_mod_strfree(mod, model);
topo_mod_strfree(mod, firm);
topo_mod_strfree(mod, serial);
if (fmri == NULL) {
topo_mod_dprintf(mod, "disk_tnode_create: "
"hcfmri (%s%" PRIu64 "/%s%" PRIu64 ") error %s\n",
topo_node_name(parent), topo_node_instance(parent),
name, i, topo_strerror(topo_mod_errno(mod)));
return (-1);
}
if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
if (topo_mod_errno(mod) == EMOD_NODE_BOUND) {
nvlist_free(fmri);
return (0);
}
topo_mod_dprintf(mod, "disk_tnode_create: "
"bind (%s%" PRIu64 "/%s%" PRIu64 ") error %s\n",
topo_node_name(parent), topo_node_instance(parent),
name, i, topo_strerror(topo_mod_errno(mod)));
nvlist_free(fmri);
return (-1);
}
nvlist_free(fmri);
if (disk_set_props(mod, parent, dtn, dnode) != 0) {
topo_mod_dprintf(mod, "disk_tnode_create: "
"disk_set_props (%s%" PRIu64 "/%s%" PRIu64 ") error %s\n",
topo_node_name(parent), topo_node_instance(parent),
name, i, topo_strerror(topo_mod_errno(mod)));
topo_node_unbind(dtn);
return (-1);
}
if (dnode != NULL && dnode->ddn_devid != NULL &&
disk_add_temp_sensor(mod, dtn, dnode->ddn_devid) != 0) {
topo_mod_dprintf(mod, "disk_tnode_create: failed to create "
"temperature sensor node on bay=%" PRIu64 "/disk=0",
topo_node_instance(parent));
}
*rval = dtn;
return (0);
}
static int
disk_declare(topo_mod_t *mod, tnode_t *parent, dev_di_node_t *dnode,
tnode_t **childp)
{
tnode_t *dtn = NULL;
int rval;
rval = disk_tnode_create(mod, parent, dnode, DISK, 0, &dtn);
if (dtn == NULL) {
if (rval == 0)
return (0);
topo_mod_dprintf(mod, "disk_declare: "
"disk_tnode_create error %s\n",
topo_strerror(topo_mod_errno(mod)));
return (-1);
}
if (topo_method_register(mod, dtn, disk_methods) != 0) {
topo_mod_dprintf(mod, "disk_declare: "
"topo_method_register error %s\n",
topo_strerror(topo_mod_errno(mod)));
topo_node_unbind(dtn);
return (-1);
}
if (childp != NULL)
*childp = dtn;
return (0);
}
int
disk_declare_path(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
const char *path)
{
dev_di_node_t *dnode;
int i;
for (dnode = topo_list_next(listp); dnode != NULL;
dnode = topo_list_next(dnode)) {
if (dnode->ddn_ppath == NULL)
continue;
for (i = 0; i < dnode->ddn_ppath_count; i++) {
if (di_devfs_path_match(dnode->ddn_ppath[0], path))
return (disk_declare(mod, parent, dnode, NULL));
}
}
topo_mod_dprintf(mod, "disk_declare_path: "
"failed to find disk matching path %s", path);
return (0);
}
int
disk_declare_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
const char *addr, tnode_t **childp)
{
dev_di_node_t *dnode;
int i;
for (dnode = topo_list_next(listp); dnode != NULL;
dnode = topo_list_next(dnode)) {
if (dnode->ddn_target_port == NULL)
continue;
for (i = 0; i < dnode->ddn_ppath_count; i++) {
if ((dnode->ddn_target_port[i] != NULL) &&
(strncmp(dnode->ddn_target_port[i], addr,
strcspn(dnode->ddn_target_port[i], ":"))) == 0) {
topo_mod_dprintf(mod, "disk_declare_addr: "
"found disk matching addr %s", addr);
return (disk_declare(mod, parent, dnode,
childp));
}
}
}
topo_mod_dprintf(mod, "disk_declare_addr: "
"failed to find disk matching addr %s", addr);
return (1);
}
int
disk_declare_bridge(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
const char *addr, tnode_t **childp)
{
dev_di_node_t *dnode;
int i;
for (dnode = topo_list_next(listp); dnode != NULL;
dnode = topo_list_next(dnode)) {
if (dnode->ddn_bridge_port == NULL)
continue;
for (i = 0; i < dnode->ddn_ppath_count; i++) {
if ((dnode->ddn_bridge_port[i] != NULL) &&
(strncmp(dnode->ddn_bridge_port[i], addr,
strcspn(dnode->ddn_bridge_port[i], ":"))) == 0) {
topo_mod_dprintf(mod, "disk_declare_bridge: "
"found disk matching bridge %s", addr);
return (disk_declare(mod, parent, dnode,
childp));
}
}
}
topo_mod_dprintf(mod, "disk_declare_bridge: "
"failed to find disk matching bridge %s", addr);
return (1);
}
int
disk_declare_non_enumerated(topo_mod_t *mod, tnode_t *parent, tnode_t **childp)
{
return (disk_declare(mod, parent, NULL, childp));
}
static int
disk_devlink_callback(di_devlink_t dl, void *arg)
{
disk_cbdata_t *cbp = (disk_cbdata_t *)arg;
topo_mod_t *mod = cbp->dcb_mod;
dev_di_node_t *dnode = cbp->dcb_dnode;
const char *devpath;
char *ctds, *slice;
devpath = di_devlink_path(dl);
if ((dnode == NULL) || (devpath == NULL))
return (DI_WALK_TERMINATE);
if (((ctds = strrchr(devpath, '/')) != NULL) &&
((slice = strchr(ctds, 's')) != NULL))
*slice = '\0';
dnode->ddn_lpath = topo_mod_strdup(mod, ctds ? ctds + 1 : devpath);
if (ctds && slice)
*slice = 's';
return (DI_WALK_TERMINATE);
}
static void
dev_di_node_free(topo_mod_t *mod, dev_di_node_t *dnode)
{
int i;
if (dnode->ddn_devid)
topo_mod_strfree(mod, dnode->ddn_devid);
for (i = 0; i < dnode->ddn_ppath_count; i++) {
topo_mod_strfree(mod, dnode->ddn_ppath[i]);
topo_mod_strfree(mod, dnode->ddn_target_port[i]);
topo_mod_strfree(mod, dnode->ddn_attached_port[i]);
topo_mod_strfree(mod, dnode->ddn_bridge_port[i]);
}
topo_mod_free(mod, dnode->ddn_ppath,
dnode->ddn_ppath_count * sizeof (char *));
topo_mod_free(mod, dnode->ddn_target_port,
dnode->ddn_ppath_count * sizeof (char *));
topo_mod_free(mod, dnode->ddn_attached_port,
dnode->ddn_ppath_count * sizeof (char *));
topo_mod_free(mod, dnode->ddn_bridge_port,
dnode->ddn_ppath_count * sizeof (char *));
topo_mod_strfree(mod, dnode->ddn_dpath);
topo_mod_strfree(mod, dnode->ddn_lpath);
topo_mod_strfree(mod, dnode->ddn_mfg);
topo_mod_strfree(mod, dnode->ddn_model);
topo_mod_strfree(mod, dnode->ddn_serial);
topo_mod_strfree(mod, dnode->ddn_firm);
topo_mod_strfree(mod, dnode->ddn_cap);
topo_mod_free(mod, dnode, sizeof (dev_di_node_t));
}
static int
dev_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp)
{
topo_mod_t *mod = cbp->dcb_mod;
dev_di_node_t *dnode;
di_path_t pnode;
char *path;
int mlen;
char *minorpath;
char *extn = ":a";
char *s;
int64_t *nblocksp;
uint64_t nblocks;
int *blksizep;
uint_t blksize;
char lentry[MAXPATHLEN];
int pathcount;
int *inq_dtype, itype;
int i;
if (devid) {
for (dnode = topo_list_next(cbp->dcb_list);
dnode != NULL; dnode = topo_list_next(dnode)) {
if (dnode->ddn_devid &&
devid_str_compare(dnode->ddn_devid, devid) == 0) {
topo_mod_dprintf(mod, "dev_di_node_add: "
"already there %s\n", devid);
return (0);
}
}
}
if ((dnode = topo_mod_zalloc(mod, sizeof (dev_di_node_t))) == NULL)
return (-1);
if (devid) {
dnode->ddn_devid = topo_mod_strdup(mod, devid);
if (dnode->ddn_devid == NULL)
goto error;
}
if ((path = di_devfs_path(node)) == NULL) {
(void) topo_mod_seterrno(mod, errno);
goto error;
}
dnode->ddn_dpath = topo_mod_strdup(mod, path);
di_devfs_path_free(path);
if (dnode->ddn_dpath == NULL)
goto error;
pathcount = 0;
pnode = NULL;
while ((pnode = di_path_client_next_path(node, pnode)) != NULL) {
pathcount++;
}
if (pathcount == 0) {
if ((dnode->ddn_ppath =
topo_mod_zalloc(mod, sizeof (char *))) == NULL)
goto error;
dnode->ddn_ppath_count = 1;
if ((dnode->ddn_ppath[0] = topo_mod_strdup(mod,
dnode->ddn_dpath)) == NULL)
goto error;
if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
sizeof (char *))) == NULL)
goto error;
if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
sizeof (char *))) == NULL)
goto error;
if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
sizeof (char *))) == NULL)
goto error;
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
if ((dnode->ddn_target_port[0] =
topo_mod_strdup(mod,
scsi_wwnstr_skip_ua_prefix(s))) ==
NULL)
goto error;
}
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
if ((dnode->ddn_attached_port[0] =
topo_mod_strdup(mod,
scsi_wwnstr_skip_ua_prefix(s))) ==
NULL)
goto error;
}
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
if ((dnode->ddn_bridge_port[0] =
topo_mod_strdup(mod,
scsi_wwnstr_skip_ua_prefix(s))) ==
NULL)
goto error;
}
} else {
if ((dnode->ddn_ppath = topo_mod_zalloc(mod,
pathcount * sizeof (char *))) == NULL)
goto error;
dnode->ddn_ppath_count = pathcount;
if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
pathcount * sizeof (char *))) == NULL)
goto error;
if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
pathcount * sizeof (char *))) == NULL)
goto error;
if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
pathcount * sizeof (char *))) == NULL)
goto error;
pnode = NULL;
pathcount = 0;
while ((pnode = di_path_client_next_path(node,
pnode)) != NULL) {
if ((path = di_path_devfs_path(pnode)) == NULL) {
(void) topo_mod_seterrno(mod, errno);
goto error;
}
dnode->ddn_ppath[pathcount] =
topo_mod_strdup(mod, path);
di_devfs_path_free(path);
if (dnode->ddn_ppath[pathcount] == NULL)
goto error;
if ((di_path_prop_lookup_strings(pnode,
SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
if ((dnode->ddn_target_port[pathcount] =
topo_mod_strdup(mod,
scsi_wwnstr_skip_ua_prefix(s))) ==
NULL)
goto error;
}
if ((di_path_prop_lookup_strings(pnode,
SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
if ((dnode->ddn_attached_port[pathcount] =
topo_mod_strdup(mod,
scsi_wwnstr_skip_ua_prefix(s))) ==
NULL)
goto error;
}
if ((di_path_prop_lookup_strings(pnode,
SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
if ((dnode->ddn_bridge_port[pathcount] =
topo_mod_strdup(mod,
scsi_wwnstr_skip_ua_prefix(s))) ==
NULL)
goto error;
}
pathcount++;
}
}
if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
&inq_dtype) > 0) {
dnode->ddn_dtype = *inq_dtype;
itype = (*inq_dtype) & DTYPE_MASK;
if (itype == DTYPE_DIRECT) {
mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
if ((minorpath = topo_mod_alloc(mod, mlen)) == NULL)
goto error;
(void) snprintf(minorpath, mlen, "%s%s",
dnode->ddn_dpath, extn);
cbp->dcb_dnode = dnode;
(void) di_devlink_walk(cbp->dcb_devhdl, "^dsk/",
minorpath, DI_PRIMARY_LINK, cbp,
disk_devlink_callback);
topo_mod_free(mod, minorpath, mlen);
if (dnode->ddn_lpath == NULL) {
topo_mod_dprintf(mod, "dev_di_node_add: "
"failed to determine logical path");
}
}
} else {
dnode->ddn_dtype = DTYPE_UNKNOWN;
}
if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
INQUIRY_VENDOR_ID, &s) > 0) {
if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL)
goto error;
}
if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
INQUIRY_PRODUCT_ID, &s) > 0) {
if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL)
goto error;
}
if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
INQUIRY_REVISION_ID, &s) > 0) {
if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL)
goto error;
}
if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
INQUIRY_SERIAL_NO, &s) > 0) {
if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL)
goto error;
} else {
di_node_t parent;
if ((parent = di_parent_node(node)) != DI_NODE_NIL &&
di_prop_lookup_strings(DDI_DEV_T_ANY, parent,
"usb-serialno", &s) > 0) {
if ((dnode->ddn_serial = disk_trim_whitespace(mod,
s)) == NULL) {
goto error;
}
}
}
if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
"device-nblocks", &nblocksp) > 0) {
nblocks = (uint64_t)*nblocksp;
if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
"device-blksize", &blksizep) > 0)
blksize = (uint_t)*blksizep;
else
blksize = DEV_BSIZE;
(void) snprintf(lentry, sizeof (lentry),
"%" PRIu64, nblocks * blksize);
if ((dnode->ddn_cap = topo_mod_strdup(mod, lentry)) == NULL)
goto error;
}
topo_mod_dprintf(mod, "dev_di_node_add: "
"adding %s\n", devid ? dnode->ddn_devid : "NULL devid");
topo_mod_dprintf(mod, " "
" %s\n", dnode->ddn_dpath);
for (i = 0; i < dnode->ddn_ppath_count; i++) {
topo_mod_dprintf(mod, " "
" %s\n", dnode->ddn_ppath[i]);
}
topo_list_append(cbp->dcb_list, dnode);
return (0);
error:
dev_di_node_free(mod, dnode);
return (-1);
}
static int
dev_walk_di_nodes(di_node_t node, void *arg)
{
char *devidstr = NULL;
char *s;
int *val;
if (di_path_client_next_path(node, NULL) == NULL &&
di_prop_lookup_strings(DDI_DEV_T_ANY, node,
SCSI_ADDR_PROP_TARGET_PORT, &s) <= 0 &&
di_prop_lookup_ints(DDI_DEV_T_ANY, node,
SCSI_ADDR_PROP_TARGET, &val) <= 0) {
return (DI_WALK_CONTINUE);
}
(void) di_prop_lookup_strings(DDI_DEV_T_ANY, node,
DEVID_PROP_NAME, &devidstr);
(void) dev_di_node_add(node, devidstr, arg);
return (DI_WALK_CONTINUE);
}
int
dev_list_gather(topo_mod_t *mod, topo_list_t *listp)
{
di_node_t devtree;
di_devlink_handle_t devhdl;
disk_cbdata_t dcb;
if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
topo_mod_dprintf(mod, "disk_list_gather: "
"topo_mod_devinfo() failed");
return (-1);
}
if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) {
topo_mod_dprintf(mod, "disk_list_gather: "
"di_devlink_init() failed");
return (-1);
}
dcb.dcb_mod = mod;
dcb.dcb_list = listp;
dcb.dcb_devhdl = devhdl;
(void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
dev_walk_di_nodes);
(void) di_devlink_fini(&devhdl);
return (0);
}
void
dev_list_free(topo_mod_t *mod, topo_list_t *listp)
{
dev_di_node_t *dnode;
while ((dnode = topo_list_next(listp)) != NULL) {
topo_list_delete(listp, dnode);
dev_di_node_free(mod, dnode);
}
}
static int
disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
nvlist_t *in_nvl, nvlist_t **out_nvl)
{
disk_status_t *dsp;
char *devpath, *fullpath;
size_t pathlen;
nvlist_t *status;
int err;
*out_nvl = NULL;
if (vers != TOPO_METH_DISK_STATUS_VERSION)
return (topo_mod_seterrno(mod, EMOD_VER_NEW));
if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
devpath = NULL;
pathlen = 0;
} else {
if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
TOPO_IO_DEV_PATH, &devpath, &err) != 0)
return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
pathlen = strlen(devpath) + sizeof ("/devices") +
sizeof (PHYS_EXTN) - 1;
if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
return (topo_mod_seterrno(mod, EMOD_NOMEM));
(void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
PHYS_EXTN);
topo_mod_strfree(mod, devpath);
}
if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
if (devpath)
topo_mod_free(mod, fullpath, pathlen);
return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
EMOD_NOMEM : EMOD_METHOD_NOTSUP));
}
if (devpath)
topo_mod_free(mod, fullpath, pathlen);
if ((status = disk_status_get(dsp)) == NULL) {
err = (disk_status_errno(dsp) == EDS_NOMEM ?
EMOD_NOMEM : EMOD_METHOD_NOTSUP);
disk_status_close(dsp);
return (topo_mod_seterrno(mod, err));
}
*out_nvl = status;
disk_status_close(dsp);
return (0);
}