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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 /* 28 * This plugin checks the status of FC-AL disks periodically and 29 * in response to PICL events. It adjusts the state of the FC-AL LEDs 30 * to match the disk status. 31 */ 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <limits.h> 36 #include <unistd.h> 37 #include <fcntl.h> 38 #include <alloca.h> 39 #include <syslog.h> 40 #include <string.h> 41 #include <libintl.h> 42 #include <pthread.h> 43 #include <sys/types.h> 44 #include <sys/systeminfo.h> 45 #include <sys/param.h> 46 #include <poll.h> 47 #include <errno.h> 48 #include <libnvpair.h> 49 #include "fcal_leds.h" 50 51 static void fcal_leds_register(void); 52 static void fcal_leds_init(void); 53 static void fcal_leds_fini(void); 54 static void *fcal_poll_thread(void *args); 55 static FILE *open_config(void); 56 static int read_led_state(ptree_rarg_t *parg, void *buf); 57 static void add_led_refs(led_dtls_t *dtls); 58 static void delete_led_refs(led_dtls_t *dtls); 59 static void piclfcal_evhandler(const char *ename, const void *earg, 60 size_t size, void *cookie); 61 62 /* 63 * Global thread data 64 */ 65 led_dtls_t *g_led_dtls = NULL; 66 pthread_cond_t g_cv; 67 pthread_cond_t g_cv_ack; 68 pthread_mutex_t g_mutex; 69 volatile int g_event_flag; 70 volatile boolean_t g_finish_now = B_FALSE; 71 volatile boolean_t g_leds_thread_ack = B_FALSE; 72 73 static picld_plugin_reg_t my_reg_info = { 74 PICLD_PLUGIN_VERSION_1, 75 PICLD_PLUGIN_NON_CRITICAL, 76 "SUNW_fcal_leds", 77 fcal_leds_init, 78 fcal_leds_fini 79 }; 80 81 static boolean_t cvAndMutexInit = B_FALSE; 82 static pthread_t ledsthr_tid; 83 static pthread_attr_t ledsthr_attr; 84 static boolean_t ledsthr_created = B_FALSE; 85 static pthread_t pollthr_tid; 86 static pthread_attr_t pollthr_attr; 87 static boolean_t pollthr_created = B_FALSE; 88 static volatile boolean_t poll_thread_ack = B_FALSE; 89 90 /* 91 * look up table for LED state 92 */ 93 static struct { 94 const led_state_t led_state; 95 const char *state_str; 96 } state_lookup[] = { 97 { LED_STATE_OFF, FCAL_PICL_LED_OFF }, 98 { LED_STATE_ON, FCAL_PICL_LED_ON }, 99 { LED_STATE_TEST, FCAL_PICL_LED_TEST } 100 }; 101 102 #define state_lkup_len (sizeof (state_lookup) / sizeof (state_lookup[0])) 103 104 /* 105 * executed as part of .init when the plugin is dlopen()ed 106 */ 107 #pragma init(fcal_leds_register) 108 109 static void 110 fcal_leds_register(void) 111 { 112 (void) picld_plugin_register(&my_reg_info); 113 } 114 115 /* ARGSUSED */ 116 static void 117 piclfcal_evhandler(const char *ename, const void *earg, size_t size, 118 void *cookie) 119 { 120 int r; 121 122 if (earg == NULL) 123 return; 124 125 r = pthread_mutex_lock(&g_mutex); 126 127 if (r != 0) { 128 SYSLOG(LOG_ERR, EM_MUTEX_FAIL, mystrerror(r)); 129 return; 130 } 131 g_event_flag |= FCAL_EV_CONFIG; 132 (void) pthread_cond_signal(&g_cv); 133 134 (void) pthread_mutex_unlock(&g_mutex); 135 } 136 137 /* 138 * Locate and open relevant config file 139 */ 140 static FILE * 141 open_config(void) 142 { 143 FILE *fp = NULL; 144 char nmbuf[SYS_NMLN]; 145 char fname[PATH_MAX]; 146 147 if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) == -1) 148 return (NULL); 149 (void) snprintf(fname, sizeof (fname), PICLD_PLAT_PLUGIN_DIRF, nmbuf); 150 (void) strlcat(fname, FCAL_LEDS_CONF_FILE, sizeof (fname)); 151 fp = fopen(fname, "r"); 152 if (fp == NULL) { 153 SYSLOG(LOG_ERR, EM_CANT_OPEN, fname); 154 } 155 return (fp); 156 } 157 158 /* 159 * read volatile property function for led State 160 */ 161 static int 162 read_led_state(ptree_rarg_t *parg, void *buf) 163 { 164 led_dtls_t *dtls = g_led_dtls; 165 picl_nodehdl_t nodeh = parg->nodeh; 166 /* 167 * valbuf has space for a unit address at the end 168 */ 169 char valbuf[MAX_LEN_UNIT_ADDRESS]; 170 char *ptr; 171 uint_t addr; 172 int disk, led; 173 led_state_t stat; 174 /* 175 * each led-unit node has a UnitAddress property set to the bit 176 * value associated with the led. Read that property 177 */ 178 int r = ptree_get_propval_by_name(nodeh, PICL_PROP_UNIT_ADDRESS, 179 valbuf, sizeof (valbuf)); 180 if (r != PICL_SUCCESS) 181 return (r); 182 valbuf[sizeof (valbuf) - 1] = '\0'; /* ensure null terminated */ 183 /* UnitAddress is a string of hex digits, convert to an int */ 184 addr = strtoul(valbuf, &ptr, 16); 185 if (dtls == NULL) 186 return (PICL_PROPVALUNAVAILABLE); 187 /* 188 * search the leds of each disk for a match with this UnitAddress 189 */ 190 for (disk = 0; disk < dtls->n_disks; disk++) { 191 for (led = 0; led < FCAL_LED_CNT; led++) { 192 if (addr == dtls->led_addr[led][disk]) 193 break; 194 } 195 if (led < FCAL_LED_CNT) 196 break; 197 } 198 if (disk == dtls->n_disks) 199 return (PICL_PROPVALUNAVAILABLE); 200 stat = dtls->led_state[led][disk]; 201 /* 202 * state_lookup is a table relating led-state enums to equivalent 203 * text strings. Locate the string for the current state. 204 */ 205 for (r = 0; r < state_lkup_len; r++) { 206 if (state_lookup[r].led_state == stat) { 207 (void) strlcpy(buf, state_lookup[r].state_str, 208 MAX_LEN_LED_STATE); 209 return (PICL_SUCCESS); 210 } 211 } 212 return (PICL_PROPVALUNAVAILABLE); 213 } 214 215 int 216 find_disk_slot(led_dtls_t *dtls, int disk, picl_nodehdl_t *nodeh) 217 { 218 int r; 219 int unitlen; 220 char unitstr[MAXPATHLEN]; 221 222 if (dtls->disk_unit_parent == NULL) { 223 return (PICL_NODENOTFOUND); 224 } 225 unitlen = strlen(dtls->disk_unit_parent); 226 /* 227 * get search string buffer, allow space for address 228 */ 229 (void) strlcpy(unitstr, dtls->disk_unit_parent, MAXPATHLEN); 230 (void) snprintf(unitstr + unitlen, MAXPATHLEN - unitlen, "%x", disk); 231 r = ptree_get_node_by_path(unitstr, nodeh); 232 return (r); 233 } 234 235 int 236 create_Device_table(picl_prophdl_t *tbl_h, picl_prophdl_t *tableh) 237 { 238 int r; 239 ptree_propinfo_t propinfo; 240 241 r = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 242 PICL_PTYPE_TABLE, PICL_READ, sizeof (picl_prophdl_t), 243 PICL_PROP_DEVICES, NULL, NULL); 244 if (r != PICL_SUCCESS) { 245 return (r); 246 } 247 r = ptree_create_table(tbl_h); 248 if (r != PICL_SUCCESS) { 249 return (r); 250 } 251 r = ptree_create_prop(&propinfo, tbl_h, tableh); 252 return (r); 253 } 254 255 /* 256 * Locate disk-slot nodes and add DeviceTable of LED references 257 * Also add a volatile State property to each LED node 258 */ 259 static void 260 add_led_refs(led_dtls_t *dtls) 261 { 262 int d, i, r; 263 int ledlen; 264 char ledstr[MAXPATHLEN]; 265 picl_nodehdl_t slot_node; 266 267 if (dtls->disk_led_nodes == NULL) { 268 return; 269 } 270 ledlen = strlen(dtls->disk_led_nodes); 271 /* set up search string in buffer with space to append address */ 272 (void) strlcpy(ledstr, dtls->disk_led_nodes, MAXPATHLEN); 273 for (d = 0; d < dtls->n_disks; d++) { 274 picl_prophdl_t tbl_hdl; 275 picl_prophdl_t tbl_prop_hdl; 276 picl_nodehdl_t led_node_hdl; 277 picl_prophdl_t tbl_prop[FCAL_DEVTABLE_NCOLS]; 278 ptree_propinfo_t propinfo; 279 280 r = create_Device_table(&tbl_hdl, &tbl_prop_hdl); 281 if (r != PICL_SUCCESS) 282 break; 283 284 /* 285 * locate disk-slot node in frutree 286 */ 287 if (find_disk_slot(dtls, d, &slot_node) != PICL_SUCCESS) 288 break; 289 290 for (i = 0; i < FCAL_LED_CNT; i++) { 291 /* 292 * For each disk-slot in frutree, add a device 293 * table of references to relevant LEDs. 294 * En passant, add a volatile State property to 295 * each LED node found. 296 */ 297 /* 298 * append led address to search string 299 */ 300 (void) snprintf(ledstr + ledlen, MAXPATHLEN - ledlen, 301 "%x", dtls->led_addr[i][d]); 302 r = ptree_get_node_by_path(ledstr, &led_node_hdl); 303 if (r != PICL_SUCCESS) { 304 break; 305 } 306 r = ptree_init_propinfo(&propinfo, 307 PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, 308 PICL_READ | PICL_VOLATILE, MAX_LEN_LED_STATE, 309 PICL_PROP_STATE, read_led_state, NULL); 310 if (r != PICL_SUCCESS) { 311 break; 312 } 313 r = ptree_create_and_add_prop(led_node_hdl, 314 &propinfo, NULL, NULL); 315 if (r != PICL_SUCCESS) { 316 break; 317 } 318 r = ptree_init_propinfo(&propinfo, 319 PTREE_PROPINFO_VERSION, PICL_PTYPE_CHARSTRING, 320 PICL_READ, sizeof (PICL_CLASS_LED), 321 PICL_PROP_CLASS, NULL, NULL); 322 if (r != PICL_SUCCESS) { 323 break; 324 } 325 r = ptree_create_prop(&propinfo, PICL_CLASS_LED, 326 &tbl_prop[0]); 327 if (r != PICL_SUCCESS) { 328 break; 329 } 330 r = ptree_init_propinfo(&propinfo, 331 PTREE_PROPINFO_VERSION, PICL_PTYPE_REFERENCE, 332 PICL_READ, sizeof (picl_prophdl_t), 333 FCAL_PICL_LED_REF, NULL, NULL); 334 if (r != PICL_SUCCESS) { 335 break; 336 } 337 r = ptree_create_prop(&propinfo, &led_node_hdl, 338 &tbl_prop[1]); 339 if (r != PICL_SUCCESS) { 340 break; 341 } 342 r = ptree_add_row_to_table(tbl_hdl, 343 FCAL_DEVTABLE_NCOLS, tbl_prop); 344 if (r != PICL_SUCCESS) { 345 break; 346 } 347 } 348 if (r != PICL_SUCCESS) 349 break; 350 (void) ptree_add_prop(slot_node, tbl_prop_hdl); 351 } 352 } 353 354 /* 355 * This is an undo function to match add_led_refs() 356 * Locate disk-slot nodes and remove Devices table of LED references 357 * Also remove volatile State property to each LED node 358 */ 359 static void 360 delete_led_refs(led_dtls_t *dtls) 361 { 362 int d; 363 int i; 364 int r; 365 int ledlen; 366 picl_nodehdl_t node_hdl; 367 picl_prophdl_t prop_hdl; 368 char ledstr[MAXPATHLEN]; 369 370 if (dtls->disk_led_nodes == NULL) 371 return; 372 373 for (d = 0; d < dtls->n_disks; d++) { 374 if (find_disk_slot(dtls, d, &node_hdl) != PICL_SUCCESS) 375 continue; 376 if (ptree_get_prop_by_name(node_hdl, PICL_PROP_DEVICES, 377 &prop_hdl) != PICL_SUCCESS) 378 continue; 379 if (ptree_delete_prop(prop_hdl) != PICL_SUCCESS) 380 continue; 381 (void) ptree_destroy_prop(prop_hdl); 382 } 383 384 ledlen = strlen(dtls->disk_led_nodes); 385 (void) strlcpy(ledstr, dtls->disk_led_nodes, MAXPATHLEN); 386 387 for (d = 0; d < dtls->n_disks; d++) { 388 for (i = 0; i < FCAL_LED_CNT; i++) { 389 /* 390 * find each led node 391 */ 392 (void) snprintf(ledstr + ledlen, MAXPATHLEN - ledlen, 393 "%x", dtls->led_addr[i][d]); 394 r = ptree_get_node_by_path(ledstr, &node_hdl); 395 if (r != PICL_SUCCESS) 396 continue; 397 /* 398 * locate and delete the volatile State property 399 */ 400 if (ptree_get_prop_by_name(node_hdl, 401 PICL_PROP_STATE, &prop_hdl) != PICL_SUCCESS) 402 continue; 403 if (ptree_delete_prop(prop_hdl) != PICL_SUCCESS) 404 continue; 405 (void) ptree_destroy_prop(prop_hdl); 406 } 407 } 408 } 409 410 /* 411 * Poll thread. 412 * This thread sits on a poll() call for the fast poll interval. 413 * At each wake up it determines if a time event should be passed on. 414 * Poll seems to be reliable when the realtime clock is wound backwards, 415 * whereas pthread_cond_timedwait() is not. 416 */ 417 /*ARGSUSED*/ 418 static void * 419 fcal_poll_thread(void *args) 420 { 421 led_dtls_t *dtls = NULL; 422 int c; 423 int slow_poll_count; 424 boolean_t do_event; 425 426 for (;;) { 427 if (g_finish_now) { 428 c = pthread_mutex_lock(&g_mutex); 429 430 if (c != 0) { 431 SYSLOG(LOG_ERR, EM_MUTEX_FAIL, mystrerror(c)); 432 break; 433 } 434 poll_thread_ack = B_TRUE; 435 (void) pthread_cond_signal(&g_cv_ack); 436 (void) pthread_cond_wait(&g_cv, &g_mutex); 437 438 (void) pthread_mutex_unlock(&g_mutex); 439 continue; 440 } 441 442 /* 443 * If picld has been recycled, or if this is the initial 444 * entry, dtls will not match g_led_dtls. 445 * In this case, do some resetting. 446 */ 447 if (dtls != g_led_dtls) { 448 dtls = g_led_dtls; 449 slow_poll_count = dtls->slow_poll_ticks; 450 dtls->polling = B_TRUE; 451 } 452 453 c = poll(NULL, 0, dtls->fast_poll * 1000); 454 if (c == -1) { 455 SYSLOG(LOG_ERR, EM_POLL_FAIL, mystrerror(errno)); 456 break; 457 } 458 /* 459 * dtls->fast_poll_end is the number of fast poll times left 460 * before we revert to slow polling. If it is non-zero, the 461 * fcal_leds thread is do fast polling and we pass on every 462 * poll wakeup. 463 */ 464 do_event = (dtls->fast_poll_end != 0); 465 /* 466 * If a LED test is underway, fast polling would normally be 467 * set also. Just in case the timers are configured unusually, 468 * pass on all poll wakeups while a LED test is current. 469 */ 470 if ((!do_event) && is_led_test(dtls)) 471 do_event = B_TRUE; 472 if (!do_event) { 473 /* 474 * If we get here, the fcal_leds thread is only doing 475 * slow polls. Count down the slow_poll_count and set 476 * an event if it expires. 477 */ 478 if (--slow_poll_count == 0) { 479 slow_poll_count = dtls->slow_poll_ticks; 480 do_event = B_TRUE; 481 } 482 } 483 if (do_event) { 484 c = pthread_mutex_lock(&g_mutex); 485 486 if (c != 0) { 487 SYSLOG(LOG_ERR, EM_MUTEX_FAIL, mystrerror(c)); 488 break; 489 } 490 /* 491 * indicate in the event flag that this is a time event 492 */ 493 g_event_flag |= FCAL_EV_POLL; 494 (void) pthread_cond_signal(&g_cv); 495 496 (void) pthread_mutex_unlock(&g_mutex); 497 } 498 } 499 500 dtls->polling = B_FALSE; 501 502 /* 503 * if picld restarted, allow this thread to be recreated 504 */ 505 pollthr_created = B_FALSE; 506 507 return ((void *)errno); 508 } 509 510 /* 511 * Init entry point of the plugin 512 * Opens and parses config file. 513 * Establishes an interrupt routine to catch DEVICE ADDED/REMOVED events 514 * and starts a new thread for polling FC-AL disk status information. 515 */ 516 static void 517 fcal_leds_init(void) 518 { 519 FILE *fp; 520 int err = 0; 521 522 if ((fp = open_config()) == NULL) 523 return; 524 if (fc_led_parse(fp, &g_led_dtls) != 0) { 525 (void) fclose(fp); 526 return; 527 } 528 (void) fclose(fp); 529 g_finish_now = B_FALSE; 530 g_event_flag = 0; 531 532 if (!cvAndMutexInit) { 533 if ((pthread_cond_init(&g_cv, NULL) == 0) && 534 (pthread_cond_init(&g_cv_ack, NULL) == 0) && 535 (pthread_mutex_init(&g_mutex, NULL) == 0)) { 536 cvAndMutexInit = B_TRUE; 537 } else { 538 return; 539 } 540 } 541 542 add_led_refs(g_led_dtls); 543 544 (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED, 545 piclfcal_evhandler, NULL); 546 (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED, 547 piclfcal_evhandler, NULL); 548 549 if (ledsthr_created || pollthr_created) { 550 /* 551 * so this is a restart, wake up sleeping threads 552 */ 553 err = pthread_mutex_lock(&g_mutex); 554 555 if (err != 0) { 556 SYSLOG(LOG_ERR, EM_MUTEX_FAIL, mystrerror(err)); 557 return; 558 } 559 g_leds_thread_ack = B_FALSE; 560 poll_thread_ack = B_FALSE; 561 (void) pthread_cond_broadcast(&g_cv); 562 563 (void) pthread_mutex_unlock(&g_mutex); 564 } 565 if (!ledsthr_created) { 566 if ((pthread_attr_init(&ledsthr_attr) != 0) || 567 (pthread_attr_setscope(&ledsthr_attr, 568 PTHREAD_SCOPE_SYSTEM) != 0)) 569 return; 570 571 if ((err = pthread_create(&ledsthr_tid, &ledsthr_attr, 572 fcal_leds_thread, g_led_dtls)) != 0) { 573 SYSLOG(LOG_ERR, EM_THREAD_CREATE_FAILED, 574 mystrerror(err)); 575 return; 576 } 577 578 ledsthr_created = B_TRUE; 579 } 580 581 if (pollthr_created == B_FALSE) { 582 if ((pthread_attr_init(&pollthr_attr) != 0) || 583 (pthread_attr_setscope(&pollthr_attr, 584 PTHREAD_SCOPE_SYSTEM) != 0)) 585 return; 586 587 if ((err = pthread_create(&pollthr_tid, &pollthr_attr, 588 fcal_poll_thread, g_led_dtls)) != 0) { 589 g_led_dtls->polling = B_FALSE; 590 SYSLOG(LOG_ERR, EM_THREAD_CREATE_FAILED, 591 mystrerror(err)); 592 return; 593 } 594 595 pollthr_created = B_TRUE; 596 } 597 } 598 599 /* 600 * fini entry point of the plugin 601 */ 602 static void 603 fcal_leds_fini(void) 604 { 605 int c; 606 607 /* unregister event handlers */ 608 (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED, 609 piclfcal_evhandler, NULL); 610 (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED, 611 piclfcal_evhandler, NULL); 612 /* 613 * it's very confusing to leave uncontrolled leds on, so clear them. 614 */ 615 if (g_led_dtls != NULL) { 616 int ledNo; 617 int diskNo; 618 for (ledNo = 0; ledNo < FCAL_LED_CNT; ledNo++) { 619 if ((g_led_dtls->led_addr[ledNo] == NULL) || 620 (g_led_dtls->led_state[ledNo] == NULL)) { 621 break; /* incomplete setup */ 622 } 623 for (diskNo = 0; diskNo < g_led_dtls->n_disks; 624 diskNo++) { 625 clr_led(diskNo, LED_PROPS_START + 1 + ledNo, 626 g_led_dtls); 627 } 628 } 629 } 630 /* 631 * tell other threads to stop 632 */ 633 if (cvAndMutexInit && (ledsthr_created || pollthr_created)) { 634 g_finish_now = B_TRUE; 635 c = pthread_mutex_lock(&g_mutex); 636 if (c != 0) { 637 SYSLOG(LOG_ERR, EM_MUTEX_FAIL, mystrerror(c)); 638 } else { 639 (void) pthread_cond_broadcast(&g_cv); 640 (void) pthread_mutex_unlock(&g_mutex); 641 642 /* 643 * and wait for them to acknowledge 644 */ 645 while ((ledsthr_created && !g_leds_thread_ack) || 646 (pollthr_created && !poll_thread_ack)) { 647 c = pthread_mutex_lock(&g_mutex); 648 if (c != 0) { 649 SYSLOG(LOG_ERR, EM_MUTEX_FAIL, 650 mystrerror(c)); 651 break; 652 } 653 (void) pthread_cond_wait(&g_cv_ack, &g_mutex); 654 (void) pthread_mutex_unlock(&g_mutex); 655 } 656 } 657 } 658 /* 659 * remove picl nodes created by this plugin 660 */ 661 if (g_led_dtls != NULL) { 662 for (c = 0; c < g_led_dtls->n_disks; c++) { 663 /* 664 * remove all disk unit nodes from frutree 665 */ 666 delete_disk_unit(g_led_dtls, c); 667 } 668 /* 669 * remove Devices tables of references to leds 670 * and led State properties 671 */ 672 delete_led_refs(g_led_dtls); 673 /* 674 * finally free the led details 675 */ 676 free_led_dtls(g_led_dtls); 677 g_led_dtls = NULL; 678 } 679 } 680