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