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