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