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