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 * Copyright (c) 2017, Joyent, Inc. 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 <sys/scsi/generic/inquiry.h> 52 #include "disk.h" 53 54 /* common callback information for di_walk_node() and di_devlink_walk */ 55 typedef struct disk_cbdata { 56 topo_mod_t *dcb_mod; 57 topo_list_t *dcb_list; 58 59 di_devlink_handle_t dcb_devhdl; 60 dev_di_node_t *dcb_dnode; /* for di_devlink_walk only */ 61 } disk_cbdata_t; 62 63 /* 64 * Given a /devices path for a whole disk, appending this extension gives the 65 * path to a raw device that can be opened. 66 */ 67 #if defined(__i386) || defined(__amd64) 68 #define PHYS_EXTN ":q,raw" 69 #elif defined(__sparc) || defined(__sparcv9) 70 #define PHYS_EXTN ":c,raw" 71 #else 72 #error Unknown architecture 73 #endif 74 75 /* 76 * Methods for disks. This is used by the disk-transport module to 77 * generate ereports based off SCSI disk status. 78 */ 79 static int disk_status(topo_mod_t *, tnode_t *, topo_version_t, 80 nvlist_t *, nvlist_t **); 81 82 static const topo_method_t disk_methods[] = { 83 { TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC, 84 TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL, 85 disk_status }, 86 { NULL } 87 }; 88 89 static const topo_pgroup_info_t io_pgroup = { 90 TOPO_PGROUP_IO, 91 TOPO_STABILITY_PRIVATE, 92 TOPO_STABILITY_PRIVATE, 93 1 94 }; 95 96 static const topo_pgroup_info_t disk_auth_pgroup = { 97 FM_FMRI_AUTHORITY, 98 TOPO_STABILITY_PRIVATE, 99 TOPO_STABILITY_PRIVATE, 100 1 101 }; 102 103 static const topo_pgroup_info_t storage_pgroup = { 104 TOPO_PGROUP_STORAGE, 105 TOPO_STABILITY_PRIVATE, 106 TOPO_STABILITY_PRIVATE, 107 1 108 }; 109 110 /* 111 * Set the properties of the disk node, from dev_di_node_t data. 112 * Properties include: 113 * group: protocol properties: resource, asru, label, fru 114 * group: authority properties: product-id, chasis-id, server-id 115 * group: io properties: devfs-path, devid 116 * group: storage properties: 117 * - logical-disk, disk-model, disk-manufacturer, serial-number 118 * - firmware-revision, capacity-in-bytes 119 * 120 * NOTE: the io and storage groups won't be present if the dnode passed in is 121 * NULL. This happens when a disk is found through ses, but is not enumerated 122 * in the devinfo tree. 123 */ 124 static int 125 disk_set_props(topo_mod_t *mod, tnode_t *parent, 126 tnode_t *dtn, dev_di_node_t *dnode) 127 { 128 nvlist_t *asru = NULL; 129 char *label = NULL; 130 nvlist_t *fmri = NULL; 131 int err; 132 133 /* pull the label property down from our parent 'bay' node */ 134 if (topo_node_label(parent, &label, &err) != 0) { 135 topo_mod_dprintf(mod, "disk_set_props: " 136 "label error %s\n", topo_strerror(err)); 137 goto error; 138 } 139 if (topo_node_label_set(dtn, label, &err) != 0) { 140 topo_mod_dprintf(mod, "disk_set_props: " 141 "label_set error %s\n", topo_strerror(err)); 142 goto error; 143 } 144 145 /* get the resource fmri, and use it as the fru */ 146 if (topo_node_resource(dtn, &fmri, &err) != 0) { 147 topo_mod_dprintf(mod, "disk_set_props: " 148 "resource error: %s\n", topo_strerror(err)); 149 goto error; 150 } 151 if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) { 152 topo_mod_dprintf(mod, "disk_set_props: " 153 "fru_set error: %s\n", topo_strerror(err)); 154 goto error; 155 } 156 157 /* create/set the authority group */ 158 if ((topo_pgroup_create(dtn, &disk_auth_pgroup, &err) != 0) && 159 (err != ETOPO_PROP_DEFD)) { 160 topo_mod_dprintf(mod, "disk_set_props: " 161 "create disk_auth error %s\n", topo_strerror(err)); 162 goto error; 163 } 164 165 /* create the storage group */ 166 if (topo_pgroup_create(dtn, &storage_pgroup, &err) != 0) { 167 topo_mod_dprintf(mod, "disk_set_props: " 168 "create storage error %s\n", topo_strerror(err)); 169 goto error; 170 } 171 172 /* no dnode was found for this disk - skip the io and storage groups */ 173 if (dnode == NULL) { 174 err = 0; 175 goto out; 176 } 177 178 /* form and set the asru */ 179 if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION, 180 dnode->ddn_dpath, dnode->ddn_devid)) == NULL) { 181 err = ETOPO_FMRI_UNKNOWN; 182 topo_mod_dprintf(mod, "disk_set_props: " 183 "asru error %s\n", topo_strerror(err)); 184 goto error; 185 } 186 if (topo_node_asru_set(dtn, asru, 0, &err) != 0) { 187 topo_mod_dprintf(mod, "disk_set_props: " 188 "asru_set error %s\n", topo_strerror(err)); 189 goto error; 190 } 191 192 /* create/set the devfs-path and devid in the io group */ 193 if (topo_pgroup_create(dtn, &io_pgroup, &err) != 0) { 194 topo_mod_dprintf(mod, "disk_set_props: " 195 "create io error %s\n", topo_strerror(err)); 196 goto error; 197 } 198 199 if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH, 200 TOPO_PROP_IMMUTABLE, dnode->ddn_dpath, &err) != 0) { 201 topo_mod_dprintf(mod, "disk_set_props: " 202 "set dev error %s\n", topo_strerror(err)); 203 goto error; 204 } 205 206 if (dnode->ddn_devid && topo_prop_set_string(dtn, TOPO_PGROUP_IO, 207 TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, dnode->ddn_devid, &err) != 0) { 208 topo_mod_dprintf(mod, "disk_set_props: " 209 "set devid error %s\n", topo_strerror(err)); 210 goto error; 211 } 212 213 if (dnode->ddn_ppath_count != 0 && 214 topo_prop_set_string_array(dtn, TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH, 215 TOPO_PROP_IMMUTABLE, (const char **)dnode->ddn_ppath, 216 dnode->ddn_ppath_count, &err) != 0) { 217 topo_mod_dprintf(mod, "disk_set_props: " 218 "set phys-path error %s\n", topo_strerror(err)); 219 goto error; 220 } 221 222 /* set the storage group public /dev name */ 223 if (dnode->ddn_lpath != NULL && 224 topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE, 225 TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE, 226 dnode->ddn_lpath, &err) != 0) { 227 topo_mod_dprintf(mod, "disk_set_props: " 228 "set disk_name error %s\n", topo_strerror(err)); 229 goto error; 230 } 231 232 /* populate other misc storage group properties */ 233 if (dnode->ddn_mfg && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE, 234 TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE, 235 dnode->ddn_mfg, &err) != 0)) { 236 topo_mod_dprintf(mod, "disk_set_props: " 237 "set mfg error %s\n", topo_strerror(err)); 238 goto error; 239 } 240 if (dnode->ddn_model && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE, 241 TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE, 242 dnode->ddn_model, &err) != 0)) { 243 topo_mod_dprintf(mod, "disk_set_props: " 244 "set model error %s\n", topo_strerror(err)); 245 goto error; 246 } 247 if (dnode->ddn_serial && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE, 248 TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE, 249 dnode->ddn_serial, &err) != 0)) { 250 topo_mod_dprintf(mod, "disk_set_props: " 251 "set serial error %s\n", topo_strerror(err)); 252 goto error; 253 } 254 if (dnode->ddn_firm && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE, 255 TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE, 256 dnode->ddn_firm, &err) != 0)) { 257 topo_mod_dprintf(mod, "disk_set_props: " 258 "set firm error %s\n", topo_strerror(err)); 259 goto error; 260 } 261 if (dnode->ddn_cap && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE, 262 TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE, 263 dnode->ddn_cap, &err) != 0)) { 264 topo_mod_dprintf(mod, "disk_set_props: " 265 "set cap error %s\n", topo_strerror(err)); 266 goto error; 267 } 268 err = 0; 269 270 out: 271 nvlist_free(fmri); 272 if (label) 273 topo_mod_strfree(mod, label); 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 * Try to find a disk based on the bridge-port property. This is most often used 501 * for SATA devices which are attached to a SAS controller and are therefore 502 * behind a SATL bridge port. SES only knows of devices based on this SAS WWN, 503 * not based on any SATA GUIDs. 504 */ 505 int 506 disk_declare_bridge(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp, 507 const char *addr, tnode_t **childp) 508 { 509 dev_di_node_t *dnode; 510 int i; 511 512 /* Check for match using addr. */ 513 for (dnode = topo_list_next(listp); dnode != NULL; 514 dnode = topo_list_next(dnode)) { 515 if (dnode->ddn_bridge_port == NULL) 516 continue; 517 518 for (i = 0; i < dnode->ddn_ppath_count; i++) { 519 if ((dnode->ddn_bridge_port[i] != NULL) && 520 (strncmp(dnode->ddn_bridge_port[i], addr, 521 strcspn(dnode->ddn_bridge_port[i], ":"))) == 0) { 522 topo_mod_dprintf(mod, "disk_declare_bridge: " 523 "found disk matching bridge %s", addr); 524 return (disk_declare(mod, parent, dnode, 525 childp)); 526 } 527 } 528 } 529 530 topo_mod_dprintf(mod, "disk_declare_bridge: " 531 "failed to find disk matching bridge %s", addr); 532 533 return (1); 534 } 535 536 /* 537 * Used to declare a disk that has been discovered through other means (usually 538 * ses), that is not enumerated in the devinfo tree. 539 */ 540 int 541 disk_declare_non_enumerated(topo_mod_t *mod, tnode_t *parent, tnode_t **childp) 542 { 543 return (disk_declare(mod, parent, NULL, childp)); 544 } 545 546 /* di_devlink callback for dev_di_node_add */ 547 static int 548 disk_devlink_callback(di_devlink_t dl, void *arg) 549 { 550 disk_cbdata_t *cbp = (disk_cbdata_t *)arg; 551 topo_mod_t *mod = cbp->dcb_mod; 552 dev_di_node_t *dnode = cbp->dcb_dnode; 553 const char *devpath; 554 char *ctds, *slice; 555 556 devpath = di_devlink_path(dl); 557 if ((dnode == NULL) || (devpath == NULL)) 558 return (DI_WALK_TERMINATE); 559 560 /* trim the slice off the public name */ 561 if (((ctds = strrchr(devpath, '/')) != NULL) && 562 ((slice = strchr(ctds, 's')) != NULL)) 563 *slice = '\0'; 564 565 /* Establish the public /dev name (no slice) */ 566 dnode->ddn_lpath = topo_mod_strdup(mod, ctds ? ctds + 1 : devpath); 567 568 if (ctds && slice) 569 *slice = 's'; 570 return (DI_WALK_TERMINATE); 571 } 572 573 static void 574 dev_di_node_free(topo_mod_t *mod, dev_di_node_t *dnode) 575 { 576 int i; 577 578 /* free the stuff we point to */ 579 if (dnode->ddn_devid) 580 topo_mod_strfree(mod, dnode->ddn_devid); 581 for (i = 0; i < dnode->ddn_ppath_count; i++) { 582 /* topo_mod_strfree does NULL checking. */ 583 topo_mod_strfree(mod, dnode->ddn_ppath[i]); 584 topo_mod_strfree(mod, dnode->ddn_target_port[i]); 585 topo_mod_strfree(mod, dnode->ddn_attached_port[i]); 586 topo_mod_strfree(mod, dnode->ddn_bridge_port[i]); 587 } 588 topo_mod_free(mod, dnode->ddn_ppath, 589 dnode->ddn_ppath_count * sizeof (char *)); 590 topo_mod_free(mod, dnode->ddn_target_port, 591 dnode->ddn_ppath_count * sizeof (char *)); 592 topo_mod_free(mod, dnode->ddn_attached_port, 593 dnode->ddn_ppath_count * sizeof (char *)); 594 topo_mod_free(mod, dnode->ddn_bridge_port, 595 dnode->ddn_ppath_count * sizeof (char *)); 596 topo_mod_strfree(mod, dnode->ddn_dpath); 597 topo_mod_strfree(mod, dnode->ddn_lpath); 598 599 topo_mod_strfree(mod, dnode->ddn_mfg); 600 topo_mod_strfree(mod, dnode->ddn_model); 601 topo_mod_strfree(mod, dnode->ddn_serial); 602 topo_mod_strfree(mod, dnode->ddn_firm); 603 topo_mod_strfree(mod, dnode->ddn_cap); 604 605 /* free self */ 606 topo_mod_free(mod, dnode, sizeof (dev_di_node_t)); 607 } 608 609 static int 610 dev_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp) 611 { 612 topo_mod_t *mod = cbp->dcb_mod; 613 dev_di_node_t *dnode; 614 di_path_t pnode; 615 char *path; 616 int mlen; 617 char *minorpath; 618 char *extn = ":a"; 619 char *s; 620 int64_t *nblocksp; 621 uint64_t nblocks; 622 int *dblksizep; 623 uint_t dblksize; 624 char lentry[MAXPATHLEN]; 625 int pathcount; 626 int *inq_dtype, itype; 627 int i; 628 629 if (devid) { 630 /* 631 * Check for list duplicate using devid search. 632 * Note if there is no devid, then we can end up with duplicates 633 * in the list, but this doesn't do any harm. 634 */ 635 for (dnode = topo_list_next(cbp->dcb_list); 636 dnode != NULL; dnode = topo_list_next(dnode)) { 637 if (dnode->ddn_devid && 638 devid_str_compare(dnode->ddn_devid, devid) == 0) { 639 topo_mod_dprintf(mod, "dev_di_node_add: " 640 "already there %s\n", devid); 641 return (0); 642 } 643 } 644 } 645 646 if ((dnode = topo_mod_zalloc(mod, sizeof (dev_di_node_t))) == NULL) 647 return (-1); 648 649 if (devid) { 650 /* Establish the devid. */ 651 dnode->ddn_devid = topo_mod_strdup(mod, devid); 652 if (dnode->ddn_devid == NULL) 653 goto error; 654 } 655 656 /* Establish the devinfo dpath */ 657 if ((path = di_devfs_path(node)) == NULL) { 658 (void) topo_mod_seterrno(mod, errno); 659 goto error; 660 } 661 662 dnode->ddn_dpath = topo_mod_strdup(mod, path); 663 di_devfs_path_free(path); 664 if (dnode->ddn_dpath == NULL) 665 goto error; 666 667 /* 668 * Establish the physical ppath and target ports. If the device is 669 * non-mpxio then dpath and ppath are the same, and the target port is a 670 * property of the device node. 671 * 672 * If dpath is a client node under scsi_vhci, then iterate over all 673 * paths and get their physical paths and target port properrties. 674 * di_path_client_next_path call below will 675 * return non-NULL, and ppath is set to the physical path to the first 676 * pathinfo node. 677 * 678 * NOTE: It is possible to get a generic.vs.non-generic path 679 * for di_devfs_path.vs.di_path_devfs_path like: 680 * xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0 681 * pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0 682 * To resolve this issue disk_declare_path() needs to use the 683 * special di_devfs_path_match() interface. 684 */ 685 pathcount = 0; 686 pnode = NULL; 687 while ((pnode = di_path_client_next_path(node, pnode)) != NULL) { 688 pathcount++; 689 } 690 691 if (pathcount == 0) { 692 if ((dnode->ddn_ppath = 693 topo_mod_zalloc(mod, sizeof (char *))) == NULL) 694 goto error; 695 696 dnode->ddn_ppath_count = 1; 697 if ((dnode->ddn_ppath[0] = topo_mod_strdup(mod, 698 dnode->ddn_dpath)) == NULL) 699 goto error; 700 701 if ((dnode->ddn_target_port = topo_mod_zalloc(mod, 702 sizeof (char *))) == NULL) 703 goto error; 704 705 if ((dnode->ddn_attached_port = topo_mod_zalloc(mod, 706 sizeof (char *))) == NULL) 707 goto error; 708 709 if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod, 710 sizeof (char *))) == NULL) 711 goto error; 712 713 /* There should be only one target port for a devinfo node. */ 714 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node, 715 SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) { 716 if ((dnode->ddn_target_port[0] = 717 topo_mod_strdup(mod, 718 scsi_wwnstr_skip_ua_prefix(s))) == 719 NULL) 720 goto error; 721 } 722 723 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node, 724 SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) { 725 /* There should be one attached port if any. */ 726 if ((dnode->ddn_attached_port[0] = 727 topo_mod_strdup(mod, 728 scsi_wwnstr_skip_ua_prefix(s))) == 729 NULL) 730 goto error; 731 } 732 733 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node, 734 SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) { 735 /* There should be one bridge port if any. */ 736 if ((dnode->ddn_bridge_port[0] = 737 topo_mod_strdup(mod, 738 scsi_wwnstr_skip_ua_prefix(s))) == 739 NULL) 740 goto error; 741 } 742 743 } else { 744 /* processing a scsi_vhci device. */ 745 if ((dnode->ddn_ppath = topo_mod_zalloc(mod, 746 pathcount * sizeof (char *))) == NULL) 747 goto error; 748 749 dnode->ddn_ppath_count = pathcount; 750 751 if ((dnode->ddn_target_port = topo_mod_zalloc(mod, 752 pathcount * sizeof (char *))) == NULL) 753 goto error; 754 755 if ((dnode->ddn_attached_port = topo_mod_zalloc(mod, 756 pathcount * sizeof (char *))) == NULL) 757 goto error; 758 759 if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod, 760 pathcount * sizeof (char *))) == NULL) 761 goto error; 762 763 pnode = NULL; 764 pathcount = 0; 765 while ((pnode = di_path_client_next_path(node, 766 pnode)) != NULL) { 767 if ((path = di_path_devfs_path(pnode)) == NULL) { 768 (void) topo_mod_seterrno(mod, errno); 769 goto error; 770 } 771 772 dnode->ddn_ppath[pathcount] = 773 topo_mod_strdup(mod, path); 774 di_devfs_path_free(path); 775 if (dnode->ddn_ppath[pathcount] == NULL) 776 goto error; 777 778 if ((di_path_prop_lookup_strings(pnode, 779 SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) { 780 if ((dnode->ddn_target_port[pathcount] = 781 topo_mod_strdup(mod, 782 scsi_wwnstr_skip_ua_prefix(s))) == 783 NULL) 784 goto error; 785 } 786 787 if ((di_path_prop_lookup_strings(pnode, 788 SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) { 789 if ((dnode->ddn_attached_port[pathcount] = 790 topo_mod_strdup(mod, 791 scsi_wwnstr_skip_ua_prefix(s))) == 792 NULL) 793 goto error; 794 } 795 796 if ((di_path_prop_lookup_strings(pnode, 797 SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) { 798 if ((dnode->ddn_bridge_port[pathcount] = 799 topo_mod_strdup(mod, 800 scsi_wwnstr_skip_ua_prefix(s))) == 801 NULL) 802 goto error; 803 } 804 805 pathcount++; 806 } 807 } 808 809 /* 810 * Find the public /dev name for a disk by adding a minor name and using 811 * di_devlink interface for reverse translation (use devinfo path). 812 */ 813 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type", 814 &inq_dtype) > 0) { 815 dnode->ddn_dtype = *inq_dtype; 816 itype = (*inq_dtype) & DTYPE_MASK; 817 if (itype == DTYPE_DIRECT) { 818 mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1; 819 if ((minorpath = topo_mod_alloc(mod, mlen)) == NULL) 820 goto error; 821 (void) snprintf(minorpath, mlen, "%s%s", 822 dnode->ddn_dpath, extn); 823 cbp->dcb_dnode = dnode; 824 (void) di_devlink_walk(cbp->dcb_devhdl, "^dsk/", 825 minorpath, DI_PRIMARY_LINK, cbp, 826 disk_devlink_callback); 827 topo_mod_free(mod, minorpath, mlen); 828 if (dnode->ddn_lpath == NULL) { 829 topo_mod_dprintf(mod, "dev_di_node_add: " 830 "failed to determine logical path"); 831 } 832 } 833 } else { 834 dnode->ddn_dtype = DTYPE_UNKNOWN; 835 } 836 837 /* cache various bits of optional information about the device. */ 838 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 839 INQUIRY_VENDOR_ID, &s) > 0) { 840 if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL) 841 goto error; 842 } 843 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 844 INQUIRY_PRODUCT_ID, &s) > 0) { 845 if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL) 846 goto error; 847 } 848 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 849 INQUIRY_REVISION_ID, &s) > 0) { 850 if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL) 851 goto error; 852 } 853 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 854 INQUIRY_SERIAL_NO, &s) > 0) { 855 if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL) 856 goto error; 857 } 858 if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, 859 "device-nblocks", &nblocksp) > 0) { 860 nblocks = (uint64_t)*nblocksp; 861 /* 862 * To save kernel memory, the driver may not define 863 * "device-dblksize" when its value is default DEV_BSIZE. 864 */ 865 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 866 "device-dblksize", &dblksizep) > 0) 867 dblksize = (uint_t)*dblksizep; 868 else 869 dblksize = DEV_BSIZE; /* default value */ 870 (void) snprintf(lentry, sizeof (lentry), 871 "%" PRIu64, nblocks * dblksize); 872 if ((dnode->ddn_cap = topo_mod_strdup(mod, lentry)) == NULL) 873 goto error; 874 } 875 876 topo_mod_dprintf(mod, "dev_di_node_add: " 877 "adding %s\n", devid ? dnode->ddn_devid : "NULL devid"); 878 topo_mod_dprintf(mod, " " 879 " %s\n", dnode->ddn_dpath); 880 for (i = 0; i < dnode->ddn_ppath_count; i++) { 881 topo_mod_dprintf(mod, " " 882 " %s\n", dnode->ddn_ppath[i]); 883 } 884 topo_list_append(cbp->dcb_list, dnode); 885 return (0); 886 887 error: 888 dev_di_node_free(mod, dnode); 889 return (-1); 890 } 891 892 /* di_walk_node callback for disk_list_gather */ 893 static int 894 dev_walk_di_nodes(di_node_t node, void *arg) 895 { 896 char *devidstr = NULL; 897 char *s; 898 int *val; 899 900 /* 901 * If it's not a scsi_vhci client and doesn't have a target_port 902 * property and doesn't have a target property then it's not a storage 903 * device and we're not interested. 904 */ 905 if (di_path_client_next_path(node, NULL) == NULL && 906 di_prop_lookup_strings(DDI_DEV_T_ANY, node, 907 SCSI_ADDR_PROP_TARGET_PORT, &s) <= 0 && 908 di_prop_lookup_ints(DDI_DEV_T_ANY, node, 909 SCSI_ADDR_PROP_TARGET, &val) <= 0) { 910 return (DI_WALK_CONTINUE); 911 } 912 (void) di_prop_lookup_strings(DDI_DEV_T_ANY, node, 913 DEVID_PROP_NAME, &devidstr); 914 915 /* create/find the devid scsi topology node */ 916 (void) dev_di_node_add(node, devidstr, arg); 917 918 return (DI_WALK_CONTINUE); 919 } 920 921 int 922 dev_list_gather(topo_mod_t *mod, topo_list_t *listp) 923 { 924 di_node_t devtree; 925 di_devlink_handle_t devhdl; 926 disk_cbdata_t dcb; 927 928 if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) { 929 topo_mod_dprintf(mod, "disk_list_gather: " 930 "topo_mod_devinfo() failed"); 931 return (-1); 932 } 933 934 if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) { 935 topo_mod_dprintf(mod, "disk_list_gather: " 936 "di_devlink_init() failed"); 937 return (-1); 938 } 939 940 dcb.dcb_mod = mod; 941 dcb.dcb_list = listp; 942 dcb.dcb_devhdl = devhdl; 943 944 /* walk the devinfo snapshot looking for disk nodes */ 945 (void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb, 946 dev_walk_di_nodes); 947 948 (void) di_devlink_fini(&devhdl); 949 950 return (0); 951 } 952 953 void 954 dev_list_free(topo_mod_t *mod, topo_list_t *listp) 955 { 956 dev_di_node_t *dnode; 957 958 while ((dnode = topo_list_next(listp)) != NULL) { 959 /* order of delete/free is important */ 960 topo_list_delete(listp, dnode); 961 dev_di_node_free(mod, dnode); 962 } 963 } 964 965 /* 966 * Query the current disk status. If successful, the disk status is returned 967 * as an nvlist consisting of at least the following members: 968 * 969 * protocol string Supported protocol (currently "scsi") 970 * 971 * status nvlist Arbitrary protocol-specific information 972 * about the current state of the disk. 973 * 974 * faults nvlist A list of supported faults. Each 975 * element of this list is a boolean value. 976 * An element's existence indicates that 977 * the drive supports detecting this fault, 978 * and the value indicates the current 979 * state of the fault. 980 * 981 * <fault-name> nvlist For each fault named in 'faults', a 982 * nvlist describing protocol-specific 983 * attributes of the fault. 984 * 985 * This method relies on the libdiskstatus library to query this information. 986 */ 987 static int 988 disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers, 989 nvlist_t *in_nvl, nvlist_t **out_nvl) 990 { 991 disk_status_t *dsp; 992 char *devpath, *fullpath; 993 size_t pathlen; 994 nvlist_t *status; 995 int err; 996 997 *out_nvl = NULL; 998 999 if (vers != TOPO_METH_DISK_STATUS_VERSION) 1000 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 1001 1002 /* 1003 * If the caller specifies the "path" parameter, then this indicates 1004 * that we should use this instead of deriving it from the topo node 1005 * itself. 1006 */ 1007 if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) { 1008 devpath = NULL; 1009 } else { 1010 /* 1011 * Get the /devices path and attempt to open the disk status 1012 * handle. 1013 */ 1014 if (topo_prop_get_string(nodep, TOPO_PGROUP_IO, 1015 TOPO_IO_DEV_PATH, &devpath, &err) != 0) 1016 return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP)); 1017 1018 /* 1019 * Note that sizeof(string) includes the terminating NULL byte 1020 */ 1021 pathlen = strlen(devpath) + sizeof ("/devices") + 1022 sizeof (PHYS_EXTN) - 1; 1023 1024 if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL) 1025 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 1026 1027 (void) snprintf(fullpath, pathlen, "/devices%s%s", devpath, 1028 PHYS_EXTN); 1029 1030 topo_mod_strfree(mod, devpath); 1031 } 1032 1033 if ((dsp = disk_status_open(fullpath, &err)) == NULL) { 1034 if (devpath) 1035 topo_mod_free(mod, fullpath, pathlen); 1036 return (topo_mod_seterrno(mod, err == EDS_NOMEM ? 1037 EMOD_NOMEM : EMOD_METHOD_NOTSUP)); 1038 } 1039 1040 if (devpath) 1041 topo_mod_free(mod, fullpath, pathlen); 1042 1043 if ((status = disk_status_get(dsp)) == NULL) { 1044 err = (disk_status_errno(dsp) == EDS_NOMEM ? 1045 EMOD_NOMEM : EMOD_METHOD_NOTSUP); 1046 disk_status_close(dsp); 1047 return (topo_mod_seterrno(mod, err)); 1048 } 1049 1050 *out_nvl = status; 1051 disk_status_close(dsp); 1052 return (0); 1053 } 1054