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 #include <dirent.h> 28 #include <devid.h> 29 #include <fm/libdiskstatus.h> 30 #include <inttypes.h> 31 #include <pthread.h> 32 #include <strings.h> 33 #include <unistd.h> 34 #include <sys/dkio.h> 35 #include <sys/fm/protocol.h> 36 #include <sys/scsi/scsi_types.h> 37 38 #include "disk.h" 39 #include "ses.h" 40 41 #define SES_VERSION 1 42 43 #define SES_SNAP_FREQ 1000 /* in milliseconds */ 44 45 #define SES_STATUS_UNAVAIL(s) \ 46 ((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED) 47 48 /* 49 * Because multiple SES targets can be part of a single chassis, we construct 50 * our own hierarchy that takes this into account. These SES targets may refer 51 * to the same devices (multiple paths) or to different devices (managing 52 * different portions of the space). We arrange things into a 53 * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all 54 * nodes found so far. 55 */ 56 typedef struct ses_alt_node { 57 topo_list_t san_link; 58 ses_node_t *san_node; 59 } ses_alt_node_t; 60 61 typedef struct ses_enum_node { 62 topo_list_t sen_link; 63 ses_node_t *sen_node; 64 topo_list_t sen_alt_nodes; 65 uint64_t sen_type; 66 uint64_t sen_instance; 67 ses_enum_target_t *sen_target; 68 } ses_enum_node_t; 69 70 typedef struct ses_enum_chassis { 71 topo_list_t sec_link; 72 topo_list_t sec_nodes; 73 topo_list_t sec_targets; 74 const char *sec_csn; 75 ses_node_t *sec_enclosure; 76 ses_enum_target_t *sec_target; 77 topo_instance_t sec_instance; 78 boolean_t sec_hasdev; 79 boolean_t sec_internal; 80 } ses_enum_chassis_t; 81 82 typedef struct ses_enum_data { 83 topo_list_t sed_disks; 84 topo_list_t sed_chassis; 85 ses_enum_chassis_t *sed_current; 86 ses_enum_target_t *sed_target; 87 int sed_errno; 88 char *sed_name; 89 topo_mod_t *sed_mod; 90 topo_instance_t sed_instance; 91 } ses_enum_data_t; 92 93 static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 94 nvlist_t **); 95 static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 96 nvlist_t **); 97 98 static const topo_method_t ses_component_methods[] = { 99 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 100 TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present }, 101 { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 102 TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 103 { NULL } 104 }; 105 106 static const topo_method_t ses_bay_methods[] = { 107 { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 108 TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 109 { NULL } 110 }; 111 112 static const topo_method_t ses_enclosure_methods[] = { 113 { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC, 114 TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains }, 115 { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 116 TOPO_STABILITY_INTERNAL, ses_enc_enum_facility }, 117 { NULL } 118 }; 119 120 static void 121 ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp) 122 { 123 if (--stp->set_refcount == 0) { 124 ses_snap_rele(stp->set_snap); 125 ses_close(stp->set_target); 126 topo_mod_strfree(mod, stp->set_devpath); 127 topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 128 } 129 } 130 131 static void 132 ses_data_free(ses_enum_data_t *sdp) 133 { 134 topo_mod_t *mod = sdp->sed_mod; 135 ses_enum_chassis_t *cp; 136 ses_enum_node_t *np; 137 ses_enum_target_t *tp; 138 ses_alt_node_t *ap; 139 140 while ((cp = topo_list_next(&sdp->sed_chassis)) != NULL) { 141 topo_list_delete(&sdp->sed_chassis, cp); 142 143 while ((np = topo_list_next(&cp->sec_nodes)) != NULL) { 144 while ((ap = topo_list_next(&np->sen_alt_nodes)) != 145 NULL) { 146 topo_list_delete(&np->sen_alt_nodes, ap); 147 topo_mod_free(mod, ap, sizeof (ses_alt_node_t)); 148 } 149 topo_list_delete(&cp->sec_nodes, np); 150 topo_mod_free(mod, np, sizeof (ses_enum_node_t)); 151 } 152 153 while ((tp = topo_list_next(&cp->sec_targets)) != NULL) { 154 topo_list_delete(&cp->sec_targets, tp); 155 ses_target_free(mod, tp); 156 } 157 158 topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t)); 159 } 160 161 disk_list_free(mod, &sdp->sed_disks); 162 topo_mod_free(mod, sdp, sizeof (ses_enum_data_t)); 163 } 164 165 /* 166 * For enclosure nodes, we have a special contains method. By default, the hc 167 * walker will compare the node name and instance number to determine if an 168 * FMRI matches. For enclosures where the enumeration order is impossible to 169 * predict, we instead use the chassis-id as a unique identifier, and ignore 170 * the instance number. 171 */ 172 static int 173 fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2) 174 { 175 uint8_t v1, v2; 176 nvlist_t **hcp1, **hcp2; 177 int err, i; 178 uint_t nhcp1, nhcp2; 179 nvlist_t *a1, *a2; 180 char *c1, *c2; 181 int mindepth; 182 183 if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 || 184 nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 || 185 v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION) 186 return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION)); 187 188 err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1); 189 err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2); 190 if (err != 0) 191 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 192 193 /* 194 * If the chassis-id doesn't match, then these FMRIs are not 195 * equivalent. If one of the FMRIs doesn't have a chassis ID, then we 196 * have no choice but to fall back to the instance ID. 197 */ 198 if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 && 199 nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 && 200 nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 && 201 nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) { 202 if (strcmp(c1, c2) != 0) 203 return (0); 204 205 mindepth = 1; 206 } else { 207 mindepth = 0; 208 } 209 210 if (nhcp2 < nhcp1) 211 return (0); 212 213 for (i = 0; i < nhcp1; i++) { 214 char *nm1 = NULL; 215 char *nm2 = NULL; 216 char *id1 = NULL; 217 char *id2 = NULL; 218 219 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1); 220 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2); 221 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1); 222 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2); 223 if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL) 224 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 225 226 if (strcmp(nm1, nm2) == 0 && 227 (i < mindepth || strcmp(id1, id2) == 0)) 228 continue; 229 230 return (0); 231 } 232 233 return (1); 234 } 235 236 /*ARGSUSED*/ 237 static int 238 ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 239 nvlist_t *in, nvlist_t **out) 240 { 241 int ret; 242 nvlist_t *nv1, *nv2; 243 244 if (version > TOPO_METH_CONTAINS_VERSION) 245 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 246 247 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 || 248 nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0) 249 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 250 251 ret = fmri_contains(mod, nv1, nv2); 252 if (ret < 0) 253 return (-1); 254 255 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) { 256 if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET, 257 ret) == 0) 258 return (0); 259 else 260 nvlist_free(*out); 261 } 262 263 return (-1); 264 265 } 266 267 /* 268 * Return a current instance of the node. This is somewhat complicated because 269 * we need to take a new snapshot in order to get the new data, but we don't 270 * want to be constantly taking SES snapshots if the consumer is going to do a 271 * series of queries. So we adopt the strategy of assuming that the SES state 272 * is not going to be rapidly changing, and limit our snapshot frequency to 273 * some defined bounds. 274 */ 275 ses_node_t * 276 ses_node_lock(topo_mod_t *mod, tnode_t *tn) 277 { 278 struct timeval tv; 279 ses_enum_target_t *tp = topo_node_getspecific(tn); 280 uint64_t prev, now; 281 ses_snap_t *snap; 282 int err; 283 uint64_t nodeid; 284 ses_node_t *np; 285 286 if (tp == NULL) { 287 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 288 return (NULL); 289 } 290 291 (void) pthread_mutex_lock(&tp->set_lock); 292 293 /* 294 * Determine if we need to take a new snapshot. 295 */ 296 if (gettimeofday(&tv, NULL) != 0) { 297 tv.tv_sec = time(NULL); 298 tv.tv_usec = 0; 299 } 300 301 now = tv.tv_sec * 1000 + tv.tv_usec / 1000; 302 prev = tp->set_snaptime.tv_sec * 1000 + 303 tp->set_snaptime.tv_usec / 1000; 304 305 if (now - prev > SES_SNAP_FREQ && 306 (snap = ses_snap_new(tp->set_target)) != NULL) { 307 if (ses_snap_generation(snap) != 308 ses_snap_generation(tp->set_snap)) { 309 /* 310 * If we find ourselves in this situation, we're in 311 * trouble. The generation count has changed, which 312 * indicates that our current topology is out of date. 313 * But we need to consult the new topology in order to 314 * determine presence at this moment in time. We can't 315 * go back and change the topo snapshot in situ, so 316 * we'll just have to fail the call in this unlikely 317 * scenario. 318 */ 319 ses_snap_rele(snap); 320 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 321 (void) pthread_mutex_unlock(&tp->set_lock); 322 return (NULL); 323 } else { 324 ses_snap_rele(tp->set_snap); 325 tp->set_snap = snap; 326 } 327 tp->set_snaptime = tv; 328 } 329 330 snap = tp->set_snap; 331 332 verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES, 333 TOPO_PROP_NODE_ID, &nodeid, &err) == 0); 334 verify((np = ses_node_lookup(snap, nodeid)) != NULL); 335 336 return (np); 337 } 338 339 /*ARGSUSED*/ 340 void 341 ses_node_unlock(topo_mod_t *mod, tnode_t *tn) 342 { 343 ses_enum_target_t *tp = topo_node_getspecific(tn); 344 345 verify(tp != NULL); 346 347 (void) pthread_mutex_unlock(&tp->set_lock); 348 } 349 350 /* 351 * Determine if the element is present. 352 */ 353 /*ARGSUSED*/ 354 static int 355 ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 356 nvlist_t *in, nvlist_t **out) 357 { 358 boolean_t present; 359 ses_node_t *np; 360 nvlist_t *props, *nvl; 361 uint64_t status; 362 363 if ((np = ses_node_lock(mod, tn)) == NULL) 364 return (-1); 365 366 verify((props = ses_node_props(np)) != NULL); 367 verify(nvlist_lookup_uint64(props, 368 SES_PROP_STATUS_CODE, &status) == 0); 369 370 ses_node_unlock(mod, tn); 371 372 present = (status != SES_ESC_NOT_INSTALLED); 373 374 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 375 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 376 377 if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, 378 present) != 0) { 379 nvlist_free(nvl); 380 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 381 } 382 383 *out = nvl; 384 385 return (0); 386 } 387 388 /* 389 * Sets standard properties for a ses node (enclosure or bay). This includes 390 * setting the FRU to be the same as the resource, as well as setting the 391 * authority information. 392 */ 393 static int 394 ses_set_standard_props(topo_mod_t *mod, tnode_t *tn, nvlist_t *auth, 395 uint64_t nodeid, const char *path) 396 { 397 int err; 398 char *product, *chassis; 399 nvlist_t *fmri; 400 topo_pgroup_info_t pgi; 401 402 /* 403 * Set the authority explicitly if specified. 404 */ 405 if (auth) { 406 verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 407 &product) == 0); 408 verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, 409 &chassis) == 0); 410 if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 411 FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product, 412 &err) != 0 || 413 topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 414 FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis, 415 &err) != 0 || 416 topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 417 FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "", 418 &err) != 0) { 419 topo_mod_dprintf(mod, "failed to add authority " 420 "properties: %s\n", topo_strerror(err)); 421 return (topo_mod_seterrno(mod, err)); 422 } 423 } 424 425 /* 426 * Copy the resource and set that as the FRU. 427 */ 428 if (topo_node_resource(tn, &fmri, &err) != 0) { 429 topo_mod_dprintf(mod, 430 "topo_node_resource() failed : %s\n", 431 topo_strerror(err)); 432 return (topo_mod_seterrno(mod, err)); 433 } 434 435 if (topo_node_fru_set(tn, fmri, 0, &err) != 0) { 436 topo_mod_dprintf(mod, 437 "topo_node_fru_set() failed : %s\n", 438 topo_strerror(err)); 439 nvlist_free(fmri); 440 return (topo_mod_seterrno(mod, err)); 441 } 442 443 nvlist_free(fmri); 444 445 /* 446 * Set the SES-specific properties so that consumers can query 447 * additional information about the particular SES element. 448 */ 449 pgi.tpi_name = TOPO_PGROUP_SES; 450 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 451 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 452 pgi.tpi_version = TOPO_VERSION; 453 if (topo_pgroup_create(tn, &pgi, &err) != 0) { 454 topo_mod_dprintf(mod, "failed to create propgroup " 455 "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err)); 456 return (-1); 457 } 458 459 if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 460 TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 461 nodeid, &err) != 0) { 462 topo_mod_dprintf(mod, 463 "failed to create property %s: %s\n", 464 TOPO_PROP_NODE_ID, topo_strerror(err)); 465 return (-1); 466 } 467 468 if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 469 TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE, 470 path, &err) != 0) { 471 topo_mod_dprintf(mod, 472 "failed to create property %s: %s\n", 473 TOPO_PROP_TARGET_PATH, topo_strerror(err)); 474 return (-1); 475 } 476 477 return (0); 478 } 479 480 /* 481 * Callback to add a disk to a given bay. We first check the status-code to 482 * determine if a disk is present, ignoring those that aren't in an appropriate 483 * state. We then scan the parent bay node's SAS address array to determine 484 * possible attached SAS addresses. We create a disk node if the disk is not 485 * SAS or the SES target does not support the necessary pages for this; if we 486 * find the SAS address, we create a disk node and also correlate it with 487 * the corresponding Solaris device node to fill in the rest of the data. 488 */ 489 static int 490 ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props) 491 { 492 topo_mod_t *mod = sdp->sed_mod; 493 uint64_t status; 494 nvlist_t **sas; 495 uint_t s, nsas; 496 char **paths; 497 int err; 498 499 /* 500 * Skip devices that are not in a present (and possibly damaged) state. 501 */ 502 if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 503 return (0); 504 505 if (status != SES_ESC_OK && 506 status != SES_ESC_CRITICAL && 507 status != SES_ESC_NONCRITICAL && 508 status != SES_ESC_UNRECOVERABLE && 509 status != SES_ESC_NO_ACCESS) 510 return (0); 511 512 topo_mod_dprintf(mod, "found attached disk"); 513 514 /* 515 * Create the disk range. 516 */ 517 if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) { 518 topo_mod_dprintf(mod, 519 "topo_node_create_range() failed: %s", 520 topo_mod_errmsg(mod)); 521 return (-1); 522 } 523 524 /* 525 * Look through all SAS addresses and attempt to correlate them to a 526 * known Solaris device. If we don't find a matching node, then we 527 * don't enumerate the disk node. 528 */ 529 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 530 &sas, &nsas) != 0) 531 return (0); 532 533 if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES, 534 TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0) 535 return (0); 536 537 err = 0; 538 539 for (s = 0; s < nsas; s++) { 540 if (disk_declare_addr(mod, pnode, 541 &sdp->sed_disks, paths[s]) != 0 && 542 topo_mod_errno(mod) != EMOD_NODE_BOUND) { 543 err = -1; 544 break; 545 } 546 } 547 548 for (s = 0; s < nsas; s++) 549 topo_mod_free(mod, paths[s], strlen(paths[s]) + 1); 550 topo_mod_free(mod, paths, nsas * sizeof (char *)); 551 552 return (err); 553 } 554 555 static int 556 ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp) 557 { 558 ses_alt_node_t *ap; 559 ses_node_t *np; 560 nvlist_t *props; 561 562 nvlist_t **phys; 563 uint_t i, j, n_phys, all_phys = 0; 564 char **paths; 565 uint64_t addr; 566 size_t len; 567 int terr, err = -1; 568 569 for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 570 ap = topo_list_next(ap)) { 571 np = ap->san_node; 572 props = ses_node_props(np); 573 574 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 575 &phys, &n_phys) != 0) 576 continue; 577 578 all_phys += n_phys; 579 } 580 581 if (all_phys == 0) 582 return (0); 583 584 if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL) 585 return (-1); 586 587 for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 588 ap = topo_list_next(ap)) { 589 np = ap->san_node; 590 props = ses_node_props(np); 591 592 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 593 &phys, &n_phys) != 0) 594 continue; 595 596 for (j = 0; j < n_phys; j++) { 597 if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR, 598 &addr) != 0) 599 continue; 600 601 len = snprintf(NULL, 0, "%016llx", addr) + 1; 602 if ((paths[i] = topo_mod_alloc(mod, len)) == NULL) 603 goto error; 604 605 (void) snprintf(paths[i], len, "%016llx", addr); 606 607 ++i; 608 } 609 } 610 611 err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 612 TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, 613 (const char **)paths, i, &terr); 614 if (err != 0) 615 err = topo_mod_seterrno(mod, terr); 616 617 error: 618 for (i = 0; i < all_phys && paths[i] != NULL; i++) 619 topo_mod_free(mod, paths[i], strlen(paths[i]) + 1); 620 topo_mod_free(mod, paths, all_phys * sizeof (char *)); 621 622 return (err); 623 } 624 625 /* 626 * Callback to create a basic node (bay, psu, fan, or controller). 627 */ 628 static int 629 ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, 630 tnode_t *pnode, const char *nodename, const char *labelname) 631 { 632 ses_node_t *np = snp->sen_node; 633 ses_node_t *parent; 634 uint64_t instance = snp->sen_instance; 635 topo_mod_t *mod = sdp->sed_mod; 636 nvlist_t *props, *aprops; 637 nvlist_t *auth = NULL, *fmri = NULL; 638 tnode_t *tn; 639 char label[128]; 640 int err; 641 char *part = NULL, *serial = NULL, *revision = NULL; 642 char *desc; 643 boolean_t report; 644 645 props = ses_node_props(np); 646 647 (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part); 648 (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial); 649 650 topo_mod_dprintf(mod, "adding %s %llu", nodename, instance); 651 652 /* 653 * Create the node. The interesting information is all copied from the 654 * parent enclosure node, so there is not much to do. 655 */ 656 if ((auth = topo_mod_auth(mod, pnode)) == NULL) 657 goto error; 658 659 /* 660 * We want to report revision information for the controller nodes, but 661 * we do not get per-element revision information. However, we do have 662 * revision information for the entire enclosure, and we can use the 663 * 'reported-via' property to know that this controller corresponds to 664 * the given revision information. This means we cannot get revision 665 * information for targets we are not explicitly connected to, but 666 * there is little we can do about the situation. 667 */ 668 if (strcmp(nodename, CONTROLLER) == 0 && 669 nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 && 670 report) { 671 for (parent = ses_node_parent(np); parent != NULL; 672 parent = ses_node_parent(parent)) { 673 if (ses_node_type(parent) == SES_NODE_ENCLOSURE) { 674 (void) nvlist_lookup_string( 675 ses_node_props(parent), 676 SES_EN_PROP_REV, &revision); 677 break; 678 } 679 } 680 } 681 682 if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 683 nodename, (topo_instance_t)instance, NULL, auth, part, revision, 684 serial)) == NULL) { 685 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 686 topo_mod_errmsg(mod)); 687 goto error; 688 } 689 690 if ((tn = topo_node_bind(mod, pnode, nodename, 691 instance, fmri)) == NULL) { 692 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 693 topo_mod_errmsg(mod)); 694 goto error; 695 } 696 697 /* 698 * For the node label, we look for the following in order: 699 * 700 * <ses-description> 701 * <ses-class-description> <instance> 702 * <default-type-label> <instance> 703 */ 704 if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 705 desc[0] == '\0') { 706 parent = ses_node_parent(np); 707 aprops = ses_node_props(parent); 708 if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION, 709 &desc) == 0 && desc[0] != '\0') 710 labelname = desc; 711 (void) snprintf(label, sizeof (label), "%s %llu", desc, 712 instance); 713 desc = label; 714 } 715 716 if (topo_node_label_set(tn, desc, &err) != 0) 717 goto error; 718 719 if (ses_set_standard_props(mod, tn, NULL, ses_node_id(np), 720 snp->sen_target->set_devpath) != 0) 721 goto error; 722 723 if (strcmp(nodename, "bay") == 0) { 724 if (ses_add_bay_props(mod, tn, snp) != 0) 725 goto error; 726 727 if (ses_create_disk(sdp, tn, props) != 0) 728 goto error; 729 730 if (topo_method_register(mod, tn, ses_bay_methods) != 0) { 731 topo_mod_dprintf(mod, 732 "topo_method_register() failed: %s", 733 topo_mod_errmsg(mod)); 734 goto error; 735 } 736 } else { 737 /* 738 * Only fan, psu, and controller nodes have a 'present' method. 739 * Bay nodes are always present, and disk nodes are present by 740 * virtue of being enumerated. 741 */ 742 if (topo_method_register(mod, tn, ses_component_methods) != 0) { 743 topo_mod_dprintf(mod, 744 "topo_method_register() failed: %s", 745 topo_mod_errmsg(mod)); 746 goto error; 747 } 748 749 } 750 751 snp->sen_target->set_refcount++; 752 topo_node_setspecific(tn, snp->sen_target); 753 754 nvlist_free(auth); 755 nvlist_free(fmri); 756 return (0); 757 758 error: 759 nvlist_free(auth); 760 nvlist_free(fmri); 761 return (-1); 762 } 763 764 /* 765 * Instantiate any children of a given type. 766 */ 767 static int 768 ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type, 769 const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp, 770 boolean_t dorange) 771 { 772 topo_mod_t *mod = sdp->sed_mod; 773 boolean_t found; 774 uint64_t max; 775 ses_enum_node_t *snp; 776 777 /* 778 * First go through and count how many matching nodes we have. 779 */ 780 max = 0; 781 found = B_FALSE; 782 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 783 snp = topo_list_next(snp)) { 784 if (snp->sen_type == type) { 785 found = B_TRUE; 786 if (snp->sen_instance > max) 787 max = snp->sen_instance; 788 } 789 } 790 791 /* 792 * No enclosure should export both DEVICE and ARRAY_DEVICE elements. 793 * Since we map both of these to 'disk', if an enclosure does this, we 794 * just ignore the array elements. 795 */ 796 if (!found || 797 (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev)) 798 return (0); 799 800 topo_mod_dprintf(mod, "%s: creating %llu %s nodes", 801 cp->sec_csn, max, nodename); 802 803 if (dorange && topo_node_range_create(mod, pnode, 804 nodename, 0, max) != 0) { 805 topo_mod_dprintf(mod, 806 "topo_node_create_range() failed: %s", 807 topo_mod_errmsg(mod)); 808 return (-1); 809 } 810 811 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 812 snp = topo_list_next(snp)) { 813 if (snp->sen_type == type) { 814 if (ses_create_generic(sdp, snp, pnode, 815 nodename, defaultlabel) != 0) 816 return (-1); 817 } 818 } 819 820 return (0); 821 } 822 823 /* 824 * Instantiate a new chassis instance in the topology. 825 */ 826 static int 827 ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp) 828 { 829 topo_mod_t *mod = sdp->sed_mod; 830 nvlist_t *props; 831 char *raw_manufacturer, *raw_model, *raw_revision; 832 char *manufacturer = NULL, *model = NULL, *product = NULL; 833 char *revision = NULL; 834 char *serial; 835 size_t prodlen; 836 tnode_t *tn; 837 nvlist_t *fmri = NULL, *auth = NULL; 838 int ret = -1; 839 ses_enum_node_t *snp; 840 841 /* 842 * Ignore any internal enclosures. 843 */ 844 if (cp->sec_internal) 845 return (0); 846 847 /* 848 * Check to see if there are any devices presennt in the chassis. If 849 * not, ignore the chassis alltogether. This is most useful for 850 * ignoring internal HBAs that present a SES target but don't actually 851 * manage any of the devices. 852 */ 853 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 854 snp = topo_list_next(snp)) { 855 if (snp->sen_type == SES_ET_DEVICE || 856 snp->sen_type == SES_ET_ARRAY_DEVICE) 857 break; 858 } 859 860 if (snp == NULL) 861 return (0); 862 863 props = ses_node_props(cp->sec_enclosure); 864 865 /* 866 * We use the following property mappings: 867 * 868 * manufacturer vendor-id 869 * model product-id 870 * serial-number libses-chassis-serial 871 */ 872 verify(nvlist_lookup_string(props, SES_EN_PROP_VID, 873 &raw_manufacturer) == 0); 874 verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0); 875 verify(nvlist_lookup_string(props, SES_EN_PROP_REV, 876 &raw_revision) == 0); 877 verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0); 878 879 /* 880 * To construct the authority information, we 'clean' each string by 881 * removing any offensive characters and trimmming whitespace. For the 882 * 'product-id', we use a concatenation of 'manufacturer-model'. We 883 * also take the numerical serial number and convert it to a string. 884 */ 885 if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL || 886 (model = disk_auth_clean(mod, raw_model)) == NULL || 887 (revision = disk_auth_clean(mod, raw_revision)) == NULL) { 888 goto error; 889 } 890 891 prodlen = strlen(manufacturer) + strlen(model) + 2; 892 if ((product = topo_mod_alloc(mod, prodlen)) == NULL) 893 goto error; 894 895 (void) snprintf(product, prodlen, "%s-%s", manufacturer, model); 896 897 /* 898 * Construct the topo node and bind it to our parent. 899 */ 900 if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) 901 goto error; 902 903 if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 || 904 nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) { 905 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 906 goto error; 907 } 908 909 /* 910 * We pass NULL for the parent FMRI because there is no resource 911 * associated with it. For the toplevel enclosure, we leave the 912 * serial/part/revision portions empty, which are reserved for 913 * individual components within the chassis. 914 */ 915 if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 916 SES_ENCLOSURE, cp->sec_instance, NULL, auth, 917 model, revision, serial)) == NULL) { 918 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 919 topo_mod_errmsg(mod)); 920 goto error; 921 } 922 923 if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE, 924 cp->sec_instance, fmri)) == NULL) { 925 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 926 topo_mod_errmsg(mod)); 927 goto error; 928 } 929 930 if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 931 topo_mod_dprintf(mod, 932 "topo_method_register() failed: %s", 933 topo_mod_errmsg(mod)); 934 goto error; 935 } 936 937 if (ses_set_standard_props(mod, tn, auth, 938 ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0) 939 goto error; 940 941 /* 942 * Create the nodes for power supplies, fans, and devices. 943 */ 944 if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY, 945 PSU, "PSU", cp, B_TRUE) != 0 || 946 ses_create_children(sdp, tn, SES_ET_COOLING, 947 FAN, "FAN", cp, B_TRUE) != 0 || 948 ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 949 CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 || 950 ses_create_children(sdp, tn, SES_ET_DEVICE, 951 BAY, "BAY", cp, B_TRUE) != 0 || 952 ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 953 BAY, "BAY", cp, B_TRUE) != 0) 954 goto error; 955 956 snp->sen_target->set_refcount++; 957 topo_node_setspecific(tn, snp->sen_target); 958 959 ret = 0; 960 error: 961 topo_mod_strfree(mod, manufacturer); 962 topo_mod_strfree(mod, model); 963 topo_mod_strfree(mod, revision); 964 topo_mod_strfree(mod, product); 965 966 nvlist_free(fmri); 967 nvlist_free(auth); 968 return (ret); 969 } 970 971 /* 972 * Create a bay node explicitly enumerated via XML. 973 */ 974 static int 975 ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode) 976 { 977 topo_mod_t *mod = sdp->sed_mod; 978 ses_enum_chassis_t *cp; 979 980 /* 981 * Iterate over chassis looking for an internal enclosure. This 982 * property is set via a vendor-specific plugin, and there should only 983 * ever be a single internal chassis in a system. 984 */ 985 for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 986 cp = topo_list_next(cp)) { 987 if (cp->sec_internal) 988 break; 989 } 990 991 if (cp == NULL) { 992 topo_mod_dprintf(mod, "failed to find internal chassis\n"); 993 return (-1); 994 } 995 996 if (ses_create_children(sdp, pnode, SES_ET_DEVICE, 997 BAY, "BAY", cp, B_FALSE) != 0 || 998 ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE, 999 BAY, "BAY", cp, B_FALSE) != 0) 1000 return (-1); 1001 1002 return (0); 1003 } 1004 /* 1005 * Gather nodes from the current SES target into our chassis list, merging the 1006 * results if necessary. 1007 */ 1008 static ses_walk_action_t 1009 ses_enum_gather(ses_node_t *np, void *data) 1010 { 1011 nvlist_t *props = ses_node_props(np); 1012 ses_enum_data_t *sdp = data; 1013 topo_mod_t *mod = sdp->sed_mod; 1014 ses_enum_chassis_t *cp; 1015 ses_enum_node_t *snp; 1016 ses_alt_node_t *sap; 1017 char *csn; 1018 uint64_t instance, type; 1019 uint64_t prevstatus, status; 1020 boolean_t report, internal; 1021 1022 if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 1023 /* 1024 * If we have already identified the chassis for this target, 1025 * then this is a secondary enclosure and we should ignore it, 1026 * along with the rest of the tree (since this is depth-first). 1027 */ 1028 if (sdp->sed_current != NULL) 1029 return (SES_WALK_ACTION_TERMINATE); 1030 1031 /* 1032 * Go through the list of chassis we have seen so far and see 1033 * if this serial number matches one of the known values. 1034 */ 1035 if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 1036 &csn) != 0) 1037 return (SES_WALK_ACTION_TERMINATE); 1038 1039 for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 1040 cp = topo_list_next(cp)) { 1041 if (strcmp(cp->sec_csn, csn) == 0) { 1042 topo_mod_dprintf(mod, "%s: part of already " 1043 "known chassis %s", sdp->sed_name, csn); 1044 break; 1045 } 1046 } 1047 1048 if (cp == NULL) { 1049 topo_mod_dprintf(mod, "%s: creating chassis %s", 1050 sdp->sed_name, csn); 1051 1052 if ((cp = topo_mod_zalloc(mod, 1053 sizeof (ses_enum_chassis_t))) == NULL) 1054 goto error; 1055 1056 if (nvlist_lookup_boolean_value(props, 1057 LIBSES_EN_PROP_INTERNAL, &internal) == 0) 1058 cp->sec_internal = internal; 1059 1060 cp->sec_csn = csn; 1061 cp->sec_enclosure = np; 1062 cp->sec_target = sdp->sed_target; 1063 cp->sec_instance = sdp->sed_instance++; 1064 topo_list_append(&sdp->sed_chassis, cp); 1065 } 1066 1067 topo_list_append(&cp->sec_targets, sdp->sed_target); 1068 sdp->sed_current = cp; 1069 1070 } else if (ses_node_type(np) == SES_NODE_ELEMENT) { 1071 /* 1072 * If we haven't yet seen an enclosure node and identified the 1073 * current chassis, something is very wrong; bail out. 1074 */ 1075 if (sdp->sed_current == NULL) 1076 return (SES_WALK_ACTION_TERMINATE); 1077 1078 /* 1079 * If this isn't one of the element types we care about, then 1080 * ignore it. 1081 */ 1082 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 1083 &type) == 0); 1084 if (type != SES_ET_DEVICE && 1085 type != SES_ET_ARRAY_DEVICE && 1086 type != SES_ET_COOLING && 1087 type != SES_ET_POWER_SUPPLY && 1088 type != SES_ET_ESC_ELECTRONICS) 1089 return (SES_WALK_ACTION_CONTINUE); 1090 1091 /* 1092 * Get the current instance number and see if we already know 1093 * about this element. If so, it means we have multiple paths 1094 * to the same elements, and we should ignore the current path. 1095 */ 1096 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 1097 &instance) == 0); 1098 if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE) 1099 (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, 1100 &instance); 1101 1102 cp = sdp->sed_current; 1103 1104 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1105 snp = topo_list_next(snp)) { 1106 if (snp->sen_type == type && 1107 snp->sen_instance == instance) 1108 break; 1109 } 1110 1111 /* 1112 * We prefer the new element under the following circumstances: 1113 * 1114 * - The currently known element's status is unknown or not 1115 * available, but the new element has a known status. This 1116 * occurs if a given element is only available through a 1117 * particular target. 1118 * 1119 * - This is an ESC_ELECTRONICS element, and the 'reported-via' 1120 * property is set. This allows us to get reliable firmware 1121 * revision information from the enclosure node. 1122 */ 1123 if (snp != NULL) { 1124 if (nvlist_lookup_uint64( 1125 ses_node_props(snp->sen_node), 1126 SES_PROP_STATUS_CODE, &prevstatus) != 0) 1127 prevstatus = SES_ESC_UNSUPPORTED; 1128 if (nvlist_lookup_uint64( 1129 props, SES_PROP_STATUS_CODE, &status) != 0) 1130 status = SES_ESC_UNSUPPORTED; 1131 if (nvlist_lookup_boolean_value( 1132 props, SES_PROP_REPORT, &report) != 0) 1133 report = B_FALSE; 1134 1135 if ((SES_STATUS_UNAVAIL(prevstatus) && 1136 !SES_STATUS_UNAVAIL(status)) || 1137 (type == SES_ET_ESC_ELECTRONICS && 1138 report)) { 1139 snp->sen_node = np; 1140 snp->sen_target = sdp->sed_target; 1141 } 1142 1143 if ((sap = topo_mod_zalloc(mod, 1144 sizeof (ses_alt_node_t))) == NULL) 1145 goto error; 1146 1147 sap->san_node = np; 1148 topo_list_append(&snp->sen_alt_nodes, sap); 1149 1150 return (SES_WALK_ACTION_CONTINUE); 1151 } 1152 1153 if ((snp = topo_mod_zalloc(mod, 1154 sizeof (ses_enum_node_t))) == NULL) 1155 goto error; 1156 1157 if ((sap = topo_mod_zalloc(mod, 1158 sizeof (ses_alt_node_t))) == NULL) { 1159 topo_mod_free(mod, snp, sizeof (ses_enum_node_t)); 1160 goto error; 1161 } 1162 1163 topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)", 1164 sdp->sed_name, type, instance); 1165 snp->sen_node = np; 1166 snp->sen_type = type; 1167 snp->sen_instance = instance; 1168 snp->sen_target = sdp->sed_target; 1169 sap->san_node = np; 1170 topo_list_append(&snp->sen_alt_nodes, sap); 1171 topo_list_append(&cp->sec_nodes, snp); 1172 1173 if (type == SES_ET_DEVICE) 1174 cp->sec_hasdev = B_TRUE; 1175 } 1176 1177 return (SES_WALK_ACTION_CONTINUE); 1178 1179 error: 1180 sdp->sed_errno = -1; 1181 return (SES_WALK_ACTION_TERMINATE); 1182 } 1183 1184 static int 1185 ses_process_dir(const char *dirpath, ses_enum_data_t *sdp) 1186 { 1187 topo_mod_t *mod = sdp->sed_mod; 1188 DIR *dir; 1189 struct dirent *dp; 1190 char path[PATH_MAX]; 1191 ses_enum_target_t *stp; 1192 int err = -1; 1193 1194 /* 1195 * Open the SES target directory and iterate over any available 1196 * targets. 1197 */ 1198 if ((dir = opendir(dirpath)) == NULL) { 1199 /* 1200 * If the SES target directory does not exist, then return as if 1201 * there are no active targets. 1202 */ 1203 topo_mod_dprintf(mod, "failed to open ses " 1204 "directory '%s'", dirpath); 1205 return (0); 1206 } 1207 1208 while ((dp = readdir(dir)) != NULL) { 1209 if (strcmp(dp->d_name, ".") == 0 || 1210 strcmp(dp->d_name, "..") == 0) 1211 continue; 1212 1213 /* 1214 * Create a new target instance and take a snapshot. 1215 */ 1216 if ((stp = topo_mod_zalloc(mod, 1217 sizeof (ses_enum_target_t))) == NULL) 1218 goto error; 1219 1220 (void) pthread_mutex_init(&stp->set_lock, NULL); 1221 1222 (void) snprintf(path, sizeof (path), "%s/%s", dirpath, 1223 dp->d_name); 1224 1225 /* 1226 * We keep track of the SES device path and export it on a 1227 * per-node basis to allow higher level software to get to the 1228 * corresponding SES state. 1229 */ 1230 if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) { 1231 topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1232 goto error; 1233 } 1234 1235 if ((stp->set_target = 1236 ses_open(LIBSES_VERSION, path)) == NULL) { 1237 topo_mod_dprintf(mod, "failed to open ses target " 1238 "'%s': %s", dp->d_name, ses_errmsg()); 1239 topo_mod_strfree(mod, stp->set_devpath); 1240 topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1241 continue; 1242 } 1243 1244 stp->set_refcount = 1; 1245 sdp->sed_target = stp; 1246 stp->set_snap = ses_snap_hold(stp->set_target); 1247 if (gettimeofday(&stp->set_snaptime, NULL) != 0) 1248 stp->set_snaptime.tv_sec = time(NULL); 1249 1250 /* 1251 * Enumerate over all SES elements and merge them into the 1252 * correct ses_enum_chassis_t. 1253 */ 1254 sdp->sed_current = NULL; 1255 sdp->sed_errno = 0; 1256 sdp->sed_name = dp->d_name; 1257 (void) ses_walk(stp->set_snap, ses_enum_gather, sdp); 1258 1259 if (sdp->sed_errno != 0) 1260 goto error; 1261 } 1262 1263 err = 0; 1264 error: 1265 closedir(dir); 1266 return (err); 1267 } 1268 1269 static void 1270 ses_release(topo_mod_t *mod, tnode_t *tn) 1271 { 1272 ses_enum_target_t *stp; 1273 1274 if ((stp = topo_node_getspecific(tn)) != NULL) 1275 ses_target_free(mod, stp); 1276 } 1277 1278 /*ARGSUSED*/ 1279 static int 1280 ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 1281 topo_instance_t min, topo_instance_t max, void *arg, void *notused) 1282 { 1283 ses_enum_chassis_t *cp; 1284 ses_enum_data_t *data; 1285 1286 /* 1287 * Check to make sure we're being invoked sensibly, and that we're not 1288 * being invoked as part of a post-processing step. 1289 */ 1290 if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0) 1291 return (0); 1292 1293 /* 1294 * If this is the first time we've called our enumeration method, then 1295 * gather information about any available enclosures. 1296 */ 1297 if ((data = topo_mod_getspecific(mod)) == NULL) { 1298 if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) == 1299 NULL) 1300 return (-1); 1301 1302 data->sed_mod = mod; 1303 topo_mod_setspecific(mod, data); 1304 1305 if (disk_list_gather(mod, &data->sed_disks) != 0) 1306 goto error; 1307 1308 /* 1309 * We search both the ses(7D) and sgen(7D) locations, so we are 1310 * independent of any particular driver class bindings. 1311 */ 1312 if (ses_process_dir("/dev/es", data) != 0 || 1313 ses_process_dir("/dev/scsi/ses", data) != 0) 1314 goto error; 1315 } 1316 1317 if (strcmp(name, SES_ENCLOSURE) == 0) { 1318 /* 1319 * This is a request to enumerate external enclosures. Go 1320 * through all the targets and create chassis nodes where 1321 * necessary. 1322 */ 1323 for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 1324 cp = topo_list_next(cp)) { 1325 if (ses_create_chassis(data, rnode, cp) != 0) 1326 goto error; 1327 } 1328 } else { 1329 /* 1330 * This is a request to enumerate a specific bay underneath the 1331 * root chassis (for internal disks). 1332 */ 1333 if (ses_create_bays(data, rnode) != 0) 1334 goto error; 1335 } 1336 1337 /* 1338 * This is a bit of a kludge. In order to allow internal disks to be 1339 * enumerated and share snapshot-specific information with the external 1340 * enclosure enumeration, we rely on the fact that we will be invoked 1341 * for the 'ses-enclosure' node last. 1342 */ 1343 if (strcmp(name, SES_ENCLOSURE) == 0) { 1344 ses_data_free(data); 1345 topo_mod_setspecific(mod, NULL); 1346 } 1347 return (0); 1348 1349 error: 1350 ses_data_free(data); 1351 topo_mod_setspecific(mod, NULL); 1352 return (-1); 1353 } 1354 1355 static const topo_modops_t ses_ops = 1356 { ses_enum, ses_release }; 1357 1358 static topo_modinfo_t ses_info = 1359 { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops }; 1360 1361 /*ARGSUSED*/ 1362 int 1363 _topo_init(topo_mod_t *mod, topo_version_t version) 1364 { 1365 if (getenv("TOPOSESDEBUG") != NULL) 1366 topo_mod_setdebug(mod); 1367 1368 topo_mod_dprintf(mod, "initializing %s enumerator\n", 1369 SES_ENCLOSURE); 1370 1371 return (topo_mod_register(mod, &ses_info, TOPO_VERSION)); 1372 } 1373 1374 void 1375 _topo_fini(topo_mod_t *mod) 1376 { 1377 topo_mod_unregister(mod); 1378 } 1379