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