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