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