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