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