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