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