xref: /onnv-gate/usr/src/lib/fm/topo/modules/common/disk/disk_common.c (revision 12477:3b66a54aab1c)
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