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