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