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