Home | History | Annotate | Download | only in disk
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * Functions in this file are shared between the disk and ses enumerators.
     29  *
     30  * A topo_list_t of all disks is returned by a successful disk_list_gather()
     31  * call, and the list is freed by a disk_list_free(). To create a 'disk' topo
     32  * node below a specific 'bay' parent node either disk_declare_path() or
     33  * disk_declare_addr() are called. The caller determines which 'disk' is
     34  * in which 'bay'. A disk's 'label' and 'authority' information come from
     35  * its parent 'bay' node.
     36  */
     37 
     38 #include <ctype.h>
     39 #include <strings.h>
     40 #include <libdevinfo.h>
     41 #include <devid.h>
     42 #include <sys/libdevid.h>
     43 #include <pthread.h>
     44 #include <inttypes.h>
     45 #include <sys/dkio.h>
     46 #include <sys/scsi/scsi_types.h>
     47 #include <fm/topo_mod.h>
     48 #include <fm/topo_list.h>
     49 #include <fm/libdiskstatus.h>
     50 #include <sys/fm/protocol.h>
     51 #include "disk.h"
     52 
     53 /*
     54  * disk node information.
     55  */
     56 typedef struct disk_di_node {
     57 	topo_list_t	ddn_list;	/* list of disks */
     58 
     59 	/* the following two fields are always defined */
     60 	char		*ddn_devid;	/* devid of disk */
     61 	char		*ddn_dpath;	/* path to devinfo (may be vhci) */
     62 	char		**ddn_ppath;	/* physical path to device (phci) */
     63 	int		ddn_ppath_count;
     64 
     65 	char		*ddn_lpath;	/* logical path (public /dev name) */
     66 
     67 	char		*ddn_mfg;	/* misc information about device */
     68 	char		*ddn_model;
     69 	char		*ddn_serial;
     70 	char		*ddn_firm;
     71 	char		*ddn_cap;
     72 
     73 	char		**ddn_target_port;
     74 	int		ddn_target_port_count;
     75 } disk_di_node_t;
     76 
     77 /* common callback information for di_walk_node() and di_devlink_walk */
     78 typedef struct disk_cbdata {
     79 	topo_mod_t		*dcb_mod;
     80 	topo_list_t		*dcb_list;
     81 
     82 	di_devlink_handle_t	dcb_devhdl;
     83 	disk_di_node_t		*dcb_dnode;	/* for di_devlink_walk only */
     84 } disk_cbdata_t;
     85 
     86 /*
     87  * Given a /devices path for a whole disk, appending this extension gives the
     88  * path to a raw device that can be opened.
     89  */
     90 #if defined(__i386) || defined(__amd64)
     91 #define	PHYS_EXTN	":q,raw"
     92 #elif defined(__sparc) || defined(__sparcv9)
     93 #define	PHYS_EXTN	":c,raw"
     94 #else
     95 #error	Unknown architecture
     96 #endif
     97 
     98 /*
     99  * Methods for disks. This is used by the disk-transport module to
    100  * generate ereports based off SCSI disk status.
    101  */
    102 static int disk_status(topo_mod_t *, tnode_t *, topo_version_t,
    103 	nvlist_t *, nvlist_t **);
    104 
    105 static const topo_method_t disk_methods[] = {
    106 	{ TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC,
    107 	    TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL,
    108 	    disk_status },
    109 	{ NULL }
    110 };
    111 
    112 static const topo_pgroup_info_t io_pgroup = {
    113 	TOPO_PGROUP_IO,
    114 	TOPO_STABILITY_PRIVATE,
    115 	TOPO_STABILITY_PRIVATE,
    116 	1
    117 };
    118 
    119 static const topo_pgroup_info_t disk_auth_pgroup = {
    120 	FM_FMRI_AUTHORITY,
    121 	TOPO_STABILITY_PRIVATE,
    122 	TOPO_STABILITY_PRIVATE,
    123 	1
    124 };
    125 
    126 static const topo_pgroup_info_t storage_pgroup = {
    127 	TOPO_PGROUP_STORAGE,
    128 	TOPO_STABILITY_PRIVATE,
    129 	TOPO_STABILITY_PRIVATE,
    130 	1
    131 };
    132 
    133 /*
    134  * Set the properties of the disk node, from disk_di_node_t data.
    135  * Properties include:
    136  *	group: protocol	 properties: resource, asru, label, fru
    137  *	group: authority properties: product-id, chasis-id, server-id
    138  *	group: io	 properties: devfs-path, devid
    139  *	group: storage	 properties:
    140  *		- logical-disk, disk-model, disk-manufacturer, serial-number
    141  *		- firmware-revision, capacity-in-bytes
    142  */
    143 static int
    144 disk_set_props(topo_mod_t *mod, tnode_t *parent,
    145     tnode_t *dtn, disk_di_node_t *dnode)
    146 {
    147 	nvlist_t	*asru = NULL;
    148 	char		*label = NULL;
    149 	nvlist_t	*fmri = NULL;
    150 	int		err;
    151 
    152 	/* form and set the asru */
    153 	if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
    154 	    dnode->ddn_dpath, dnode->ddn_devid)) == NULL) {
    155 		err = ETOPO_FMRI_UNKNOWN;
    156 		topo_mod_dprintf(mod, "disk_set_props: "
    157 		    "asru error %s\n", topo_strerror(err));
    158 		goto error;
    159 	}
    160 	if (topo_node_asru_set(dtn, asru, 0, &err) != 0) {
    161 		topo_mod_dprintf(mod, "disk_set_props: "
    162 		    "asru_set error %s\n", topo_strerror(err));
    163 		goto error;
    164 	}
    165 
    166 	/* pull the label property down from our parent 'bay' node */
    167 	if (topo_node_label(parent, &label, &err) != 0) {
    168 		topo_mod_dprintf(mod, "disk_set_props: "
    169 		    "label error %s\n", topo_strerror(err));
    170 		goto error;
    171 	}
    172 	if (topo_node_label_set(dtn, label, &err) != 0) {
    173 		topo_mod_dprintf(mod, "disk_set_props: "
    174 		    "label_set error %s\n", topo_strerror(err));
    175 		goto error;
    176 	}
    177 
    178 	/* get the resource fmri, and use it as the fru */
    179 	if (topo_node_resource(dtn, &fmri, &err) != 0) {
    180 		topo_mod_dprintf(mod, "disk_set_props: "
    181 		    "resource error: %s\n", topo_strerror(err));
    182 		goto error;
    183 	}
    184 	if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) {
    185 		topo_mod_dprintf(mod, "disk_set_props: "
    186 		    "fru_set error: %s\n", topo_strerror(err));
    187 		goto error;
    188 	}
    189 
    190 	/* create/set the authority group */
    191 	if ((topo_pgroup_create(dtn, &disk_auth_pgroup, &err) != 0) &&
    192 	    (err != ETOPO_PROP_DEFD)) {
    193 		topo_mod_dprintf(mod, "disk_set_props: "
    194 		    "create disk_auth error %s\n", topo_strerror(err));
    195 		goto error;
    196 	}
    197 
    198 	/* create/set the devfs-path and devid in the io group */
    199 	if (topo_pgroup_create(dtn, &io_pgroup, &err) != 0) {
    200 		topo_mod_dprintf(mod, "disk_set_props: "
    201 		    "create io error %s\n", topo_strerror(err));
    202 		goto error;
    203 	}
    204 
    205 	if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH,
    206 	    TOPO_PROP_IMMUTABLE, dnode->ddn_dpath, &err) != 0) {
    207 		topo_mod_dprintf(mod, "disk_set_props: "
    208 		    "set dev error %s\n", topo_strerror(err));
    209 		goto error;
    210 	}
    211 
    212 	if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEVID,
    213 	    TOPO_PROP_IMMUTABLE, dnode->ddn_devid, &err) != 0) {
    214 		topo_mod_dprintf(mod, "disk_set_props: "
    215 		    "set devid error %s\n", topo_strerror(err));
    216 		goto error;
    217 	}
    218 
    219 	if (dnode->ddn_ppath_count != 0 &&
    220 	    topo_prop_set_string_array(dtn, TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH,
    221 	    TOPO_PROP_IMMUTABLE, (const char **)dnode->ddn_ppath,
    222 	    dnode->ddn_ppath_count, &err) != 0) {
    223 		topo_mod_dprintf(mod, "disk_set_props: "
    224 		    "set phys-path error %s\n", topo_strerror(err));
    225 		goto error;
    226 	}
    227 
    228 	/* create the storage group */
    229 	if (topo_pgroup_create(dtn, &storage_pgroup, &err) != 0) {
    230 		topo_mod_dprintf(mod, "disk_set_props: "
    231 		    "create storage error %s\n", topo_strerror(err));
    232 		goto error;
    233 	}
    234 
    235 	/* set the storage group public /dev name */
    236 	if (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
    237 	    TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
    238 	    dnode->ddn_lpath, &err) != 0) {
    239 		topo_mod_dprintf(mod, "disk_set_props: "
    240 		    "set disk_name error %s\n", topo_strerror(err));
    241 		goto error;
    242 	}
    243 
    244 	/* populate other misc storage group properties */
    245 	if (dnode->ddn_mfg && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
    246 	    TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE,
    247 	    dnode->ddn_mfg, &err) != 0)) {
    248 		topo_mod_dprintf(mod, "disk_set_props: "
    249 		    "set mfg error %s\n", topo_strerror(err));
    250 		goto error;
    251 	}
    252 	if (dnode->ddn_model && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
    253 	    TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE,
    254 	    dnode->ddn_model, &err) != 0)) {
    255 		topo_mod_dprintf(mod, "disk_set_props: "
    256 		    "set model error %s\n", topo_strerror(err));
    257 		goto error;
    258 	}
    259 	if (dnode->ddn_serial && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
    260 	    TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE,
    261 	    dnode->ddn_serial, &err) != 0)) {
    262 		topo_mod_dprintf(mod, "disk_set_props: "
    263 		    "set serial error %s\n", topo_strerror(err));
    264 		goto error;
    265 	}
    266 	if (dnode->ddn_firm && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
    267 	    TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
    268 	    dnode->ddn_firm, &err) != 0)) {
    269 		topo_mod_dprintf(mod, "disk_set_props: "
    270 		    "set firm error %s\n", topo_strerror(err));
    271 		goto error;
    272 	}
    273 	if (dnode->ddn_cap && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
    274 	    TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE,
    275 	    dnode->ddn_cap, &err) != 0)) {
    276 		topo_mod_dprintf(mod, "disk_set_props: "
    277 		    "set cap error %s\n", topo_strerror(err));
    278 		goto error;
    279 	}
    280 	err = 0;
    281 
    282 out:	if (fmri)
    283 		nvlist_free(fmri);
    284 	if (label)
    285 		topo_mod_strfree(mod, label);
    286 	if (asru)
    287 		nvlist_free(asru);
    288 	return (err);
    289 
    290 error:	err = topo_mod_seterrno(mod, err);
    291 	goto out;
    292 }
    293 
    294 /*
    295  * Trim leading and trailing whitespace from the string.
    296  */
    297 static char *
    298 disk_trim_whitespace(topo_mod_t *mod, const char *begin)
    299 {
    300 	const char *end;
    301 	char *buf;
    302 	size_t count;
    303 
    304 	if (begin == NULL)
    305 		return (NULL);
    306 
    307 	end = begin + strlen(begin);
    308 
    309 	while (begin < end && isspace(*begin))
    310 		begin++;
    311 	while (begin < end && isspace(*(end - 1)))
    312 		end--;
    313 
    314 	count = end - begin;
    315 	if ((buf = topo_mod_alloc(mod, count + 1)) == NULL)
    316 		return (NULL);
    317 
    318 	(void) strlcpy(buf, begin, count + 1);
    319 
    320 	return (buf);
    321 }
    322 
    323 /*
    324  * Manufacturing strings can contain characters that are invalid for use in hc
    325  * authority names.  This trims leading and trailing whitespace, and
    326  * substitutes any characters known to be bad.
    327  */
    328 char *
    329 disk_auth_clean(topo_mod_t *mod, const char *str)
    330 {
    331 	char *buf, *p;
    332 
    333 	if (str == NULL)
    334 		return (NULL);
    335 
    336 	if ((buf = topo_mod_strdup(mod, str)) == NULL)
    337 		return (NULL);
    338 
    339 	while ((p = strpbrk(buf, " :=")) != NULL)
    340 		*p = '-';
    341 
    342 	return (buf);
    343 }
    344 
    345 /* create the disk topo node */
    346 static tnode_t *
    347 disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
    348     disk_di_node_t *dnode, const char *name, topo_instance_t i)
    349 {
    350 	int		len;
    351 	nvlist_t	*fmri;
    352 	tnode_t		*dtn;
    353 	char		*part = NULL;
    354 	nvlist_t	*auth;
    355 	char		*mfg, *model, *firm, *serial;
    356 
    357 	mfg = disk_auth_clean(mod, dnode->ddn_mfg);
    358 	model = disk_auth_clean(mod, dnode->ddn_model);
    359 	firm = disk_auth_clean(mod, dnode->ddn_firm);
    360 	serial = disk_auth_clean(mod, dnode->ddn_serial);
    361 
    362 	/* form 'part=' of fmri as "<mfg>-<model>" */
    363 	if (mfg != NULL && model != NULL) {
    364 		len = strlen(mfg) + 1 + strlen(model) + 1;
    365 		if ((part = topo_mod_alloc(mod, len)) != NULL)
    366 			(void) snprintf(part, len, "%s-%s",
    367 			    mfg, model);
    368 	}
    369 
    370 	auth = topo_mod_auth(mod, parent);
    371 	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, NULL,
    372 	    auth, part ? part : model, firm, serial);
    373 	nvlist_free(auth);
    374 
    375 	topo_mod_strfree(mod, part);
    376 	topo_mod_strfree(mod, mfg);
    377 	topo_mod_strfree(mod, model);
    378 	topo_mod_strfree(mod, firm);
    379 	topo_mod_strfree(mod, serial);
    380 
    381 	if (fmri == NULL) {
    382 		topo_mod_dprintf(mod, "disk_tnode_create: "
    383 		    "hcfmri (%s%d/%s%d) error %s\n",
    384 		    topo_node_name(parent), topo_node_instance(parent),
    385 		    name, i, topo_strerror(topo_mod_errno(mod)));
    386 		return (NULL);
    387 	}
    388 
    389 	if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
    390 		topo_mod_dprintf(mod, "disk_tnode_create: "
    391 		    "bind (%s%d/%s%d) error %s\n",
    392 		    topo_node_name(parent), topo_node_instance(parent),
    393 		    name, i, topo_strerror(topo_mod_errno(mod)));
    394 		nvlist_free(fmri);
    395 		return (NULL);
    396 	}
    397 	nvlist_free(fmri);
    398 
    399 	/* add the properties of the disk */
    400 	if (disk_set_props(mod, parent, dtn, dnode) != 0) {
    401 		topo_mod_dprintf(mod, "disk_tnode_create: "
    402 		    "disk_set_props (%s%d/%s%d) error %s\n",
    403 		    topo_node_name(parent), topo_node_instance(parent),
    404 		    name, i, topo_strerror(topo_mod_errno(mod)));
    405 		topo_node_unbind(dtn);
    406 		return (NULL);
    407 	}
    408 	return (dtn);
    409 }
    410 
    411 static int
    412 disk_declare(topo_mod_t *mod, tnode_t *parent, disk_di_node_t *dnode)
    413 {
    414 	tnode_t		*dtn;
    415 
    416 	/* create the disk topo node: one disk per 'bay' */
    417 	dtn = disk_tnode_create(mod, parent, dnode, DISK, 0);
    418 	if (dtn == NULL) {
    419 		topo_mod_dprintf(mod, "disk_declare: "
    420 		    "disk_tnode_create error %s\n",
    421 		    topo_strerror(topo_mod_errno(mod)));
    422 		return (-1);
    423 	}
    424 
    425 	/* register disk_methods against the disk topo node */
    426 	if (topo_method_register(mod, dtn, disk_methods) != 0) {
    427 		topo_mod_dprintf(mod, "disk_declare: "
    428 		    "topo_method_register error %s\n",
    429 		    topo_strerror(topo_mod_errno(mod)));
    430 		topo_node_unbind(dtn);
    431 		return (-1);
    432 	}
    433 	return (0);
    434 }
    435 
    436 int
    437 disk_declare_path(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
    438     const char *path)
    439 {
    440 	disk_di_node_t		*dnode;
    441 	int i;
    442 
    443 	/*
    444 	 * Check for match using physical phci (ddn_ppath). Use
    445 	 * di_devfs_path_match so generic.vs.non-generic names match.
    446 	 */
    447 	for (dnode = topo_list_next(listp); dnode != NULL;
    448 	    dnode = topo_list_next(dnode)) {
    449 		if (dnode->ddn_ppath == NULL)
    450 			continue;
    451 
    452 		for (i = 0; i < dnode->ddn_ppath_count; i++) {
    453 			if (di_devfs_path_match(dnode->ddn_ppath[0], path))
    454 				return (disk_declare(mod, parent, dnode));
    455 		}
    456 	}
    457 
    458 	topo_mod_dprintf(mod, "disk_declare_path: "
    459 	    "failed to find disk matching path %s", path);
    460 	return (0);
    461 }
    462 
    463 int
    464 disk_declare_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
    465     const char *addr)
    466 {
    467 	disk_di_node_t *dnode;
    468 	int i;
    469 
    470 	/* Check for match using addr. */
    471 	for (dnode = topo_list_next(listp); dnode != NULL;
    472 	    dnode = topo_list_next(dnode)) {
    473 		if (dnode->ddn_target_port == NULL)
    474 			continue;
    475 
    476 		for (i = 0; i < dnode->ddn_target_port_count; i++) {
    477 			if (strncmp(dnode->ddn_target_port[i], addr,
    478 			    strcspn(dnode->ddn_target_port[i], ":")) == 0)
    479 				return (disk_declare(mod, parent, dnode));
    480 		}
    481 	}
    482 
    483 	topo_mod_dprintf(mod, "disk_declare_addr: "
    484 	    "failed to find disk matching addr %s", addr);
    485 	return (0);
    486 }
    487 
    488 /* di_devlink callback for disk_di_node_add */
    489 static int
    490 disk_devlink_callback(di_devlink_t dl, void *arg)
    491 {
    492 	disk_cbdata_t	*cbp = (disk_cbdata_t *)arg;
    493 	topo_mod_t	*mod = cbp->dcb_mod;
    494 	disk_di_node_t	*dnode = cbp->dcb_dnode;
    495 	const char	*devpath;
    496 	char		*ctds, *slice;
    497 
    498 	devpath = di_devlink_path(dl);
    499 	if ((dnode == NULL) || (devpath == NULL))
    500 		return (DI_WALK_TERMINATE);
    501 
    502 	/* trim the slice off the public name */
    503 	if (((ctds = strrchr(devpath, '/')) != NULL) &&
    504 	    ((slice = strchr(ctds, 's')) != NULL))
    505 		*slice = '\0';
    506 
    507 	/* Establish the public /dev name (no slice) */
    508 	dnode->ddn_lpath = topo_mod_strdup(mod, ctds ? ctds + 1 : devpath);
    509 
    510 	if (ctds && slice)
    511 		*slice = 's';
    512 	return (DI_WALK_TERMINATE);
    513 }
    514 
    515 static void
    516 disk_di_node_free(topo_mod_t *mod, disk_di_node_t *dnode)
    517 {
    518 	int i;
    519 
    520 	/* free the stuff we point to */
    521 	topo_mod_strfree(mod, dnode->ddn_devid);
    522 	for (i = 0; i < dnode->ddn_ppath_count; i++)
    523 		topo_mod_strfree(mod, dnode->ddn_ppath[i]);
    524 	topo_mod_free(mod, dnode->ddn_ppath,
    525 	    dnode->ddn_ppath_count * sizeof (uintptr_t));
    526 	topo_mod_strfree(mod, dnode->ddn_dpath);
    527 	topo_mod_strfree(mod, dnode->ddn_lpath);
    528 
    529 	topo_mod_strfree(mod, dnode->ddn_mfg);
    530 	topo_mod_strfree(mod, dnode->ddn_model);
    531 	topo_mod_strfree(mod, dnode->ddn_serial);
    532 	topo_mod_strfree(mod, dnode->ddn_firm);
    533 	topo_mod_strfree(mod, dnode->ddn_cap);
    534 
    535 	for (i = 0; i < dnode->ddn_target_port_count; i++)
    536 		topo_mod_strfree(mod, dnode->ddn_target_port[i]);
    537 	topo_mod_free(mod, dnode->ddn_target_port,
    538 	    dnode->ddn_target_port_count * sizeof (uintptr_t));
    539 
    540 	/* free self */
    541 	topo_mod_free(mod, dnode, sizeof (disk_di_node_t));
    542 }
    543 
    544 static int
    545 disk_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp)
    546 {
    547 	topo_mod_t	*mod = cbp->dcb_mod;
    548 	disk_di_node_t	*dnode;
    549 	di_path_t	pnode;
    550 	char		*path;
    551 	int		mlen;
    552 	char		*minorpath;
    553 	char		*extn = ":a";
    554 	char		*s;
    555 	int64_t		*nblocksp;
    556 	uint64_t	nblocks;
    557 	int		*dblksizep;
    558 	uint_t		dblksize;
    559 	char		lentry[MAXPATHLEN];
    560 	int		pathcount, portcount;
    561 	int 		ret, i;
    562 
    563 	/* check for list duplicate using devid search */
    564 	for (dnode = topo_list_next(cbp->dcb_list);
    565 	    dnode != NULL; dnode = topo_list_next(dnode)) {
    566 		if (devid_str_compare(dnode->ddn_devid, devid) == 0) {
    567 			topo_mod_dprintf(mod, "disk_di_node_add: "
    568 			    "already there %s\n", devid);
    569 			return (0);
    570 		}
    571 	}
    572 
    573 	if ((dnode = topo_mod_zalloc(mod, sizeof (disk_di_node_t))) == NULL)
    574 		return (-1);
    575 
    576 	/* Establish the devid. */
    577 	dnode->ddn_devid = topo_mod_strdup(mod, devid);
    578 	if (dnode->ddn_devid == NULL)
    579 		goto error;
    580 
    581 	/* Establish the devinfo dpath */
    582 	if ((path = di_devfs_path(node)) == NULL) {
    583 		(void) topo_mod_seterrno(mod, errno);
    584 		goto error;
    585 	}
    586 
    587 	dnode->ddn_dpath = topo_mod_strdup(mod, path);
    588 	di_devfs_path_free(path);
    589 	if (dnode->ddn_dpath == NULL)
    590 		goto error;
    591 
    592 	/*
    593 	 * Establish the physical ppath and target ports. If the device is
    594 	 * non-mpxio then dpath and ppath are the same, and the target port is a
    595 	 * property of the device node.
    596 	 *
    597 	 * If dpath is a client node under scsi_vhci, then iterate over all
    598 	 * paths and get their physical paths and target port properrties.
    599 	 * di_path_client_next_path call below will
    600 	 * return non-NULL, and ppath is set to the physical path to the first
    601 	 * pathinfo node.
    602 	 *
    603 	 * NOTE: It is possible to get a generic.vs.non-generic path
    604 	 * for di_devfs_path.vs.di_path_devfs_path like:
    605 	 *    xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
    606 	 *  pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
    607 	 * To resolve this issue disk_declare_path() needs to use the
    608 	 * special di_devfs_path_match() interface.
    609 	 */
    610 	pathcount = portcount = 0;
    611 	pnode = NULL;
    612 	while ((pnode = di_path_client_next_path(node, pnode)) != NULL) {
    613 		if ((ret = di_path_prop_lookup_strings(pnode,
    614 		    SCSI_ADDR_PROP_TARGET_PORT, &s)) > 0)
    615 			portcount += ret;
    616 		pathcount++;
    617 	}
    618 
    619 	if (pathcount == 0) {
    620 		if ((dnode->ddn_ppath =
    621 		    topo_mod_zalloc(mod, sizeof (uintptr_t))) == NULL)
    622 			goto error;
    623 
    624 		dnode->ddn_ppath_count = 1;
    625 		if ((dnode->ddn_ppath[0] = topo_mod_strdup(mod,
    626 		    dnode->ddn_dpath)) == NULL)
    627 			goto error;
    628 
    629 		if ((ret = di_prop_lookup_strings(DDI_DEV_T_ANY, node,
    630 		    SCSI_ADDR_PROP_TARGET_PORT, &s)) > 0) {
    631 			if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
    632 			    ret * sizeof (uintptr_t))) == NULL)
    633 				goto error;
    634 			dnode->ddn_target_port_count = ret;
    635 
    636 			for (i = 0; i < ret; i++) {
    637 				if ((dnode->ddn_target_port[i] =
    638 				    topo_mod_strdup(mod,
    639 				    scsi_wwnstr_skip_ua_prefix(s))) ==
    640 				    NULL)
    641 					goto error;
    642 
    643 				s += strlen(s) + 1;
    644 			}
    645 		}
    646 	} else {
    647 		if ((dnode->ddn_ppath = topo_mod_zalloc(mod,
    648 		    pathcount * sizeof (uintptr_t))) == NULL)
    649 			goto error;
    650 
    651 		dnode->ddn_ppath_count = pathcount;
    652 
    653 		if (portcount != 0 &&
    654 		    ((dnode->ddn_target_port = topo_mod_zalloc(mod,
    655 		    portcount * sizeof (uintptr_t)))) == NULL)
    656 			goto error;
    657 
    658 		dnode->ddn_target_port_count = portcount;
    659 
    660 		pnode = NULL;
    661 		pathcount = portcount = 0;
    662 		while ((pnode = di_path_client_next_path(node,
    663 		    pnode)) != NULL) {
    664 			if ((path = di_path_devfs_path(pnode)) == NULL) {
    665 				(void) topo_mod_seterrno(mod, errno);
    666 				goto error;
    667 			}
    668 
    669 			dnode->ddn_ppath[pathcount] =
    670 			    topo_mod_strdup(mod, path);
    671 			di_devfs_path_free(path);
    672 			if (dnode->ddn_ppath[pathcount] == NULL)
    673 				goto error;
    674 
    675 			if ((ret = di_path_prop_lookup_strings(pnode,
    676 			    SCSI_ADDR_PROP_TARGET_PORT, &s)) > 0) {
    677 				for (i = 0; i < ret; i++) {
    678 					if ((dnode->ddn_target_port[portcount] =
    679 					    topo_mod_strdup(mod,
    680 					    scsi_wwnstr_skip_ua_prefix(s))) ==
    681 					    NULL)
    682 						goto error;
    683 
    684 					portcount++;
    685 					s += strlen(s) + 1;
    686 				}
    687 			}
    688 
    689 			pathcount++;
    690 		}
    691 	}
    692 
    693 	/*
    694 	 * Find the public /dev name by adding a minor name and using
    695 	 * di_devlink interface for reverse translation (use devinfo path).
    696 	 */
    697 	mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
    698 	if ((minorpath = topo_mod_alloc(mod, mlen)) == NULL)
    699 		goto error;
    700 	(void) snprintf(minorpath, mlen, "%s%s", dnode->ddn_dpath, extn);
    701 	cbp->dcb_dnode = dnode;
    702 	(void) di_devlink_walk(cbp->dcb_devhdl, "^dsk/", minorpath,
    703 	    DI_PRIMARY_LINK, cbp, disk_devlink_callback);
    704 	topo_mod_free(mod, minorpath, mlen);
    705 	if (dnode->ddn_lpath == NULL) {
    706 		topo_mod_dprintf(mod, "disk_di_node_add: "
    707 		    "failed to determine logical path");
    708 		goto error;
    709 	}
    710 
    711 	/* cache various bits of optional information about the disk */
    712 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
    713 	    INQUIRY_VENDOR_ID, &s) > 0) {
    714 		if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL)
    715 			goto error;
    716 	}
    717 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
    718 	    INQUIRY_PRODUCT_ID, &s) > 0) {
    719 		if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL)
    720 			goto error;
    721 	}
    722 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
    723 	    INQUIRY_REVISION_ID, &s) > 0) {
    724 		if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL)
    725 			goto error;
    726 	}
    727 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
    728 	    INQUIRY_SERIAL_NO, &s) > 0) {
    729 		if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL)
    730 			goto error;
    731 	}
    732 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
    733 	    "device-nblocks", &nblocksp) > 0) {
    734 		nblocks = (uint64_t)*nblocksp;
    735 		/*
    736 		 * To save kernel memory, the driver may not define
    737 		 * "device-dblksize" when its value is default DEV_BSIZE.
    738 		 */
    739 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
    740 		    "device-dblksize", &dblksizep) > 0)
    741 			dblksize = (uint_t)*dblksizep;
    742 		else
    743 			dblksize = DEV_BSIZE;		/* default value */
    744 		(void) snprintf(lentry, sizeof (lentry),
    745 		    "%" PRIu64, nblocks * dblksize);
    746 		if ((dnode->ddn_cap = topo_mod_strdup(mod, lentry)) == NULL)
    747 			goto error;
    748 	}
    749 
    750 	topo_mod_dprintf(mod, "disk_di_node_add: "
    751 	    "adding %s\n", dnode->ddn_devid);
    752 	topo_mod_dprintf(mod, "                  "
    753 	    "       %s\n", dnode->ddn_dpath);
    754 	for (i = 0; i < dnode->ddn_ppath_count; i++) {
    755 		topo_mod_dprintf(mod, "                  "
    756 		    "       %s\n", dnode->ddn_ppath[i]);
    757 	}
    758 	topo_list_append(cbp->dcb_list, dnode);
    759 	return (0);
    760 
    761 error:
    762 	disk_di_node_free(mod, dnode);
    763 	return (-1);
    764 }
    765 
    766 /* di_walk_node callback for disk_list_gather */
    767 static int
    768 disk_walk_di_nodes(di_node_t node, void *arg)
    769 {
    770 	char			*devidstr = NULL;
    771 
    772 	/* only interested in nodes that have devids */
    773 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
    774 	    DEVID_PROP_NAME, &devidstr) < 0) {
    775 		return (DI_WALK_CONTINUE);
    776 	}
    777 
    778 	/* create/find the devid scsi topology node */
    779 	(void) disk_di_node_add(node, devidstr, arg);
    780 
    781 	return (DI_WALK_CONTINUE);
    782 }
    783 
    784 int
    785 disk_list_gather(topo_mod_t *mod, topo_list_t *listp)
    786 {
    787 	di_node_t		devtree;
    788 	di_devlink_handle_t	devhdl;
    789 	disk_cbdata_t		dcb;
    790 
    791 	if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
    792 		topo_mod_dprintf(mod, "disk_list_gather: "
    793 		    "topo_mod_devinfo() failed");
    794 		return (-1);
    795 	}
    796 
    797 	if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) {
    798 		topo_mod_dprintf(mod, "disk_list_gather: "
    799 		    "di_devlink_init() failed");
    800 		return (-1);
    801 	}
    802 
    803 	dcb.dcb_mod = mod;
    804 	dcb.dcb_list = listp;
    805 	dcb.dcb_devhdl = devhdl;
    806 
    807 	/* walk the devinfo snapshot looking for nodes with devids */
    808 	(void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
    809 	    disk_walk_di_nodes);
    810 
    811 	(void) di_devlink_fini(&devhdl);
    812 
    813 	return (0);
    814 }
    815 
    816 void
    817 disk_list_free(topo_mod_t *mod, topo_list_t *listp)
    818 {
    819 	disk_di_node_t	*dnode;
    820 
    821 	while ((dnode = topo_list_next(listp)) != NULL) {
    822 		/* order of delete/free is important */
    823 		topo_list_delete(listp, dnode);
    824 		disk_di_node_free(mod, dnode);
    825 	}
    826 }
    827 
    828 /*
    829  * Query the current disk status. If successful, the disk status is returned
    830  * as an nvlist consisting of at least the following members:
    831  *
    832  *	protocol	string		Supported protocol (currently "scsi")
    833  *
    834  *	status		nvlist		Arbitrary protocol-specific information
    835  *					about the current state of the disk.
    836  *
    837  *	faults		nvlist		A list of supported faults. Each
    838  *					element of this list is a boolean value.
    839  *					An element's existence indicates that
    840  *					the drive supports detecting this fault,
    841  *					and the value indicates the current
    842  *					state of the fault.
    843  *
    844  *	<fault-name>	nvlist		For each fault named in 'faults', a
    845  *					nvlist describing protocol-specific
    846  *					attributes of the fault.
    847  *
    848  * This method relies on the libdiskstatus library to query this information.
    849  */
    850 static int
    851 disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
    852     nvlist_t *in_nvl, nvlist_t **out_nvl)
    853 {
    854 	disk_status_t	*dsp;
    855 	char		*devpath, *fullpath;
    856 	size_t		pathlen;
    857 	nvlist_t	*status;
    858 	int		err;
    859 
    860 	*out_nvl = NULL;
    861 
    862 	if (vers != TOPO_METH_DISK_STATUS_VERSION)
    863 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    864 
    865 	/*
    866 	 * If the caller specifies the "path" parameter, then this indicates
    867 	 * that we should use this instead of deriving it from the topo node
    868 	 * itself.
    869 	 */
    870 	if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
    871 		devpath = NULL;
    872 	} else {
    873 		/*
    874 		 * Get the /devices path and attempt to open the disk status
    875 		 * handle.
    876 		 */
    877 		if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
    878 		    TOPO_IO_DEV_PATH, &devpath, &err) != 0)
    879 			return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
    880 
    881 		/*
    882 		 * Note that sizeof(string) includes the terminating NULL byte
    883 		 */
    884 		pathlen = strlen(devpath) + sizeof ("/devices") +
    885 		    sizeof (PHYS_EXTN) - 1;
    886 
    887 		if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
    888 			return (topo_mod_seterrno(mod, EMOD_NOMEM));
    889 
    890 		(void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
    891 		    PHYS_EXTN);
    892 
    893 		topo_mod_strfree(mod, devpath);
    894 	}
    895 
    896 	if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
    897 		if (devpath)
    898 			topo_mod_free(mod, fullpath, pathlen);
    899 		return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
    900 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP));
    901 	}
    902 
    903 	if (devpath)
    904 		topo_mod_free(mod, fullpath, pathlen);
    905 
    906 	if ((status = disk_status_get(dsp)) == NULL) {
    907 		err = (disk_status_errno(dsp) == EDS_NOMEM ?
    908 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP);
    909 		disk_status_close(dsp);
    910 		return (topo_mod_seterrno(mod, err));
    911 	}
    912 
    913 	*out_nvl = status;
    914 	disk_status_close(dsp);
    915 	return (0);
    916 }
    917