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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <alloca.h> 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 <string.h> 34 #include <unistd.h> 35 #include <sys/dkio.h> 36 #include <sys/fm/protocol.h> 37 #include <sys/libdevid.h> 38 #include <sys/scsi/scsi_types.h> 39 #include <sys/byteorder.h> 40 41 #include "disk.h" 42 #include "ses.h" 43 44 #define SES_VERSION 1 45 46 #define SES_STARTING_SUBCHASSIS 256 /* valid subchassis IDs are uint8_t */ 47 #define NO_SUBCHASSIS ((uint64_t)-1) 48 49 static int ses_snap_freq = 250; /* in milliseconds */ 50 51 #define SES_STATUS_UNAVAIL(s) \ 52 ((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED) 53 54 /* 55 * Because multiple SES targets can be part of a single chassis, we construct 56 * our own hierarchy that takes this into account. These SES targets may refer 57 * to the same devices (multiple paths) or to different devices (managing 58 * different portions of the space). We arrange things into a 59 * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all 60 * nodes found so far. 61 */ 62 typedef struct ses_alt_node { 63 topo_list_t san_link; 64 ses_node_t *san_node; 65 } ses_alt_node_t; 66 67 typedef struct ses_enum_node { 68 topo_list_t sen_link; 69 ses_node_t *sen_node; 70 topo_list_t sen_alt_nodes; 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_subchassis; 79 topo_list_t sec_nodes; 80 topo_list_t sec_targets; 81 const char *sec_csn; 82 ses_node_t *sec_enclosure; 83 ses_enum_target_t *sec_target; 84 topo_instance_t sec_instance; 85 topo_instance_t sec_scinstance; 86 topo_instance_t sec_maxinstance; 87 boolean_t sec_hasdev; 88 boolean_t sec_internal; 89 } ses_enum_chassis_t; 90 91 typedef struct ses_enum_data { 92 topo_list_t sed_devs; 93 topo_list_t sed_chassis; 94 ses_enum_chassis_t *sed_current; 95 ses_enum_target_t *sed_target; 96 int sed_errno; 97 char *sed_name; 98 topo_mod_t *sed_mod; 99 topo_instance_t sed_instance; 100 } ses_enum_data_t; 101 102 typedef struct sas_connector_phy_data { 103 uint64_t index; 104 uint64_t phy_mask; 105 } sas_connector_phy_data_t; 106 107 typedef struct sas_connector_type { 108 uint64_t type; 109 char *name; 110 } sas_connector_type_t; 111 112 static const sas_connector_type_t sas_connector_type_list[] = { 113 { 0x0, "Information unknown" }, 114 { 0x1, "External SAS 4x receptacle (see SAS-2 and SFF-8470)" }, 115 { 0x2, "Exteranl Mini SAS 4x receptacle (see SAS-2 and SFF-8088)" }, 116 { 0xF, "Vendor-specific external connector" }, 117 { 0x10, "Internal wide SAS 4i plug (see SAS-2 and SFF-8484)" }, 118 { 0x11, 119 "Internal wide Mini SAS 4i receptacle (see SAS-2 and SFF-8087)" }, 120 { 0x20, "Internal SAS Drive receptacle (see SAS-2 and SFF-8482)" }, 121 { 0x21, "Internal SATA host plug (see SAS-2 and SATA-2)" }, 122 { 0x22, "Internal SAS Drive plug (see SAS-2 and SFF-8482)" }, 123 { 0x23, "Internal SATA device plug (see SAS-2 and SATA-2)" }, 124 { 0x2F, "Internal SAS virtual connector" }, 125 { 0x3F, "Vendor-specific internal connector" }, 126 { 0x70, "Other Vendor-specific connector" }, 127 { 0x71, "Other Vendor-specific connector" }, 128 { 0x72, "Other Vendor-specific connector" }, 129 { 0x73, "Other Vendor-specific connector" }, 130 { 0x74, "Other Vendor-specific connector" }, 131 { 0x75, "Other Vendor-specific connector" }, 132 { 0x76, "Other Vendor-specific connector" }, 133 { 0x77, "Other Vendor-specific connector" }, 134 { 0x78, "Other Vendor-specific connector" }, 135 { 0x79, "Other Vendor-specific connector" }, 136 { 0x7A, "Other Vendor-specific connector" }, 137 { 0x7B, "Other Vendor-specific connector" }, 138 { 0x7C, "Other Vendor-specific connector" }, 139 { 0x7D, "Other Vendor-specific connector" }, 140 { 0x7E, "Other Vendor-specific connector" }, 141 { 0x7F, "Other Vendor-specific connector" }, 142 { 0x80, "Not Defined" } 143 }; 144 145 #define SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED 0x80 146 #define SAS_CONNECTOR_TYPE_NOT_DEFINED \ 147 "Connector type not definedi by SES-2 standard" 148 #define SAS_CONNECTOR_TYPE_RESERVED \ 149 "Connector type reserved by SES-2 standard" 150 151 typedef enum { 152 SES_NEW_CHASSIS = 0x1, 153 SES_NEW_SUBCHASSIS = 0x2, 154 SES_DUP_CHASSIS = 0x4, 155 SES_DUP_SUBCHASSIS = 0x8 156 } ses_chassis_type_e; 157 158 static const topo_pgroup_info_t io_pgroup = { 159 TOPO_PGROUP_IO, 160 TOPO_STABILITY_PRIVATE, 161 TOPO_STABILITY_PRIVATE, 162 1 163 }; 164 165 static const topo_pgroup_info_t storage_pgroup = { 166 TOPO_PGROUP_STORAGE, 167 TOPO_STABILITY_PRIVATE, 168 TOPO_STABILITY_PRIVATE, 169 1 170 }; 171 172 static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 173 nvlist_t **); 174 static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 175 nvlist_t **); 176 177 static const topo_method_t ses_component_methods[] = { 178 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 179 TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present }, 180 { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 181 TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 182 { TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC, 183 TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL, 184 topo_method_sensor_failure }, 185 { NULL } 186 }; 187 188 static const topo_method_t ses_bay_methods[] = { 189 { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 190 TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 191 { NULL } 192 }; 193 194 static const topo_method_t ses_enclosure_methods[] = { 195 { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC, 196 TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains }, 197 { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 198 TOPO_STABILITY_INTERNAL, ses_enc_enum_facility }, 199 { NULL } 200 }; 201 202 static void 203 ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp) 204 { 205 if (--stp->set_refcount == 0) { 206 ses_snap_rele(stp->set_snap); 207 ses_close(stp->set_target); 208 topo_mod_strfree(mod, stp->set_devpath); 209 topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 210 } 211 } 212 213 static void 214 ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp) 215 { 216 topo_mod_t *mod = sdp->sed_mod; 217 ses_enum_chassis_t *cp; 218 ses_enum_node_t *np; 219 ses_enum_target_t *tp; 220 ses_alt_node_t *ap; 221 topo_list_t *cpl; 222 223 224 if (pcp != NULL) 225 cpl = &pcp->sec_subchassis; 226 else 227 cpl = &sdp->sed_chassis; 228 229 while ((cp = topo_list_next(cpl)) != NULL) { 230 topo_list_delete(cpl, cp); 231 232 while ((np = topo_list_next(&cp->sec_nodes)) != NULL) { 233 while ((ap = topo_list_next(&np->sen_alt_nodes)) != 234 NULL) { 235 topo_list_delete(&np->sen_alt_nodes, ap); 236 topo_mod_free(mod, ap, sizeof (ses_alt_node_t)); 237 } 238 topo_list_delete(&cp->sec_nodes, np); 239 topo_mod_free(mod, np, sizeof (ses_enum_node_t)); 240 } 241 242 while ((tp = topo_list_next(&cp->sec_targets)) != NULL) { 243 topo_list_delete(&cp->sec_targets, tp); 244 ses_target_free(mod, tp); 245 } 246 247 topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t)); 248 } 249 250 if (pcp == NULL) { 251 dev_list_free(mod, &sdp->sed_devs); 252 topo_mod_free(mod, sdp, sizeof (ses_enum_data_t)); 253 } 254 } 255 256 /* 257 * For enclosure nodes, we have a special contains method. By default, the hc 258 * walker will compare the node name and instance number to determine if an 259 * FMRI matches. For enclosures where the enumeration order is impossible to 260 * predict, we instead use the chassis-id as a unique identifier, and ignore 261 * the instance number. 262 */ 263 static int 264 fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2) 265 { 266 uint8_t v1, v2; 267 nvlist_t **hcp1, **hcp2; 268 int err, i; 269 uint_t nhcp1, nhcp2; 270 nvlist_t *a1, *a2; 271 char *c1, *c2; 272 int mindepth; 273 274 if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 || 275 nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 || 276 v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION) 277 return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION)); 278 279 err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1); 280 err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2); 281 if (err != 0) 282 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 283 284 /* 285 * If the chassis-id doesn't match, then these FMRIs are not 286 * equivalent. If one of the FMRIs doesn't have a chassis ID, then we 287 * have no choice but to fall back to the instance ID. 288 */ 289 if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 && 290 nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 && 291 nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 && 292 nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) { 293 if (strcmp(c1, c2) != 0) 294 return (0); 295 296 mindepth = 1; 297 } else { 298 mindepth = 0; 299 } 300 301 if (nhcp2 < nhcp1) 302 return (0); 303 304 for (i = 0; i < nhcp1; i++) { 305 char *nm1 = NULL; 306 char *nm2 = NULL; 307 char *id1 = NULL; 308 char *id2 = NULL; 309 310 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1); 311 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2); 312 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1); 313 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2); 314 if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL) 315 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 316 317 if (strcmp(nm1, nm2) == 0 && 318 (i < mindepth || strcmp(id1, id2) == 0)) 319 continue; 320 321 return (0); 322 } 323 324 return (1); 325 } 326 327 /*ARGSUSED*/ 328 static int 329 ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 330 nvlist_t *in, nvlist_t **out) 331 { 332 int ret; 333 nvlist_t *nv1, *nv2; 334 335 if (version > TOPO_METH_CONTAINS_VERSION) 336 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 337 338 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 || 339 nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0) 340 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 341 342 ret = fmri_contains(mod, nv1, nv2); 343 if (ret < 0) 344 return (-1); 345 346 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) { 347 if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET, 348 ret) == 0) 349 return (0); 350 else 351 nvlist_free(*out); 352 } 353 354 return (-1); 355 356 } 357 358 /* 359 * Return a current instance of the node. This is somewhat complicated because 360 * we need to take a new snapshot in order to get the new data, but we don't 361 * want to be constantly taking SES snapshots if the consumer is going to do a 362 * series of queries. So we adopt the strategy of assuming that the SES state 363 * is not going to be rapidly changing, and limit our snapshot frequency to 364 * some defined bounds. 365 */ 366 ses_node_t * 367 ses_node_lock(topo_mod_t *mod, tnode_t *tn) 368 { 369 ses_enum_target_t *tp = topo_node_getspecific(tn); 370 hrtime_t now; 371 ses_snap_t *snap; 372 int err; 373 uint64_t nodeid; 374 ses_node_t *np; 375 376 if (tp == NULL) { 377 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 378 return (NULL); 379 } 380 381 (void) pthread_mutex_lock(&tp->set_lock); 382 383 /* 384 * Determine if we need to take a new snapshot. 385 */ 386 now = gethrtime(); 387 388 if (now - tp->set_snaptime > (ses_snap_freq * 1000 * 1000) && 389 (snap = ses_snap_new(tp->set_target)) != NULL) { 390 if (ses_snap_generation(snap) != 391 ses_snap_generation(tp->set_snap)) { 392 /* 393 * If we find ourselves in this situation, we're in 394 * trouble. The generation count has changed, which 395 * indicates that our current topology is out of date. 396 * But we need to consult the new topology in order to 397 * determine presence at this moment in time. We can't 398 * go back and change the topo snapshot in situ, so 399 * we'll just have to fail the call in this unlikely 400 * scenario. 401 */ 402 ses_snap_rele(snap); 403 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 404 (void) pthread_mutex_unlock(&tp->set_lock); 405 return (NULL); 406 } else { 407 ses_snap_rele(tp->set_snap); 408 tp->set_snap = snap; 409 } 410 tp->set_snaptime = gethrtime(); 411 } 412 413 snap = tp->set_snap; 414 415 verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES, 416 TOPO_PROP_NODE_ID, &nodeid, &err) == 0); 417 verify((np = ses_node_lookup(snap, nodeid)) != NULL); 418 419 return (np); 420 } 421 422 /*ARGSUSED*/ 423 void 424 ses_node_unlock(topo_mod_t *mod, tnode_t *tn) 425 { 426 ses_enum_target_t *tp = topo_node_getspecific(tn); 427 428 verify(tp != NULL); 429 430 (void) pthread_mutex_unlock(&tp->set_lock); 431 } 432 433 /* 434 * Determine if the element is present. 435 */ 436 /*ARGSUSED*/ 437 static int 438 ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 439 nvlist_t *in, nvlist_t **out) 440 { 441 boolean_t present; 442 ses_node_t *np; 443 nvlist_t *props, *nvl; 444 uint64_t status; 445 446 if ((np = ses_node_lock(mod, tn)) == NULL) 447 return (-1); 448 449 verify((props = ses_node_props(np)) != NULL); 450 verify(nvlist_lookup_uint64(props, 451 SES_PROP_STATUS_CODE, &status) == 0); 452 453 ses_node_unlock(mod, tn); 454 455 present = (status != SES_ESC_NOT_INSTALLED); 456 457 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 458 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 459 460 if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, 461 present) != 0) { 462 nvlist_free(nvl); 463 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 464 } 465 466 *out = nvl; 467 468 return (0); 469 } 470 471 /* 472 * Sets standard properties for a ses node (enclosure, bay, controller 473 * or expander). 474 * This includes setting the FRU, as well as setting the 475 * authority information. When the fru topo node(frutn) is not NULL 476 * its resouce should be used as FRU. 477 */ 478 static int 479 ses_set_standard_props(topo_mod_t *mod, tnode_t *frutn, tnode_t *tn, 480 nvlist_t *auth, uint64_t nodeid, const char *path) 481 { 482 int err; 483 char *product, *chassis; 484 nvlist_t *fmri; 485 topo_pgroup_info_t pgi; 486 487 /* 488 * Set the authority explicitly if specified. 489 */ 490 if (auth) { 491 verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 492 &product) == 0); 493 verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, 494 &chassis) == 0); 495 if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 496 FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product, 497 &err) != 0 || 498 topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 499 FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis, 500 &err) != 0 || 501 topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 502 FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "", 503 &err) != 0) { 504 topo_mod_dprintf(mod, "failed to add authority " 505 "properties: %s\n", topo_strerror(err)); 506 return (topo_mod_seterrno(mod, err)); 507 } 508 } 509 510 /* 511 * Copy the resource and set that as the FRU. 512 */ 513 if (frutn != NULL) { 514 if (topo_node_resource(frutn, &fmri, &err) != 0) { 515 topo_mod_dprintf(mod, 516 "topo_node_resource() failed : %s\n", 517 topo_strerror(err)); 518 return (topo_mod_seterrno(mod, err)); 519 } 520 } else { 521 if (topo_node_resource(tn, &fmri, &err) != 0) { 522 topo_mod_dprintf(mod, 523 "topo_node_resource() failed : %s\n", 524 topo_strerror(err)); 525 return (topo_mod_seterrno(mod, err)); 526 } 527 } 528 529 if (topo_node_fru_set(tn, fmri, 0, &err) != 0) { 530 topo_mod_dprintf(mod, 531 "topo_node_fru_set() failed : %s\n", 532 topo_strerror(err)); 533 nvlist_free(fmri); 534 return (topo_mod_seterrno(mod, err)); 535 } 536 537 nvlist_free(fmri); 538 539 /* 540 * Set the SES-specific properties so that consumers can query 541 * additional information about the particular SES element. 542 */ 543 pgi.tpi_name = TOPO_PGROUP_SES; 544 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 545 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 546 pgi.tpi_version = TOPO_VERSION; 547 if (topo_pgroup_create(tn, &pgi, &err) != 0) { 548 topo_mod_dprintf(mod, "failed to create propgroup " 549 "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err)); 550 return (-1); 551 } 552 553 if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 554 TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 555 nodeid, &err) != 0) { 556 topo_mod_dprintf(mod, 557 "failed to create property %s: %s\n", 558 TOPO_PROP_NODE_ID, topo_strerror(err)); 559 return (-1); 560 } 561 562 if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 563 TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE, 564 path, &err) != 0) { 565 topo_mod_dprintf(mod, 566 "failed to create property %s: %s\n", 567 TOPO_PROP_TARGET_PATH, topo_strerror(err)); 568 return (-1); 569 } 570 571 return (0); 572 } 573 574 /* 575 * Callback to add a disk to a given bay. We first check the status-code to 576 * determine if a disk is present, ignoring those that aren't in an appropriate 577 * state. We then scan the parent bay node's SAS address array to determine 578 * possible attached SAS addresses. We create a disk node if the disk is not 579 * SAS or the SES target does not support the necessary pages for this; if we 580 * find the SAS address, we create a disk node and also correlate it with 581 * the corresponding Solaris device node to fill in the rest of the data. 582 */ 583 static int 584 ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props) 585 { 586 topo_mod_t *mod = sdp->sed_mod; 587 uint64_t status; 588 nvlist_t **sas; 589 uint_t s, nsas; 590 char **paths; 591 int err, ret; 592 tnode_t *child = NULL; 593 594 /* 595 * Skip devices that are not in a present (and possibly damaged) state. 596 */ 597 if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 598 return (0); 599 600 if (status != SES_ESC_UNSUPPORTED && 601 status != SES_ESC_OK && 602 status != SES_ESC_CRITICAL && 603 status != SES_ESC_NONCRITICAL && 604 status != SES_ESC_UNRECOVERABLE && 605 status != SES_ESC_NO_ACCESS) 606 return (0); 607 608 topo_mod_dprintf(mod, "found attached disk"); 609 610 /* 611 * Create the disk range. 612 */ 613 if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) { 614 topo_mod_dprintf(mod, 615 "topo_node_create_range() failed: %s", 616 topo_mod_errmsg(mod)); 617 return (-1); 618 } 619 620 /* 621 * Look through all SAS addresses and attempt to correlate them to a 622 * known Solaris device. If we don't find a matching node, then we 623 * don't enumerate the disk node. 624 */ 625 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 626 &sas, &nsas) != 0) 627 return (0); 628 629 if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES, 630 TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0) 631 return (0); 632 633 err = 0; 634 635 for (s = 0; s < nsas; s++) { 636 ret = disk_declare_addr(mod, pnode, &sdp->sed_devs, paths[s], 637 &child); 638 if (ret == 0) { 639 break; 640 } else if (ret < 0) { 641 err = -1; 642 break; 643 } 644 } 645 646 if (s == nsas) 647 disk_declare_non_enumerated(mod, pnode, &child); 648 649 /* copy sas_addresses (target-ports) from parent (with 'w'added) */ 650 if (child != NULL) { 651 int i; 652 char **tports; 653 uint64_t wwn; 654 655 tports = topo_mod_zalloc(mod, sizeof (char *) * nsas); 656 if (tports != NULL) { 657 for (i = 0; i < nsas; i++) { 658 if (scsi_wwnstr_to_wwn(paths[i], &wwn) != 659 DDI_SUCCESS) 660 break; 661 tports[i] = scsi_wwn_to_wwnstr(wwn, 1, NULL); 662 if (tports[i] == NULL) 663 break; 664 } 665 /* if they all worked then create the property */ 666 if (i == nsas) 667 (void) topo_prop_set_string_array(child, 668 TOPO_PGROUP_STORAGE, 669 TOPO_STORAGE_TARGET_PORT_L0IDS, 670 TOPO_PROP_IMMUTABLE, (const char **)tports, 671 nsas, &err); 672 673 for (i = 0; i < nsas; i++) 674 if (tports[i] != NULL) 675 scsi_free_wwnstr(tports[i]); 676 topo_mod_free(mod, tports, sizeof (char *) * nsas); 677 } 678 } 679 680 for (s = 0; s < nsas; s++) 681 topo_mod_free(mod, paths[s], strlen(paths[s]) + 1); 682 topo_mod_free(mod, paths, nsas * sizeof (char *)); 683 684 return (err); 685 } 686 687 static int 688 ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp) 689 { 690 ses_alt_node_t *ap; 691 ses_node_t *np; 692 nvlist_t *props; 693 694 nvlist_t **phys; 695 uint_t i, j, n_phys, all_phys = 0; 696 char **paths; 697 uint64_t addr; 698 size_t len; 699 int terr, err = -1; 700 701 for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 702 ap = topo_list_next(ap)) { 703 np = ap->san_node; 704 props = ses_node_props(np); 705 706 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 707 &phys, &n_phys) != 0) 708 continue; 709 710 all_phys += n_phys; 711 } 712 713 if (all_phys == 0) 714 return (0); 715 716 if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL) 717 return (-1); 718 719 for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 720 ap = topo_list_next(ap)) { 721 np = ap->san_node; 722 props = ses_node_props(np); 723 724 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 725 &phys, &n_phys) != 0) 726 continue; 727 728 for (j = 0; j < n_phys; j++) { 729 if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR, 730 &addr) != 0) 731 continue; 732 733 len = snprintf(NULL, 0, "%016llx", addr) + 1; 734 if ((paths[i] = topo_mod_alloc(mod, len)) == NULL) 735 goto error; 736 737 (void) snprintf(paths[i], len, "%016llx", addr); 738 739 ++i; 740 } 741 } 742 743 err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 744 TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, 745 (const char **)paths, i, &terr); 746 if (err != 0) 747 err = topo_mod_seterrno(mod, terr); 748 749 error: 750 for (i = 0; i < all_phys && paths[i] != NULL; i++) 751 topo_mod_free(mod, paths[i], strlen(paths[i]) + 1); 752 topo_mod_free(mod, paths, all_phys * sizeof (char *)); 753 754 return (err); 755 } 756 757 /* 758 * Callback to create a basic node (bay, psu, fan, or controller and expander). 759 */ 760 static int 761 ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, 762 tnode_t *pnode, const char *nodename, const char *labelname, tnode_t **node) 763 { 764 ses_node_t *np = snp->sen_node; 765 ses_node_t *parent; 766 uint64_t instance = snp->sen_instance; 767 topo_mod_t *mod = sdp->sed_mod; 768 nvlist_t *props, *aprops; 769 nvlist_t *auth = NULL, *fmri = NULL; 770 tnode_t *tn = NULL, *frutn = NULL; 771 char label[128]; 772 int err; 773 char *part = NULL, *serial = NULL, *revision = NULL; 774 char *desc; 775 boolean_t report; 776 777 props = ses_node_props(np); 778 779 (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part); 780 (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial); 781 782 topo_mod_dprintf(mod, "adding %s %llu", nodename, instance); 783 784 /* 785 * Create the node. The interesting information is all copied from the 786 * parent enclosure node, so there is not much to do. 787 */ 788 if ((auth = topo_mod_auth(mod, pnode)) == NULL) 789 goto error; 790 791 /* 792 * We want to report revision information for the controller nodes, but 793 * we do not get per-element revision information. However, we do have 794 * revision information for the entire enclosure, and we can use the 795 * 'reported-via' property to know that this controller corresponds to 796 * the given revision information. This means we cannot get revision 797 * information for targets we are not explicitly connected to, but 798 * there is little we can do about the situation. 799 */ 800 if (strcmp(nodename, CONTROLLER) == 0 && 801 nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 && 802 report) { 803 for (parent = ses_node_parent(np); parent != NULL; 804 parent = ses_node_parent(parent)) { 805 if (ses_node_type(parent) == SES_NODE_ENCLOSURE) { 806 (void) nvlist_lookup_string( 807 ses_node_props(parent), 808 SES_EN_PROP_REV, &revision); 809 break; 810 } 811 } 812 } 813 814 if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 815 nodename, (topo_instance_t)instance, NULL, auth, part, revision, 816 serial)) == NULL) { 817 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 818 topo_mod_errmsg(mod)); 819 goto error; 820 } 821 822 if ((tn = topo_node_bind(mod, pnode, nodename, 823 instance, fmri)) == NULL) { 824 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 825 topo_mod_errmsg(mod)); 826 goto error; 827 } 828 829 /* 830 * For the node label, we look for the following in order: 831 * 832 * <ses-description> 833 * <ses-class-description> <instance> 834 * <default-type-label> <instance> 835 */ 836 if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 837 desc[0] == '\0') { 838 parent = ses_node_parent(np); 839 aprops = ses_node_props(parent); 840 if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION, 841 &desc) != 0 || desc[0] == '\0') 842 desc = (char *)labelname; 843 (void) snprintf(label, sizeof (label), "%s %llu", desc, 844 instance); 845 desc = label; 846 } 847 848 if (topo_node_label_set(tn, desc, &err) != 0) 849 goto error; 850 851 /* 852 * For an expander node, set the FRU to its parent(controller). 853 * For a connector node, set the FRU to its grand parent(controller). 854 */ 855 if (strcmp(nodename, SASEXPANDER) == 0) { 856 frutn = pnode; 857 } else if (strcmp(nodename, RECEPTACLE) == 0) { 858 frutn = topo_node_parent(pnode); 859 } 860 861 if (ses_set_standard_props(mod, frutn, tn, NULL, ses_node_id(np), 862 snp->sen_target->set_devpath) != 0) 863 goto error; 864 865 if (strcmp(nodename, BAY) == 0) { 866 if (ses_add_bay_props(mod, tn, snp) != 0) 867 goto error; 868 869 if (ses_create_disk(sdp, tn, props) != 0) 870 goto error; 871 872 if (topo_method_register(mod, tn, ses_bay_methods) != 0) { 873 topo_mod_dprintf(mod, 874 "topo_method_register() failed: %s", 875 topo_mod_errmsg(mod)); 876 goto error; 877 } 878 } else if ((strcmp(nodename, FAN) == 0) || 879 (strcmp(nodename, PSU) == 0) || 880 (strcmp(nodename, CONTROLLER) == 0)) { 881 /* 882 * Only fan, psu, and controller nodes have a 'present' method. 883 * Bay nodes are always present, and disk nodes are present by 884 * virtue of being enumerated and SAS expander nodes and 885 * SAS connector nodes are also always present once 886 * the parent controller is found. 887 */ 888 if (topo_method_register(mod, tn, ses_component_methods) != 0) { 889 topo_mod_dprintf(mod, 890 "topo_method_register() failed: %s", 891 topo_mod_errmsg(mod)); 892 goto error; 893 } 894 895 } 896 897 snp->sen_target->set_refcount++; 898 topo_node_setspecific(tn, snp->sen_target); 899 900 nvlist_free(auth); 901 nvlist_free(fmri); 902 if (node != NULL) *node = tn; 903 return (0); 904 905 error: 906 nvlist_free(auth); 907 nvlist_free(fmri); 908 return (-1); 909 } 910 911 /* 912 * Create SAS expander specific props. 913 */ 914 /*ARGSUSED*/ 915 static int 916 ses_set_expander_props(ses_enum_data_t *sdp, ses_enum_node_t *snp, 917 tnode_t *ptnode, tnode_t *tnode, int *phycount, int64_t *connlist) 918 { 919 ses_node_t *np = snp->sen_node; 920 topo_mod_t *mod = sdp->sed_mod; 921 nvlist_t *auth = NULL, *fmri = NULL; 922 nvlist_t *props, **phylist; 923 int err, i; 924 uint_t pcount; 925 uint64_t sasaddr, connidx; 926 char sasaddr_str[17]; 927 boolean_t found = B_FALSE; 928 dev_di_node_t *dnode; 929 930 props = ses_node_props(np); 931 932 /* 933 * the uninstalled expander is not enumerated by checking 934 * the element status code. No present present' method provided. 935 */ 936 /* 937 * Get the Expander SAS address. It should exist. 938 */ 939 if (nvlist_lookup_uint64(props, SES_EXP_PROP_SAS_ADDR, 940 &sasaddr) != 0) { 941 topo_mod_dprintf(mod, 942 "Failed to get prop %s.", SES_EXP_PROP_SAS_ADDR); 943 goto error; 944 } 945 946 (void) sprintf(sasaddr_str, "%llx", sasaddr); 947 948 /* search matching dev_di_node. */ 949 for (dnode = topo_list_next(&sdp->sed_devs); dnode != NULL; 950 dnode = topo_list_next(dnode)) { 951 if (strstr(dnode->ddn_dpath, sasaddr_str) != NULL) { 952 found = B_TRUE; 953 break; 954 } 955 } 956 957 if (!found) { 958 topo_mod_dprintf(mod, 959 "ses_set_expander_props: Failed to find matching " 960 "devinfo node for Exapnder SAS address %s", 961 SES_EXP_PROP_SAS_ADDR); 962 /* continue on to get storage group props. */ 963 } else { 964 /* create/set the devfs-path and devid in the io group */ 965 if (topo_pgroup_create(tnode, &io_pgroup, &err) != 0) { 966 topo_mod_dprintf(mod, "ses_set_expander_props: " 967 "create io error %s\n", topo_strerror(err)); 968 goto error; 969 } else { 970 if (topo_prop_set_string(tnode, TOPO_PGROUP_IO, 971 TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, 972 dnode->ddn_dpath, &err) != 0) { 973 topo_mod_dprintf(mod, "ses_set_expander_props: " 974 "set dev error %s\n", topo_strerror(err)); 975 } 976 if (topo_prop_set_string(tnode, TOPO_PGROUP_IO, 977 TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, 978 dnode->ddn_devid, &err) != 0) { 979 topo_mod_dprintf(mod, "ses_set_expander_props: " 980 "set devid error %s\n", topo_strerror(err)); 981 } 982 if (dnode->ddn_ppath_count != 0 && 983 topo_prop_set_string_array(tnode, TOPO_PGROUP_IO, 984 TOPO_IO_PHYS_PATH, TOPO_PROP_IMMUTABLE, 985 (const char **)dnode->ddn_ppath, 986 dnode->ddn_ppath_count, &err) != 0) { 987 topo_mod_dprintf(mod, "ses_set_expander_props: " 988 "set phys-path error %s\n", 989 topo_strerror(err)); 990 } 991 } 992 } 993 994 /* create the storage group */ 995 if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) { 996 topo_mod_dprintf(mod, "ses_set_expander_props: " 997 "create storage error %s\n", topo_strerror(err)); 998 goto error; 999 } else { 1000 /* set the SAS address prop of the expander. */ 1001 if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE, 1002 TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, sasaddr_str, 1003 &err) != 0) { 1004 topo_mod_dprintf(mod, "ses_set_expander_props: " 1005 "set %S error %s\n", TOPO_PROP_SAS_ADDR, 1006 topo_strerror(err)); 1007 } 1008 1009 /* Get the phy information for the expander */ 1010 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 1011 &phylist, &pcount) != 0) { 1012 topo_mod_dprintf(mod, 1013 "Failed to get prop %s.", SES_SAS_PROP_PHYS); 1014 } else { 1015 /* 1016 * For each phy, get the connector element index and 1017 * stores into connector element index array. 1018 */ 1019 *phycount = pcount; 1020 for (i = 0; i < pcount; i++) { 1021 if (nvlist_lookup_uint64(phylist[i], 1022 SES_PROP_CE_IDX, &connidx) == 0) { 1023 if (connidx != 0xff) { 1024 connlist[i] = connidx; 1025 } else { 1026 connlist[i] = -1; 1027 } 1028 } else { 1029 /* Fail to get the index. set to -1. */ 1030 connlist[i] = -1; 1031 } 1032 } 1033 1034 /* set the phy count prop of the expander. */ 1035 if (topo_prop_set_uint64(tnode, TOPO_PGROUP_STORAGE, 1036 TOPO_PROP_PHY_COUNT, TOPO_PROP_IMMUTABLE, pcount, 1037 &err) != 0) { 1038 topo_mod_dprintf(mod, "ses_set_expander_props: " 1039 "set %S error %s\n", TOPO_PROP_PHY_COUNT, 1040 topo_strerror(err)); 1041 } 1042 1043 /* 1044 * set the connector element index of 1045 * the expander phys. 1046 */ 1047 } 1048 1049 /* populate other misc storage group properties */ 1050 if (found) { 1051 if (dnode->ddn_mfg && (topo_prop_set_string(tnode, 1052 TOPO_PGROUP_STORAGE, TOPO_STORAGE_MANUFACTURER, 1053 TOPO_PROP_IMMUTABLE, dnode->ddn_mfg, &err) != 0)) { 1054 topo_mod_dprintf(mod, "ses_set_expander_props: " 1055 "set mfg error %s\n", topo_strerror(err)); 1056 } 1057 1058 if (dnode->ddn_model && (topo_prop_set_string(tnode, 1059 TOPO_PGROUP_STORAGE, TOPO_STORAGE_MODEL, 1060 TOPO_PROP_IMMUTABLE, 1061 dnode->ddn_model, &err) != 0)) { 1062 topo_mod_dprintf(mod, "ses_set_expander_props: " 1063 "set model error %s\n", topo_strerror(err)); 1064 } 1065 1066 if (dnode->ddn_serial && (topo_prop_set_string(tnode, 1067 TOPO_PGROUP_STORAGE, TOPO_STORAGE_SERIAL_NUM, 1068 TOPO_PROP_IMMUTABLE, 1069 dnode->ddn_serial, &err) != 0)) { 1070 topo_mod_dprintf(mod, "ses_set_expander_props: " 1071 "set serial error %s\n", 1072 topo_strerror(err)); 1073 } 1074 1075 if (dnode->ddn_firm && (topo_prop_set_string(tnode, 1076 TOPO_PGROUP_STORAGE, 1077 TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE, 1078 dnode->ddn_firm, &err) != 0)) { 1079 topo_mod_dprintf(mod, "ses_set_expander_props: " 1080 "set firm error %s\n", topo_strerror(err)); 1081 } 1082 } 1083 } 1084 1085 return (0); 1086 1087 error: 1088 nvlist_free(auth); 1089 nvlist_free(fmri); 1090 return (-1); 1091 } 1092 1093 /* 1094 * Create SAS expander specific props. 1095 */ 1096 /*ARGSUSED*/ 1097 static int 1098 ses_set_connector_props(ses_enum_data_t *sdp, ses_enum_node_t *snp, 1099 tnode_t *tnode, int64_t phy_mask) 1100 { 1101 ses_node_t *np = snp->sen_node; 1102 topo_mod_t *mod = sdp->sed_mod; 1103 nvlist_t *props; 1104 int err, i; 1105 uint64_t conntype; 1106 char phymask_str[17], *conntype_str; 1107 boolean_t found; 1108 1109 props = ses_node_props(np); 1110 1111 /* 1112 * convert phy mask to string. 1113 */ 1114 (void) snprintf(phymask_str, 17, "%llx", phy_mask); 1115 1116 /* create the storage group */ 1117 if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) { 1118 topo_mod_dprintf(mod, "ses_set_expander_props: " 1119 "create storage error %s\n", topo_strerror(err)); 1120 return (-1); 1121 } else { 1122 /* set the SAS address prop of the expander. */ 1123 if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE, 1124 TOPO_STORAGE_SAS_PHY_MASK, TOPO_PROP_IMMUTABLE, 1125 phymask_str, &err) != 0) { 1126 topo_mod_dprintf(mod, "ses_set_expander_props: " 1127 "set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK, 1128 topo_strerror(err)); 1129 } 1130 1131 /* Get the connector type information for the expander */ 1132 if (nvlist_lookup_uint64(props, 1133 SES_SC_PROP_CONNECTOR_TYPE, &conntype) != 0) { 1134 topo_mod_dprintf(mod, "Failed to get prop %s.", 1135 TOPO_STORAGE_SAS_PHY_MASK); 1136 } else { 1137 found = B_FALSE; 1138 for (i = 0; ; i++) { 1139 if (sas_connector_type_list[i].type == 1140 SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) { 1141 break; 1142 } 1143 if (sas_connector_type_list[i].type == 1144 conntype) { 1145 conntype_str = 1146 sas_connector_type_list[i].name; 1147 found = B_TRUE; 1148 break; 1149 } 1150 } 1151 1152 if (!found) { 1153 if (conntype < 1154 SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) { 1155 conntype_str = 1156 SAS_CONNECTOR_TYPE_RESERVED; 1157 } else { 1158 conntype_str = 1159 SAS_CONNECTOR_TYPE_NOT_DEFINED; 1160 } 1161 } 1162 1163 /* set the phy count prop of the expander. */ 1164 if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE, 1165 TOPO_STORAGE_SAS_CONNECTOR_TYPE, 1166 TOPO_PROP_IMMUTABLE, conntype_str, &err) != 0) { 1167 topo_mod_dprintf(mod, "ses_set_expander_props: " 1168 "set %S error %s\n", TOPO_PROP_PHY_COUNT, 1169 topo_strerror(err)); 1170 } 1171 } 1172 } 1173 1174 return (0); 1175 } 1176 1177 /* 1178 * Instantiate SAS expander nodes for a given ESC Electronics node(controller) 1179 * nodes. 1180 */ 1181 /*ARGSUSED*/ 1182 static int 1183 ses_create_esc_sasspecific(ses_enum_data_t *sdp, ses_enum_node_t *snp, 1184 tnode_t *pnode, ses_enum_chassis_t *cp, 1185 boolean_t dorange) 1186 { 1187 topo_mod_t *mod = sdp->sed_mod; 1188 tnode_t *exptn, *contn; 1189 boolean_t found; 1190 sas_connector_phy_data_t connectors[64] = {NULL}; 1191 uint64_t max; 1192 ses_enum_node_t *ctlsnp, *xsnp, *consnp; 1193 ses_node_t *np = snp->sen_node; 1194 nvlist_t *props, *psprops; 1195 uint64_t index, psindex, conindex, psstatus, i, j, count; 1196 int64_t cidxlist[256] = {NULL}; 1197 int phycount; 1198 1199 props = ses_node_props(np); 1200 1201 if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX, 1202 &index) != 0) 1203 return (-1); 1204 1205 /* 1206 * For SES constroller node, check to see if there are 1207 * associated SAS expanders. 1208 */ 1209 found = B_FALSE; 1210 max = 0; 1211 for (ctlsnp = topo_list_next(&cp->sec_nodes); ctlsnp != NULL; 1212 ctlsnp = topo_list_next(ctlsnp)) { 1213 if (ctlsnp->sen_type == SES_ET_SAS_EXPANDER) { 1214 found = B_TRUE; 1215 if (ctlsnp->sen_instance > max) 1216 max = ctlsnp->sen_instance; 1217 } 1218 } 1219 1220 /* 1221 * No SAS expander found notthing to process. 1222 */ 1223 if (!found) 1224 return (0); 1225 1226 topo_mod_dprintf(mod, "%s Controller %d: creating " 1227 "%llu %s nodes", cp->sec_csn, index, max + 1, SASEXPANDER); 1228 1229 /* 1230 * The max number represent the number of elements 1231 * deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX 1232 * of SET_ET_SAS_EXPANDER type element. 1233 * 1234 * There may be multiple ESC Electronics element(controllers) 1235 * within JBOD(typicall two for redundancy) and SAS expander 1236 * elements are associated with only one of them. We are 1237 * still creating the range based max number here. 1238 * That will cover the case that all expanders are associated 1239 * with one SES controller. 1240 */ 1241 if (dorange && topo_node_range_create(mod, pnode, 1242 SASEXPANDER, 0, max) != 0) { 1243 topo_mod_dprintf(mod, 1244 "topo_node_create_range() failed: %s", 1245 topo_mod_errmsg(mod)); 1246 return (-1); 1247 } 1248 1249 /* 1250 * Search exapnders with the parent index matching with 1251 * ESC Electronics element index. 1252 * Note the index used here is a global index across 1253 * SES elements. 1254 */ 1255 for (xsnp = topo_list_next(&cp->sec_nodes); xsnp != NULL; 1256 xsnp = topo_list_next(xsnp)) { 1257 if (xsnp->sen_type == SES_ET_SAS_EXPANDER) { 1258 /* 1259 * get the parent ESC controller. 1260 */ 1261 psprops = ses_node_props(xsnp->sen_node); 1262 if (nvlist_lookup_uint64(psprops, 1263 SES_PROP_STATUS_CODE, &psstatus) == 0) { 1264 if (psstatus == SES_ESC_NOT_INSTALLED) { 1265 /* 1266 * Not installed. 1267 * Don't create a ndoe. 1268 */ 1269 continue; 1270 } 1271 } else { 1272 /* 1273 * The element should have status code. 1274 * If not there is no way to find 1275 * out if the expander element exist or 1276 * not. 1277 */ 1278 continue; 1279 } 1280 1281 /* Get the physical parent index to compare. */ 1282 if (nvlist_lookup_uint64(psprops, 1283 LIBSES_PROP_PHYS_PARENT, &psindex) == 0) { 1284 if (index == psindex) { 1285 /* indentation moved forward */ 1286 /* 1287 * Handle basic node information of SAS expander 1288 * element - binding to parent node and 1289 * allocating FMRI... 1290 */ 1291 if (ses_create_generic(sdp, xsnp, pnode, SASEXPANDER, 1292 "SAS-EXPANDER", &exptn) != 0) 1293 continue; 1294 /* 1295 * Now handle SAS expander unique portion of node creation. 1296 * The max nubmer of the phy count is 256 since SES-2 1297 * defines as 1 byte field. The cidxlist has the same 1298 * number of elements. 1299 * 1300 * We use size 64 array to store the connectors. 1301 * Typically a connectors associated with 4 phys so that 1302 * matches with the max number of connecters associated 1303 * with an expander. 1304 * The phy count goes up to 38 for Sun supported 1305 * JBOD. 1306 */ 1307 memset(cidxlist, 0, sizeof (int64_t) * 64); 1308 if (ses_set_expander_props(sdp, xsnp, pnode, exptn, &phycount, 1309 cidxlist) != 0) { 1310 /* 1311 * error on getting specific prop failed. 1312 * continue on. Note that the node is 1313 * left bound. 1314 */ 1315 continue; 1316 } 1317 1318 /* 1319 * count represetns the number of connectors discovered so far. 1320 */ 1321 count = 0; 1322 memset(connectors, 0, sizeof (sas_connector_phy_data_t) * 64); 1323 for (i = 0; i < phycount; i++) { 1324 if (cidxlist[i] != -1) { 1325 /* connector index is valid. */ 1326 for (j = 0; j < count; j++) { 1327 if (connectors[j].index == 1328 cidxlist[i]) { 1329 /* 1330 * Just update phy mask. 1331 * The postion for connector 1332 * index lists(cidxlist index) 1333 * is set. 1334 */ 1335 connectors[j].phy_mask = 1336 connectors[j].phy_mask | 1337 (1ULL << i); 1338 break; 1339 } 1340 } 1341 /* 1342 * If j and count matche a new connector 1343 * index is found. 1344 */ 1345 if (j == count) { 1346 /* add a new index and phy mask. */ 1347 connectors[count].index = cidxlist[i]; 1348 connectors[count].phy_mask = 1349 connectors[count].phy_mask | 1350 (1ULL << i); 1351 count++; 1352 } 1353 } 1354 } 1355 1356 /* 1357 * create range for the connector nodes. 1358 * The class index of the ses connector element 1359 * is set as the instance nubmer for the node. 1360 * Even though one expander may not have all connectors 1361 * are associated with we are creating the range with 1362 * max possible instance number. 1363 */ 1364 found = B_FALSE; 1365 max = 0; 1366 for (consnp = topo_list_next(&cp->sec_nodes); 1367 consnp != NULL; consnp = topo_list_next(consnp)) { 1368 if (consnp->sen_type == SES_ET_SAS_CONNECTOR) { 1369 psprops = ses_node_props(consnp->sen_node); 1370 found = B_TRUE; 1371 if (consnp->sen_instance > max) 1372 max = consnp->sen_instance; 1373 } 1374 } 1375 1376 /* 1377 * No SAS connector found nothing to process. 1378 */ 1379 if (!found) 1380 return (0); 1381 1382 if (dorange && topo_node_range_create(mod, exptn, 1383 RECEPTACLE, 0, max) != 0) { 1384 topo_mod_dprintf(mod, 1385 "topo_node_create_range() failed: %s", 1386 topo_mod_errmsg(mod)); 1387 return (-1); 1388 } 1389 1390 /* search matching connector element using the index. */ 1391 for (i = 0; i < count; i++) { 1392 found = B_FALSE; 1393 for (consnp = topo_list_next(&cp->sec_nodes); 1394 consnp != NULL; consnp = topo_list_next(consnp)) { 1395 if (consnp->sen_type == SES_ET_SAS_CONNECTOR) { 1396 psprops = ses_node_props( 1397 consnp->sen_node); 1398 /* 1399 * Get the physical parent index to 1400 * compare. 1401 * The connector elements are children 1402 * of ESC Electronics element even 1403 * though we enumerate them under 1404 * an expander in libtopo. 1405 */ 1406 if (nvlist_lookup_uint64(psprops, 1407 SES_PROP_ELEMENT_ONLY_INDEX, 1408 &conindex) == 0) { 1409 if (conindex == 1410 connectors[i].index) { 1411 found = B_TRUE; 1412 break; 1413 } 1414 } 1415 } 1416 } 1417 1418 /* now create a libtopo node. */ 1419 if (found) { 1420 /* Create generic props. */ 1421 if (ses_create_generic(sdp, consnp, exptn, 1422 RECEPTACLE, "RECEPTACLE", &contn) != 1423 0) { 1424 continue; 1425 } 1426 /* Create connector specific props. */ 1427 if (ses_set_connector_props(sdp, consnp, 1428 contn, connectors[i].phy_mask) != 0) { 1429 continue; 1430 } 1431 } 1432 } 1433 /* end indentation change */ 1434 } 1435 } 1436 } 1437 } 1438 1439 return (0); 1440 } 1441 1442 /* 1443 * Instantiate any protocol specific portion of a node. 1444 */ 1445 /*ARGSUSED*/ 1446 static int 1447 ses_create_protocol_specific(ses_enum_data_t *sdp, ses_enum_node_t *snp, 1448 tnode_t *pnode, uint64_t type, ses_enum_chassis_t *cp, 1449 boolean_t dorange) 1450 { 1451 1452 if (type == SES_ET_ESC_ELECTRONICS) { 1453 /* create SAS specific children(expanders and connectors. */ 1454 return (ses_create_esc_sasspecific(sdp, snp, pnode, cp, 1455 dorange)); 1456 } 1457 1458 return (0); 1459 } 1460 1461 /* 1462 * Instantiate any children of a given type. 1463 */ 1464 static int 1465 ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type, 1466 const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp, 1467 boolean_t dorange) 1468 { 1469 topo_mod_t *mod = sdp->sed_mod; 1470 boolean_t found; 1471 uint64_t max; 1472 ses_enum_node_t *snp; 1473 tnode_t *tn; 1474 1475 /* 1476 * First go through and count how many matching nodes we have. 1477 */ 1478 max = 0; 1479 found = B_FALSE; 1480 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1481 snp = topo_list_next(snp)) { 1482 if (snp->sen_type == type) { 1483 found = B_TRUE; 1484 if (snp->sen_instance > max) 1485 max = snp->sen_instance; 1486 } 1487 } 1488 1489 /* 1490 * No enclosure should export both DEVICE and ARRAY_DEVICE elements. 1491 * Since we map both of these to 'disk', if an enclosure does this, we 1492 * just ignore the array elements. 1493 */ 1494 if (!found || 1495 (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev)) 1496 return (0); 1497 1498 topo_mod_dprintf(mod, "%s: creating %llu %s nodes", 1499 cp->sec_csn, max + 1, nodename); 1500 1501 if (dorange && topo_node_range_create(mod, pnode, 1502 nodename, 0, max) != 0) { 1503 topo_mod_dprintf(mod, 1504 "topo_node_create_range() failed: %s", 1505 topo_mod_errmsg(mod)); 1506 return (-1); 1507 } 1508 1509 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1510 snp = topo_list_next(snp)) { 1511 if (snp->sen_type == type) { 1512 if (ses_create_generic(sdp, snp, pnode, 1513 nodename, defaultlabel, &tn) != 0) 1514 return (-1); 1515 /* 1516 * For some SES element there may be protocol specific 1517 * information to process. Here we are processing 1518 * the association between enclosure controller and 1519 * SAS expanders. 1520 */ 1521 if (type == SES_ET_ESC_ELECTRONICS) { 1522 /* create SAS expander node */ 1523 if (ses_create_protocol_specific(sdp, snp, 1524 tn, type, cp, dorange) != 0) { 1525 return (-1); 1526 } 1527 } 1528 1529 } 1530 } 1531 1532 return (0); 1533 } 1534 1535 /* 1536 * Instantiate a new subchassis instance in the topology. 1537 */ 1538 static int 1539 ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode, 1540 ses_enum_chassis_t *scp) 1541 { 1542 topo_mod_t *mod = sdp->sed_mod; 1543 tnode_t *tn; 1544 nvlist_t *props; 1545 nvlist_t *auth = NULL, *fmri = NULL; 1546 uint64_t instance = scp->sec_instance; 1547 char *desc; 1548 char label[128]; 1549 char **paths; 1550 int i, err; 1551 ses_enum_target_t *stp; 1552 int ret = -1; 1553 1554 /* 1555 * Copy authority information from parent enclosure node 1556 */ 1557 if ((auth = topo_mod_auth(mod, pnode)) == NULL) 1558 goto error; 1559 1560 /* 1561 * Record the subchassis serial number in the FMRI. 1562 * For now, we assume that logical id is the subchassis serial number. 1563 * If this assumption changes in future, then the following 1564 * piece of code will need to be updated via an RFE. 1565 */ 1566 if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 1567 SUBCHASSIS, (topo_instance_t)instance, NULL, auth, NULL, NULL, 1568 NULL)) == NULL) { 1569 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 1570 topo_mod_errmsg(mod)); 1571 goto error; 1572 } 1573 1574 if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS, 1575 instance, fmri)) == NULL) { 1576 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 1577 topo_mod_errmsg(mod)); 1578 goto error; 1579 } 1580 1581 props = ses_node_props(scp->sec_enclosure); 1582 1583 /* 1584 * Look for the subchassis label in the following order: 1585 * <ses-description> 1586 * <ses-class-description> <instance> 1587 * <default-type-label> <instance> 1588 * 1589 * For subchassis, the default label is "SUBCHASSIS" 1590 */ 1591 if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 1592 desc[0] == '\0') { 1593 if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, 1594 &desc) == 0 && desc[0] != '\0') 1595 (void) snprintf(label, sizeof (label), "%s %llu", desc, 1596 instance); 1597 else 1598 (void) snprintf(label, sizeof (label), 1599 "SUBCHASSIS %llu", instance); 1600 desc = label; 1601 } 1602 1603 if (topo_node_label_set(tn, desc, &err) != 0) 1604 goto error; 1605 1606 if (ses_set_standard_props(mod, NULL, tn, NULL, 1607 ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0) 1608 goto error; 1609 1610 /* 1611 * Set the 'chassis-type' property for this subchassis. This is either 1612 * 'ses-class-description' or 'subchassis'. 1613 */ 1614 if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, &desc) != 0) 1615 desc = "subchassis"; 1616 1617 if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 1618 TOPO_PROP_CHASSIS_TYPE, TOPO_PROP_IMMUTABLE, desc, &err) != 0) { 1619 topo_mod_dprintf(mod, "failed to create property %s: %s\n", 1620 TOPO_PROP_CHASSIS_TYPE, topo_strerror(err)); 1621 goto error; 1622 } 1623 1624 /* 1625 * For enclosures, we want to include all possible targets (for upgrade 1626 * purposes). 1627 */ 1628 for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 1629 stp = topo_list_next(stp), i++) 1630 ; 1631 1632 verify(i != 0); 1633 paths = alloca(i * sizeof (char *)); 1634 1635 for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 1636 stp = topo_list_next(stp), i++) 1637 paths[i] = stp->set_devpath; 1638 1639 if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 1640 TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 1641 i, &err) != 0) { 1642 topo_mod_dprintf(mod, "failed to create property %s: %s\n", 1643 TOPO_PROP_PATHS, topo_strerror(err)); 1644 goto error; 1645 } 1646 1647 if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 1648 topo_mod_dprintf(mod, "topo_method_register() failed: %s", 1649 topo_mod_errmsg(mod)); 1650 goto error; 1651 } 1652 1653 /* 1654 * Create the nodes for controllers and bays. 1655 */ 1656 if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 1657 CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 || 1658 ses_create_children(sdp, tn, SES_ET_DEVICE, 1659 BAY, "BAY", scp, B_TRUE) != 0 || 1660 ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 1661 BAY, "BAY", scp, B_TRUE) != 0) 1662 goto error; 1663 1664 ret = 0; 1665 1666 error: 1667 nvlist_free(auth); 1668 nvlist_free(fmri); 1669 return (ret); 1670 } 1671 1672 /* 1673 * Instantiate a new chassis instance in the topology. 1674 */ 1675 static int 1676 ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp) 1677 { 1678 topo_mod_t *mod = sdp->sed_mod; 1679 nvlist_t *props; 1680 char *raw_manufacturer, *raw_model, *raw_revision; 1681 char *manufacturer = NULL, *model = NULL, *product = NULL; 1682 char *revision = NULL; 1683 char *serial; 1684 char **paths; 1685 size_t prodlen; 1686 tnode_t *tn; 1687 nvlist_t *fmri = NULL, *auth = NULL; 1688 int ret = -1; 1689 ses_enum_node_t *snp; 1690 ses_enum_target_t *stp; 1691 ses_enum_chassis_t *scp; 1692 int i, err; 1693 uint64_t sc_count = 0; 1694 1695 /* 1696 * Ignore any internal enclosures. 1697 */ 1698 if (cp->sec_internal) 1699 return (0); 1700 1701 /* 1702 * Check to see if there are any devices presennt in the chassis. If 1703 * not, ignore the chassis alltogether. This is most useful for 1704 * ignoring internal HBAs that present a SES target but don't actually 1705 * manage any of the devices. 1706 */ 1707 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1708 snp = topo_list_next(snp)) { 1709 if (snp->sen_type == SES_ET_DEVICE || 1710 snp->sen_type == SES_ET_ARRAY_DEVICE) 1711 break; 1712 } 1713 1714 if (snp == NULL) 1715 return (0); 1716 1717 props = ses_node_props(cp->sec_enclosure); 1718 1719 /* 1720 * We use the following property mappings: 1721 * 1722 * manufacturer vendor-id 1723 * model product-id 1724 * serial-number libses-chassis-serial 1725 */ 1726 verify(nvlist_lookup_string(props, SES_EN_PROP_VID, 1727 &raw_manufacturer) == 0); 1728 verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0); 1729 verify(nvlist_lookup_string(props, SES_EN_PROP_REV, 1730 &raw_revision) == 0); 1731 verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0); 1732 1733 /* 1734 * To construct the authority information, we 'clean' each string by 1735 * removing any offensive characters and trimmming whitespace. For the 1736 * 'product-id', we use a concatenation of 'manufacturer-model'. We 1737 * also take the numerical serial number and convert it to a string. 1738 */ 1739 if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL || 1740 (model = disk_auth_clean(mod, raw_model)) == NULL || 1741 (revision = disk_auth_clean(mod, raw_revision)) == NULL) { 1742 goto error; 1743 } 1744 1745 prodlen = strlen(manufacturer) + strlen(model) + 2; 1746 if ((product = topo_mod_alloc(mod, prodlen)) == NULL) 1747 goto error; 1748 1749 (void) snprintf(product, prodlen, "%s-%s", manufacturer, model); 1750 1751 /* 1752 * Construct the topo node and bind it to our parent. 1753 */ 1754 if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) 1755 goto error; 1756 1757 if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 || 1758 nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) { 1759 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 1760 goto error; 1761 } 1762 1763 /* 1764 * We pass NULL for the parent FMRI because there is no resource 1765 * associated with it. For the toplevel enclosure, we leave the 1766 * serial/part/revision portions empty, which are reserved for 1767 * individual components within the chassis. 1768 */ 1769 if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 1770 SES_ENCLOSURE, cp->sec_instance, NULL, auth, 1771 model, revision, serial)) == NULL) { 1772 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 1773 topo_mod_errmsg(mod)); 1774 goto error; 1775 } 1776 1777 if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE, 1778 cp->sec_instance, fmri)) == NULL) { 1779 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 1780 topo_mod_errmsg(mod)); 1781 goto error; 1782 } 1783 1784 if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 1785 topo_mod_dprintf(mod, 1786 "topo_method_register() failed: %s", 1787 topo_mod_errmsg(mod)); 1788 goto error; 1789 } 1790 1791 if (ses_set_standard_props(mod, NULL, tn, auth, 1792 ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0) 1793 goto error; 1794 1795 /* 1796 * For enclosures, we want to include all possible targets (for upgrade 1797 * purposes). 1798 */ 1799 for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 1800 stp = topo_list_next(stp), i++) 1801 ; 1802 1803 verify(i != 0); 1804 paths = alloca(i * sizeof (char *)); 1805 1806 for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 1807 stp = topo_list_next(stp), i++) 1808 paths[i] = stp->set_devpath; 1809 1810 1811 if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 1812 TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 1813 i, &err) != 0) { 1814 topo_mod_dprintf(mod, 1815 "failed to create property %s: %s\n", 1816 TOPO_PROP_PATHS, topo_strerror(err)); 1817 goto error; 1818 } 1819 1820 /* 1821 * Create the nodes for power supplies, fans, controllers and devices. 1822 * Note that SAS exopander nodes and connector nodes are handled 1823 * through protocol specific processing of controllers. 1824 */ 1825 if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY, 1826 PSU, "PSU", cp, B_TRUE) != 0 || 1827 ses_create_children(sdp, tn, SES_ET_COOLING, 1828 FAN, "FAN", cp, B_TRUE) != 0 || 1829 ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 1830 CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 || 1831 ses_create_children(sdp, tn, SES_ET_DEVICE, 1832 BAY, "BAY", cp, B_TRUE) != 0 || 1833 ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 1834 BAY, "BAY", cp, B_TRUE) != 0) 1835 goto error; 1836 1837 if (cp->sec_maxinstance >= 0 && 1838 (topo_node_range_create(mod, tn, SUBCHASSIS, 0, 1839 cp->sec_maxinstance) != 0)) { 1840 topo_mod_dprintf(mod, "topo_node_create_range() failed: %s", 1841 topo_mod_errmsg(mod)); 1842 goto error; 1843 } 1844 1845 for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL; 1846 scp = topo_list_next(scp)) { 1847 1848 if (ses_create_subchassis(sdp, tn, scp) != 0) 1849 goto error; 1850 1851 topo_mod_dprintf(mod, "created Subchassis node with " 1852 "instance %u\nand target (%s) under Chassis with CSN %s", 1853 scp->sec_instance, scp->sec_target->set_devpath, 1854 cp->sec_csn); 1855 1856 sc_count++; 1857 } 1858 1859 topo_mod_dprintf(mod, "%s: created %llu %s nodes", 1860 cp->sec_csn, sc_count, SUBCHASSIS); 1861 1862 cp->sec_target->set_refcount++; 1863 topo_node_setspecific(tn, cp->sec_target); 1864 1865 ret = 0; 1866 error: 1867 topo_mod_strfree(mod, manufacturer); 1868 topo_mod_strfree(mod, model); 1869 topo_mod_strfree(mod, revision); 1870 topo_mod_strfree(mod, product); 1871 1872 nvlist_free(fmri); 1873 nvlist_free(auth); 1874 return (ret); 1875 } 1876 1877 /* 1878 * Create a bay node explicitly enumerated via XML. 1879 */ 1880 static int 1881 ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode) 1882 { 1883 topo_mod_t *mod = sdp->sed_mod; 1884 ses_enum_chassis_t *cp; 1885 1886 /* 1887 * Iterate over chassis looking for an internal enclosure. This 1888 * property is set via a vendor-specific plugin, and there should only 1889 * ever be a single internal chassis in a system. 1890 */ 1891 for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 1892 cp = topo_list_next(cp)) { 1893 if (cp->sec_internal) 1894 break; 1895 } 1896 1897 if (cp == NULL) { 1898 topo_mod_dprintf(mod, "failed to find internal chassis\n"); 1899 return (-1); 1900 } 1901 1902 if (ses_create_children(sdp, pnode, SES_ET_DEVICE, 1903 BAY, "BAY", cp, B_FALSE) != 0 || 1904 ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE, 1905 BAY, "BAY", cp, B_FALSE) != 0) 1906 return (-1); 1907 1908 return (0); 1909 } 1910 1911 /* 1912 * Initialize chassis or subchassis. 1913 */ 1914 static int 1915 ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp, 1916 ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props, 1917 uint64_t subchassis, ses_chassis_type_e flags) 1918 { 1919 boolean_t internal, ident; 1920 1921 assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS | 1922 SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0); 1923 1924 assert(cp != NULL); 1925 assert(np != NULL); 1926 assert(props != NULL); 1927 1928 if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS)) 1929 assert(pcp != NULL); 1930 1931 topo_mod_dprintf(mod, "ses_init_chassis: %s: index %llu, flags (%d)", 1932 sdp->sed_name, subchassis, flags); 1933 1934 if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) { 1935 1936 topo_mod_dprintf(mod, "new chassis/subchassis"); 1937 if (nvlist_lookup_boolean_value(props, 1938 LIBSES_EN_PROP_INTERNAL, &internal) == 0) 1939 cp->sec_internal = internal; 1940 1941 cp->sec_enclosure = np; 1942 cp->sec_target = sdp->sed_target; 1943 1944 if (flags & SES_NEW_CHASSIS) { 1945 if (!cp->sec_internal) 1946 cp->sec_instance = sdp->sed_instance++; 1947 topo_list_append(&sdp->sed_chassis, cp); 1948 } else { 1949 if (subchassis != NO_SUBCHASSIS) 1950 cp->sec_instance = subchassis; 1951 else 1952 cp->sec_instance = pcp->sec_scinstance++; 1953 1954 if (cp->sec_instance > pcp->sec_maxinstance) 1955 pcp->sec_maxinstance = cp->sec_instance; 1956 1957 topo_list_append(&pcp->sec_subchassis, cp); 1958 } 1959 1960 } else { 1961 topo_mod_dprintf(mod, "dup chassis/subchassis"); 1962 if (nvlist_lookup_boolean_value(props, 1963 SES_PROP_IDENT, &ident) == 0) { 1964 topo_mod_dprintf(mod, "overriding enclosure node"); 1965 1966 cp->sec_enclosure = np; 1967 cp->sec_target = sdp->sed_target; 1968 } 1969 } 1970 1971 topo_list_append(&cp->sec_targets, sdp->sed_target); 1972 sdp->sed_current = cp; 1973 1974 return (0); 1975 } 1976 1977 /* 1978 * Gather nodes from the current SES target into our chassis list, merging the 1979 * results if necessary. 1980 */ 1981 static ses_walk_action_t 1982 ses_enum_gather(ses_node_t *np, void *data) 1983 { 1984 nvlist_t *props = ses_node_props(np); 1985 ses_enum_data_t *sdp = data; 1986 topo_mod_t *mod = sdp->sed_mod; 1987 ses_enum_chassis_t *cp, *scp; 1988 ses_enum_node_t *snp; 1989 ses_alt_node_t *sap; 1990 char *csn; 1991 uint64_t instance, type; 1992 uint64_t prevstatus, status; 1993 boolean_t report; 1994 uint64_t subchassis = NO_SUBCHASSIS; 1995 1996 if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 1997 /* 1998 * If we have already identified the chassis for this target, 1999 * then this is a secondary enclosure and we should ignore it, 2000 * along with the rest of the tree (since this is depth-first). 2001 */ 2002 if (sdp->sed_current != NULL) 2003 return (SES_WALK_ACTION_TERMINATE); 2004 2005 /* 2006 * Go through the list of chassis we have seen so far and see 2007 * if this serial number matches one of the known values. 2008 * If so, check whether this enclosure is a subchassis. 2009 */ 2010 if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 2011 &csn) != 0) 2012 return (SES_WALK_ACTION_TERMINATE); 2013 2014 (void) nvlist_lookup_uint64(props, LIBSES_EN_PROP_SUBCHASSIS_ID, 2015 &subchassis); 2016 2017 topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) " 2018 "CSN (%s), subchassis (%llu)", sdp->sed_name, csn, 2019 subchassis); 2020 2021 /* 2022 * We need to determine whether this enclosure node 2023 * represents a chassis or a subchassis. Since we may 2024 * receive the enclosure nodes in a non-deterministic 2025 * manner, we need to account for all possible combinations: 2026 * 1. Chassis for the current CSN has not yet been 2027 * allocated 2028 * 1.1 This is a new chassis: 2029 * allocate and instantiate the chassis 2030 * 1.2 This is a new subchassis: 2031 * allocate a placeholder chassis 2032 * allocate and instantiate the subchassis 2033 * link the subchassis to the chassis 2034 * 2. Chassis for the current CSN has been allocated 2035 * 2.1 This is a duplicate chassis enclosure 2036 * check whether to override old chassis 2037 * append to chassis' target list 2038 * 2.2 Only placeholder chassis exists 2039 * fill in the chassis fields 2040 * 2.3 This is a new subchassis 2041 * allocate and instantiate the subchassis 2042 * link the subchassis to the chassis 2043 * 2.4 This is a duplicate subchassis enclosure 2044 * check whether to override old chassis 2045 * append to chassis' target list 2046 */ 2047 2048 for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 2049 cp = topo_list_next(cp)) 2050 if (strcmp(cp->sec_csn, csn) == 0) 2051 break; 2052 2053 if (cp == NULL) { 2054 /* 1. Haven't seen a chassis with this CSN before */ 2055 2056 if ((cp = topo_mod_zalloc(mod, 2057 sizeof (ses_enum_chassis_t))) == NULL) 2058 goto error; 2059 2060 cp->sec_scinstance = SES_STARTING_SUBCHASSIS; 2061 cp->sec_maxinstance = -1; 2062 cp->sec_csn = csn; 2063 2064 if (subchassis == NO_SUBCHASSIS) { 2065 /* 1.1 This is a new chassis */ 2066 2067 topo_mod_dprintf(mod, "%s: Initialize new " 2068 "chassis with CSN %s", sdp->sed_name, csn); 2069 2070 if (ses_init_chassis(mod, sdp, NULL, cp, 2071 np, props, NO_SUBCHASSIS, 2072 SES_NEW_CHASSIS) < 0) 2073 goto error; 2074 } else { 2075 /* 1.2 This is a new subchassis */ 2076 2077 topo_mod_dprintf(mod, "%s: Initialize new " 2078 "subchassis with CSN %s and index %llu", 2079 sdp->sed_name, csn, subchassis); 2080 2081 if ((scp = topo_mod_zalloc(mod, 2082 sizeof (ses_enum_chassis_t))) == NULL) 2083 goto error; 2084 2085 scp->sec_csn = csn; 2086 2087 if (ses_init_chassis(mod, sdp, cp, scp, np, 2088 props, subchassis, SES_NEW_SUBCHASSIS) < 0) 2089 goto error; 2090 } 2091 } else { 2092 /* 2093 * We have a chassis or subchassis with this CSN. If 2094 * it's a chassis, we must check to see whether it is 2095 * a placeholder previously created because we found a 2096 * subchassis with this CSN. We will know that because 2097 * the sec_target value will not be set; it is set only 2098 * in ses_init_chassis(). In that case, initialise it 2099 * as a new chassis; otherwise, it's a duplicate and we 2100 * need to append only. 2101 */ 2102 if (subchassis == NO_SUBCHASSIS) { 2103 if (cp->sec_target != NULL) { 2104 /* 2.1 This is a duplicate chassis */ 2105 2106 topo_mod_dprintf(mod, "%s: Append " 2107 "duplicate chassis with CSN (%s)", 2108 sdp->sed_name, csn); 2109 2110 if (ses_init_chassis(mod, sdp, NULL, cp, 2111 np, props, NO_SUBCHASSIS, 2112 SES_DUP_CHASSIS) < 0) 2113 goto error; 2114 } else { 2115 /* Placeholder chassis - init it up */ 2116 topo_mod_dprintf(mod, "%s: Initialize" 2117 "placeholder chassis with CSN %s", 2118 sdp->sed_name, csn); 2119 2120 if (ses_init_chassis(mod, sdp, NULL, 2121 cp, np, props, NO_SUBCHASSIS, 2122 SES_NEW_CHASSIS) < 0) 2123 goto error; 2124 2125 } 2126 } else { 2127 /* This is a subchassis */ 2128 2129 for (scp = topo_list_next(&cp->sec_subchassis); 2130 scp != NULL; scp = topo_list_next(scp)) 2131 if (scp->sec_instance == subchassis) 2132 break; 2133 2134 if (scp == NULL) { 2135 /* 2.3 This is a new subchassis */ 2136 2137 topo_mod_dprintf(mod, "%s: Initialize " 2138 "new subchassis with CSN (%s) " 2139 "and LID (%s)", 2140 sdp->sed_name, csn); 2141 2142 if ((scp = topo_mod_zalloc(mod, 2143 sizeof (ses_enum_chassis_t))) 2144 == NULL) 2145 goto error; 2146 2147 scp->sec_csn = csn; 2148 2149 if (ses_init_chassis(mod, sdp, cp, scp, 2150 np, props, subchassis, 2151 SES_NEW_SUBCHASSIS) < 0) 2152 goto error; 2153 } else { 2154 /* 2.4 This is a duplicate subchassis */ 2155 2156 topo_mod_dprintf(mod, "%s: Append " 2157 "duplicate subchassis with " 2158 "CSN (%s)", sdp->sed_name, csn); 2159 2160 if (ses_init_chassis(mod, sdp, cp, scp, 2161 np, props, subchassis, 2162 SES_DUP_SUBCHASSIS) < 0) 2163 goto error; 2164 } 2165 } 2166 } 2167 } else if (ses_node_type(np) == SES_NODE_ELEMENT) { 2168 /* 2169 * If we haven't yet seen an enclosure node and identified the 2170 * current chassis, something is very wrong; bail out. 2171 */ 2172 if (sdp->sed_current == NULL) 2173 return (SES_WALK_ACTION_TERMINATE); 2174 2175 /* 2176 * If this isn't one of the element types we care about, then 2177 * ignore it. 2178 */ 2179 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 2180 &type) == 0); 2181 if (type != SES_ET_DEVICE && 2182 type != SES_ET_ARRAY_DEVICE && 2183 type != SES_ET_COOLING && 2184 type != SES_ET_POWER_SUPPLY && 2185 type != SES_ET_ESC_ELECTRONICS && 2186 type != SES_ET_SAS_EXPANDER && 2187 type != SES_ET_SAS_CONNECTOR) 2188 return (SES_WALK_ACTION_CONTINUE); 2189 2190 /* 2191 * Get the current instance number and see if we already know 2192 * about this element. If so, it means we have multiple paths 2193 * to the same elements, and we should ignore the current path. 2194 */ 2195 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 2196 &instance) == 0); 2197 if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE) 2198 (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, 2199 &instance); 2200 2201 cp = sdp->sed_current; 2202 2203 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 2204 snp = topo_list_next(snp)) { 2205 if (snp->sen_type == type && 2206 snp->sen_instance == instance) 2207 break; 2208 } 2209 2210 /* 2211 * We prefer the new element under the following circumstances: 2212 * 2213 * - The currently known element's status is unknown or not 2214 * available, but the new element has a known status. This 2215 * occurs if a given element is only available through a 2216 * particular target. 2217 * 2218 * - This is an ESC_ELECTRONICS element, and the 'reported-via' 2219 * property is set. This allows us to get reliable firmware 2220 * revision information from the enclosure node. 2221 */ 2222 if (snp != NULL) { 2223 if (nvlist_lookup_uint64( 2224 ses_node_props(snp->sen_node), 2225 SES_PROP_STATUS_CODE, &prevstatus) != 0) 2226 prevstatus = SES_ESC_UNSUPPORTED; 2227 if (nvlist_lookup_uint64( 2228 props, SES_PROP_STATUS_CODE, &status) != 0) 2229 status = SES_ESC_UNSUPPORTED; 2230 if (nvlist_lookup_boolean_value( 2231 props, SES_PROP_REPORT, &report) != 0) 2232 report = B_FALSE; 2233 2234 if ((SES_STATUS_UNAVAIL(prevstatus) && 2235 !SES_STATUS_UNAVAIL(status)) || 2236 (type == SES_ET_ESC_ELECTRONICS && 2237 report)) { 2238 snp->sen_node = np; 2239 snp->sen_target = sdp->sed_target; 2240 } 2241 2242 if ((sap = topo_mod_zalloc(mod, 2243 sizeof (ses_alt_node_t))) == NULL) 2244 goto error; 2245 2246 sap->san_node = np; 2247 topo_list_append(&snp->sen_alt_nodes, sap); 2248 2249 return (SES_WALK_ACTION_CONTINUE); 2250 } 2251 2252 if ((snp = topo_mod_zalloc(mod, 2253 sizeof (ses_enum_node_t))) == NULL) 2254 goto error; 2255 2256 if ((sap = topo_mod_zalloc(mod, 2257 sizeof (ses_alt_node_t))) == NULL) { 2258 topo_mod_free(mod, snp, sizeof (ses_enum_node_t)); 2259 goto error; 2260 } 2261 2262 topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)", 2263 sdp->sed_name, type, instance); 2264 snp->sen_node = np; 2265 snp->sen_type = type; 2266 snp->sen_instance = instance; 2267 snp->sen_target = sdp->sed_target; 2268 sap->san_node = np; 2269 topo_list_append(&snp->sen_alt_nodes, sap); 2270 topo_list_append(&cp->sec_nodes, snp); 2271 2272 if (type == SES_ET_DEVICE) 2273 cp->sec_hasdev = B_TRUE; 2274 } 2275 2276 return (SES_WALK_ACTION_CONTINUE); 2277 2278 error: 2279 sdp->sed_errno = -1; 2280 return (SES_WALK_ACTION_TERMINATE); 2281 } 2282 2283 static int 2284 ses_process_dir(const char *dirpath, ses_enum_data_t *sdp) 2285 { 2286 topo_mod_t *mod = sdp->sed_mod; 2287 DIR *dir; 2288 struct dirent *dp; 2289 char path[PATH_MAX]; 2290 ses_enum_target_t *stp; 2291 int err = -1; 2292 2293 /* 2294 * Open the SES target directory and iterate over any available 2295 * targets. 2296 */ 2297 if ((dir = opendir(dirpath)) == NULL) { 2298 /* 2299 * If the SES target directory does not exist, then return as if 2300 * there are no active targets. 2301 */ 2302 topo_mod_dprintf(mod, "failed to open ses " 2303 "directory '%s'", dirpath); 2304 return (0); 2305 } 2306 2307 while ((dp = readdir(dir)) != NULL) { 2308 if (strcmp(dp->d_name, ".") == 0 || 2309 strcmp(dp->d_name, "..") == 0) 2310 continue; 2311 2312 /* 2313 * Create a new target instance and take a snapshot. 2314 */ 2315 if ((stp = topo_mod_zalloc(mod, 2316 sizeof (ses_enum_target_t))) == NULL) 2317 goto error; 2318 2319 (void) pthread_mutex_init(&stp->set_lock, NULL); 2320 2321 (void) snprintf(path, sizeof (path), "%s/%s", dirpath, 2322 dp->d_name); 2323 2324 /* 2325 * We keep track of the SES device path and export it on a 2326 * per-node basis to allow higher level software to get to the 2327 * corresponding SES state. 2328 */ 2329 if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) { 2330 topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 2331 goto error; 2332 } 2333 2334 if ((stp->set_target = 2335 ses_open(LIBSES_VERSION, path)) == NULL) { 2336 topo_mod_dprintf(mod, "failed to open ses target " 2337 "'%s': %s", dp->d_name, ses_errmsg()); 2338 topo_mod_strfree(mod, stp->set_devpath); 2339 topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 2340 continue; 2341 } 2342 2343 stp->set_refcount = 1; 2344 sdp->sed_target = stp; 2345 stp->set_snap = ses_snap_hold(stp->set_target); 2346 stp->set_snaptime = gethrtime(); 2347 2348 /* 2349 * Enumerate over all SES elements and merge them into the 2350 * correct ses_enum_chassis_t. 2351 */ 2352 sdp->sed_current = NULL; 2353 sdp->sed_errno = 0; 2354 sdp->sed_name = dp->d_name; 2355 (void) ses_walk(stp->set_snap, ses_enum_gather, sdp); 2356 2357 if (sdp->sed_errno != 0) 2358 goto error; 2359 } 2360 2361 err = 0; 2362 error: 2363 closedir(dir); 2364 return (err); 2365 } 2366 2367 static void 2368 ses_release(topo_mod_t *mod, tnode_t *tn) 2369 { 2370 ses_enum_target_t *stp; 2371 2372 if ((stp = topo_node_getspecific(tn)) != NULL) 2373 ses_target_free(mod, stp); 2374 } 2375 2376 /*ARGSUSED*/ 2377 static int 2378 ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 2379 topo_instance_t min, topo_instance_t max, void *arg, void *notused) 2380 { 2381 ses_enum_chassis_t *cp; 2382 ses_enum_data_t *data; 2383 2384 /* 2385 * Check to make sure we're being invoked sensibly, and that we're not 2386 * being invoked as part of a post-processing step. 2387 */ 2388 if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0) 2389 return (0); 2390 2391 /* 2392 * If this is the first time we've called our enumeration method, then 2393 * gather information about any available enclosures. 2394 */ 2395 if ((data = topo_mod_getspecific(mod)) == NULL) { 2396 if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) == 2397 NULL) 2398 return (-1); 2399 2400 data->sed_mod = mod; 2401 topo_mod_setspecific(mod, data); 2402 2403 if (dev_list_gather(mod, &data->sed_devs) != 0) 2404 goto error; 2405 2406 /* 2407 * We search both the ses(7D) and sgen(7D) locations, so we are 2408 * independent of any particular driver class bindings. 2409 */ 2410 if (ses_process_dir("/dev/es", data) != 0 || 2411 ses_process_dir("/dev/scsi/ses", data) != 0) 2412 goto error; 2413 } 2414 2415 if (strcmp(name, SES_ENCLOSURE) == 0) { 2416 /* 2417 * This is a request to enumerate external enclosures. Go 2418 * through all the targets and create chassis nodes where 2419 * necessary. 2420 */ 2421 for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 2422 cp = topo_list_next(cp)) { 2423 if (ses_create_chassis(data, rnode, cp) != 0) 2424 goto error; 2425 } 2426 } else { 2427 /* 2428 * This is a request to enumerate a specific bay underneath the 2429 * root chassis (for internal disks). 2430 */ 2431 if (ses_create_bays(data, rnode) != 0) 2432 goto error; 2433 } 2434 2435 /* 2436 * This is a bit of a kludge. In order to allow internal disks to be 2437 * enumerated and share snapshot-specific information with the external 2438 * enclosure enumeration, we rely on the fact that we will be invoked 2439 * for the 'ses-enclosure' node last. 2440 */ 2441 if (strcmp(name, SES_ENCLOSURE) == 0) { 2442 for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 2443 cp = topo_list_next(cp)) 2444 ses_data_free(data, cp); 2445 ses_data_free(data, NULL); 2446 topo_mod_setspecific(mod, NULL); 2447 } 2448 return (0); 2449 2450 error: 2451 for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 2452 cp = topo_list_next(cp)) 2453 ses_data_free(data, cp); 2454 ses_data_free(data, NULL); 2455 topo_mod_setspecific(mod, NULL); 2456 return (-1); 2457 } 2458 2459 static const topo_modops_t ses_ops = 2460 { ses_enum, ses_release }; 2461 2462 static topo_modinfo_t ses_info = 2463 { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops }; 2464 2465 /*ARGSUSED*/ 2466 int 2467 _topo_init(topo_mod_t *mod, topo_version_t version) 2468 { 2469 if (getenv("TOPOSESDEBUG") != NULL) 2470 topo_mod_setdebug(mod); 2471 2472 topo_mod_dprintf(mod, "initializing %s enumerator\n", 2473 SES_ENCLOSURE); 2474 2475 return (topo_mod_register(mod, &ses_info, TOPO_VERSION)); 2476 } 2477 2478 void 2479 _topo_fini(topo_mod_t *mod) 2480 { 2481 topo_mod_unregister(mod); 2482 } 2483