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