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 io_pgroup = { 168 TOPO_PGROUP_IO, 169 TOPO_STABILITY_PRIVATE, 170 TOPO_STABILITY_PRIVATE, 171 1 172 }; 173 174 static const topo_pgroup_info_t storage_pgroup = { 175 TOPO_PGROUP_STORAGE, 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; 1380 dev_di_node_t *dnode; 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 if (strstr(dnode->ddn_dpath, sasaddr_str) != NULL) { 1404 found = B_TRUE; 1405 break; 1406 } 1407 } 1408 1409 if (!found) { 1410 topo_mod_dprintf(mod, 1411 "ses_set_expander_props: Failed to find matching " 1412 "devinfo node for Exapnder SAS address %s", 1413 SES_EXP_PROP_SAS_ADDR); 1414 /* continue on to get storage group props. */ 1415 } else { 1416 /* create/set the devfs-path and devid in the io group */ 1417 if (topo_pgroup_create(tnode, &io_pgroup, &err) != 0) { 1418 topo_mod_dprintf(mod, "ses_set_expander_props: " 1419 "create io error %s\n", topo_strerror(err)); 1420 goto error; 1421 } else { 1422 if (topo_prop_set_string(tnode, TOPO_PGROUP_IO, 1423 TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, 1424 dnode->ddn_dpath, &err) != 0) { 1425 topo_mod_dprintf(mod, "ses_set_expander_props: " 1426 "set dev error %s\n", topo_strerror(err)); 1427 } 1428 if (topo_prop_set_string(tnode, TOPO_PGROUP_IO, 1429 TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, 1430 dnode->ddn_devid, &err) != 0) { 1431 topo_mod_dprintf(mod, "ses_set_expander_props: " 1432 "set devid error %s\n", topo_strerror(err)); 1433 } 1434 if (dnode->ddn_ppath_count != 0 && 1435 topo_prop_set_string_array(tnode, TOPO_PGROUP_IO, 1436 TOPO_IO_PHYS_PATH, TOPO_PROP_IMMUTABLE, 1437 (const char **)dnode->ddn_ppath, 1438 dnode->ddn_ppath_count, &err) != 0) { 1439 topo_mod_dprintf(mod, "ses_set_expander_props: " 1440 "set phys-path error %s\n", 1441 topo_strerror(err)); 1442 } 1443 } 1444 } 1445 1446 /* create the storage group */ 1447 if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) { 1448 topo_mod_dprintf(mod, "ses_set_expander_props: " 1449 "create storage error %s\n", topo_strerror(err)); 1450 goto error; 1451 } else { 1452 /* set the SAS address prop of the expander. */ 1453 if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE, 1454 TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, sasaddr_str, 1455 &err) != 0) { 1456 topo_mod_dprintf(mod, "ses_set_expander_props: " 1457 "set %S error %s\n", TOPO_PROP_SAS_ADDR, 1458 topo_strerror(err)); 1459 } 1460 1461 /* Get the phy information for the expander */ 1462 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 1463 &phylist, &pcount) != 0) { 1464 topo_mod_dprintf(mod, 1465 "Failed to get prop %s.", SES_SAS_PROP_PHYS); 1466 } else { 1467 /* 1468 * For each phy, get the connector element index and 1469 * stores into connector element index array. 1470 */ 1471 *phycount = pcount; 1472 for (i = 0; i < pcount; i++) { 1473 if (nvlist_lookup_uint64(phylist[i], 1474 SES_PROP_CE_IDX, &connidx) == 0) { 1475 if (connidx != 0xff) { 1476 connlist[i] = connidx; 1477 } else { 1478 connlist[i] = -1; 1479 } 1480 } else { 1481 /* Fail to get the index. set to -1. */ 1482 connlist[i] = -1; 1483 } 1484 } 1485 1486 /* set the phy count prop of the expander. */ 1487 if (topo_prop_set_uint64(tnode, TOPO_PGROUP_STORAGE, 1488 TOPO_PROP_PHY_COUNT, TOPO_PROP_IMMUTABLE, pcount, 1489 &err) != 0) { 1490 topo_mod_dprintf(mod, "ses_set_expander_props: " 1491 "set %S error %s\n", TOPO_PROP_PHY_COUNT, 1492 topo_strerror(err)); 1493 } 1494 1495 /* 1496 * set the connector element index of 1497 * the expander phys. 1498 */ 1499 } 1500 1501 /* populate other misc storage group properties */ 1502 if (found) { 1503 if (dnode->ddn_mfg && (topo_prop_set_string(tnode, 1504 TOPO_PGROUP_STORAGE, TOPO_STORAGE_MANUFACTURER, 1505 TOPO_PROP_IMMUTABLE, dnode->ddn_mfg, &err) != 0)) { 1506 topo_mod_dprintf(mod, "ses_set_expander_props: " 1507 "set mfg error %s\n", topo_strerror(err)); 1508 } 1509 1510 if (dnode->ddn_model && (topo_prop_set_string(tnode, 1511 TOPO_PGROUP_STORAGE, TOPO_STORAGE_MODEL, 1512 TOPO_PROP_IMMUTABLE, 1513 dnode->ddn_model, &err) != 0)) { 1514 topo_mod_dprintf(mod, "ses_set_expander_props: " 1515 "set model error %s\n", topo_strerror(err)); 1516 } 1517 1518 if (dnode->ddn_serial && (topo_prop_set_string(tnode, 1519 TOPO_PGROUP_STORAGE, TOPO_STORAGE_SERIAL_NUM, 1520 TOPO_PROP_IMMUTABLE, 1521 dnode->ddn_serial, &err) != 0)) { 1522 topo_mod_dprintf(mod, "ses_set_expander_props: " 1523 "set serial error %s\n", 1524 topo_strerror(err)); 1525 } 1526 1527 if (dnode->ddn_firm && (topo_prop_set_string(tnode, 1528 TOPO_PGROUP_STORAGE, 1529 TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE, 1530 dnode->ddn_firm, &err) != 0)) { 1531 topo_mod_dprintf(mod, "ses_set_expander_props: " 1532 "set firm error %s\n", topo_strerror(err)); 1533 } 1534 } 1535 } 1536 1537 return (0); 1538 1539 error: 1540 nvlist_free(auth); 1541 nvlist_free(fmri); 1542 return (-1); 1543 } 1544 1545 /* 1546 * Create SAS expander specific props. 1547 */ 1548 /*ARGSUSED*/ 1549 static int 1550 ses_set_connector_props(ses_enum_data_t *sdp, ses_enum_node_t *snp, 1551 tnode_t *tnode, int64_t phy_mask) 1552 { 1553 ses_node_t *np = snp->sen_node; 1554 topo_mod_t *mod = sdp->sed_mod; 1555 nvlist_t *props; 1556 int err, i; 1557 uint64_t conntype; 1558 char phymask_str[17], *conntype_str; 1559 boolean_t found; 1560 1561 props = ses_node_props(np); 1562 1563 /* 1564 * convert phy mask to string. 1565 */ 1566 (void) snprintf(phymask_str, 17, "%llx", phy_mask); 1567 1568 /* create the storage group */ 1569 if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) { 1570 topo_mod_dprintf(mod, "ses_set_expander_props: " 1571 "create storage error %s\n", topo_strerror(err)); 1572 return (-1); 1573 } else { 1574 /* set the SAS address prop of the expander. */ 1575 if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE, 1576 TOPO_STORAGE_SAS_PHY_MASK, TOPO_PROP_IMMUTABLE, 1577 phymask_str, &err) != 0) { 1578 topo_mod_dprintf(mod, "ses_set_expander_props: " 1579 "set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK, 1580 topo_strerror(err)); 1581 } 1582 1583 /* Get the connector type information for the expander */ 1584 if (nvlist_lookup_uint64(props, 1585 SES_SC_PROP_CONNECTOR_TYPE, &conntype) != 0) { 1586 topo_mod_dprintf(mod, "Failed to get prop %s.", 1587 TOPO_STORAGE_SAS_PHY_MASK); 1588 } else { 1589 found = B_FALSE; 1590 for (i = 0; ; i++) { 1591 if (sas_connector_type_list[i].type == 1592 SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) { 1593 break; 1594 } 1595 if (sas_connector_type_list[i].type == 1596 conntype) { 1597 conntype_str = 1598 sas_connector_type_list[i].name; 1599 found = B_TRUE; 1600 break; 1601 } 1602 } 1603 1604 if (!found) { 1605 if (conntype < 1606 SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) { 1607 conntype_str = 1608 SAS_CONNECTOR_TYPE_RESERVED; 1609 } else { 1610 conntype_str = 1611 SAS_CONNECTOR_TYPE_NOT_DEFINED; 1612 } 1613 } 1614 1615 /* set the phy count prop of the expander. */ 1616 if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE, 1617 TOPO_STORAGE_SAS_CONNECTOR_TYPE, 1618 TOPO_PROP_IMMUTABLE, conntype_str, &err) != 0) { 1619 topo_mod_dprintf(mod, "ses_set_expander_props: " 1620 "set %S error %s\n", TOPO_PROP_PHY_COUNT, 1621 topo_strerror(err)); 1622 } 1623 } 1624 } 1625 1626 return (0); 1627 } 1628 1629 /* 1630 * Instantiate SAS expander nodes for a given ESC Electronics node(controller) 1631 * nodes. 1632 */ 1633 /*ARGSUSED*/ 1634 static int 1635 ses_create_esc_sasspecific(ses_enum_data_t *sdp, ses_enum_node_t *snp, 1636 tnode_t *pnode, ses_enum_chassis_t *cp, 1637 boolean_t dorange) 1638 { 1639 topo_mod_t *mod = sdp->sed_mod; 1640 tnode_t *exptn, *contn; 1641 boolean_t found; 1642 sas_connector_phy_data_t connectors[64] = {NULL}; 1643 uint64_t max; 1644 ses_enum_node_t *ctlsnp, *xsnp, *consnp; 1645 ses_node_t *np = snp->sen_node; 1646 nvlist_t *props, *psprops; 1647 uint64_t index, psindex, conindex, psstatus, i, j, count; 1648 int64_t cidxlist[256] = {NULL}; 1649 int phycount; 1650 1651 props = ses_node_props(np); 1652 1653 if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX, 1654 &index) != 0) 1655 return (-1); 1656 1657 /* 1658 * For SES constroller node, check to see if there are 1659 * associated SAS expanders. 1660 */ 1661 found = B_FALSE; 1662 max = 0; 1663 for (ctlsnp = topo_list_next(&cp->sec_nodes); ctlsnp != NULL; 1664 ctlsnp = topo_list_next(ctlsnp)) { 1665 if (ctlsnp->sen_type == SES_ET_SAS_EXPANDER) { 1666 found = B_TRUE; 1667 if (ctlsnp->sen_instance > max) 1668 max = ctlsnp->sen_instance; 1669 } 1670 } 1671 1672 /* 1673 * No SAS expander found notthing to process. 1674 */ 1675 if (!found) 1676 return (0); 1677 1678 topo_mod_dprintf(mod, "%s Controller %d: creating " 1679 "%llu %s nodes", cp->sec_csn, index, max + 1, SASEXPANDER); 1680 1681 /* 1682 * The max number represent the number of elements 1683 * deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX 1684 * of SET_ET_SAS_EXPANDER type element. 1685 * 1686 * There may be multiple ESC Electronics element(controllers) 1687 * within JBOD(typicall two for redundancy) and SAS expander 1688 * elements are associated with only one of them. We are 1689 * still creating the range based max number here. 1690 * That will cover the case that all expanders are associated 1691 * with one SES controller. 1692 */ 1693 if (dorange && topo_node_range_create(mod, pnode, 1694 SASEXPANDER, 0, max) != 0) { 1695 topo_mod_dprintf(mod, 1696 "topo_node_create_range() failed: %s", 1697 topo_mod_errmsg(mod)); 1698 return (-1); 1699 } 1700 1701 /* 1702 * Search exapnders with the parent index matching with 1703 * ESC Electronics element index. 1704 * Note the index used here is a global index across 1705 * SES elements. 1706 */ 1707 for (xsnp = topo_list_next(&cp->sec_nodes); xsnp != NULL; 1708 xsnp = topo_list_next(xsnp)) { 1709 if (xsnp->sen_type == SES_ET_SAS_EXPANDER) { 1710 /* 1711 * get the parent ESC controller. 1712 */ 1713 psprops = ses_node_props(xsnp->sen_node); 1714 if (nvlist_lookup_uint64(psprops, 1715 SES_PROP_STATUS_CODE, &psstatus) == 0) { 1716 if (psstatus == SES_ESC_NOT_INSTALLED) { 1717 /* 1718 * Not installed. 1719 * Don't create a ndoe. 1720 */ 1721 continue; 1722 } 1723 } else { 1724 /* 1725 * The element should have status code. 1726 * If not there is no way to find 1727 * out if the expander element exist or 1728 * not. 1729 */ 1730 continue; 1731 } 1732 1733 /* Get the physical parent index to compare. */ 1734 if (nvlist_lookup_uint64(psprops, 1735 LIBSES_PROP_PHYS_PARENT, &psindex) == 0) { 1736 if (index == psindex) { 1737 /* indentation moved forward */ 1738 /* 1739 * Handle basic node information of SAS expander 1740 * element - binding to parent node and 1741 * allocating FMRI... 1742 */ 1743 if (ses_create_generic(sdp, xsnp, pnode, SASEXPANDER, 1744 "SAS-EXPANDER", &exptn) != 0) 1745 continue; 1746 /* 1747 * Now handle SAS expander unique portion of node creation. 1748 * The max nubmer of the phy count is 256 since SES-2 1749 * defines as 1 byte field. The cidxlist has the same 1750 * number of elements. 1751 * 1752 * We use size 64 array to store the connectors. 1753 * Typically a connectors associated with 4 phys so that 1754 * matches with the max number of connecters associated 1755 * with an expander. 1756 * The phy count goes up to 38 for Sun supported 1757 * JBOD. 1758 */ 1759 memset(cidxlist, 0, sizeof (int64_t) * 64); 1760 if (ses_set_expander_props(sdp, xsnp, pnode, exptn, &phycount, 1761 cidxlist) != 0) { 1762 /* 1763 * error on getting specific prop failed. 1764 * continue on. Note that the node is 1765 * left bound. 1766 */ 1767 continue; 1768 } 1769 1770 /* 1771 * count represetns the number of connectors discovered so far. 1772 */ 1773 count = 0; 1774 memset(connectors, 0, sizeof (sas_connector_phy_data_t) * 64); 1775 for (i = 0; i < phycount; i++) { 1776 if (cidxlist[i] != -1) { 1777 /* connector index is valid. */ 1778 for (j = 0; j < count; j++) { 1779 if (connectors[j].index == 1780 cidxlist[i]) { 1781 /* 1782 * Just update phy mask. 1783 * The postion for connector 1784 * index lists(cidxlist index) 1785 * is set. 1786 */ 1787 connectors[j].phy_mask = 1788 connectors[j].phy_mask | 1789 (1ULL << i); 1790 break; 1791 } 1792 } 1793 /* 1794 * If j and count matche a new connector 1795 * index is found. 1796 */ 1797 if (j == count) { 1798 /* add a new index and phy mask. */ 1799 connectors[count].index = cidxlist[i]; 1800 connectors[count].phy_mask = 1801 connectors[count].phy_mask | 1802 (1ULL << i); 1803 count++; 1804 } 1805 } 1806 } 1807 1808 /* 1809 * create range for the connector nodes. 1810 * The class index of the ses connector element 1811 * is set as the instance nubmer for the node. 1812 * Even though one expander may not have all connectors 1813 * are associated with we are creating the range with 1814 * max possible instance number. 1815 */ 1816 found = B_FALSE; 1817 max = 0; 1818 for (consnp = topo_list_next(&cp->sec_nodes); 1819 consnp != NULL; consnp = topo_list_next(consnp)) { 1820 if (consnp->sen_type == SES_ET_SAS_CONNECTOR) { 1821 psprops = ses_node_props(consnp->sen_node); 1822 found = B_TRUE; 1823 if (consnp->sen_instance > max) 1824 max = consnp->sen_instance; 1825 } 1826 } 1827 1828 /* 1829 * No SAS connector found nothing to process. 1830 */ 1831 if (!found) 1832 return (0); 1833 1834 if (dorange && topo_node_range_create(mod, exptn, 1835 RECEPTACLE, 0, max) != 0) { 1836 topo_mod_dprintf(mod, 1837 "topo_node_create_range() failed: %s", 1838 topo_mod_errmsg(mod)); 1839 return (-1); 1840 } 1841 1842 /* search matching connector element using the index. */ 1843 for (i = 0; i < count; i++) { 1844 found = B_FALSE; 1845 for (consnp = topo_list_next(&cp->sec_nodes); 1846 consnp != NULL; consnp = topo_list_next(consnp)) { 1847 if (consnp->sen_type == SES_ET_SAS_CONNECTOR) { 1848 psprops = ses_node_props( 1849 consnp->sen_node); 1850 /* 1851 * Get the physical parent index to 1852 * compare. 1853 * The connector elements are children 1854 * of ESC Electronics element even 1855 * though we enumerate them under 1856 * an expander in libtopo. 1857 */ 1858 if (nvlist_lookup_uint64(psprops, 1859 SES_PROP_ELEMENT_ONLY_INDEX, 1860 &conindex) == 0) { 1861 if (conindex == 1862 connectors[i].index) { 1863 found = B_TRUE; 1864 break; 1865 } 1866 } 1867 } 1868 } 1869 1870 /* now create a libtopo node. */ 1871 if (found) { 1872 /* Create generic props. */ 1873 if (ses_create_generic(sdp, consnp, exptn, 1874 RECEPTACLE, "RECEPTACLE", &contn) != 1875 0) { 1876 continue; 1877 } 1878 /* Create connector specific props. */ 1879 if (ses_set_connector_props(sdp, consnp, 1880 contn, connectors[i].phy_mask) != 0) { 1881 continue; 1882 } 1883 } 1884 } 1885 /* end indentation change */ 1886 } 1887 } 1888 } 1889 } 1890 1891 return (0); 1892 } 1893 1894 /* 1895 * Instantiate any protocol specific portion of a node. 1896 */ 1897 /*ARGSUSED*/ 1898 static int 1899 ses_create_protocol_specific(ses_enum_data_t *sdp, ses_enum_node_t *snp, 1900 tnode_t *pnode, uint64_t type, ses_enum_chassis_t *cp, 1901 boolean_t dorange) 1902 { 1903 1904 if (type == SES_ET_ESC_ELECTRONICS) { 1905 /* create SAS specific children(expanders and connectors. */ 1906 return (ses_create_esc_sasspecific(sdp, snp, pnode, cp, 1907 dorange)); 1908 } 1909 1910 return (0); 1911 } 1912 1913 /* 1914 * Instantiate any children of a given type. 1915 */ 1916 static int 1917 ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type, 1918 const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp, 1919 boolean_t dorange) 1920 { 1921 topo_mod_t *mod = sdp->sed_mod; 1922 boolean_t found; 1923 uint64_t max; 1924 ses_enum_node_t *snp; 1925 tnode_t *tn; 1926 1927 /* 1928 * First go through and count how many matching nodes we have. 1929 */ 1930 max = 0; 1931 found = B_FALSE; 1932 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1933 snp = topo_list_next(snp)) { 1934 if (snp->sen_type == type) { 1935 found = B_TRUE; 1936 if (snp->sen_instance > max) 1937 max = snp->sen_instance; 1938 } 1939 } 1940 1941 /* 1942 * No enclosure should export both DEVICE and ARRAY_DEVICE elements. 1943 * Since we map both of these to 'disk', if an enclosure does this, we 1944 * just ignore the array elements. 1945 */ 1946 if (!found || 1947 (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev)) 1948 return (0); 1949 1950 topo_mod_dprintf(mod, "%s: creating %llu %s nodes", 1951 cp->sec_csn, max + 1, nodename); 1952 1953 if (dorange && topo_node_range_create(mod, pnode, 1954 nodename, 0, max) != 0) { 1955 topo_mod_dprintf(mod, 1956 "topo_node_create_range() failed: %s", 1957 topo_mod_errmsg(mod)); 1958 return (-1); 1959 } 1960 1961 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1962 snp = topo_list_next(snp)) { 1963 if (snp->sen_type == type) { 1964 if (ses_create_generic(sdp, snp, pnode, 1965 nodename, defaultlabel, &tn) != 0) 1966 return (-1); 1967 /* 1968 * For some SES element there may be protocol specific 1969 * information to process. Here we are processing 1970 * the association between enclosure controller and 1971 * SAS expanders. 1972 */ 1973 if (type == SES_ET_ESC_ELECTRONICS) { 1974 /* create SAS expander node */ 1975 if (ses_create_protocol_specific(sdp, snp, 1976 tn, type, cp, dorange) != 0) { 1977 return (-1); 1978 } 1979 } 1980 1981 } 1982 } 1983 1984 return (0); 1985 } 1986 1987 /* 1988 * Instantiate a new subchassis instance in the topology. 1989 */ 1990 static int 1991 ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode, 1992 ses_enum_chassis_t *scp) 1993 { 1994 topo_mod_t *mod = sdp->sed_mod; 1995 tnode_t *tn; 1996 nvlist_t *props; 1997 nvlist_t *auth = NULL, *fmri = NULL; 1998 uint64_t instance = scp->sec_instance; 1999 char *desc; 2000 char label[128]; 2001 char **paths; 2002 int i, err; 2003 ses_enum_target_t *stp; 2004 int ret = -1; 2005 2006 /* 2007 * Copy authority information from parent enclosure node 2008 */ 2009 if ((auth = topo_mod_auth(mod, pnode)) == NULL) 2010 goto error; 2011 2012 /* 2013 * Record the subchassis serial number in the FMRI. 2014 * For now, we assume that logical id is the subchassis serial number. 2015 * If this assumption changes in future, then the following 2016 * piece of code will need to be updated via an RFE. 2017 */ 2018 if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 2019 SUBCHASSIS, (topo_instance_t)instance, NULL, auth, NULL, NULL, 2020 NULL)) == NULL) { 2021 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 2022 topo_mod_errmsg(mod)); 2023 goto error; 2024 } 2025 2026 if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS, 2027 instance, fmri)) == NULL) { 2028 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 2029 topo_mod_errmsg(mod)); 2030 goto error; 2031 } 2032 2033 props = ses_node_props(scp->sec_enclosure); 2034 2035 /* 2036 * Look for the subchassis label in the following order: 2037 * <ses-description> 2038 * <ses-class-description> <instance> 2039 * <default-type-label> <instance> 2040 * 2041 * For subchassis, the default label is "SUBCHASSIS" 2042 */ 2043 if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 2044 desc[0] == '\0') { 2045 if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, 2046 &desc) == 0 && desc[0] != '\0') 2047 (void) snprintf(label, sizeof (label), "%s %llu", desc, 2048 instance); 2049 else 2050 (void) snprintf(label, sizeof (label), 2051 "SUBCHASSIS %llu", instance); 2052 desc = label; 2053 } 2054 2055 if (topo_node_label_set(tn, desc, &err) != 0) 2056 goto error; 2057 2058 if (ses_set_standard_props(mod, NULL, tn, NULL, 2059 ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0) 2060 goto error; 2061 2062 /* 2063 * Set the 'chassis-type' property for this subchassis. This is either 2064 * 'ses-class-description' or 'subchassis'. 2065 */ 2066 if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, &desc) != 0) 2067 desc = "subchassis"; 2068 2069 if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 2070 TOPO_PROP_CHASSIS_TYPE, TOPO_PROP_IMMUTABLE, desc, &err) != 0) { 2071 topo_mod_dprintf(mod, "failed to create property %s: %s\n", 2072 TOPO_PROP_CHASSIS_TYPE, topo_strerror(err)); 2073 goto error; 2074 } 2075 2076 /* 2077 * For enclosures, we want to include all possible targets (for upgrade 2078 * purposes). 2079 */ 2080 for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 2081 stp = topo_list_next(stp), i++) 2082 ; 2083 2084 verify(i != 0); 2085 paths = alloca(i * sizeof (char *)); 2086 2087 for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 2088 stp = topo_list_next(stp), i++) 2089 paths[i] = stp->set_devpath; 2090 2091 if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 2092 TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 2093 i, &err) != 0) { 2094 topo_mod_dprintf(mod, "failed to create property %s: %s\n", 2095 TOPO_PROP_PATHS, topo_strerror(err)); 2096 goto error; 2097 } 2098 2099 if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 2100 topo_mod_dprintf(mod, "topo_method_register() failed: %s", 2101 topo_mod_errmsg(mod)); 2102 goto error; 2103 } 2104 2105 /* 2106 * Create the nodes for controllers and bays. 2107 */ 2108 if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 2109 CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 || 2110 ses_create_children(sdp, tn, SES_ET_DEVICE, 2111 BAY, "BAY", scp, B_TRUE) != 0 || 2112 ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 2113 BAY, "BAY", scp, B_TRUE) != 0) 2114 goto error; 2115 2116 ret = 0; 2117 2118 error: 2119 nvlist_free(auth); 2120 nvlist_free(fmri); 2121 return (ret); 2122 } 2123 2124 /* 2125 * Instantiate a new chassis instance in the topology. 2126 */ 2127 static int 2128 ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp) 2129 { 2130 topo_mod_t *mod = sdp->sed_mod; 2131 nvlist_t *props; 2132 char *raw_manufacturer, *raw_model, *raw_revision; 2133 char *manufacturer = NULL, *model = NULL, *product = NULL; 2134 char *revision = NULL; 2135 char *serial; 2136 char **paths; 2137 size_t prodlen; 2138 tnode_t *tn; 2139 nvlist_t *fmri = NULL, *auth = NULL; 2140 int ret = -1; 2141 ses_enum_node_t *snp; 2142 ses_enum_target_t *stp; 2143 ses_enum_chassis_t *scp; 2144 int i, err; 2145 uint64_t sc_count = 0; 2146 2147 /* 2148 * Ignore any internal enclosures. 2149 */ 2150 if (cp->sec_internal) 2151 return (0); 2152 2153 /* 2154 * Check to see if there are any devices presennt in the chassis. If 2155 * not, ignore the chassis alltogether. This is most useful for 2156 * ignoring internal HBAs that present a SES target but don't actually 2157 * manage any of the devices. 2158 */ 2159 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 2160 snp = topo_list_next(snp)) { 2161 if (snp->sen_type == SES_ET_DEVICE || 2162 snp->sen_type == SES_ET_ARRAY_DEVICE) 2163 break; 2164 } 2165 2166 if (snp == NULL) 2167 return (0); 2168 2169 props = ses_node_props(cp->sec_enclosure); 2170 2171 /* 2172 * We use the following property mappings: 2173 * 2174 * manufacturer vendor-id 2175 * model product-id 2176 * serial-number libses-chassis-serial 2177 */ 2178 verify(nvlist_lookup_string(props, SES_EN_PROP_VID, 2179 &raw_manufacturer) == 0); 2180 verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0); 2181 verify(nvlist_lookup_string(props, SES_EN_PROP_REV, 2182 &raw_revision) == 0); 2183 verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0); 2184 2185 /* 2186 * To construct the authority information, we 'clean' each string by 2187 * removing any offensive characters and trimmming whitespace. For the 2188 * 'product-id', we use a concatenation of 'manufacturer-model'. We 2189 * also take the numerical serial number and convert it to a string. 2190 */ 2191 if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL || 2192 (model = disk_auth_clean(mod, raw_model)) == NULL || 2193 (revision = disk_auth_clean(mod, raw_revision)) == NULL) { 2194 goto error; 2195 } 2196 2197 prodlen = strlen(manufacturer) + strlen(model) + 2; 2198 if ((product = topo_mod_alloc(mod, prodlen)) == NULL) 2199 goto error; 2200 2201 (void) snprintf(product, prodlen, "%s-%s", manufacturer, model); 2202 2203 /* 2204 * Construct the topo node and bind it to our parent. 2205 */ 2206 if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) 2207 goto error; 2208 2209 if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 || 2210 nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) { 2211 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 2212 goto error; 2213 } 2214 2215 /* 2216 * We pass NULL for the parent FMRI because there is no resource 2217 * associated with it. For the toplevel enclosure, we leave the 2218 * serial/part/revision portions empty, which are reserved for 2219 * individual components within the chassis. 2220 */ 2221 if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 2222 SES_ENCLOSURE, cp->sec_instance, NULL, auth, 2223 model, revision, serial)) == NULL) { 2224 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 2225 topo_mod_errmsg(mod)); 2226 goto error; 2227 } 2228 2229 if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE, 2230 cp->sec_instance, fmri)) == NULL) { 2231 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 2232 topo_mod_errmsg(mod)); 2233 goto error; 2234 } 2235 2236 if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 2237 topo_mod_dprintf(mod, 2238 "topo_method_register() failed: %s", 2239 topo_mod_errmsg(mod)); 2240 goto error; 2241 } 2242 2243 if (ses_set_standard_props(mod, NULL, tn, auth, 2244 ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0) 2245 goto error; 2246 2247 /* 2248 * For enclosures, we want to include all possible targets (for upgrade 2249 * purposes). 2250 */ 2251 for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 2252 stp = topo_list_next(stp), i++) 2253 ; 2254 2255 verify(i != 0); 2256 paths = alloca(i * sizeof (char *)); 2257 2258 for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 2259 stp = topo_list_next(stp), i++) 2260 paths[i] = stp->set_devpath; 2261 2262 2263 if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 2264 TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 2265 i, &err) != 0) { 2266 topo_mod_dprintf(mod, 2267 "failed to create property %s: %s\n", 2268 TOPO_PROP_PATHS, topo_strerror(err)); 2269 goto error; 2270 } 2271 2272 /* 2273 * Create the nodes for power supplies, fans, controllers and devices. 2274 * Note that SAS exopander nodes and connector nodes are handled 2275 * through protocol specific processing of controllers. 2276 */ 2277 if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY, 2278 PSU, "PSU", cp, B_TRUE) != 0 || 2279 ses_create_children(sdp, tn, SES_ET_COOLING, 2280 FAN, "FAN", cp, B_TRUE) != 0 || 2281 ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 2282 CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 || 2283 ses_create_children(sdp, tn, SES_ET_DEVICE, 2284 BAY, "BAY", cp, B_TRUE) != 0 || 2285 ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 2286 BAY, "BAY", cp, B_TRUE) != 0) 2287 goto error; 2288 2289 if (cp->sec_maxinstance >= 0 && 2290 (topo_node_range_create(mod, tn, SUBCHASSIS, 0, 2291 cp->sec_maxinstance) != 0)) { 2292 topo_mod_dprintf(mod, "topo_node_create_range() failed: %s", 2293 topo_mod_errmsg(mod)); 2294 goto error; 2295 } 2296 2297 for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL; 2298 scp = topo_list_next(scp)) { 2299 2300 if (ses_create_subchassis(sdp, tn, scp) != 0) 2301 goto error; 2302 2303 topo_mod_dprintf(mod, "created Subchassis node with " 2304 "instance %u\nand target (%s) under Chassis with CSN %s", 2305 scp->sec_instance, scp->sec_target->set_devpath, 2306 cp->sec_csn); 2307 2308 sc_count++; 2309 } 2310 2311 topo_mod_dprintf(mod, "%s: created %llu %s nodes", 2312 cp->sec_csn, sc_count, SUBCHASSIS); 2313 2314 cp->sec_target->set_refcount++; 2315 topo_node_setspecific(tn, cp->sec_target); 2316 2317 ret = 0; 2318 error: 2319 topo_mod_strfree(mod, manufacturer); 2320 topo_mod_strfree(mod, model); 2321 topo_mod_strfree(mod, revision); 2322 topo_mod_strfree(mod, product); 2323 2324 nvlist_free(fmri); 2325 nvlist_free(auth); 2326 return (ret); 2327 } 2328 2329 /* 2330 * Create a bay node explicitly enumerated via XML. 2331 */ 2332 static int 2333 ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode) 2334 { 2335 topo_mod_t *mod = sdp->sed_mod; 2336 ses_enum_chassis_t *cp; 2337 2338 /* 2339 * Iterate over chassis looking for an internal enclosure. This 2340 * property is set via a vendor-specific plugin, and there should only 2341 * ever be a single internal chassis in a system. 2342 */ 2343 for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 2344 cp = topo_list_next(cp)) { 2345 if (cp->sec_internal) 2346 break; 2347 } 2348 2349 if (cp == NULL) { 2350 topo_mod_dprintf(mod, "failed to find internal chassis\n"); 2351 return (-1); 2352 } 2353 2354 if (ses_create_children(sdp, pnode, SES_ET_DEVICE, 2355 BAY, "BAY", cp, B_FALSE) != 0 || 2356 ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE, 2357 BAY, "BAY", cp, B_FALSE) != 0) 2358 return (-1); 2359 2360 return (0); 2361 } 2362 2363 /* 2364 * Initialize chassis or subchassis. 2365 */ 2366 static int 2367 ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp, 2368 ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props, 2369 uint64_t subchassis, ses_chassis_type_e flags) 2370 { 2371 boolean_t internal, ident; 2372 2373 assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS | 2374 SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0); 2375 2376 assert(cp != NULL); 2377 assert(np != NULL); 2378 assert(props != NULL); 2379 2380 if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS)) 2381 assert(pcp != NULL); 2382 2383 topo_mod_dprintf(mod, "ses_init_chassis: %s: index %llu, flags (%d)", 2384 sdp->sed_name, subchassis, flags); 2385 2386 if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) { 2387 2388 topo_mod_dprintf(mod, "new chassis/subchassis"); 2389 if (nvlist_lookup_boolean_value(props, 2390 LIBSES_EN_PROP_INTERNAL, &internal) == 0) 2391 cp->sec_internal = internal; 2392 2393 cp->sec_enclosure = np; 2394 cp->sec_target = sdp->sed_target; 2395 2396 if (flags & SES_NEW_CHASSIS) { 2397 if (!cp->sec_internal) 2398 cp->sec_instance = sdp->sed_instance++; 2399 topo_list_append(&sdp->sed_chassis, cp); 2400 } else { 2401 if (subchassis != NO_SUBCHASSIS) 2402 cp->sec_instance = subchassis; 2403 else 2404 cp->sec_instance = pcp->sec_scinstance++; 2405 2406 if (cp->sec_instance > pcp->sec_maxinstance) 2407 pcp->sec_maxinstance = cp->sec_instance; 2408 2409 topo_list_append(&pcp->sec_subchassis, cp); 2410 } 2411 2412 } else { 2413 topo_mod_dprintf(mod, "dup chassis/subchassis"); 2414 if (nvlist_lookup_boolean_value(props, 2415 SES_PROP_IDENT, &ident) == 0) { 2416 topo_mod_dprintf(mod, "overriding enclosure node"); 2417 2418 cp->sec_enclosure = np; 2419 cp->sec_target = sdp->sed_target; 2420 } 2421 } 2422 2423 topo_list_append(&cp->sec_targets, sdp->sed_target); 2424 sdp->sed_current = cp; 2425 2426 return (0); 2427 } 2428 2429 /* 2430 * Gather nodes from the current SES target into our chassis list, merging the 2431 * results if necessary. 2432 */ 2433 static ses_walk_action_t 2434 ses_enum_gather(ses_node_t *np, void *data) 2435 { 2436 nvlist_t *props = ses_node_props(np); 2437 ses_enum_data_t *sdp = data; 2438 topo_mod_t *mod = sdp->sed_mod; 2439 ses_enum_chassis_t *cp, *scp; 2440 ses_enum_node_t *snp; 2441 ses_alt_node_t *sap; 2442 char *csn; 2443 uint64_t instance, type; 2444 uint64_t prevstatus, status; 2445 boolean_t report; 2446 uint64_t subchassis = NO_SUBCHASSIS; 2447 2448 if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 2449 /* 2450 * If we have already identified the chassis for this target, 2451 * then this is a secondary enclosure and we should ignore it, 2452 * along with the rest of the tree (since this is depth-first). 2453 */ 2454 if (sdp->sed_current != NULL) 2455 return (SES_WALK_ACTION_TERMINATE); 2456 2457 /* 2458 * Go through the list of chassis we have seen so far and see 2459 * if this serial number matches one of the known values. 2460 * If so, check whether this enclosure is a subchassis. 2461 */ 2462 if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 2463 &csn) != 0) 2464 return (SES_WALK_ACTION_TERMINATE); 2465 2466 (void) nvlist_lookup_uint64(props, LIBSES_EN_PROP_SUBCHASSIS_ID, 2467 &subchassis); 2468 2469 topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) " 2470 "CSN (%s), subchassis (%llu)", sdp->sed_name, csn, 2471 subchassis); 2472 2473 /* 2474 * We need to determine whether this enclosure node 2475 * represents a chassis or a subchassis. Since we may 2476 * receive the enclosure nodes in a non-deterministic 2477 * manner, we need to account for all possible combinations: 2478 * 1. Chassis for the current CSN has not yet been 2479 * allocated 2480 * 1.1 This is a new chassis: 2481 * allocate and instantiate the chassis 2482 * 1.2 This is a new subchassis: 2483 * allocate a placeholder chassis 2484 * allocate and instantiate the subchassis 2485 * link the subchassis to the chassis 2486 * 2. Chassis for the current CSN has been allocated 2487 * 2.1 This is a duplicate chassis enclosure 2488 * check whether to override old chassis 2489 * append to chassis' target list 2490 * 2.2 Only placeholder chassis exists 2491 * fill in the chassis fields 2492 * 2.3 This is a new subchassis 2493 * allocate and instantiate the subchassis 2494 * link the subchassis to the chassis 2495 * 2.4 This is a duplicate subchassis enclosure 2496 * check whether to override old chassis 2497 * append to chassis' target list 2498 */ 2499 2500 for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 2501 cp = topo_list_next(cp)) 2502 if (strcmp(cp->sec_csn, csn) == 0) 2503 break; 2504 2505 if (cp == NULL) { 2506 /* 1. Haven't seen a chassis with this CSN before */ 2507 2508 if ((cp = topo_mod_zalloc(mod, 2509 sizeof (ses_enum_chassis_t))) == NULL) 2510 goto error; 2511 2512 cp->sec_scinstance = SES_STARTING_SUBCHASSIS; 2513 cp->sec_maxinstance = -1; 2514 cp->sec_csn = csn; 2515 2516 if (subchassis == NO_SUBCHASSIS) { 2517 /* 1.1 This is a new chassis */ 2518 2519 topo_mod_dprintf(mod, "%s: Initialize new " 2520 "chassis with CSN %s", sdp->sed_name, csn); 2521 2522 if (ses_init_chassis(mod, sdp, NULL, cp, 2523 np, props, NO_SUBCHASSIS, 2524 SES_NEW_CHASSIS) < 0) 2525 goto error; 2526 } else { 2527 /* 1.2 This is a new subchassis */ 2528 2529 topo_mod_dprintf(mod, "%s: Initialize new " 2530 "subchassis with CSN %s and index %llu", 2531 sdp->sed_name, csn, subchassis); 2532 2533 if ((scp = topo_mod_zalloc(mod, 2534 sizeof (ses_enum_chassis_t))) == NULL) 2535 goto error; 2536 2537 scp->sec_csn = csn; 2538 2539 if (ses_init_chassis(mod, sdp, cp, scp, np, 2540 props, subchassis, SES_NEW_SUBCHASSIS) < 0) 2541 goto error; 2542 } 2543 } else { 2544 /* 2545 * We have a chassis or subchassis with this CSN. If 2546 * it's a chassis, we must check to see whether it is 2547 * a placeholder previously created because we found a 2548 * subchassis with this CSN. We will know that because 2549 * the sec_target value will not be set; it is set only 2550 * in ses_init_chassis(). In that case, initialise it 2551 * as a new chassis; otherwise, it's a duplicate and we 2552 * need to append only. 2553 */ 2554 if (subchassis == NO_SUBCHASSIS) { 2555 if (cp->sec_target != NULL) { 2556 /* 2.1 This is a duplicate chassis */ 2557 2558 topo_mod_dprintf(mod, "%s: Append " 2559 "duplicate chassis with CSN (%s)", 2560 sdp->sed_name, csn); 2561 2562 if (ses_init_chassis(mod, sdp, NULL, cp, 2563 np, props, NO_SUBCHASSIS, 2564 SES_DUP_CHASSIS) < 0) 2565 goto error; 2566 } else { 2567 /* Placeholder chassis - init it up */ 2568 topo_mod_dprintf(mod, "%s: Initialize" 2569 "placeholder chassis with CSN %s", 2570 sdp->sed_name, csn); 2571 2572 if (ses_init_chassis(mod, sdp, NULL, 2573 cp, np, props, NO_SUBCHASSIS, 2574 SES_NEW_CHASSIS) < 0) 2575 goto error; 2576 2577 } 2578 } else { 2579 /* This is a subchassis */ 2580 2581 for (scp = topo_list_next(&cp->sec_subchassis); 2582 scp != NULL; scp = topo_list_next(scp)) 2583 if (scp->sec_instance == subchassis) 2584 break; 2585 2586 if (scp == NULL) { 2587 /* 2.3 This is a new subchassis */ 2588 2589 topo_mod_dprintf(mod, "%s: Initialize " 2590 "new subchassis with CSN (%s) " 2591 "and LID (%s)", 2592 sdp->sed_name, csn); 2593 2594 if ((scp = topo_mod_zalloc(mod, 2595 sizeof (ses_enum_chassis_t))) 2596 == NULL) 2597 goto error; 2598 2599 scp->sec_csn = csn; 2600 2601 if (ses_init_chassis(mod, sdp, cp, scp, 2602 np, props, subchassis, 2603 SES_NEW_SUBCHASSIS) < 0) 2604 goto error; 2605 } else { 2606 /* 2.4 This is a duplicate subchassis */ 2607 2608 topo_mod_dprintf(mod, "%s: Append " 2609 "duplicate subchassis with " 2610 "CSN (%s)", sdp->sed_name, csn); 2611 2612 if (ses_init_chassis(mod, sdp, cp, scp, 2613 np, props, subchassis, 2614 SES_DUP_SUBCHASSIS) < 0) 2615 goto error; 2616 } 2617 } 2618 } 2619 } else if (ses_node_type(np) == SES_NODE_ELEMENT) { 2620 /* 2621 * If we haven't yet seen an enclosure node and identified the 2622 * current chassis, something is very wrong; bail out. 2623 */ 2624 if (sdp->sed_current == NULL) 2625 return (SES_WALK_ACTION_TERMINATE); 2626 2627 /* 2628 * If this isn't one of the element types we care about, then 2629 * ignore it. 2630 */ 2631 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 2632 &type) == 0); 2633 if (type != SES_ET_DEVICE && 2634 type != SES_ET_ARRAY_DEVICE && 2635 type != SES_ET_COOLING && 2636 type != SES_ET_POWER_SUPPLY && 2637 type != SES_ET_ESC_ELECTRONICS && 2638 type != SES_ET_SAS_EXPANDER && 2639 type != SES_ET_SAS_CONNECTOR) 2640 return (SES_WALK_ACTION_CONTINUE); 2641 2642 /* 2643 * Get the current instance number and see if we already know 2644 * about this element. If so, it means we have multiple paths 2645 * to the same elements, and we should ignore the current path. 2646 */ 2647 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 2648 &instance) == 0); 2649 if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE) 2650 (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, 2651 &instance); 2652 2653 cp = sdp->sed_current; 2654 2655 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 2656 snp = topo_list_next(snp)) { 2657 if (snp->sen_type == type && 2658 snp->sen_instance == instance) 2659 break; 2660 } 2661 2662 /* 2663 * We prefer the new element under the following circumstances: 2664 * 2665 * - The currently known element's status is unknown or not 2666 * available, but the new element has a known status. This 2667 * occurs if a given element is only available through a 2668 * particular target. 2669 * 2670 * - This is an ESC_ELECTRONICS element, and the 'reported-via' 2671 * property is set. This allows us to get reliable firmware 2672 * revision information from the enclosure node. 2673 */ 2674 if (snp != NULL) { 2675 if (nvlist_lookup_uint64( 2676 ses_node_props(snp->sen_node), 2677 SES_PROP_STATUS_CODE, &prevstatus) != 0) 2678 prevstatus = SES_ESC_UNSUPPORTED; 2679 if (nvlist_lookup_uint64( 2680 props, SES_PROP_STATUS_CODE, &status) != 0) 2681 status = SES_ESC_UNSUPPORTED; 2682 if (nvlist_lookup_boolean_value( 2683 props, SES_PROP_REPORT, &report) != 0) 2684 report = B_FALSE; 2685 2686 if ((SES_STATUS_UNAVAIL(prevstatus) && 2687 !SES_STATUS_UNAVAIL(status)) || 2688 (type == SES_ET_ESC_ELECTRONICS && 2689 report)) { 2690 snp->sen_node = np; 2691 snp->sen_target = sdp->sed_target; 2692 } 2693 2694 if ((sap = topo_mod_zalloc(mod, 2695 sizeof (ses_alt_node_t))) == NULL) 2696 goto error; 2697 2698 sap->san_node = np; 2699 topo_list_append(&snp->sen_alt_nodes, sap); 2700 2701 return (SES_WALK_ACTION_CONTINUE); 2702 } 2703 2704 if ((snp = topo_mod_zalloc(mod, 2705 sizeof (ses_enum_node_t))) == NULL) 2706 goto error; 2707 2708 if ((sap = topo_mod_zalloc(mod, 2709 sizeof (ses_alt_node_t))) == NULL) { 2710 topo_mod_free(mod, snp, sizeof (ses_enum_node_t)); 2711 goto error; 2712 } 2713 2714 topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)", 2715 sdp->sed_name, type, instance); 2716 snp->sen_node = np; 2717 snp->sen_type = type; 2718 snp->sen_instance = instance; 2719 snp->sen_target = sdp->sed_target; 2720 sap->san_node = np; 2721 topo_list_append(&snp->sen_alt_nodes, sap); 2722 topo_list_append(&cp->sec_nodes, snp); 2723 2724 if (type == SES_ET_DEVICE) 2725 cp->sec_hasdev = B_TRUE; 2726 } 2727 2728 return (SES_WALK_ACTION_CONTINUE); 2729 2730 error: 2731 sdp->sed_errno = -1; 2732 return (SES_WALK_ACTION_TERMINATE); 2733 } 2734 2735 static int 2736 ses_process_dir(const char *dirpath, ses_enum_data_t *sdp) 2737 { 2738 topo_mod_t *mod = sdp->sed_mod; 2739 DIR *dir; 2740 struct dirent *dp; 2741 char path[PATH_MAX]; 2742 ses_enum_target_t *stp; 2743 int err = -1; 2744 2745 /* 2746 * Open the SES target directory and iterate over any available 2747 * targets. 2748 */ 2749 if ((dir = opendir(dirpath)) == NULL) { 2750 /* 2751 * If the SES target directory does not exist, then return as if 2752 * there are no active targets. 2753 */ 2754 topo_mod_dprintf(mod, "failed to open ses " 2755 "directory '%s'", dirpath); 2756 return (0); 2757 } 2758 2759 while ((dp = readdir(dir)) != NULL) { 2760 if (strcmp(dp->d_name, ".") == 0 || 2761 strcmp(dp->d_name, "..") == 0) 2762 continue; 2763 2764 /* 2765 * Create a new target instance and take a snapshot. 2766 */ 2767 if ((stp = topo_mod_zalloc(mod, 2768 sizeof (ses_enum_target_t))) == NULL) 2769 goto error; 2770 2771 (void) pthread_mutex_init(&stp->set_lock, NULL); 2772 2773 (void) snprintf(path, sizeof (path), "%s/%s", dirpath, 2774 dp->d_name); 2775 2776 /* 2777 * We keep track of the SES device path and export it on a 2778 * per-node basis to allow higher level software to get to the 2779 * corresponding SES state. 2780 */ 2781 if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) { 2782 topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 2783 goto error; 2784 } 2785 2786 if ((stp->set_target = 2787 ses_open(LIBSES_VERSION, path)) == NULL) { 2788 topo_mod_dprintf(mod, "failed to open ses target " 2789 "'%s': %s", dp->d_name, ses_errmsg()); 2790 ses_sof_alloc(mod, stp->set_devpath); 2791 topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 2792 continue; 2793 } 2794 topo_mod_dprintf(mod, "open contract"); 2795 ses_ssl_alloc(mod, stp); 2796 ses_create_contract(mod, stp); 2797 2798 stp->set_refcount = 1; 2799 sdp->sed_target = stp; 2800 stp->set_snap = ses_snap_hold(stp->set_target); 2801 stp->set_snaptime = gethrtime(); 2802 2803 /* 2804 * Enumerate over all SES elements and merge them into the 2805 * correct ses_enum_chassis_t. 2806 */ 2807 sdp->sed_current = NULL; 2808 sdp->sed_errno = 0; 2809 sdp->sed_name = dp->d_name; 2810 (void) ses_walk(stp->set_snap, ses_enum_gather, sdp); 2811 2812 if (sdp->sed_errno != 0) 2813 goto error; 2814 } 2815 2816 err = 0; 2817 error: 2818 closedir(dir); 2819 return (err); 2820 } 2821 2822 static void 2823 ses_release(topo_mod_t *mod, tnode_t *tn) 2824 { 2825 ses_enum_target_t *stp; 2826 2827 if ((stp = topo_node_getspecific(tn)) != NULL) { 2828 topo_node_setspecific(tn, NULL); 2829 ses_target_free(mod, stp); 2830 } 2831 } 2832 2833 /*ARGSUSED*/ 2834 static int 2835 ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 2836 topo_instance_t min, topo_instance_t max, void *arg, void *notused) 2837 { 2838 ses_enum_chassis_t *cp; 2839 ses_enum_data_t *data; 2840 2841 /* 2842 * Check to make sure we're being invoked sensibly, and that we're not 2843 * being invoked as part of a post-processing step. 2844 */ 2845 if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0) 2846 return (0); 2847 2848 /* 2849 * If this is the first time we've called our enumeration method, then 2850 * gather information about any available enclosures. 2851 */ 2852 if ((data = topo_mod_getspecific(mod)) == NULL) { 2853 ses_sof_freeall(mod); 2854 if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) == 2855 NULL) 2856 return (-1); 2857 2858 data->sed_mod = mod; 2859 topo_mod_setspecific(mod, data); 2860 2861 if (dev_list_gather(mod, &data->sed_devs) != 0) 2862 goto error; 2863 2864 /* 2865 * We search both the ses(7D) and sgen(7D) locations, so we are 2866 * independent of any particular driver class bindings. 2867 */ 2868 if (ses_process_dir("/dev/es", data) != 0 || 2869 ses_process_dir("/dev/scsi/ses", data) != 0) 2870 goto error; 2871 } 2872 2873 if (strcmp(name, SES_ENCLOSURE) == 0) { 2874 /* 2875 * This is a request to enumerate external enclosures. Go 2876 * through all the targets and create chassis nodes where 2877 * necessary. 2878 */ 2879 for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 2880 cp = topo_list_next(cp)) { 2881 if (ses_create_chassis(data, rnode, cp) != 0) 2882 goto error; 2883 } 2884 } else { 2885 /* 2886 * This is a request to enumerate a specific bay underneath the 2887 * root chassis (for internal disks). 2888 */ 2889 if (ses_create_bays(data, rnode) != 0) 2890 goto error; 2891 } 2892 2893 /* 2894 * This is a bit of a kludge. In order to allow internal disks to be 2895 * enumerated and share snapshot-specific information with the external 2896 * enclosure enumeration, we rely on the fact that we will be invoked 2897 * for the 'ses-enclosure' node last. 2898 */ 2899 if (strcmp(name, SES_ENCLOSURE) == 0) { 2900 for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 2901 cp = topo_list_next(cp)) 2902 ses_data_free(data, cp); 2903 ses_data_free(data, NULL); 2904 topo_mod_setspecific(mod, NULL); 2905 } 2906 return (0); 2907 2908 error: 2909 for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 2910 cp = topo_list_next(cp)) 2911 ses_data_free(data, cp); 2912 ses_data_free(data, NULL); 2913 topo_mod_setspecific(mod, NULL); 2914 return (-1); 2915 } 2916 2917 static const topo_modops_t ses_ops = 2918 { ses_enum, ses_release }; 2919 2920 static topo_modinfo_t ses_info = 2921 { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops }; 2922 2923 /*ARGSUSED*/ 2924 int 2925 _topo_init(topo_mod_t *mod, topo_version_t version) 2926 { 2927 int rval; 2928 2929 if (getenv("TOPOSESDEBUG") != NULL) 2930 topo_mod_setdebug(mod); 2931 2932 topo_mod_dprintf(mod, "initializing %s enumerator\n", 2933 SES_ENCLOSURE); 2934 2935 if ((rval = topo_mod_register(mod, &ses_info, TOPO_VERSION)) == 0) 2936 ses_thread_init(mod); 2937 2938 return (rval); 2939 } 2940 2941 void 2942 _topo_fini(topo_mod_t *mod) 2943 { 2944 ses_thread_fini(); 2945 ses_sof_freeall(mod); 2946 topo_mod_unregister(mod); 2947 } 2948