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:    if (fmri)
 270                 nvlist_free(fmri);
 271         if (label)
 272                 topo_mod_strfree(mod, label);
 273         if (asru)
 274                 nvlist_free(asru);
 275         return (err);
 276 
 277 error:  err = topo_mod_seterrno(mod, err);
 278         goto out;
 279 }
 280 
 281 /*
 282  * Trim leading and trailing whitespace from the string.
 283  */
 284 static char *
 285 disk_trim_whitespace(topo_mod_t *mod, const char *begin)
 286 {
 287         const char *end;
 288         char *buf;
 289         size_t count;
 290 
 291         if (begin == NULL)
 292                 return (NULL);
 293 
 294         end = begin + strlen(begin);
 295 
 296         while (begin < end && isspace(*begin))
 297                 begin++;
 298         while (begin < end && isspace(*(end - 1)))
 299                 end--;
 300 
 301         count = end - begin;
 302         if ((buf = topo_mod_alloc(mod, count + 1)) == NULL)
 303                 return (NULL);
 304 
 305         (void) strlcpy(buf, begin, count + 1);
 306 
 307         return (buf);
 308 }
 309 
 310 /*
 311  * Manufacturing strings can contain characters that are invalid for use in hc
 312  * authority names.  This trims leading and trailing whitespace, and
 313  * substitutes any characters known to be bad.
 314  */
 315 char *
 316 disk_auth_clean(topo_mod_t *mod, const char *str)
 317 {
 318         char *buf, *p;
 319 
 320         if (str == NULL)
 321                 return (NULL);
 322 
 323         if ((buf = topo_mod_strdup(mod, str)) == NULL)
 324                 return (NULL);
 325 
 326         while ((p = strpbrk(buf, " :=")) != NULL)
 327                 *p = '-';
 328 
 329         return (buf);
 330 }
 331 
 332 /* create the disk topo node */
 333 static int
 334 disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
 335     dev_di_node_t *dnode, const char *name, topo_instance_t i, tnode_t **rval)
 336 {
 337         int             len;
 338         nvlist_t        *fmri;
 339         tnode_t         *dtn;
 340         char            *part = NULL;
 341         nvlist_t        *auth;
 342         char            *mfg, *model, *firm, *serial;
 343 
 344         *rval = NULL;
 345         if (dnode != NULL) {
 346                 mfg = disk_auth_clean(mod, dnode->ddn_mfg);
 347                 model = disk_auth_clean(mod, dnode->ddn_model);
 348                 firm = disk_auth_clean(mod, dnode->ddn_firm);
 349                 serial = disk_auth_clean(mod, dnode->ddn_serial);
 350         } else {
 351                 mfg = model = firm = serial = NULL;
 352         }
 353 
 354         /* form 'part=' of fmri as "<mfg>-<model>" */
 355         if (mfg != NULL && model != NULL) {
 356                 len = strlen(mfg) + 1 + strlen(model) + 1;
 357                 if ((part = topo_mod_alloc(mod, len)) != NULL)
 358                         (void) snprintf(part, len, "%s-%s",
 359                             mfg, model);
 360         }
 361 
 362         auth = topo_mod_auth(mod, parent);
 363         fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, NULL,
 364             auth, part ? part : model, firm, serial);
 365         nvlist_free(auth);
 366 
 367         topo_mod_strfree(mod, part);
 368         topo_mod_strfree(mod, mfg);
 369         topo_mod_strfree(mod, model);
 370         topo_mod_strfree(mod, firm);
 371         topo_mod_strfree(mod, serial);
 372 
 373         if (fmri == NULL) {
 374                 topo_mod_dprintf(mod, "disk_tnode_create: "
 375                     "hcfmri (%s%d/%s%d) error %s\n",
 376                     topo_node_name(parent), topo_node_instance(parent),
 377                     name, i, topo_strerror(topo_mod_errno(mod)));
 378                 return (-1);
 379         }
 380 
 381         if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
 382                 if (topo_mod_errno(mod) == EMOD_NODE_BOUND) {
 383                         /*
 384                          * if disk 0 is already there then we're done
 385                          */
 386                         nvlist_free(fmri);
 387                         return (0);
 388                 }
 389                 topo_mod_dprintf(mod, "disk_tnode_create: "
 390                     "bind (%s%d/%s%d) error %s\n",
 391                     topo_node_name(parent), topo_node_instance(parent),
 392                     name, i, topo_strerror(topo_mod_errno(mod)));
 393                 nvlist_free(fmri);
 394                 return (-1);
 395         }
 396         nvlist_free(fmri);
 397 
 398         /* add the properties of the disk */
 399         if (disk_set_props(mod, parent, dtn, dnode) != 0) {
 400                 topo_mod_dprintf(mod, "disk_tnode_create: "
 401                     "disk_set_props (%s%d/%s%d) error %s\n",
 402                     topo_node_name(parent), topo_node_instance(parent),
 403                     name, i, topo_strerror(topo_mod_errno(mod)));
 404                 topo_node_unbind(dtn);
 405                 return (-1);
 406         }
 407         *rval = dtn;
 408         return (0);
 409 }
 410 
 411 static int
 412 disk_declare(topo_mod_t *mod, tnode_t *parent, dev_di_node_t *dnode,
 413     tnode_t **childp)
 414 {
 415         tnode_t         *dtn = NULL;
 416         int             rval;
 417 
 418         rval = disk_tnode_create(mod, parent, dnode, DISK, 0, &dtn);
 419         if (dtn == NULL) {
 420                 if (rval == 0)
 421                         return (0);
 422                 topo_mod_dprintf(mod, "disk_declare: "
 423                     "disk_tnode_create error %s\n",
 424                     topo_strerror(topo_mod_errno(mod)));
 425                 return (-1);
 426         }
 427 
 428         /* register disk_methods against the disk topo node */
 429         if (topo_method_register(mod, dtn, disk_methods) != 0) {
 430                 topo_mod_dprintf(mod, "disk_declare: "
 431                     "topo_method_register error %s\n",
 432                     topo_strerror(topo_mod_errno(mod)));
 433                 topo_node_unbind(dtn);
 434                 return (-1);
 435         }
 436         if (childp != NULL)
 437                 *childp = dtn;
 438         return (0);
 439 }
 440 
 441 int
 442 disk_declare_path(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
 443     const char *path)
 444 {
 445         dev_di_node_t           *dnode;
 446         int i;
 447 
 448         /*
 449          * Check for match using physical phci (ddn_ppath). Use
 450          * di_devfs_path_match so generic.vs.non-generic names match.
 451          */
 452         for (dnode = topo_list_next(listp); dnode != NULL;
 453             dnode = topo_list_next(dnode)) {
 454                 if (dnode->ddn_ppath == NULL)
 455                         continue;
 456 
 457                 for (i = 0; i < dnode->ddn_ppath_count; i++) {
 458                         if (di_devfs_path_match(dnode->ddn_ppath[0], path))
 459                                 return (disk_declare(mod, parent, dnode, NULL));
 460                 }
 461         }
 462 
 463         topo_mod_dprintf(mod, "disk_declare_path: "
 464             "failed to find disk matching path %s", path);
 465         return (0);
 466 }
 467 
 468 int
 469 disk_declare_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
 470     const char *addr, tnode_t **childp)
 471 {
 472         dev_di_node_t *dnode;
 473         int i;
 474 
 475         /* Check for match using addr. */
 476         for (dnode = topo_list_next(listp); dnode != NULL;
 477             dnode = topo_list_next(dnode)) {
 478                 if (dnode->ddn_target_port == NULL)
 479                         continue;
 480 
 481                 for (i = 0; i < dnode->ddn_ppath_count; i++) {
 482                         if ((dnode->ddn_target_port[i] != NULL) &&
 483                             (strncmp(dnode->ddn_target_port[i], addr,
 484                             strcspn(dnode->ddn_target_port[i], ":"))) == 0) {
 485                                 topo_mod_dprintf(mod, "disk_declare_addr: "
 486                                     "found disk matching addr %s", addr);
 487                                 return (disk_declare(mod, parent, dnode,
 488                                     childp));
 489                         }
 490                 }
 491         }
 492 
 493         topo_mod_dprintf(mod, "disk_declare_addr: "
 494             "failed to find disk matching addr %s", addr);
 495 
 496         return (1);
 497 }
 498 
 499 /*
 500  * Used to declare a disk that has been discovered through other means (usually
 501  * ses), that is not enumerated in the devinfo tree.
 502  */
 503 int
 504 disk_declare_non_enumerated(topo_mod_t *mod, tnode_t *parent, tnode_t **childp)
 505 {
 506         return (disk_declare(mod, parent, NULL, childp));
 507 }
 508 
 509 /* di_devlink callback for dev_di_node_add */
 510 static int
 511 disk_devlink_callback(di_devlink_t dl, void *arg)
 512 {
 513         disk_cbdata_t   *cbp = (disk_cbdata_t *)arg;
 514         topo_mod_t      *mod = cbp->dcb_mod;
 515         dev_di_node_t   *dnode = cbp->dcb_dnode;
 516         const char      *devpath;
 517         char            *ctds, *slice;
 518 
 519         devpath = di_devlink_path(dl);
 520         if ((dnode == NULL) || (devpath == NULL))
 521                 return (DI_WALK_TERMINATE);
 522 
 523         /* trim the slice off the public name */
 524         if (((ctds = strrchr(devpath, '/')) != NULL) &&
 525             ((slice = strchr(ctds, 's')) != NULL))
 526                 *slice = '\0';
 527 
 528         /* Establish the public /dev name (no slice) */
 529         dnode->ddn_lpath = topo_mod_strdup(mod, ctds ? ctds + 1 : devpath);
 530 
 531         if (ctds && slice)
 532                 *slice = 's';
 533         return (DI_WALK_TERMINATE);
 534 }
 535 
 536 static void
 537 dev_di_node_free(topo_mod_t *mod, dev_di_node_t *dnode)
 538 {
 539         int i;
 540 
 541         /* free the stuff we point to */
 542         if (dnode->ddn_devid)
 543                 topo_mod_strfree(mod, dnode->ddn_devid);
 544         for (i = 0; i < dnode->ddn_ppath_count; i++) {
 545                 /* topo_mod_strfree does NULL checking. */
 546                 topo_mod_strfree(mod, dnode->ddn_ppath[i]);
 547                 topo_mod_strfree(mod, dnode->ddn_target_port[i]);
 548                 topo_mod_strfree(mod, dnode->ddn_attached_port[i]);
 549                 topo_mod_strfree(mod, dnode->ddn_bridge_port[i]);
 550         }
 551         topo_mod_free(mod, dnode->ddn_ppath,
 552             dnode->ddn_ppath_count * sizeof (char *));
 553         topo_mod_free(mod, dnode->ddn_target_port,
 554             dnode->ddn_ppath_count * sizeof (char *));
 555         topo_mod_free(mod, dnode->ddn_attached_port,
 556             dnode->ddn_ppath_count * sizeof (char *));
 557         topo_mod_free(mod, dnode->ddn_bridge_port,
 558             dnode->ddn_ppath_count * sizeof (char *));
 559         topo_mod_strfree(mod, dnode->ddn_dpath);
 560         topo_mod_strfree(mod, dnode->ddn_lpath);
 561 
 562         topo_mod_strfree(mod, dnode->ddn_mfg);
 563         topo_mod_strfree(mod, dnode->ddn_model);
 564         topo_mod_strfree(mod, dnode->ddn_serial);
 565         topo_mod_strfree(mod, dnode->ddn_firm);
 566         topo_mod_strfree(mod, dnode->ddn_cap);
 567 
 568         /* free self */
 569         topo_mod_free(mod, dnode, sizeof (dev_di_node_t));
 570 }
 571 
 572 static int
 573 dev_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp)
 574 {
 575         topo_mod_t      *mod = cbp->dcb_mod;
 576         dev_di_node_t   *dnode;
 577         di_path_t       pnode;
 578         char            *path;
 579         int             mlen;
 580         char            *minorpath;
 581         char            *extn = ":a";
 582         char            *s;
 583         int64_t         *nblocksp;
 584         uint64_t        nblocks;
 585         int             *dblksizep;
 586         uint_t          dblksize;
 587         char            lentry[MAXPATHLEN];
 588         int             pathcount;
 589         int             *inq_dtype, itype;
 590         int             i;
 591 
 592         if (devid) {
 593                 /*
 594                  * Check for list duplicate using devid search.
 595                  * Note if there is no devid, then we can end up with duplicates
 596                  * in the list, but this doesn't do any harm.
 597                  */
 598                 for (dnode = topo_list_next(cbp->dcb_list);
 599                     dnode != NULL; dnode = topo_list_next(dnode)) {
 600                         if (dnode->ddn_devid &&
 601                             devid_str_compare(dnode->ddn_devid, devid) == 0) {
 602                                 topo_mod_dprintf(mod, "dev_di_node_add: "
 603                                     "already there %s\n", devid);
 604                                 return (0);
 605                         }
 606                 }
 607         }
 608 
 609         if ((dnode = topo_mod_zalloc(mod, sizeof (dev_di_node_t))) == NULL)
 610                 return (-1);
 611 
 612         if (devid) {
 613                 /* Establish the devid. */
 614                 dnode->ddn_devid = topo_mod_strdup(mod, devid);
 615                 if (dnode->ddn_devid == NULL)
 616                         goto error;
 617         }
 618 
 619         /* Establish the devinfo dpath */
 620         if ((path = di_devfs_path(node)) == NULL) {
 621                 (void) topo_mod_seterrno(mod, errno);
 622                 goto error;
 623         }
 624 
 625         dnode->ddn_dpath = topo_mod_strdup(mod, path);
 626         di_devfs_path_free(path);
 627         if (dnode->ddn_dpath == NULL)
 628                 goto error;
 629 
 630         /*
 631          * Establish the physical ppath and target ports. If the device is
 632          * non-mpxio then dpath and ppath are the same, and the target port is a
 633          * property of the device node.
 634          *
 635          * If dpath is a client node under scsi_vhci, then iterate over all
 636          * paths and get their physical paths and target port properrties.
 637          * di_path_client_next_path call below will
 638          * return non-NULL, and ppath is set to the physical path to the first
 639          * pathinfo node.
 640          *
 641          * NOTE: It is possible to get a generic.vs.non-generic path
 642          * for di_devfs_path.vs.di_path_devfs_path like:
 643          *    xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
 644          *  pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
 645          * To resolve this issue disk_declare_path() needs to use the
 646          * special di_devfs_path_match() interface.
 647          */
 648         pathcount = 0;
 649         pnode = NULL;
 650         while ((pnode = di_path_client_next_path(node, pnode)) != NULL) {
 651                 pathcount++;
 652         }
 653 
 654         if (pathcount == 0) {
 655                 if ((dnode->ddn_ppath =
 656                     topo_mod_zalloc(mod, sizeof (char *))) == NULL)
 657                         goto error;
 658 
 659                 dnode->ddn_ppath_count = 1;
 660                 if ((dnode->ddn_ppath[0] = topo_mod_strdup(mod,
 661                     dnode->ddn_dpath)) == NULL)
 662                         goto error;
 663 
 664                 if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
 665                     sizeof (char *))) == NULL)
 666                         goto error;
 667 
 668                 if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
 669                     sizeof (char *))) == NULL)
 670                         goto error;
 671 
 672                 if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
 673                     sizeof (char *))) == NULL)
 674                         goto error;
 675 
 676                 /* There should be only one target port for a devinfo node. */
 677                 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 678                     SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
 679                         if ((dnode->ddn_target_port[0] =
 680                             topo_mod_strdup(mod,
 681                             scsi_wwnstr_skip_ua_prefix(s))) ==
 682                             NULL)
 683                                 goto error;
 684                 }
 685 
 686                 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 687                     SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
 688                         /* There should be one attached port if any. */
 689                         if ((dnode->ddn_attached_port[0] =
 690                             topo_mod_strdup(mod,
 691                             scsi_wwnstr_skip_ua_prefix(s))) ==
 692                             NULL)
 693                                 goto error;
 694                 }
 695 
 696                 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 697                     SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
 698                         /* There should be one bridge port if any. */
 699                         if ((dnode->ddn_bridge_port[0] =
 700                             topo_mod_strdup(mod,
 701                             scsi_wwnstr_skip_ua_prefix(s))) ==
 702                             NULL)
 703                                 goto error;
 704                 }
 705 
 706         } else {
 707                 /* processing a scsi_vhci device. */
 708                 if ((dnode->ddn_ppath = topo_mod_zalloc(mod,
 709                     pathcount * sizeof (char *))) == NULL)
 710                         goto error;
 711 
 712                 dnode->ddn_ppath_count = pathcount;
 713 
 714                 if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
 715                     pathcount * sizeof (char *))) == NULL)
 716                         goto error;
 717 
 718                 if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
 719                     pathcount * sizeof (char *))) == NULL)
 720                         goto error;
 721 
 722                 if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
 723                     pathcount * sizeof (char *))) == NULL)
 724                         goto error;
 725 
 726                 pnode = NULL;
 727                 pathcount = 0;
 728                 while ((pnode = di_path_client_next_path(node,
 729                     pnode)) != NULL) {
 730                         if ((path = di_path_devfs_path(pnode)) == NULL) {
 731                                 (void) topo_mod_seterrno(mod, errno);
 732                                 goto error;
 733                         }
 734 
 735                         dnode->ddn_ppath[pathcount] =
 736                             topo_mod_strdup(mod, path);
 737                         di_devfs_path_free(path);
 738                         if (dnode->ddn_ppath[pathcount] == NULL)
 739                                 goto error;
 740 
 741                         if ((di_path_prop_lookup_strings(pnode,
 742                             SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
 743                                 if ((dnode->ddn_target_port[pathcount] =
 744                                     topo_mod_strdup(mod,
 745                                     scsi_wwnstr_skip_ua_prefix(s))) ==
 746                                     NULL)
 747                                         goto error;
 748                         }
 749 
 750                         if ((di_path_prop_lookup_strings(pnode,
 751                             SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
 752                                 if ((dnode->ddn_attached_port[pathcount] =
 753                                     topo_mod_strdup(mod,
 754                                     scsi_wwnstr_skip_ua_prefix(s))) ==
 755                                     NULL)
 756                                         goto error;
 757                         }
 758 
 759                         if ((di_path_prop_lookup_strings(pnode,
 760                             SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
 761                                 if ((dnode->ddn_bridge_port[pathcount] =
 762                                     topo_mod_strdup(mod,
 763                                     scsi_wwnstr_skip_ua_prefix(s))) ==
 764                                     NULL)
 765                                         goto error;
 766                         }
 767 
 768                         pathcount++;
 769                 }
 770         }
 771 
 772         /*
 773          * Find the public /dev name for a disk by adding a minor name and using
 774          * di_devlink interface for reverse translation (use devinfo path).
 775          */
 776         if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
 777             &inq_dtype) > 0) {
 778                 dnode->ddn_dtype = *inq_dtype;
 779                 itype = (*inq_dtype) & DTYPE_MASK;
 780                 if (itype == DTYPE_DIRECT) {
 781                         mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
 782                         if ((minorpath = topo_mod_alloc(mod, mlen)) == NULL)
 783                                 goto error;
 784                         (void) snprintf(minorpath, mlen, "%s%s",
 785                             dnode->ddn_dpath, extn);
 786                         cbp->dcb_dnode = dnode;
 787                         (void) di_devlink_walk(cbp->dcb_devhdl, "^dsk/",
 788                             minorpath, DI_PRIMARY_LINK, cbp,
 789                             disk_devlink_callback);
 790                         topo_mod_free(mod, minorpath, mlen);
 791                         if (dnode->ddn_lpath == NULL) {
 792                                 topo_mod_dprintf(mod, "dev_di_node_add: "
 793                                     "failed to determine logical path");
 794                         }
 795                 }
 796         } else {
 797                 dnode->ddn_dtype = DTYPE_UNKNOWN;
 798         }
 799 
 800         /* cache various bits of optional information about the device. */
 801         if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 802             INQUIRY_VENDOR_ID, &s) > 0) {
 803                 if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL)
 804                         goto error;
 805         }
 806         if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 807             INQUIRY_PRODUCT_ID, &s) > 0) {
 808                 if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL)
 809                         goto error;
 810         }
 811         if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 812             INQUIRY_REVISION_ID, &s) > 0) {
 813                 if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL)
 814                         goto error;
 815         }
 816         if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 817             INQUIRY_SERIAL_NO, &s) > 0) {
 818                 if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL)
 819                         goto error;
 820         }
 821         if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
 822             "device-nblocks", &nblocksp) > 0) {
 823                 nblocks = (uint64_t)*nblocksp;
 824                 /*
 825                  * To save kernel memory, the driver may not define
 826                  * "device-dblksize" when its value is default DEV_BSIZE.
 827                  */
 828                 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
 829                     "device-dblksize", &dblksizep) > 0)
 830                         dblksize = (uint_t)*dblksizep;
 831                 else
 832                         dblksize = DEV_BSIZE;           /* default value */
 833                 (void) snprintf(lentry, sizeof (lentry),
 834                     "%" PRIu64, nblocks * dblksize);
 835                 if ((dnode->ddn_cap = topo_mod_strdup(mod, lentry)) == NULL)
 836                         goto error;
 837         }
 838 
 839         topo_mod_dprintf(mod, "dev_di_node_add: "
 840             "adding %s\n", devid ? dnode->ddn_devid : "NULL devid");
 841         topo_mod_dprintf(mod, "                  "
 842             "       %s\n", dnode->ddn_dpath);
 843         for (i = 0; i < dnode->ddn_ppath_count; i++) {
 844                 topo_mod_dprintf(mod, "                  "
 845                     "       %s\n", dnode->ddn_ppath[i]);
 846         }
 847         topo_list_append(cbp->dcb_list, dnode);
 848         return (0);
 849 
 850 error:
 851         dev_di_node_free(mod, dnode);
 852         return (-1);
 853 }
 854 
 855 /* di_walk_node callback for disk_list_gather */
 856 static int
 857 dev_walk_di_nodes(di_node_t node, void *arg)
 858 {
 859         char                    *devidstr = NULL;
 860         char                    *s;
 861         int                     *val;
 862 
 863         /*
 864          * If it's not a scsi_vhci client and doesn't have a target_port
 865          * property and doesn't have a target property then it's not a storage
 866          * device and we're not interested.
 867          */
 868         if (di_path_client_next_path(node, NULL) == NULL &&
 869             di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 870             SCSI_ADDR_PROP_TARGET_PORT, &s) <= 0 &&
 871             di_prop_lookup_ints(DDI_DEV_T_ANY, node,
 872             SCSI_ADDR_PROP_TARGET, &val) <= 0) {
 873                 return (DI_WALK_CONTINUE);
 874         }
 875         (void) di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 876             DEVID_PROP_NAME, &devidstr);
 877 
 878         /* create/find the devid scsi topology node */
 879         (void) dev_di_node_add(node, devidstr, arg);
 880 
 881         return (DI_WALK_CONTINUE);
 882 }
 883 
 884 int
 885 dev_list_gather(topo_mod_t *mod, topo_list_t *listp)
 886 {
 887         di_node_t               devtree;
 888         di_devlink_handle_t     devhdl;
 889         disk_cbdata_t           dcb;
 890 
 891         if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
 892                 topo_mod_dprintf(mod, "disk_list_gather: "
 893                     "topo_mod_devinfo() failed");
 894                 return (-1);
 895         }
 896 
 897         if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) {
 898                 topo_mod_dprintf(mod, "disk_list_gather: "
 899                     "di_devlink_init() failed");
 900                 return (-1);
 901         }
 902 
 903         dcb.dcb_mod = mod;
 904         dcb.dcb_list = listp;
 905         dcb.dcb_devhdl = devhdl;
 906 
 907         /* walk the devinfo snapshot looking for disk nodes */
 908         (void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
 909             dev_walk_di_nodes);
 910 
 911         (void) di_devlink_fini(&devhdl);
 912 
 913         return (0);
 914 }
 915 
 916 void
 917 dev_list_free(topo_mod_t *mod, topo_list_t *listp)
 918 {
 919         dev_di_node_t   *dnode;
 920 
 921         while ((dnode = topo_list_next(listp)) != NULL) {
 922                 /* order of delete/free is important */
 923                 topo_list_delete(listp, dnode);
 924                 dev_di_node_free(mod, dnode);
 925         }
 926 }
 927 
 928 /*
 929  * Query the current disk status. If successful, the disk status is returned
 930  * as an nvlist consisting of at least the following members:
 931  *
 932  *      protocol        string          Supported protocol (currently "scsi")
 933  *
 934  *      status          nvlist          Arbitrary protocol-specific information
 935  *                                      about the current state of the disk.
 936  *
 937  *      faults          nvlist          A list of supported faults. Each
 938  *                                      element of this list is a boolean value.
 939  *                                      An element's existence indicates that
 940  *                                      the drive supports detecting this fault,
 941  *                                      and the value indicates the current
 942  *                                      state of the fault.
 943  *
 944  *      <fault-name>      nvlist          For each fault named in 'faults', a
 945  *                                      nvlist describing protocol-specific
 946  *                                      attributes of the fault.
 947  *
 948  * This method relies on the libdiskstatus library to query this information.
 949  */
 950 static int
 951 disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
 952     nvlist_t *in_nvl, nvlist_t **out_nvl)
 953 {
 954         disk_status_t   *dsp;
 955         char            *devpath, *fullpath;
 956         size_t          pathlen;
 957         nvlist_t        *status;
 958         int             err;
 959 
 960         *out_nvl = NULL;
 961 
 962         if (vers != TOPO_METH_DISK_STATUS_VERSION)
 963                 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
 964 
 965         /*
 966          * If the caller specifies the "path" parameter, then this indicates
 967          * that we should use this instead of deriving it from the topo node
 968          * itself.
 969          */
 970         if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
 971                 devpath = NULL;
 972         } else {
 973                 /*
 974                  * Get the /devices path and attempt to open the disk status
 975                  * handle.
 976                  */
 977                 if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
 978                     TOPO_IO_DEV_PATH, &devpath, &err) != 0)
 979                         return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
 980 
 981                 /*
 982                  * Note that sizeof(string) includes the terminating NULL byte
 983                  */
 984                 pathlen = strlen(devpath) + sizeof ("/devices") +
 985                     sizeof (PHYS_EXTN) - 1;
 986 
 987                 if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
 988                         return (topo_mod_seterrno(mod, EMOD_NOMEM));
 989 
 990                 (void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
 991                     PHYS_EXTN);
 992 
 993                 topo_mod_strfree(mod, devpath);
 994         }
 995 
 996         if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
 997                 if (devpath)
 998                         topo_mod_free(mod, fullpath, pathlen);
 999                 return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
1000                     EMOD_NOMEM : EMOD_METHOD_NOTSUP));
1001         }
1002 
1003         if (devpath)
1004                 topo_mod_free(mod, fullpath, pathlen);
1005 
1006         if ((status = disk_status_get(dsp)) == NULL) {
1007                 err = (disk_status_errno(dsp) == EDS_NOMEM ?
1008                     EMOD_NOMEM : EMOD_METHOD_NOTSUP);
1009                 disk_status_close(dsp);
1010                 return (topo_mod_seterrno(mod, err));
1011         }
1012 
1013         *out_nvl = status;
1014         disk_status_close(dsp);
1015         return (0);
1016 }