2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * A topo_list_t of all disks is returned by a successful disk_list_gather()
2N/A * call, and the list is freed by a disk_list_free(). To create a 'disk' topo
2N/A * node below a specific 'bay' parent node either disk_declare_path() or
2N/A * disk_declare_addr() are called. The caller determines which 'disk' is
2N/A * in which 'bay'. A disk's 'label' and 'authority' information come from
2N/A * its parent 'bay' node.
2N/A */
2N/A
2N/A#include <ctype.h>
2N/A#include <strings.h>
2N/A#include <libdevinfo.h>
2N/A#include <devid.h>
2N/A#include <sys/libdevid.h>
2N/A#include <pthread.h>
2N/A#include <inttypes.h>
2N/A#include <sys/dkio.h>
2N/A#include <sys/scsi/scsi_types.h>
2N/A#include <fm/topo_mod.h>
2N/A#include <fm/topo_list.h>
2N/A#include <sys/fm/protocol.h>
2N/A#include <sys/scsi/generic/inquiry.h>
2N/A#include "disk.h"
2N/A
2N/A/* common callback information for di_walk_node() and di_devlink_walk */
2N/Atypedef struct disk_cbdata {
2N/A topo_mod_t *dcb_mod;
2N/A topo_list_t *dcb_list;
2N/A di_devlink_handle_t dcb_devhdl;
2N/A dev_di_node_t *dcb_dnode; /* for di_devlink_walk only */
2N/A} disk_cbdata_t;
2N/A
2N/A
2N/Astatic const topo_pgroup_info_t io_pgroup = {
2N/A TOPO_PGROUP_IO,
2N/A TOPO_STABILITY_PRIVATE,
2N/A TOPO_STABILITY_PRIVATE,
2N/A 1
2N/A};
2N/A
2N/Astatic const topo_pgroup_info_t disk_auth_pgroup = {
2N/A FM_FMRI_AUTHORITY,
2N/A TOPO_STABILITY_PRIVATE,
2N/A TOPO_STABILITY_PRIVATE,
2N/A 1
2N/A};
2N/A
2N/Astatic const topo_pgroup_info_t storage_pgroup = {
2N/A TOPO_PGROUP_STORAGE,
2N/A TOPO_STABILITY_PRIVATE,
2N/A TOPO_STABILITY_PRIVATE,
2N/A 1
2N/A};
2N/A
2N/A/*
2N/A * Temporary until DDI_NT_SGEN is moved from sgen to ddi header
2N/A */
2N/A#define DDI_NT_SGEN "ddi_generic:scsi"
2N/A
2N/Achar *device_nodetypes[] = {
2N/A DDI_NT_BLOCK_FABRIC,
2N/A DDI_NT_BLOCK_CHAN,
2N/A DDI_NT_SGEN,
2N/A DDI_NT_BLOCK_SAS,
2N/A DDI_NT_BLOCK_WWN,
2N/A DDI_NT_BLOCK,
2N/A DDI_NT_BLOCK_XVMD,
2N/A DDI_NT_CD,
2N/A DDI_NT_CD_CHAN,
2N/A DDI_NT_CD_XVMD,
2N/A NULL
2N/A};
2N/A
2N/A/*
2N/A * Set the properties of the disk node, from dev_di_node_t data.
2N/A * Properties include:
2N/A * group: protocol properties: resource, asru, label, fru
2N/A * group: authority properties: chassis-name, chassis-serial, chassis-part
2N/A * group: io properties: devfs-path, devid
2N/A * group: storage properties:
2N/A * - logical-disk, disk-model, disk-manufacturer, serial-number
2N/A * - firmware-revision, capacity-in-bytes
2N/A *
2N/A * NOTE: the io and storage groups won't be present if the dnode passed in is
2N/A * NULL. This happens when a disk is found through ses, but is not enumerated
2N/A * in the devinfo tree.
2N/A */
2N/Astatic int
2N/Adisk_set_props(topo_mod_t *mod, tnode_t *parent,
2N/A tnode_t *dtn, dev_di_node_t *dnode)
2N/A{
2N/A char *label = NULL;
2N/A nvlist_t *fmri = NULL;
2N/A nvlist_t *asru = NULL;
2N/A int err;
2N/A
2N/A /* pull the label property down from our parent 'bay' node */
2N/A if (topo_node_label(parent, &label, &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "label error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A if (topo_node_label_set(dtn, label, &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "label_set error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A /* get the resource fmri, and use it as the fru */
2N/A if (topo_node_resource(dtn, &fmri, &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "resource error: %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "fru_set error: %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A /* create/set the authority group */
2N/A if ((topo_pgroup_create(dtn, &disk_auth_pgroup, &err) != 0) &&
2N/A (err != ETOPO_PROP_DEFD)) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "create disk_auth error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A /* create the storage group */
2N/A if (topo_pgroup_create(dtn, &storage_pgroup, &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "create storage error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A /* no dnode was found for this disk - skip the io and storage groups */
2N/A if (dnode == NULL) {
2N/A err = 0;
2N/A goto out;
2N/A }
2N/A
2N/A /* form and set the asru */
2N/A if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
2N/A dnode->ddn_dpaths[0], dnode->ddn_devid)) == NULL) {
2N/A err = ETOPO_FMRI_UNKNOWN;
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "asru error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A if (topo_node_asru_set(dtn, asru, 0, &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "asru_set error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A /* create/set the devfs-path and devid in the io group */
2N/A if (topo_pgroup_create(dtn, &io_pgroup, &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "create io error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH,
2N/A TOPO_PROP_IMMUTABLE, dnode->ddn_dpaths[0], &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set dev error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A if (dnode->ddn_devid && topo_prop_set_string(dtn, TOPO_PGROUP_IO,
2N/A TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, dnode->ddn_devid, &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set devid error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A if (dnode->ddn_ppaths_n &&
2N/A topo_prop_set_string_array(dtn, TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH,
2N/A TOPO_PROP_IMMUTABLE, (const char **)dnode->ddn_ppaths,
2N/A dnode->ddn_ppaths_n, &err)) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set phys-path error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A /* set the storage group public /dev name */
2N/A if (dnode->ddn_lpaths && dnode->ddn_lpaths[0] &&
2N/A topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2N/A TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
2N/A dnode->ddn_lpaths[0], &err)) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set disk_name error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A /* populate other misc storage group properties */
2N/A if (dnode->ddn_mfg &&
2N/A topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2N/A TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE,
2N/A dnode->ddn_mfg, &err)) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set mfg error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A if (dnode->ddn_model &&
2N/A topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2N/A TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE,
2N/A dnode->ddn_model, &err)) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set model error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A if (dnode->ddn_serial &&
2N/A topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2N/A TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE,
2N/A dnode->ddn_serial, &err)) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set serial error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A if (dnode->ddn_firm &&
2N/A topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2N/A TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
2N/A dnode->ddn_firm, &err)) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set firm error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A if (dnode->ddn_cap &&
2N/A topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2N/A TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE,
2N/A dnode->ddn_cap, &err)) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set cap error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A if (topo_prop_set_uint32(dtn, TOPO_PGROUP_IO, TOPO_IO_EREPORT_CAPABLE,
2N/A TOPO_PROP_IMMUTABLE, dnode->ddn_ereport_capable, &err) < 0) {
2N/A topo_mod_dprintf(mod, "disk_set_props: "
2N/A "set ereport_capable error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A err = 0;
2N/A
2N/Aout: if (asru)
2N/A nvlist_free(asru);
2N/A if (fmri)
2N/A nvlist_free(fmri);
2N/A if (label)
2N/A topo_mod_strfree(mod, label);
2N/A return (err);
2N/A
2N/Aerror: err = topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Adisk_cro_mk(topo_mod_t *mod, tnode_t *receptacle, dev_di_node_t *dnode)
2N/A{
2N/A char *product = NULL;
2N/A char *chassis = NULL;
2N/A char *label = NULL;
2N/A nvlist_t *fmri_nvl;
2N/A char *fmri = NULL;
2N/A int err = 0;
2N/A
2N/A /*
2N/A * Pull the chassis name and serial number down from our receptacle
2N/A * 'bay' node (with value of UNKNOWN if not found).
2N/A */
2N/A if (topo_prop_get_string(receptacle, FM_FMRI_AUTHORITY,
2N/A FM_FMRI_AUTH_V1_CHASSIS_NM, &product, &err))
2N/A product = topo_mod_strdup(mod, "unknown");
2N/A if (topo_prop_get_string(receptacle, FM_FMRI_AUTHORITY,
2N/A FM_FMRI_AUTH_V1_CHASSIS_SN, &chassis, &err))
2N/A chassis = topo_mod_strdup(mod, "unknown");
2N/A if (topo_node_label(receptacle, &label, &err) != 0) {
2N/A topo_mod_dprintf(mod, "disk_cro_mk: "
2N/A "label error %s\n", topo_strerror(err));
2N/A goto error;
2N/A }
2N/A
2N/A /* Get string fmri of receptacle. */
2N/A if (topo_node_resource(receptacle, &fmri_nvl, &err) == 0) {
2N/A (void) topo_mod_nvl2str(mod, fmri_nvl, &fmri);
2N/A nvlist_free(fmri_nvl);
2N/A }
2N/A err = 0;
2N/A
2N/A if (dnode == NULL) {
2N/A /* make cro for empty receptacle (no occupant info) */
2N/A (void) di_cromk_recadd(topo_mod_cromk_hdl(mod), 0,
2N/A product, chassis, NULL, label,
2N/A "bay", fmri, NULL, NULL, NULL,
2N/A NULL, 0, NULL, 0, NULL, 0,
2N/A NULL, NULL, NULL, NULL, NULL, NULL,
2N/A NULL, 0, NULL, 0, NULL, 0);
2N/A } else {
2N/A (void) di_cromk_recadd(topo_mod_cromk_hdl(mod), 0,
2N/A product, chassis, NULL, label,
2N/A "bay", fmri, "disk", NULL, NULL,
2N/A dnode->ddn_dpaths, dnode->ddn_dpaths_n,
2N/A dnode->ddn_ppaths, dnode->ddn_ppaths_n,
2N/A dnode->ddn_lpaths, dnode->ddn_lpaths_n,
2N/A dnode->ddn_devid, dnode->ddn_mfg, dnode->ddn_model,
2N/A dnode->ddn_part, dnode->ddn_serial, dnode->ddn_firm,
2N/A &dnode->ddn_cap, 1,
2N/A dnode->ddn_target_ports, dnode->ddn_target_ports_n,
2N/A dnode->ddn_attached_ports, dnode->ddn_attached_ports_n);
2N/A }
2N/A
2N/Aout: if (fmri)
2N/A topo_mod_strfree(mod, fmri);
2N/A if (label)
2N/A topo_mod_strfree(mod, label);
2N/A if (chassis)
2N/A topo_mod_strfree(mod, chassis);
2N/A if (product)
2N/A topo_mod_strfree(mod, product);
2N/A return (err);
2N/A
2N/Aerror: err = topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A}
2N/A
2N/A/*
2N/A * Trim leading and trailing whitespace from the string.
2N/A */
2N/Astatic char *
2N/Adisk_trim_whitespace(topo_mod_t *mod, const char *begin)
2N/A{
2N/A const char *end;
2N/A char *buf;
2N/A size_t count;
2N/A
2N/A if (begin == NULL)
2N/A return (NULL);
2N/A
2N/A end = begin + strlen(begin);
2N/A
2N/A while (begin < end && isspace(*begin))
2N/A begin++;
2N/A while (begin < end && isspace(*(end - 1)))
2N/A end--;
2N/A
2N/A count = end - begin;
2N/A if ((buf = topo_mod_alloc(mod, count + 1)) == NULL)
2N/A return (NULL);
2N/A
2N/A (void) strlcpy(buf, begin, count + 1);
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * Manufacturing strings can contain characters that are invalid for use in hc
2N/A * authority names. This trims leading and trailing whitespace, and
2N/A * substitutes any characters known to be bad.
2N/A */
2N/Achar *
2N/Adisk_auth_clean(topo_mod_t *mod, const char *str)
2N/A{
2N/A char *buf, *p;
2N/A
2N/A if (str == NULL)
2N/A return (NULL);
2N/A
2N/A if ((buf = topo_mod_strdup(mod, str)) == NULL)
2N/A return (NULL);
2N/A
2N/A while ((p = strpbrk(buf, " :=")) != NULL)
2N/A *p = '-';
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * create the disk topo node.
2N/A *
2N/A * return value :
2N/A * 0 : either successful binding or a node is already bound.
2N/A * when a node is already bound the *dtnp is not set.
2N/A * -1 : failure
2N/A */
2N/Astatic int
2N/Adisk_tnode_create(topo_mod_t *mod, tnode_t *parent,
2N/A dev_di_node_t *dnode, const char *name, topo_instance_t i, tnode_t **dtnp)
2N/A{
2N/A nvlist_t *fmri;
2N/A tnode_t *dtn = NULL;
2N/A nvlist_t *auth;
2N/A char *mfg, *model, *part, *serial, *firm;
2N/A
2N/A *dtnp = NULL;
2N/A if (dnode != NULL) {
2N/A mfg = disk_auth_clean(mod, dnode->ddn_mfg);
2N/A model = disk_auth_clean(mod, dnode->ddn_model);
2N/A part = disk_auth_clean(mod, dnode->ddn_part);
2N/A serial = disk_auth_clean(mod, dnode->ddn_serial);
2N/A firm = disk_auth_clean(mod, dnode->ddn_firm);
2N/A } else
2N/A mfg = model = part = serial = firm = NULL;
2N/A
2N/A auth = topo_mod_auth(mod, parent);
2N/A fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, NULL,
2N/A auth, part ? part : model, firm, serial);
2N/A if (dnode != NULL && dnode->ddn_devid != NULL)
2N/A (void) nvlist_add_string(fmri, FM_FMRI_HC_DEVID,
2N/A dnode->ddn_devid);
2N/A nvlist_free(auth);
2N/A
2N/A topo_mod_strfree(mod, firm);
2N/A topo_mod_strfree(mod, serial);
2N/A topo_mod_strfree(mod, part);
2N/A topo_mod_strfree(mod, model);
2N/A topo_mod_strfree(mod, mfg);
2N/A
2N/A if (fmri == NULL) {
2N/A topo_mod_dprintf(mod, "disk_tnode_create: "
2N/A "hcfmri (%s%d/%s%d) error %s\n",
2N/A topo_node_name(parent), topo_node_instance(parent),
2N/A name, i, topo_strerror(topo_mod_errno(mod)));
2N/A return (-1);
2N/A }
2N/A
2N/A if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
2N/A if (topo_mod_errno(mod) == EMOD_NODE_BOUND) {
2N/A /*
2N/A * if disk 0 is already there then we're done
2N/A */
2N/A nvlist_free(fmri);
2N/A return (0);
2N/A }
2N/A topo_mod_dprintf(mod, "disk_tnode_create: "
2N/A "bind (%s%d/%s%d) error %s\n",
2N/A topo_node_name(parent), topo_node_instance(parent),
2N/A name, i, topo_strerror(topo_mod_errno(mod)));
2N/A nvlist_free(fmri);
2N/A return (-1);
2N/A }
2N/A nvlist_free(fmri);
2N/A
2N/A /* add the properties of the disk */
2N/A if (disk_set_props(mod, parent, dtn, dnode) != 0) {
2N/A topo_mod_dprintf(mod, "disk_tnode_create: "
2N/A "disk_set_props (%s%d/%s%d) error %s\n",
2N/A topo_node_name(parent), topo_node_instance(parent),
2N/A name, i, topo_strerror(topo_mod_errno(mod)));
2N/A topo_node_unbind(dtn);
2N/A return (-1);
2N/A }
2N/A
2N/A if (disk_cro_mk(mod, parent, dnode) != 0) {
2N/A topo_mod_dprintf(mod, "disk_tnode_create: "
2N/A "disk_cro_mk (%s%d/%s%d) error %s\n",
2N/A topo_node_name(parent), topo_node_instance(parent),
2N/A name, i, topo_strerror(topo_mod_errno(mod)));
2N/A }
2N/A
2N/A *dtnp = dtn;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * calls disk_tnode_create().
2N/A *
2N/A * return value:
2N/A * 0 : resulted disk tnode is not NULL.
2N/A * resulted disk tnode is NULL but disk_tnode_create() returns success
2N/A * because it detected the disk tnode is already bound.
2N/A *
2N/A * -1 : resulted disk tnode is NULL and no disk node is already bound.
2N/A */
2N/Astatic int
2N/Adisk_declare(topo_mod_t *mod, tnode_t *parent, dev_di_node_t *dnode)
2N/A{
2N/A tnode_t *dtn = NULL;
2N/A int rval;
2N/A
2N/A rval = disk_tnode_create(mod, parent, dnode, DISK, 0, &dtn);
2N/A if (dtn == NULL) {
2N/A if (rval == 0)
2N/A return (0);
2N/A topo_mod_dprintf(mod, "disk_declare: "
2N/A "disk_tnode_create error %s\n",
2N/A topo_strerror(topo_mod_errno(mod)));
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * declares the disk node with given the device path.
2N/A *
2N/A * return value:
2N/A * 0 or -1 returned by disk_declare().
2N/A *
2N/A * 1 : no matching devinfo node exists.
2N/A */
2N/Aint
2N/Adisk_declare_path(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
2N/A const char *path)
2N/A{
2N/A dev_di_node_t *dnode;
2N/A int i;
2N/A
2N/A /*
2N/A * Check for match using physical phci (ddn_ppaths). Use
2N/A * di_devfs_path_match so generic.vs.non-generic names match.
2N/A */
2N/A for (dnode = topo_list_next(listp); dnode != NULL;
2N/A dnode = topo_list_next(dnode)) {
2N/A if (dnode->ddn_ppaths == NULL)
2N/A continue;
2N/A for (i = 0; i < dnode->ddn_ppaths_n; i++)
2N/A if (di_devfs_path_match(dnode->ddn_ppaths[i], path))
2N/A return (disk_declare(mod, parent, dnode));
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "disk_declare_path: "
2N/A "failed to find disk matching path %s", path);
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * declares the disk node with given the device devid.
2N/A * The devid should be the resulted value from from devid_str_encode().
2N/A *
2N/A * return value:
2N/A * 0 or -1 returned by disk_declare().
2N/A *
2N/A * 1 : no matching devinfo node exists.
2N/A */
2N/Aint
2N/Adisk_declare_devid(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
2N/A const char *devid)
2N/A{
2N/A dev_di_node_t *dnode;
2N/A
2N/A /*
2N/A * Check for match using physical phci (ddn_ppaths). Use
2N/A * di_devfs_path_match so generic.vs.non-generic names match.
2N/A */
2N/A for (dnode = topo_list_next(listp); dnode != NULL;
2N/A dnode = topo_list_next(dnode)) {
2N/A if (dnode->ddn_devid &&
2N/A (devid_str_compare(dnode->ddn_devid, (char *)devid) == 0)) {
2N/A topo_mod_dprintf(mod, "disk_declare_devid: "
2N/A "found disk matching devid %s", devid);
2N/A return (disk_declare(mod, parent, dnode));
2N/A }
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "disk_declare_devid: "
2N/A "failed to find disk matching devid %s", devid);
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * declares the disk node with given the target port addreess.
2N/A *
2N/A * return value:
2N/A * 0 or -1 returned by disk_declare().
2N/A *
2N/A * 1 : no matching devinfo node exists.
2N/A */
2N/Astatic int
2N/Adisk_declare_target_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
2N/A const char *addr)
2N/A{
2N/A dev_di_node_t *dnode;
2N/A int i;
2N/A
2N/A /* Check for match using addr. */
2N/A for (dnode = topo_list_next(listp); dnode != NULL;
2N/A dnode = topo_list_next(dnode)) {
2N/A if (dnode->ddn_target_ports == NULL)
2N/A continue;
2N/A for (i = 0; i < dnode->ddn_target_ports_n; i++) {
2N/A if (dnode->ddn_target_ports[i] &&
2N/A (strncmp(dnode->ddn_target_ports[i], addr,
2N/A strcspn(dnode->ddn_target_ports[i], ":"))) == 0) {
2N/A topo_mod_dprintf(mod, "disk_declare_addr: "
2N/A "found disk matching addr %s", addr);
2N/A return (disk_declare(mod, parent, dnode));
2N/A }
2N/A }
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "disk_declare_target_addr: "
2N/A "failed to find disk matching target_port addr %s", addr);
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * declares the disk node with given the target port addreesses.
2N/A *
2N/A * return value:
2N/A * 0, -1 or 1 returned by disk_declare_target_port().
2N/A *
2N/A * 1 : all target port(s) failed to be enumerated.
2N/A */
2N/Aint
2N/Adisk_declare_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
2N/A char **addrs, uint_t naddr)
2N/A{
2N/A int i, rval, ret;
2N/A
2N/A /* Check for match using addr. */
2N/A for (i = 0; i < naddr; i++) {
2N/A if (addrs[i]) {
2N/A rval = disk_declare_target_addr(mod, parent, listp,
2N/A addrs[i]);
2N/A /*
2N/A * when one target port is handled okay remaining
2N/A * target port info is already reflected to
2N/A * the resulted disk node so no need to continue.
2N/A *
2N/A * rval is 1 when no matching devinfo node is found.
2N/A * continue to next target port.
2N/A */
2N/A if (rval == 0) {
2N/A ret = 0;
2N/A break;
2N/A } else if (rval < 0) {
2N/A ret = -1;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (i == naddr) {
2N/A topo_mod_dprintf(mod, "disk_declare_addr: "
2N/A "given target port(s) failed to be enumerated.\n");
2N/A ret = 1;
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Used to declare a disk that has been discovered through other means (usually
2N/A * ses), that is not enumerated in the solaris devinfo tree.
2N/A */
2N/Aint
2N/Adisk_declare_non_enumerated(topo_mod_t *mod, tnode_t *parent)
2N/A{
2N/A return (disk_declare(mod, parent, NULL));
2N/A}
2N/A
2N/A/*
2N/A * Used to declare an empty receptacle.
2N/A */
2N/Aint
2N/Adisk_declare_empty(topo_mod_t *mod, tnode_t *parent)
2N/A{
2N/A return (disk_cro_mk(mod, parent, NULL));
2N/A}
2N/A
2N/A/*
2N/A * This function adds a new item to a ddm the array-of-pointers-to-string.
2N/A * An example call would look like:
2N/A * aps_add(mod, &dnode->ddn_lpaths, &dnode->ddn_lpaths_n, new);
2N/A * We don't have topo_mod_realloc, so we need to do the realloc by hand.
2N/A */
2N/Astatic int
2N/Aaps_add(topo_mod_t *mod, char ***p_aps, int *p_n, const char *new)
2N/A{
2N/A int n = *p_n; /* current number of elements */
2N/A char **aps; /* new aps */
2N/A
2N/A if ((p_aps == NULL) || (p_n == NULL))
2N/A return (-1); /* bad call */
2N/A aps = topo_mod_zalloc(mod, (n + 1) * sizeof (new));
2N/A if (aps == NULL)
2N/A return (-1); /* out of memory */
2N/A if (n && *p_aps) {
2N/A bcopy(*p_aps, aps, n * sizeof (new));
2N/A topo_mod_free(mod, *p_aps, n * sizeof (new));
2N/A }
2N/A aps[n] = new ? topo_mod_strdup(mod, new) : NULL;
2N/A *p_aps = aps;
2N/A *p_n = (n + 1);
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Aaps_findadd(topo_mod_t *mod, char ***p_aps, int *p_n, const char *new,
2N/A int (*cmp)(const char *, const char *), int cmp_match)
2N/A{
2N/A int n = *p_n;
2N/A int i;
2N/A
2N/A if ((p_aps == NULL) || (p_n == NULL))
2N/A return (-1); /* bad call */
2N/A for (i = 0; i < n; i++)
2N/A if ((*cmp)((*p_aps)[i], new) == cmp_match)
2N/A return (0);
2N/A return (aps_add(mod, p_aps, p_n, new));
2N/A}
2N/A
2N/Astatic void
2N/Aaps_free(topo_mod_t *mod, char **aps, int n)
2N/A{
2N/A int i;
2N/A
2N/A if ((aps == NULL) || (n == 0))
2N/A return;
2N/A for (i = 0; i < n; i++)
2N/A topo_mod_strfree(mod, aps[i]);
2N/A topo_mod_free(mod, aps, n * sizeof (char *));
2N/A}
2N/A
2N/A/* di_devlink callback to add an lpath to a dnode */
2N/Astatic int
2N/Adisk_devlink_callback(di_devlink_t dl, void *arg)
2N/A{
2N/A disk_cbdata_t *cbp = (disk_cbdata_t *)arg;
2N/A topo_mod_t *mod = cbp->dcb_mod;
2N/A dev_di_node_t *dnode = cbp->dcb_dnode;
2N/A const char *devpath;
2N/A char *ctds, *slice;
2N/A int i, n;
2N/A
2N/A devpath = di_devlink_path(dl);
2N/A if ((dnode == NULL) || (devpath == NULL))
2N/A return (DI_WALK_TERMINATE);
2N/A
2N/A /* trim the dsk/rdsk and slice off the public name */
2N/A if (((ctds = strrchr(devpath, '/')) != NULL) &&
2N/A ((slice = strrchr(ctds, 's')) != NULL))
2N/A *slice = '\0';
2N/A
2N/A n = dnode->ddn_lpaths_n;
2N/A for (i = 0; i < n; i++) {
2N/A if (ctds && (strcmp(dnode->ddn_lpaths[i], ctds) == 0))
2N/A break;
2N/A if (devpath && (strcmp(dnode->ddn_lpaths[i], devpath) == 0))
2N/A break;
2N/A }
2N/A if (i >= n)
2N/A (void) aps_add(mod, &dnode->ddn_lpaths,
2N/A &dnode->ddn_lpaths_n, ctds ? ctds + 1 : devpath);
2N/A
2N/Aout: if (ctds && slice)
2N/A *slice = 's';
2N/A return (DI_WALK_TERMINATE);
2N/A}
2N/A
2N/A/* Add devinfo information to a dnode */
2N/Astatic int
2N/Adev_di_node_add_devinfo(dev_di_node_t *dnode, di_node_t node,
2N/A disk_cbdata_t *cbp)
2N/A{
2N/A topo_mod_t *mod = cbp->dcb_mod;
2N/A char *dpath = NULL;
2N/A di_minor_t minor;
2N/A char *part;
2N/A char *minorpath;
2N/A int64_t *nblocksp;
2N/A uint64_t nblocks;
2N/A int *dblksizep;
2N/A uint_t dblksize;
2N/A char capbuf[MAXPATHLEN];
2N/A int *inq_dtypep;
2N/A char *s;
2N/A int i;
2N/A int ret = -1;
2N/A
2N/A /* cache various bits of new information about the device. */
2N/A if ((dnode->ddn_mfg == NULL) && di_prop_lookup_strings(DDI_DEV_T_ANY,
2N/A node, INQUIRY_VENDOR_ID, &s) > 0) {
2N/A if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL)
2N/A goto error;
2N/A }
2N/A if ((dnode->ddn_model == NULL) && di_prop_lookup_strings(DDI_DEV_T_ANY,
2N/A node, INQUIRY_PRODUCT_ID, &s) > 0) {
2N/A if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL)
2N/A goto error;
2N/A }
2N/A if ((dnode->ddn_part == NULL) && dnode->ddn_mfg && dnode->ddn_model) {
2N/A /* form synthetic part number for disks */
2N/A i = strlen(dnode->ddn_mfg) + 1 + strlen(dnode->ddn_model) + 1;
2N/A if ((part = topo_mod_alloc(mod, i)) == NULL)
2N/A goto error;
2N/A (void) snprintf(part, i, "%s-%s",
2N/A dnode->ddn_mfg, dnode->ddn_model);
2N/A dnode->ddn_part = part;
2N/A }
2N/A if ((dnode->ddn_serial == NULL) && di_prop_lookup_strings(DDI_DEV_T_ANY,
2N/A node, INQUIRY_SERIAL_NO, &s) > 0) {
2N/A if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL)
2N/A goto error;
2N/A }
2N/A if ((dnode->ddn_firm == NULL) && di_prop_lookup_strings(DDI_DEV_T_ANY,
2N/A node, INQUIRY_REVISION_ID, &s) > 0) {
2N/A if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL)
2N/A goto error;
2N/A }
2N/A if ((dnode->ddn_cap == NULL) && di_prop_lookup_int64(DDI_DEV_T_ANY,
2N/A node, "device-nblocks", &nblocksp) > 0) {
2N/A nblocks = (uint64_t)*nblocksp;
2N/A /*
2N/A * To save kernel memory, the driver may not define
2N/A * "device-blksize" when its value is default DEV_BSIZE.
2N/A */
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
2N/A "device-blksize", &dblksizep) > 0)
2N/A dblksize = (uint_t)*dblksizep;
2N/A else
2N/A dblksize = DEV_BSIZE; /* default value */
2N/A (void) snprintf(capbuf, sizeof (capbuf),
2N/A "%" PRIu64, nblocks * dblksize);
2N/A if ((dnode->ddn_cap = topo_mod_strdup(mod, capbuf)) == NULL)
2N/A goto error;
2N/A }
2N/A
2N/A dnode->ddn_ereport_capable = (di_prop_lookup_strings(DDI_DEV_T_ANY,
2N/A node, "fm-ereport-capable", &s) == 0);
2N/A
2N/A if ((dnode->ddn_dtype == DTYPE_UNKNOWN) &&
2N/A di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
2N/A &inq_dtypep) > 0)
2N/A dnode->ddn_dtype = *inq_dtypep;
2N/A
2N/A /* A devinfo node may add a dpath */
2N/A dpath = di_devfs_path(node);
2N/A if (dpath && aps_findadd(mod, &dnode->ddn_dpaths, &dnode->ddn_dpaths_n,
2N/A dpath, strcmp, 0))
2N/A goto error;
2N/A
2N/A /* A non-scsi_vhci devinfo node may also be a ppath */
2N/A if (dpath && (strstr(dpath, "/scsi_vhci/") == NULL) &&
2N/A aps_findadd(mod, &dnode->ddn_ppaths, &dnode->ddn_ppaths_n,
2N/A dpath, strcmp, 0))
2N/A goto error;
2N/A
2N/A /*
2N/A * A devinfo node may also add an lpath, pick any minor name so
2N/A * that we don't have a dependency on a specific minor name having
2N/A * been created duringing attach.
2N/A */
2N/A minor = di_minor_next(node, DI_MINOR_NIL);
2N/A s = minor ? di_minor_name(minor) : NULL;
2N/A if (dpath && s && ((dnode->ddn_dtype & DTYPE_MASK) == DTYPE_DIRECT)) {
2N/A i = strlen(dpath) + 1 + strlen(s) + 1;
2N/A if ((minorpath = topo_mod_alloc(mod, i)) == NULL)
2N/A goto error;
2N/A (void) snprintf(minorpath, i, "%s:%s", dpath, s);
2N/A cbp->dcb_dnode = dnode;
2N/A (void) di_devlink_walk(cbp->dcb_devhdl, "^r?dsk/",
2N/A minorpath, DI_PRIMARY_LINK, cbp, disk_devlink_callback);
2N/A topo_mod_free(mod, minorpath, i);
2N/A }
2N/A
2N/A /* A devinfo may add a target_port. */
2N/A if (((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
2N/A SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) &&
2N/A aps_findadd(mod, &dnode->ddn_target_ports,
2N/A &dnode->ddn_target_ports_n, scsi_wwnstr_skip_ua_prefix(s),
2N/A strcmp, 0))
2N/A goto error;
2N/A
2N/A /* A devinfo may add a attached_port. */
2N/A if (((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
2N/A SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) &&
2N/A aps_findadd(mod, &dnode->ddn_attached_ports,
2N/A &dnode->ddn_attached_ports_n, scsi_wwnstr_skip_ua_prefix(s),
2N/A strcmp, 0))
2N/A goto error;
2N/A
2N/A /* A devinfo may add a bridge_port. */
2N/A if (((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
2N/A SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) &&
2N/A aps_findadd(mod, &dnode->ddn_bridge_ports,
2N/A &dnode->ddn_bridge_ports_n, scsi_wwnstr_skip_ua_prefix(s),
2N/A strcmp, 0))
2N/A goto error;
2N/A ret = 0;
2N/A
2N/Aerror: if (dpath)
2N/A di_devfs_path_free(dpath);
2N/A return (ret);
2N/A}
2N/A
2N/A/* Add pathinfo information to a dnode */
2N/Astatic int
2N/Adev_di_node_add_pathinfo(dev_di_node_t *dnode, di_path_t pnode,
2N/A disk_cbdata_t *cbp)
2N/A{
2N/A topo_mod_t *mod = cbp->dcb_mod;
2N/A char *path;
2N/A char *s;
2N/A
2N/A /* A pathinfo may add a ppath */
2N/A if (((path = di_path_devfs_path(pnode)) != NULL) &&
2N/A aps_findadd(mod, &dnode->ddn_ppaths, &dnode->ddn_ppaths_n, path,
2N/A strcmp, 0)) {
2N/A di_devfs_path_free(path);
2N/A goto error;
2N/A }
2N/A if (path)
2N/A di_devfs_path_free(path);
2N/A
2N/A /* A pathinfo may add a target_port. */
2N/A if (((di_path_prop_lookup_strings(pnode,
2N/A SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) &&
2N/A aps_findadd(mod, &dnode->ddn_target_ports,
2N/A &dnode->ddn_target_ports_n, scsi_wwnstr_skip_ua_prefix(s),
2N/A strcmp, 0))
2N/A goto error;
2N/A
2N/A /* A pathinfo may add a attached_port. */
2N/A if (((di_path_prop_lookup_strings(pnode,
2N/A SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) &&
2N/A aps_findadd(mod, &dnode->ddn_attached_ports,
2N/A &dnode->ddn_attached_ports_n, scsi_wwnstr_skip_ua_prefix(s),
2N/A strcmp, 0))
2N/A goto error;
2N/A
2N/A /* A pathinfo may add a bridge_port. */
2N/A if (((di_path_prop_lookup_strings(pnode,
2N/A SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) &&
2N/A aps_findadd(mod, &dnode->ddn_bridge_ports,
2N/A &dnode->ddn_bridge_ports_n, scsi_wwnstr_skip_ua_prefix(s),
2N/A strcmp, 0))
2N/A goto error;
2N/A
2N/A return (0);
2N/A
2N/Aerror: return (-1);
2N/A}
2N/A
2N/Astatic void
2N/Adev_di_node_free(topo_mod_t *mod, dev_di_node_t *dnode)
2N/A{
2N/A if (dnode == NULL)
2N/A return;
2N/A
2N/A /* free the stuff we point to */
2N/A /* NOTE: topo_mod_strfree does NULL checking. */
2N/A topo_mod_strfree(mod, dnode->ddn_devid);
2N/A topo_mod_strfree(mod, dnode->ddn_mfg);
2N/A topo_mod_strfree(mod, dnode->ddn_model);
2N/A topo_mod_strfree(mod, dnode->ddn_part);
2N/A topo_mod_strfree(mod, dnode->ddn_serial);
2N/A topo_mod_strfree(mod, dnode->ddn_firm);
2N/A topo_mod_strfree(mod, dnode->ddn_cap);
2N/A
2N/A /* free array of pointers to strings */
2N/A aps_free(mod, dnode->ddn_dpaths, dnode->ddn_dpaths_n);
2N/A aps_free(mod, dnode->ddn_ppaths, dnode->ddn_ppaths_n);
2N/A aps_free(mod, dnode->ddn_lpaths, dnode->ddn_lpaths_n);
2N/A aps_free(mod, dnode->ddn_target_ports, dnode->ddn_target_ports_n);
2N/A aps_free(mod, dnode->ddn_attached_ports, dnode->ddn_attached_ports_n);
2N/A aps_free(mod, dnode->ddn_bridge_ports, dnode->ddn_bridge_ports_n);
2N/A
2N/A /* free self */
2N/A topo_mod_free(mod, dnode, sizeof (*dnode));
2N/A}
2N/A
2N/Astatic int
2N/Adev_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp)
2N/A{
2N/A topo_mod_t *mod = cbp->dcb_mod;
2N/A dev_di_node_t *dnode = NULL;
2N/A int dnode_new = 0;
2N/A di_path_t pnode;
2N/A int i;
2N/A
2N/A /*
2N/A * NOTE: if there is no devid, then we can end up with duplicate
2N/A * dnodes, but this doesn't do any harm.
2N/A */
2N/A if (devid) {
2N/A /* Check for duplicate using devid search. */
2N/A for (dnode = topo_list_next(cbp->dcb_list);
2N/A dnode != NULL; dnode = topo_list_next(dnode)) {
2N/A if (dnode->ddn_devid &&
2N/A devid_str_compare(dnode->ddn_devid, devid) == 0) {
2N/A topo_mod_dprintf(mod, "dev_di_node_add: "
2N/A "already there %s\n", devid);
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (dnode == NULL) {
2N/A dnode_new++; /* Allocate/append a new dnode. */
2N/A dnode = topo_mod_zalloc(mod, sizeof (*dnode));
2N/A if (dnode == NULL)
2N/A return (-1);
2N/A
2N/A dnode->ddn_dtype = DTYPE_UNKNOWN;
2N/A if (devid) {
2N/A dnode->ddn_devid = topo_mod_strdup(mod, devid);
2N/A if (dnode->ddn_devid == NULL)
2N/A goto error;
2N/A }
2N/A }
2N/A
2N/A /* Add devinfo information to dnode. */
2N/A if (dev_di_node_add_devinfo(dnode, node, cbp))
2N/A goto error;
2N/A
2N/A /*
2N/A * Establish the physical ppath and target ports. If the device is
2N/A * non-mpxio then dpath and ppath are the same, and the target port is a
2N/A * property of the device node.
2N/A *
2N/A * If dpath is a client node under scsi_vhci, then iterate over all
2N/A * paths and get their physical paths and target port properrties.
2N/A * di_path_client_next_path call below will
2N/A * return non-NULL, and ppath is set to the physical path to the first
2N/A * pathinfo node.
2N/A *
2N/A * NOTE: It is possible to get a generic.vs.non-generic path
2N/A * for di_devfs_path.vs.di_path_devfs_path like:
2N/A * xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
2N/A * pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
2N/A * To resolve this issue disk_declare_path() needs to use the
2N/A * special di_devfs_path_match() interface.
2N/A */
2N/A /* Add pathinfo information to dnode */
2N/A for (pnode = di_path_client_next_path(node, NULL); pnode;
2N/A pnode = di_path_client_next_path(node, pnode))
2N/A if (dev_di_node_add_pathinfo(dnode, pnode, cbp))
2N/A goto error;
2N/A
2N/A /* Print some information about dnode for debug. */
2N/A topo_mod_dprintf(mod, "dev_di_node_add: adding %s\n",
2N/A devid ? dnode->ddn_devid : "NULL devid");
2N/A for (i = 0; i < dnode->ddn_dpaths_n; i++)
2N/A topo_mod_dprintf(mod, "\t\tdpath %d: %s\n",
2N/A i, dnode->ddn_dpaths[i]);
2N/A for (i = 0; i < dnode->ddn_ppaths_n; i++)
2N/A topo_mod_dprintf(mod, "\t\tppath %d: %s\n",
2N/A i, dnode->ddn_ppaths[i]);
2N/A for (i = 0; i < dnode->ddn_lpaths_n; i++)
2N/A topo_mod_dprintf(mod, "\t\tlpath %d: %s\n",
2N/A i, dnode->ddn_lpaths[i]);
2N/A
2N/A if (dnode_new)
2N/A topo_list_append(cbp->dcb_list, dnode);
2N/A return (0);
2N/A
2N/Aerror: dev_di_node_free(mod, dnode);
2N/A return (-1);
2N/A}
2N/A
2N/A/* di_walk_node callback for disk_list_gather */
2N/Astatic int
2N/Adev_walk_di_nodes(di_node_t node, void *arg)
2N/A{
2N/A disk_cbdata_t *cbp = (disk_cbdata_t *)arg;
2N/A topo_mod_t *mod = cbp->dcb_mod;
2N/A di_minor_t mi_node;
2N/A char *mi_nodetype;
2N/A char **p;
2N/A char *devidstr = NULL;
2N/A char *s;
2N/A int *val;
2N/A
2N/A /*
2N/A * Check the node type so we done trigger on nodes that happen to use
2N/A * the same property names that we're interested in.
2N/A */
2N/A mi_node = di_minor_next(node, DI_MINOR_NIL);
2N/A if (mi_node == DI_MINOR_NIL) {
2N/A topo_mod_dprintf(mod, "dev_walk_di_nodes: node has no minors");
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A
2N/A mi_nodetype = di_minor_nodetype(mi_node);
2N/A if (mi_nodetype == NULL) {
2N/A topo_mod_dprintf(mod, "dev_walk_di_nodes: node has no type");
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A
2N/A p = device_nodetypes;
2N/A while (*p) {
2N/A if (strcmp(*p, mi_nodetype) == 0)
2N/A break;
2N/A p++;
2N/A }
2N/A
2N/A if (*p == NULL)
2N/A return (DI_WALK_CONTINUE);
2N/A
2N/A /*
2N/A * If it's not a scsi_vhci client and doesn't have a target_port
2N/A * property and doesn't have a target property then it's not a storage
2N/A * device and we're not interested.
2N/A */
2N/A if (di_path_client_next_path(node, NULL) == NULL &&
2N/A di_prop_lookup_strings(DDI_DEV_T_ANY, node,
2N/A SCSI_ADDR_PROP_TARGET_PORT, &s) <= 0 &&
2N/A di_prop_lookup_ints(DDI_DEV_T_ANY, node,
2N/A SCSI_ADDR_PROP_TARGET, &val) <= 0) {
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A (void) di_prop_lookup_strings(DDI_DEV_T_ANY, node,
2N/A DEVID_PROP_NAME, &devidstr);
2N/A
2N/A /* create/find the devid scsi topology node */
2N/A (void) dev_di_node_add(node, devidstr, arg);
2N/A
2N/A return (DI_WALK_CONTINUE);
2N/A}
2N/A
2N/A/*
2N/A * NOTE: dev_list_gather should be moved out and done just after
2N/A * di_init devinfo snapshot so that we only do this once per snapshot
2N/A * instead of once per ses/internal-xml storage root.
2N/A */
2N/Aint
2N/Adev_list_gather(topo_mod_t *mod, topo_list_t *listp)
2N/A{
2N/A di_node_t devtree;
2N/A di_devlink_handle_t devhdl;
2N/A disk_cbdata_t dcb;
2N/A
2N/A if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
2N/A topo_mod_dprintf(mod, "disk_list_gather: "
2N/A "topo_mod_devinfo() failed");
2N/A return (-1);
2N/A }
2N/A
2N/A if ((devhdl = topo_mod_devlink(mod)) == NULL) {
2N/A topo_mod_dprintf(mod, "disk_list_gather: "
2N/A "topo_mod_devlink() failed");
2N/A return (-1);
2N/A }
2N/A
2N/A dcb.dcb_mod = mod;
2N/A dcb.dcb_list = listp;
2N/A dcb.dcb_devhdl = devhdl;
2N/A
2N/A /* walk the devinfo snapshot looking for disk nodes */
2N/A (void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
2N/A dev_walk_di_nodes);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Adev_list_free(topo_mod_t *mod, topo_list_t *listp)
2N/A{
2N/A dev_di_node_t *dnode;
2N/A
2N/A while ((dnode = topo_list_next(listp)) != NULL) {
2N/A /* order of delete/free is important */
2N/A topo_list_delete(listp, dnode);
2N/A dev_di_node_free(mod, dnode);
2N/A }
2N/A}