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