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 }