16640Scth /*
26640Scth * CDDL HEADER START
36640Scth *
46640Scth * The contents of this file are subject to the terms of the
56640Scth * Common Development and Distribution License (the "License").
66640Scth * You may not use this file except in compliance with the License.
76640Scth *
86640Scth * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96640Scth * or http://www.opensolaris.org/os/licensing.
106640Scth * See the License for the specific language governing permissions
116640Scth * and limitations under the License.
126640Scth *
136640Scth * When distributing Covered Code, include this CDDL HEADER in each
146640Scth * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156640Scth * If applicable, add the following below this CDDL HEADER, with the
166640Scth * fields enclosed by brackets "[]" replaced with your own identifying
176640Scth * information: Portions Copyright [yyyy] [name of copyright owner]
186640Scth *
196640Scth * CDDL HEADER END
206640Scth */
216640Scth
226640Scth /*
2312106SStephen.Hanson@Sun.COM * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
246640Scth */
256640Scth
266640Scth /*
276640Scth * Functions in this file are shared between the disk and ses enumerators.
286640Scth *
296640Scth * A topo_list_t of all disks is returned by a successful disk_list_gather()
306640Scth * call, and the list is freed by a disk_list_free(). To create a 'disk' topo
316640Scth * node below a specific 'bay' parent node either disk_declare_path() or
326640Scth * disk_declare_addr() are called. The caller determines which 'disk' is
336640Scth * in which 'bay'. A disk's 'label' and 'authority' information come from
346640Scth * its parent 'bay' node.
356640Scth */
366640Scth
376869Seschrock #include <ctype.h>
386640Scth #include <strings.h>
396640Scth #include <libdevinfo.h>
406640Scth #include <devid.h>
416640Scth #include <sys/libdevid.h>
426640Scth #include <pthread.h>
436640Scth #include <inttypes.h>
446640Scth #include <sys/dkio.h>
456640Scth #include <sys/scsi/scsi_types.h>
466640Scth #include <fm/topo_mod.h>
476640Scth #include <fm/topo_list.h>
486640Scth #include <fm/libdiskstatus.h>
496640Scth #include <sys/fm/protocol.h>
5012126SHyon.Kim@Sun.COM #include <sys/scsi/generic/inquiry.h>
516640Scth #include "disk.h"
526640Scth
536640Scth /* common callback information for di_walk_node() and di_devlink_walk */
546640Scth typedef struct disk_cbdata {
556640Scth topo_mod_t *dcb_mod;
566640Scth topo_list_t *dcb_list;
576640Scth
586640Scth di_devlink_handle_t dcb_devhdl;
5912126SHyon.Kim@Sun.COM dev_di_node_t *dcb_dnode; /* for di_devlink_walk only */
606640Scth } disk_cbdata_t;
616640Scth
626640Scth /*
636640Scth * Given a /devices path for a whole disk, appending this extension gives the
646640Scth * path to a raw device that can be opened.
656640Scth */
666640Scth #if defined(__i386) || defined(__amd64)
676640Scth #define PHYS_EXTN ":q,raw"
686640Scth #elif defined(__sparc) || defined(__sparcv9)
696640Scth #define PHYS_EXTN ":c,raw"
706640Scth #else
716640Scth #error Unknown architecture
726640Scth #endif
736640Scth
746640Scth /*
756640Scth * Methods for disks. This is used by the disk-transport module to
766640Scth * generate ereports based off SCSI disk status.
776640Scth */
786640Scth static int disk_status(topo_mod_t *, tnode_t *, topo_version_t,
796640Scth nvlist_t *, nvlist_t **);
806640Scth
816640Scth static const topo_method_t disk_methods[] = {
826640Scth { TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC,
836640Scth TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL,
846640Scth disk_status },
856640Scth { NULL }
866640Scth };
876640Scth
886640Scth static const topo_pgroup_info_t io_pgroup = {
896640Scth TOPO_PGROUP_IO,
906640Scth TOPO_STABILITY_PRIVATE,
916640Scth TOPO_STABILITY_PRIVATE,
926640Scth 1
936640Scth };
946640Scth
956640Scth static const topo_pgroup_info_t disk_auth_pgroup = {
966640Scth FM_FMRI_AUTHORITY,
976640Scth TOPO_STABILITY_PRIVATE,
986640Scth TOPO_STABILITY_PRIVATE,
996640Scth 1
1006640Scth };
1016640Scth
1026640Scth static const topo_pgroup_info_t storage_pgroup = {
1036640Scth TOPO_PGROUP_STORAGE,
1046640Scth TOPO_STABILITY_PRIVATE,
1056640Scth TOPO_STABILITY_PRIVATE,
1066640Scth 1
1076640Scth };
1086640Scth
1096640Scth /*
11012126SHyon.Kim@Sun.COM * Set the properties of the disk node, from dev_di_node_t data.
1116640Scth * Properties include:
1126640Scth * group: protocol properties: resource, asru, label, fru
1136640Scth * group: authority properties: product-id, chasis-id, server-id
1146640Scth * group: io properties: devfs-path, devid
1156640Scth * group: storage properties:
1166640Scth * - logical-disk, disk-model, disk-manufacturer, serial-number
1176640Scth * - firmware-revision, capacity-in-bytes
11812106SStephen.Hanson@Sun.COM *
11912106SStephen.Hanson@Sun.COM * NOTE: the io and storage groups won't be present if the dnode passed in is
12012106SStephen.Hanson@Sun.COM * NULL. This happens when a disk is found through ses, but is not enumerated
12112106SStephen.Hanson@Sun.COM * in the devinfo tree.
1226640Scth */
1236640Scth static int
disk_set_props(topo_mod_t * mod,tnode_t * parent,tnode_t * dtn,dev_di_node_t * dnode)1246640Scth disk_set_props(topo_mod_t *mod, tnode_t *parent,
12512126SHyon.Kim@Sun.COM tnode_t *dtn, dev_di_node_t *dnode)
1266640Scth {
1276640Scth nvlist_t *asru = NULL;
1286640Scth char *label = NULL;
1296640Scth nvlist_t *fmri = NULL;
1306640Scth int err;
1316640Scth
1326640Scth /* pull the label property down from our parent 'bay' node */
1336640Scth if (topo_node_label(parent, &label, &err) != 0) {
1346640Scth topo_mod_dprintf(mod, "disk_set_props: "
1356640Scth "label error %s\n", topo_strerror(err));
1366640Scth goto error;
1376640Scth }
1386640Scth if (topo_node_label_set(dtn, label, &err) != 0) {
1396640Scth topo_mod_dprintf(mod, "disk_set_props: "
1406640Scth "label_set error %s\n", topo_strerror(err));
1416640Scth goto error;
1426640Scth }
1436640Scth
1446640Scth /* get the resource fmri, and use it as the fru */
1456640Scth if (topo_node_resource(dtn, &fmri, &err) != 0) {
1466640Scth topo_mod_dprintf(mod, "disk_set_props: "
1476640Scth "resource error: %s\n", topo_strerror(err));
1486640Scth goto error;
1496640Scth }
1506640Scth if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) {
1516640Scth topo_mod_dprintf(mod, "disk_set_props: "
1526640Scth "fru_set error: %s\n", topo_strerror(err));
1536640Scth goto error;
1546640Scth }
1556640Scth
1566640Scth /* create/set the authority group */
1576640Scth if ((topo_pgroup_create(dtn, &disk_auth_pgroup, &err) != 0) &&
1586640Scth (err != ETOPO_PROP_DEFD)) {
1596640Scth topo_mod_dprintf(mod, "disk_set_props: "
1606640Scth "create disk_auth error %s\n", topo_strerror(err));
1616640Scth goto error;
1626640Scth }
1636640Scth
16412106SStephen.Hanson@Sun.COM /* create the storage group */
16512106SStephen.Hanson@Sun.COM if (topo_pgroup_create(dtn, &storage_pgroup, &err) != 0) {
16612106SStephen.Hanson@Sun.COM topo_mod_dprintf(mod, "disk_set_props: "
16712106SStephen.Hanson@Sun.COM "create storage error %s\n", topo_strerror(err));
16812106SStephen.Hanson@Sun.COM goto error;
16912106SStephen.Hanson@Sun.COM }
17012106SStephen.Hanson@Sun.COM
17112106SStephen.Hanson@Sun.COM /* no dnode was found for this disk - skip the io and storage groups */
17212106SStephen.Hanson@Sun.COM if (dnode == NULL) {
17312106SStephen.Hanson@Sun.COM err = 0;
17412106SStephen.Hanson@Sun.COM goto out;
17512106SStephen.Hanson@Sun.COM }
17612106SStephen.Hanson@Sun.COM
17712106SStephen.Hanson@Sun.COM /* form and set the asru */
17812106SStephen.Hanson@Sun.COM if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
17912106SStephen.Hanson@Sun.COM dnode->ddn_dpath, dnode->ddn_devid)) == NULL) {
18012106SStephen.Hanson@Sun.COM err = ETOPO_FMRI_UNKNOWN;
18112106SStephen.Hanson@Sun.COM topo_mod_dprintf(mod, "disk_set_props: "
18212106SStephen.Hanson@Sun.COM "asru error %s\n", topo_strerror(err));
18312106SStephen.Hanson@Sun.COM goto error;
18412106SStephen.Hanson@Sun.COM }
18512106SStephen.Hanson@Sun.COM if (topo_node_asru_set(dtn, asru, 0, &err) != 0) {
18612106SStephen.Hanson@Sun.COM topo_mod_dprintf(mod, "disk_set_props: "
18712106SStephen.Hanson@Sun.COM "asru_set error %s\n", topo_strerror(err));
18812106SStephen.Hanson@Sun.COM goto error;
18912106SStephen.Hanson@Sun.COM }
19012106SStephen.Hanson@Sun.COM
1916640Scth /* create/set the devfs-path and devid in the io group */
1926640Scth if (topo_pgroup_create(dtn, &io_pgroup, &err) != 0) {
1936640Scth topo_mod_dprintf(mod, "disk_set_props: "
1946640Scth "create io error %s\n", topo_strerror(err));
1956640Scth goto error;
1966640Scth }
1976640Scth
1986640Scth if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH,
1996640Scth TOPO_PROP_IMMUTABLE, dnode->ddn_dpath, &err) != 0) {
2006640Scth topo_mod_dprintf(mod, "disk_set_props: "
2016640Scth "set dev error %s\n", topo_strerror(err));
2026640Scth goto error;
2036640Scth }
2046640Scth
20512106SStephen.Hanson@Sun.COM if (dnode->ddn_devid && topo_prop_set_string(dtn, TOPO_PGROUP_IO,
20612106SStephen.Hanson@Sun.COM TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, dnode->ddn_devid, &err) != 0) {
2076640Scth topo_mod_dprintf(mod, "disk_set_props: "
2086640Scth "set devid error %s\n", topo_strerror(err));
2096640Scth goto error;
2106640Scth }
2116640Scth
2126640Scth if (dnode->ddn_ppath_count != 0 &&
2136640Scth topo_prop_set_string_array(dtn, TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH,
2146640Scth TOPO_PROP_IMMUTABLE, (const char **)dnode->ddn_ppath,
2156640Scth dnode->ddn_ppath_count, &err) != 0) {
2166640Scth topo_mod_dprintf(mod, "disk_set_props: "
2176640Scth "set phys-path error %s\n", topo_strerror(err));
2186640Scth goto error;
2196640Scth }
2206640Scth
2216640Scth /* set the storage group public /dev name */
22212357SStephen.Hanson@Sun.COM if (dnode->ddn_lpath != NULL &&
22312357SStephen.Hanson@Sun.COM topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2246640Scth TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
2256640Scth dnode->ddn_lpath, &err) != 0) {
2266640Scth topo_mod_dprintf(mod, "disk_set_props: "
2276640Scth "set disk_name error %s\n", topo_strerror(err));
2286640Scth goto error;
2296640Scth }
2306640Scth
2316640Scth /* populate other misc storage group properties */
2326640Scth if (dnode->ddn_mfg && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2336640Scth TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE,
2346640Scth dnode->ddn_mfg, &err) != 0)) {
2356640Scth topo_mod_dprintf(mod, "disk_set_props: "
2366640Scth "set mfg error %s\n", topo_strerror(err));
2376640Scth goto error;
2386640Scth }
2396640Scth if (dnode->ddn_model && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2406640Scth TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE,
2416640Scth dnode->ddn_model, &err) != 0)) {
2426640Scth topo_mod_dprintf(mod, "disk_set_props: "
2436640Scth "set model error %s\n", topo_strerror(err));
2446640Scth goto error;
2456640Scth }
2466640Scth if (dnode->ddn_serial && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2476640Scth TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE,
2486640Scth dnode->ddn_serial, &err) != 0)) {
2496640Scth topo_mod_dprintf(mod, "disk_set_props: "
2506640Scth "set serial error %s\n", topo_strerror(err));
2516640Scth goto error;
2526640Scth }
2536640Scth if (dnode->ddn_firm && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2546640Scth TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
2556640Scth dnode->ddn_firm, &err) != 0)) {
2566640Scth topo_mod_dprintf(mod, "disk_set_props: "
2576640Scth "set firm error %s\n", topo_strerror(err));
2586640Scth goto error;
2596640Scth }
2606640Scth if (dnode->ddn_cap && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
2616640Scth TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE,
2626640Scth dnode->ddn_cap, &err) != 0)) {
2636640Scth topo_mod_dprintf(mod, "disk_set_props: "
2646640Scth "set cap error %s\n", topo_strerror(err));
2656640Scth goto error;
2666640Scth }
2676640Scth err = 0;
2686640Scth
2696640Scth out: if (fmri)
2706640Scth nvlist_free(fmri);
2716640Scth if (label)
2726640Scth topo_mod_strfree(mod, label);
2736640Scth if (asru)
2746640Scth nvlist_free(asru);
2756640Scth return (err);
2766640Scth
2776640Scth error: err = topo_mod_seterrno(mod, err);
2786640Scth goto out;
2796640Scth }
2806640Scth
2816869Seschrock /*
2829632SEric.Schrock@Sun.COM * Trim leading and trailing whitespace from the string.
2836869Seschrock */
2849632SEric.Schrock@Sun.COM static char *
disk_trim_whitespace(topo_mod_t * mod,const char * begin)2859632SEric.Schrock@Sun.COM disk_trim_whitespace(topo_mod_t *mod, const char *begin)
2866869Seschrock {
2876869Seschrock const char *end;
2889632SEric.Schrock@Sun.COM char *buf;
2896869Seschrock size_t count;
2906869Seschrock
2916869Seschrock if (begin == NULL)
2926869Seschrock return (NULL);
2936869Seschrock
2946869Seschrock end = begin + strlen(begin);
2956869Seschrock
2966869Seschrock while (begin < end && isspace(*begin))
2976869Seschrock begin++;
2986869Seschrock while (begin < end && isspace(*(end - 1)))
2996869Seschrock end--;
3006869Seschrock
3016869Seschrock count = end - begin;
3026869Seschrock if ((buf = topo_mod_alloc(mod, count + 1)) == NULL)
3036869Seschrock return (NULL);
3046869Seschrock
3056869Seschrock (void) strlcpy(buf, begin, count + 1);
3066869Seschrock
3079632SEric.Schrock@Sun.COM return (buf);
3089632SEric.Schrock@Sun.COM }
3099632SEric.Schrock@Sun.COM
3109632SEric.Schrock@Sun.COM /*
3119632SEric.Schrock@Sun.COM * Manufacturing strings can contain characters that are invalid for use in hc
3129632SEric.Schrock@Sun.COM * authority names. This trims leading and trailing whitespace, and
3139632SEric.Schrock@Sun.COM * substitutes any characters known to be bad.
3149632SEric.Schrock@Sun.COM */
3159632SEric.Schrock@Sun.COM char *
disk_auth_clean(topo_mod_t * mod,const char * str)3169632SEric.Schrock@Sun.COM disk_auth_clean(topo_mod_t *mod, const char *str)
3179632SEric.Schrock@Sun.COM {
3189632SEric.Schrock@Sun.COM char *buf, *p;
3199632SEric.Schrock@Sun.COM
3209632SEric.Schrock@Sun.COM if (str == NULL)
3219632SEric.Schrock@Sun.COM return (NULL);
3229632SEric.Schrock@Sun.COM
3239632SEric.Schrock@Sun.COM if ((buf = topo_mod_strdup(mod, str)) == NULL)
3249632SEric.Schrock@Sun.COM return (NULL);
3259632SEric.Schrock@Sun.COM
3269632SEric.Schrock@Sun.COM while ((p = strpbrk(buf, " :=")) != NULL)
3279632SEric.Schrock@Sun.COM *p = '-';
3286869Seschrock
3296869Seschrock return (buf);
3306869Seschrock }
3316869Seschrock
3326640Scth /* create the disk topo node */
33312106SStephen.Hanson@Sun.COM 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)3346640Scth disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
33512126SHyon.Kim@Sun.COM dev_di_node_t *dnode, const char *name, topo_instance_t i, tnode_t **rval)
3366640Scth {
3376640Scth int len;
3386640Scth nvlist_t *fmri;
3396640Scth tnode_t *dtn;
3406640Scth char *part = NULL;
3416640Scth nvlist_t *auth;
3426869Seschrock char *mfg, *model, *firm, *serial;
3436869Seschrock
34412106SStephen.Hanson@Sun.COM *rval = NULL;
34512106SStephen.Hanson@Sun.COM if (dnode != NULL) {
34612106SStephen.Hanson@Sun.COM mfg = disk_auth_clean(mod, dnode->ddn_mfg);
34712106SStephen.Hanson@Sun.COM model = disk_auth_clean(mod, dnode->ddn_model);
34812106SStephen.Hanson@Sun.COM firm = disk_auth_clean(mod, dnode->ddn_firm);
34912106SStephen.Hanson@Sun.COM serial = disk_auth_clean(mod, dnode->ddn_serial);
35012106SStephen.Hanson@Sun.COM } else {
35112106SStephen.Hanson@Sun.COM mfg = model = firm = serial = NULL;
35212106SStephen.Hanson@Sun.COM }
3536640Scth
3546640Scth /* form 'part=' of fmri as "<mfg>-<model>" */
3556869Seschrock if (mfg != NULL && model != NULL) {
3566869Seschrock len = strlen(mfg) + 1 + strlen(model) + 1;
3576640Scth if ((part = topo_mod_alloc(mod, len)) != NULL)
3586640Scth (void) snprintf(part, len, "%s-%s",
3596869Seschrock mfg, model);
3606640Scth }
3616640Scth
3626640Scth auth = topo_mod_auth(mod, parent);
3636640Scth fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, NULL,
3646869Seschrock auth, part ? part : model, firm, serial);
3656640Scth nvlist_free(auth);
3666640Scth
3676869Seschrock topo_mod_strfree(mod, part);
3686869Seschrock topo_mod_strfree(mod, mfg);
3696869Seschrock topo_mod_strfree(mod, model);
3706869Seschrock topo_mod_strfree(mod, firm);
3716869Seschrock topo_mod_strfree(mod, serial);
3726640Scth
3736640Scth if (fmri == NULL) {
3746640Scth topo_mod_dprintf(mod, "disk_tnode_create: "
3756640Scth "hcfmri (%s%d/%s%d) error %s\n",
3766640Scth topo_node_name(parent), topo_node_instance(parent),
3776640Scth name, i, topo_strerror(topo_mod_errno(mod)));
37812106SStephen.Hanson@Sun.COM return (-1);
3796640Scth }
3806640Scth
3816640Scth if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
38212106SStephen.Hanson@Sun.COM if (topo_mod_errno(mod) == EMOD_NODE_BOUND) {
38312106SStephen.Hanson@Sun.COM /*
38412106SStephen.Hanson@Sun.COM * if disk 0 is already there then we're done
38512106SStephen.Hanson@Sun.COM */
38612106SStephen.Hanson@Sun.COM nvlist_free(fmri);
38712106SStephen.Hanson@Sun.COM return (0);
38812106SStephen.Hanson@Sun.COM }
3896640Scth topo_mod_dprintf(mod, "disk_tnode_create: "
3906640Scth "bind (%s%d/%s%d) error %s\n",
3916640Scth topo_node_name(parent), topo_node_instance(parent),
3926640Scth name, i, topo_strerror(topo_mod_errno(mod)));
3936640Scth nvlist_free(fmri);
39412106SStephen.Hanson@Sun.COM return (-1);
3956640Scth }
3966640Scth nvlist_free(fmri);
3976640Scth
3986640Scth /* add the properties of the disk */
3996640Scth if (disk_set_props(mod, parent, dtn, dnode) != 0) {
4006640Scth topo_mod_dprintf(mod, "disk_tnode_create: "
4016640Scth "disk_set_props (%s%d/%s%d) error %s\n",
4026640Scth topo_node_name(parent), topo_node_instance(parent),
4036640Scth name, i, topo_strerror(topo_mod_errno(mod)));
4046640Scth topo_node_unbind(dtn);
40512106SStephen.Hanson@Sun.COM return (-1);
4066640Scth }
40712106SStephen.Hanson@Sun.COM *rval = dtn;
40812106SStephen.Hanson@Sun.COM return (0);
4096640Scth }
4106640Scth
4116640Scth static int
disk_declare(topo_mod_t * mod,tnode_t * parent,dev_di_node_t * dnode,tnode_t ** childp)41212126SHyon.Kim@Sun.COM disk_declare(topo_mod_t *mod, tnode_t *parent, dev_di_node_t *dnode,
41312106SStephen.Hanson@Sun.COM tnode_t **childp)
4146640Scth {
41512106SStephen.Hanson@Sun.COM tnode_t *dtn = NULL;
41612106SStephen.Hanson@Sun.COM int rval;
4176640Scth
41812106SStephen.Hanson@Sun.COM rval = disk_tnode_create(mod, parent, dnode, DISK, 0, &dtn);
4196640Scth if (dtn == NULL) {
42012106SStephen.Hanson@Sun.COM if (rval == 0)
42112106SStephen.Hanson@Sun.COM return (0);
4226640Scth topo_mod_dprintf(mod, "disk_declare: "
4236640Scth "disk_tnode_create error %s\n",
4246640Scth topo_strerror(topo_mod_errno(mod)));
4256640Scth return (-1);
4266640Scth }
4276640Scth
4286640Scth /* register disk_methods against the disk topo node */
4296640Scth if (topo_method_register(mod, dtn, disk_methods) != 0) {
4306640Scth topo_mod_dprintf(mod, "disk_declare: "
4316640Scth "topo_method_register error %s\n",
4326640Scth topo_strerror(topo_mod_errno(mod)));
4336640Scth topo_node_unbind(dtn);
4346640Scth return (-1);
4356640Scth }
43612106SStephen.Hanson@Sun.COM if (childp != NULL)
43712106SStephen.Hanson@Sun.COM *childp = dtn;
4386640Scth return (0);
4396640Scth }
4406640Scth
4416640Scth int
disk_declare_path(topo_mod_t * mod,tnode_t * parent,topo_list_t * listp,const char * path)4426640Scth disk_declare_path(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
4436640Scth const char *path)
4446640Scth {
44512126SHyon.Kim@Sun.COM dev_di_node_t *dnode;
4466640Scth int i;
4476640Scth
4486640Scth /*
4496640Scth * Check for match using physical phci (ddn_ppath). Use
4506640Scth * di_devfs_path_match so generic.vs.non-generic names match.
4516640Scth */
4526640Scth for (dnode = topo_list_next(listp); dnode != NULL;
4536640Scth dnode = topo_list_next(dnode)) {
4546640Scth if (dnode->ddn_ppath == NULL)
4556640Scth continue;
4566640Scth
4576640Scth for (i = 0; i < dnode->ddn_ppath_count; i++) {
4586640Scth if (di_devfs_path_match(dnode->ddn_ppath[0], path))
45912106SStephen.Hanson@Sun.COM return (disk_declare(mod, parent, dnode, NULL));
4606640Scth }
4616640Scth }
4626640Scth
4636640Scth topo_mod_dprintf(mod, "disk_declare_path: "
4646640Scth "failed to find disk matching path %s", path);
4656640Scth return (0);
4666640Scth }
4676640Scth
4686640Scth int
disk_declare_addr(topo_mod_t * mod,tnode_t * parent,topo_list_t * listp,const char * addr,tnode_t ** childp)4696640Scth disk_declare_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
47012106SStephen.Hanson@Sun.COM const char *addr, tnode_t **childp)
4716640Scth {
47212126SHyon.Kim@Sun.COM dev_di_node_t *dnode;
4736640Scth int i;
4746640Scth
4756640Scth /* Check for match using addr. */
4766640Scth for (dnode = topo_list_next(listp); dnode != NULL;
4776640Scth dnode = topo_list_next(dnode)) {
4786640Scth if (dnode->ddn_target_port == NULL)
4796640Scth continue;
4806640Scth
481*12477SHyon.Kim@Sun.COM for (i = 0; i < dnode->ddn_ppath_count; i++) {
482*12477SHyon.Kim@Sun.COM if ((dnode->ddn_target_port[i] != NULL) &&
483*12477SHyon.Kim@Sun.COM (strncmp(dnode->ddn_target_port[i], addr,
484*12477SHyon.Kim@Sun.COM strcspn(dnode->ddn_target_port[i], ":"))) == 0) {
48512464SStephen.Hanson@Sun.COM topo_mod_dprintf(mod, "disk_declare_addr: "
48612464SStephen.Hanson@Sun.COM "found disk matching addr %s", addr);
48712106SStephen.Hanson@Sun.COM return (disk_declare(mod, parent, dnode,
48812106SStephen.Hanson@Sun.COM childp));
48912464SStephen.Hanson@Sun.COM }
4906640Scth }
4916640Scth }
4926640Scth
4936640Scth topo_mod_dprintf(mod, "disk_declare_addr: "
4946640Scth "failed to find disk matching addr %s", addr);
49512106SStephen.Hanson@Sun.COM
49612106SStephen.Hanson@Sun.COM return (1);
49712106SStephen.Hanson@Sun.COM }
49812106SStephen.Hanson@Sun.COM
49912106SStephen.Hanson@Sun.COM /*
50012106SStephen.Hanson@Sun.COM * Used to declare a disk that has been discovered through other means (usually
50112106SStephen.Hanson@Sun.COM * ses), that is not enumerated in the devinfo tree.
50212106SStephen.Hanson@Sun.COM */
50312106SStephen.Hanson@Sun.COM int
disk_declare_non_enumerated(topo_mod_t * mod,tnode_t * parent,tnode_t ** childp)50412106SStephen.Hanson@Sun.COM disk_declare_non_enumerated(topo_mod_t *mod, tnode_t *parent, tnode_t **childp)
50512106SStephen.Hanson@Sun.COM {
50612106SStephen.Hanson@Sun.COM return (disk_declare(mod, parent, NULL, childp));
5076640Scth }
5086640Scth
50912126SHyon.Kim@Sun.COM /* di_devlink callback for dev_di_node_add */
5106640Scth static int
disk_devlink_callback(di_devlink_t dl,void * arg)5116640Scth disk_devlink_callback(di_devlink_t dl, void *arg)
5126640Scth {
5136640Scth disk_cbdata_t *cbp = (disk_cbdata_t *)arg;
5146640Scth topo_mod_t *mod = cbp->dcb_mod;
51512126SHyon.Kim@Sun.COM dev_di_node_t *dnode = cbp->dcb_dnode;
5166640Scth const char *devpath;
5176640Scth char *ctds, *slice;
5186640Scth
5196640Scth devpath = di_devlink_path(dl);
5206640Scth if ((dnode == NULL) || (devpath == NULL))
5216640Scth return (DI_WALK_TERMINATE);
5226640Scth
5236640Scth /* trim the slice off the public name */
5246640Scth if (((ctds = strrchr(devpath, '/')) != NULL) &&
5256640Scth ((slice = strchr(ctds, 's')) != NULL))
5266640Scth *slice = '\0';
5276640Scth
5286640Scth /* Establish the public /dev name (no slice) */
5296640Scth dnode->ddn_lpath = topo_mod_strdup(mod, ctds ? ctds + 1 : devpath);
5306640Scth
5316640Scth if (ctds && slice)
5326640Scth *slice = 's';
5336640Scth return (DI_WALK_TERMINATE);
5346640Scth }
5356640Scth
5366640Scth static void
dev_di_node_free(topo_mod_t * mod,dev_di_node_t * dnode)53712126SHyon.Kim@Sun.COM dev_di_node_free(topo_mod_t *mod, dev_di_node_t *dnode)
5386640Scth {
5396640Scth int i;
5406640Scth
5416640Scth /* free the stuff we point to */
54212106SStephen.Hanson@Sun.COM if (dnode->ddn_devid)
54312106SStephen.Hanson@Sun.COM topo_mod_strfree(mod, dnode->ddn_devid);
544*12477SHyon.Kim@Sun.COM for (i = 0; i < dnode->ddn_ppath_count; i++) {
545*12477SHyon.Kim@Sun.COM /* topo_mod_strfree does NULL checking. */
5466640Scth topo_mod_strfree(mod, dnode->ddn_ppath[i]);
547*12477SHyon.Kim@Sun.COM topo_mod_strfree(mod, dnode->ddn_target_port[i]);
548*12477SHyon.Kim@Sun.COM topo_mod_strfree(mod, dnode->ddn_attached_port[i]);
549*12477SHyon.Kim@Sun.COM topo_mod_strfree(mod, dnode->ddn_bridge_port[i]);
550*12477SHyon.Kim@Sun.COM }
5516640Scth topo_mod_free(mod, dnode->ddn_ppath,
552*12477SHyon.Kim@Sun.COM dnode->ddn_ppath_count * sizeof (char *));
553*12477SHyon.Kim@Sun.COM topo_mod_free(mod, dnode->ddn_target_port,
554*12477SHyon.Kim@Sun.COM dnode->ddn_ppath_count * sizeof (char *));
555*12477SHyon.Kim@Sun.COM topo_mod_free(mod, dnode->ddn_attached_port,
556*12477SHyon.Kim@Sun.COM dnode->ddn_ppath_count * sizeof (char *));
557*12477SHyon.Kim@Sun.COM topo_mod_free(mod, dnode->ddn_bridge_port,
558*12477SHyon.Kim@Sun.COM dnode->ddn_ppath_count * sizeof (char *));
5596640Scth topo_mod_strfree(mod, dnode->ddn_dpath);
5606640Scth topo_mod_strfree(mod, dnode->ddn_lpath);
5616640Scth
5626640Scth topo_mod_strfree(mod, dnode->ddn_mfg);
5636640Scth topo_mod_strfree(mod, dnode->ddn_model);
5646640Scth topo_mod_strfree(mod, dnode->ddn_serial);
5656640Scth topo_mod_strfree(mod, dnode->ddn_firm);
5666640Scth topo_mod_strfree(mod, dnode->ddn_cap);
5676640Scth
5686640Scth /* free self */
56912126SHyon.Kim@Sun.COM topo_mod_free(mod, dnode, sizeof (dev_di_node_t));
5706640Scth }
5716640Scth
5726640Scth static int
dev_di_node_add(di_node_t node,char * devid,disk_cbdata_t * cbp)57312126SHyon.Kim@Sun.COM dev_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp)
5746640Scth {
5756640Scth topo_mod_t *mod = cbp->dcb_mod;
57612126SHyon.Kim@Sun.COM dev_di_node_t *dnode;
5776640Scth di_path_t pnode;
5786640Scth char *path;
5796640Scth int mlen;
5806640Scth char *minorpath;
5816640Scth char *extn = ":a";
5826640Scth char *s;
5836640Scth int64_t *nblocksp;
5846640Scth uint64_t nblocks;
5856640Scth int *dblksizep;
5866640Scth uint_t dblksize;
5876640Scth char lentry[MAXPATHLEN];
588*12477SHyon.Kim@Sun.COM int pathcount;
58912126SHyon.Kim@Sun.COM int *inq_dtype, itype;
590*12477SHyon.Kim@Sun.COM int i;
5916640Scth
59212106SStephen.Hanson@Sun.COM if (devid) {
59312106SStephen.Hanson@Sun.COM /*
59412106SStephen.Hanson@Sun.COM * Check for list duplicate using devid search.
59512106SStephen.Hanson@Sun.COM * Note if there is no devid, then we can end up with duplicates
59612106SStephen.Hanson@Sun.COM * in the list, but this doesn't do any harm.
59712106SStephen.Hanson@Sun.COM */
59812106SStephen.Hanson@Sun.COM for (dnode = topo_list_next(cbp->dcb_list);
59912106SStephen.Hanson@Sun.COM dnode != NULL; dnode = topo_list_next(dnode)) {
60012106SStephen.Hanson@Sun.COM if (dnode->ddn_devid &&
60112106SStephen.Hanson@Sun.COM devid_str_compare(dnode->ddn_devid, devid) == 0) {
60212126SHyon.Kim@Sun.COM topo_mod_dprintf(mod, "dev_di_node_add: "
60312106SStephen.Hanson@Sun.COM "already there %s\n", devid);
60412106SStephen.Hanson@Sun.COM return (0);
60512106SStephen.Hanson@Sun.COM }
6066640Scth }
6076640Scth }
6086640Scth
60912126SHyon.Kim@Sun.COM if ((dnode = topo_mod_zalloc(mod, sizeof (dev_di_node_t))) == NULL)
6106640Scth return (-1);
6116640Scth
61212106SStephen.Hanson@Sun.COM if (devid) {
61312106SStephen.Hanson@Sun.COM /* Establish the devid. */
61412106SStephen.Hanson@Sun.COM dnode->ddn_devid = topo_mod_strdup(mod, devid);
61512106SStephen.Hanson@Sun.COM if (dnode->ddn_devid == NULL)
61612106SStephen.Hanson@Sun.COM goto error;
61712106SStephen.Hanson@Sun.COM }
6186640Scth
6196640Scth /* Establish the devinfo dpath */
6206640Scth if ((path = di_devfs_path(node)) == NULL) {
62111050SRobert.Johnston@Sun.COM (void) topo_mod_seterrno(mod, errno);
6226640Scth goto error;
6236640Scth }
6246640Scth
6256640Scth dnode->ddn_dpath = topo_mod_strdup(mod, path);
6266640Scth di_devfs_path_free(path);
6276640Scth if (dnode->ddn_dpath == NULL)
6286640Scth goto error;
6296640Scth
6306640Scth /*
6316640Scth * Establish the physical ppath and target ports. If the device is
6326640Scth * non-mpxio then dpath and ppath are the same, and the target port is a
6336640Scth * property of the device node.
6346640Scth *
6356640Scth * If dpath is a client node under scsi_vhci, then iterate over all
6366640Scth * paths and get their physical paths and target port properrties.
6376640Scth * di_path_client_next_path call below will
6386640Scth * return non-NULL, and ppath is set to the physical path to the first
6396640Scth * pathinfo node.
6406640Scth *
6416640Scth * NOTE: It is possible to get a generic.vs.non-generic path
6426640Scth * for di_devfs_path.vs.di_path_devfs_path like:
6436640Scth * xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
6446640Scth * pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
6456640Scth * To resolve this issue disk_declare_path() needs to use the
6466640Scth * special di_devfs_path_match() interface.
6476640Scth */
648*12477SHyon.Kim@Sun.COM pathcount = 0;
6496640Scth pnode = NULL;
6506640Scth while ((pnode = di_path_client_next_path(node, pnode)) != NULL) {
6516640Scth pathcount++;
6526640Scth }
6536640Scth
6546640Scth if (pathcount == 0) {
6556640Scth if ((dnode->ddn_ppath =
656*12477SHyon.Kim@Sun.COM topo_mod_zalloc(mod, sizeof (char *))) == NULL)
6576640Scth goto error;
6586640Scth
6596640Scth dnode->ddn_ppath_count = 1;
6606640Scth if ((dnode->ddn_ppath[0] = topo_mod_strdup(mod,
6616640Scth dnode->ddn_dpath)) == NULL)
6626640Scth goto error;
6636640Scth
664*12477SHyon.Kim@Sun.COM if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
665*12477SHyon.Kim@Sun.COM sizeof (char *))) == NULL)
666*12477SHyon.Kim@Sun.COM goto error;
667*12477SHyon.Kim@Sun.COM
668*12477SHyon.Kim@Sun.COM if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
669*12477SHyon.Kim@Sun.COM sizeof (char *))) == NULL)
670*12477SHyon.Kim@Sun.COM goto error;
671*12477SHyon.Kim@Sun.COM
672*12477SHyon.Kim@Sun.COM if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
673*12477SHyon.Kim@Sun.COM sizeof (char *))) == NULL)
674*12477SHyon.Kim@Sun.COM goto error;
675*12477SHyon.Kim@Sun.COM
676*12477SHyon.Kim@Sun.COM /* There should be only one target port for a devinfo node. */
677*12477SHyon.Kim@Sun.COM if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
678*12477SHyon.Kim@Sun.COM SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
679*12477SHyon.Kim@Sun.COM if ((dnode->ddn_target_port[0] =
680*12477SHyon.Kim@Sun.COM topo_mod_strdup(mod,
681*12477SHyon.Kim@Sun.COM scsi_wwnstr_skip_ua_prefix(s))) ==
682*12477SHyon.Kim@Sun.COM NULL)
6836640Scth goto error;
684*12477SHyon.Kim@Sun.COM }
6856640Scth
686*12477SHyon.Kim@Sun.COM if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
687*12477SHyon.Kim@Sun.COM SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
688*12477SHyon.Kim@Sun.COM /* There should be one attached port if any. */
689*12477SHyon.Kim@Sun.COM if ((dnode->ddn_attached_port[0] =
690*12477SHyon.Kim@Sun.COM topo_mod_strdup(mod,
691*12477SHyon.Kim@Sun.COM scsi_wwnstr_skip_ua_prefix(s))) ==
692*12477SHyon.Kim@Sun.COM NULL)
693*12477SHyon.Kim@Sun.COM goto error;
694*12477SHyon.Kim@Sun.COM }
6956640Scth
696*12477SHyon.Kim@Sun.COM if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
697*12477SHyon.Kim@Sun.COM SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
698*12477SHyon.Kim@Sun.COM /* There should be one bridge port if any. */
699*12477SHyon.Kim@Sun.COM if ((dnode->ddn_bridge_port[0] =
700*12477SHyon.Kim@Sun.COM topo_mod_strdup(mod,
701*12477SHyon.Kim@Sun.COM scsi_wwnstr_skip_ua_prefix(s))) ==
702*12477SHyon.Kim@Sun.COM NULL)
703*12477SHyon.Kim@Sun.COM goto error;
7046640Scth }
705*12477SHyon.Kim@Sun.COM
7066640Scth } else {
707*12477SHyon.Kim@Sun.COM /* processing a scsi_vhci device. */
7086640Scth if ((dnode->ddn_ppath = topo_mod_zalloc(mod,
709*12477SHyon.Kim@Sun.COM pathcount * sizeof (char *))) == NULL)
7106640Scth goto error;
7116640Scth
7126640Scth dnode->ddn_ppath_count = pathcount;
7136640Scth
714*12477SHyon.Kim@Sun.COM if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
715*12477SHyon.Kim@Sun.COM pathcount * sizeof (char *))) == NULL)
7166640Scth goto error;
7176640Scth
718*12477SHyon.Kim@Sun.COM if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
719*12477SHyon.Kim@Sun.COM pathcount * sizeof (char *))) == NULL)
720*12477SHyon.Kim@Sun.COM goto error;
721*12477SHyon.Kim@Sun.COM
722*12477SHyon.Kim@Sun.COM if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
723*12477SHyon.Kim@Sun.COM pathcount * sizeof (char *))) == NULL)
724*12477SHyon.Kim@Sun.COM goto error;
7256640Scth
7266640Scth pnode = NULL;
727*12477SHyon.Kim@Sun.COM pathcount = 0;
7286640Scth while ((pnode = di_path_client_next_path(node,
7296640Scth pnode)) != NULL) {
7306640Scth if ((path = di_path_devfs_path(pnode)) == NULL) {
73111050SRobert.Johnston@Sun.COM (void) topo_mod_seterrno(mod, errno);
7326640Scth goto error;
7336640Scth }
7346640Scth
7356640Scth dnode->ddn_ppath[pathcount] =
7366640Scth topo_mod_strdup(mod, path);
7376640Scth di_devfs_path_free(path);
7386640Scth if (dnode->ddn_ppath[pathcount] == NULL)
7396640Scth goto error;
7406640Scth
741*12477SHyon.Kim@Sun.COM if ((di_path_prop_lookup_strings(pnode,
742*12477SHyon.Kim@Sun.COM SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
743*12477SHyon.Kim@Sun.COM if ((dnode->ddn_target_port[pathcount] =
744*12477SHyon.Kim@Sun.COM topo_mod_strdup(mod,
745*12477SHyon.Kim@Sun.COM scsi_wwnstr_skip_ua_prefix(s))) ==
746*12477SHyon.Kim@Sun.COM NULL)
747*12477SHyon.Kim@Sun.COM goto error;
748*12477SHyon.Kim@Sun.COM }
7496640Scth
750*12477SHyon.Kim@Sun.COM if ((di_path_prop_lookup_strings(pnode,
751*12477SHyon.Kim@Sun.COM SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
752*12477SHyon.Kim@Sun.COM if ((dnode->ddn_attached_port[pathcount] =
753*12477SHyon.Kim@Sun.COM topo_mod_strdup(mod,
754*12477SHyon.Kim@Sun.COM scsi_wwnstr_skip_ua_prefix(s))) ==
755*12477SHyon.Kim@Sun.COM NULL)
756*12477SHyon.Kim@Sun.COM goto error;
757*12477SHyon.Kim@Sun.COM }
758*12477SHyon.Kim@Sun.COM
759*12477SHyon.Kim@Sun.COM if ((di_path_prop_lookup_strings(pnode,
760*12477SHyon.Kim@Sun.COM SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
761*12477SHyon.Kim@Sun.COM if ((dnode->ddn_bridge_port[pathcount] =
762*12477SHyon.Kim@Sun.COM topo_mod_strdup(mod,
763*12477SHyon.Kim@Sun.COM scsi_wwnstr_skip_ua_prefix(s))) ==
764*12477SHyon.Kim@Sun.COM NULL)
765*12477SHyon.Kim@Sun.COM goto error;
7666640Scth }
7676640Scth
7686640Scth pathcount++;
7696640Scth }
7706640Scth }
7716640Scth
7726640Scth /*
77312126SHyon.Kim@Sun.COM * Find the public /dev name for a disk by adding a minor name and using
7746640Scth * di_devlink interface for reverse translation (use devinfo path).
7756640Scth */
77612126SHyon.Kim@Sun.COM if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
77712126SHyon.Kim@Sun.COM &inq_dtype) > 0) {
778*12477SHyon.Kim@Sun.COM dnode->ddn_dtype = *inq_dtype;
77912126SHyon.Kim@Sun.COM itype = (*inq_dtype) & DTYPE_MASK;
78012126SHyon.Kim@Sun.COM if (itype == DTYPE_DIRECT) {
78112126SHyon.Kim@Sun.COM mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
78212126SHyon.Kim@Sun.COM if ((minorpath = topo_mod_alloc(mod, mlen)) == NULL)
78312126SHyon.Kim@Sun.COM goto error;
78412126SHyon.Kim@Sun.COM (void) snprintf(minorpath, mlen, "%s%s",
78512126SHyon.Kim@Sun.COM dnode->ddn_dpath, extn);
78612126SHyon.Kim@Sun.COM cbp->dcb_dnode = dnode;
78712126SHyon.Kim@Sun.COM (void) di_devlink_walk(cbp->dcb_devhdl, "^dsk/",
78812126SHyon.Kim@Sun.COM minorpath, DI_PRIMARY_LINK, cbp,
78912126SHyon.Kim@Sun.COM disk_devlink_callback);
79012126SHyon.Kim@Sun.COM topo_mod_free(mod, minorpath, mlen);
79112126SHyon.Kim@Sun.COM if (dnode->ddn_lpath == NULL) {
79212126SHyon.Kim@Sun.COM topo_mod_dprintf(mod, "dev_di_node_add: "
79312126SHyon.Kim@Sun.COM "failed to determine logical path");
79412126SHyon.Kim@Sun.COM }
79512126SHyon.Kim@Sun.COM }
796*12477SHyon.Kim@Sun.COM } else {
797*12477SHyon.Kim@Sun.COM dnode->ddn_dtype = DTYPE_UNKNOWN;
7986640Scth }
7996640Scth
80012126SHyon.Kim@Sun.COM /* cache various bits of optional information about the device. */
8016640Scth if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
8026640Scth INQUIRY_VENDOR_ID, &s) > 0) {
8039632SEric.Schrock@Sun.COM if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL)
8046640Scth goto error;
8056640Scth }
8066640Scth if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
8076640Scth INQUIRY_PRODUCT_ID, &s) > 0) {
8089632SEric.Schrock@Sun.COM if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL)
8096640Scth goto error;
8106640Scth }
8116640Scth if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
8126640Scth INQUIRY_REVISION_ID, &s) > 0) {
8139632SEric.Schrock@Sun.COM if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL)
8146640Scth goto error;
8156640Scth }
8166640Scth if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
8176640Scth INQUIRY_SERIAL_NO, &s) > 0) {
8189632SEric.Schrock@Sun.COM if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL)
8196640Scth goto error;
8206640Scth }
8216640Scth if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
8226640Scth "device-nblocks", &nblocksp) > 0) {
8236640Scth nblocks = (uint64_t)*nblocksp;
8246640Scth /*
8256640Scth * To save kernel memory, the driver may not define
8266640Scth * "device-dblksize" when its value is default DEV_BSIZE.
8276640Scth */
8286640Scth if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
8296640Scth "device-dblksize", &dblksizep) > 0)
8306640Scth dblksize = (uint_t)*dblksizep;
8316640Scth else
8326640Scth dblksize = DEV_BSIZE; /* default value */
8336640Scth (void) snprintf(lentry, sizeof (lentry),
8346640Scth "%" PRIu64, nblocks * dblksize);
8356640Scth if ((dnode->ddn_cap = topo_mod_strdup(mod, lentry)) == NULL)
8366640Scth goto error;
8376640Scth }
8386640Scth
83912126SHyon.Kim@Sun.COM topo_mod_dprintf(mod, "dev_di_node_add: "
84012106SStephen.Hanson@Sun.COM "adding %s\n", devid ? dnode->ddn_devid : "NULL devid");
8416640Scth topo_mod_dprintf(mod, " "
8426640Scth " %s\n", dnode->ddn_dpath);
8436640Scth for (i = 0; i < dnode->ddn_ppath_count; i++) {
8446640Scth topo_mod_dprintf(mod, " "
8456640Scth " %s\n", dnode->ddn_ppath[i]);
8466640Scth }
8476640Scth topo_list_append(cbp->dcb_list, dnode);
8486640Scth return (0);
8496640Scth
8506640Scth error:
85112126SHyon.Kim@Sun.COM dev_di_node_free(mod, dnode);
8526640Scth return (-1);
8536640Scth }
8546640Scth
8556640Scth /* di_walk_node callback for disk_list_gather */
8566640Scth static int
dev_walk_di_nodes(di_node_t node,void * arg)85712126SHyon.Kim@Sun.COM dev_walk_di_nodes(di_node_t node, void *arg)
8586640Scth {
8599632SEric.Schrock@Sun.COM char *devidstr = NULL;
86012106SStephen.Hanson@Sun.COM char *s;
86112464SStephen.Hanson@Sun.COM int *val;
8626640Scth
86312106SStephen.Hanson@Sun.COM /*
86412464SStephen.Hanson@Sun.COM * If it's not a scsi_vhci client and doesn't have a target_port
86512464SStephen.Hanson@Sun.COM * property and doesn't have a target property then it's not a storage
86612464SStephen.Hanson@Sun.COM * device and we're not interested.
86712106SStephen.Hanson@Sun.COM */
86812106SStephen.Hanson@Sun.COM if (di_path_client_next_path(node, NULL) == NULL &&
86912106SStephen.Hanson@Sun.COM di_prop_lookup_strings(DDI_DEV_T_ANY, node,
87012464SStephen.Hanson@Sun.COM SCSI_ADDR_PROP_TARGET_PORT, &s) <= 0 &&
87112464SStephen.Hanson@Sun.COM di_prop_lookup_ints(DDI_DEV_T_ANY, node,
87212464SStephen.Hanson@Sun.COM SCSI_ADDR_PROP_TARGET, &val) <= 0) {
8736640Scth return (DI_WALK_CONTINUE);
8749632SEric.Schrock@Sun.COM }
87512106SStephen.Hanson@Sun.COM (void) di_prop_lookup_strings(DDI_DEV_T_ANY, node,
87612106SStephen.Hanson@Sun.COM DEVID_PROP_NAME, &devidstr);
8776640Scth
8786640Scth /* create/find the devid scsi topology node */
87912126SHyon.Kim@Sun.COM (void) dev_di_node_add(node, devidstr, arg);
8809632SEric.Schrock@Sun.COM
8816640Scth return (DI_WALK_CONTINUE);
8826640Scth }
8836640Scth
8846640Scth int
dev_list_gather(topo_mod_t * mod,topo_list_t * listp)88512126SHyon.Kim@Sun.COM dev_list_gather(topo_mod_t *mod, topo_list_t *listp)
8866640Scth {
8876640Scth di_node_t devtree;
8886640Scth di_devlink_handle_t devhdl;
8896640Scth disk_cbdata_t dcb;
8906640Scth
8919632SEric.Schrock@Sun.COM if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
8926640Scth topo_mod_dprintf(mod, "disk_list_gather: "
89310527SEric.Schrock@Sun.COM "topo_mod_devinfo() failed");
8946640Scth return (-1);
8956640Scth }
8966640Scth
8976640Scth if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) {
8986640Scth topo_mod_dprintf(mod, "disk_list_gather: "
89910527SEric.Schrock@Sun.COM "di_devlink_init() failed");
9006640Scth return (-1);
9016640Scth }
9026640Scth
9036640Scth dcb.dcb_mod = mod;
9046640Scth dcb.dcb_list = listp;
9056640Scth dcb.dcb_devhdl = devhdl;
9066640Scth
90712106SStephen.Hanson@Sun.COM /* walk the devinfo snapshot looking for disk nodes */
9086640Scth (void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
90912126SHyon.Kim@Sun.COM dev_walk_di_nodes);
9106640Scth
9116640Scth (void) di_devlink_fini(&devhdl);
9126640Scth
9136640Scth return (0);
9146640Scth }
9156640Scth
9166640Scth void
dev_list_free(topo_mod_t * mod,topo_list_t * listp)91712126SHyon.Kim@Sun.COM dev_list_free(topo_mod_t *mod, topo_list_t *listp)
9186640Scth {
91912126SHyon.Kim@Sun.COM dev_di_node_t *dnode;
9206640Scth
9216640Scth while ((dnode = topo_list_next(listp)) != NULL) {
9226640Scth /* order of delete/free is important */
9236640Scth topo_list_delete(listp, dnode);
92412126SHyon.Kim@Sun.COM dev_di_node_free(mod, dnode);
9256640Scth }
9266640Scth }
9276640Scth
9286640Scth /*
9296640Scth * Query the current disk status. If successful, the disk status is returned
9306640Scth * as an nvlist consisting of at least the following members:
9316640Scth *
9326640Scth * protocol string Supported protocol (currently "scsi")
9336640Scth *
9346640Scth * status nvlist Arbitrary protocol-specific information
9356640Scth * about the current state of the disk.
9366640Scth *
9376640Scth * faults nvlist A list of supported faults. Each
9386640Scth * element of this list is a boolean value.
9396640Scth * An element's existence indicates that
9406640Scth * the drive supports detecting this fault,
9416640Scth * and the value indicates the current
9426640Scth * state of the fault.
9436640Scth *
9446640Scth * <fault-name> nvlist For each fault named in 'faults', a
9456640Scth * nvlist describing protocol-specific
9466640Scth * attributes of the fault.
9476640Scth *
9486640Scth * This method relies on the libdiskstatus library to query this information.
9496640Scth */
9506640Scth static int
disk_status(topo_mod_t * mod,tnode_t * nodep,topo_version_t vers,nvlist_t * in_nvl,nvlist_t ** out_nvl)9516640Scth disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
9526640Scth nvlist_t *in_nvl, nvlist_t **out_nvl)
9536640Scth {
9546640Scth disk_status_t *dsp;
9556640Scth char *devpath, *fullpath;
9566640Scth size_t pathlen;
9576640Scth nvlist_t *status;
9586640Scth int err;
9596640Scth
9606640Scth *out_nvl = NULL;
9616640Scth
9626640Scth if (vers != TOPO_METH_DISK_STATUS_VERSION)
9636640Scth return (topo_mod_seterrno(mod, EMOD_VER_NEW));
9646640Scth
9656640Scth /*
9666640Scth * If the caller specifies the "path" parameter, then this indicates
9676640Scth * that we should use this instead of deriving it from the topo node
9686640Scth * itself.
9696640Scth */
9706640Scth if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
9716640Scth devpath = NULL;
9726640Scth } else {
9736640Scth /*
9746640Scth * Get the /devices path and attempt to open the disk status
9756640Scth * handle.
9766640Scth */
9776640Scth if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
9786640Scth TOPO_IO_DEV_PATH, &devpath, &err) != 0)
9796640Scth return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
9806640Scth
9816640Scth /*
9826640Scth * Note that sizeof(string) includes the terminating NULL byte
9836640Scth */
9846640Scth pathlen = strlen(devpath) + sizeof ("/devices") +
9856640Scth sizeof (PHYS_EXTN) - 1;
9866640Scth
9876640Scth if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
9886640Scth return (topo_mod_seterrno(mod, EMOD_NOMEM));
9896640Scth
9906640Scth (void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
9916640Scth PHYS_EXTN);
9926640Scth
9936640Scth topo_mod_strfree(mod, devpath);
9946640Scth }
9956640Scth
9966640Scth if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
9976640Scth if (devpath)
9986640Scth topo_mod_free(mod, fullpath, pathlen);
9996640Scth return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
10006640Scth EMOD_NOMEM : EMOD_METHOD_NOTSUP));
10016640Scth }
10026640Scth
10036640Scth if (devpath)
10046640Scth topo_mod_free(mod, fullpath, pathlen);
10056640Scth
10066640Scth if ((status = disk_status_get(dsp)) == NULL) {
10076640Scth err = (disk_status_errno(dsp) == EDS_NOMEM ?
10086640Scth EMOD_NOMEM : EMOD_METHOD_NOTSUP);
10096640Scth disk_status_close(dsp);
10106640Scth return (topo_mod_seterrno(mod, err));
10116640Scth }
10126640Scth
10136640Scth *out_nvl = status;
10146640Scth disk_status_close(dsp);
10156640Scth return (0);
10166640Scth }
1017