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
fcal_leds_register(void)112 fcal_leds_register(void)
113 {
114 (void) picld_plugin_register(&my_reg_info);
115 }
116
117 /* ARGSUSED */
118 static void
piclfcal_evhandler(const char * ename,const void * earg,size_t size,void * cookie)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 *
open_config(void)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
read_led_state(ptree_rarg_t * parg,void * buf)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
find_disk_slot(led_dtls_t * dtls,int disk,picl_nodehdl_t * nodeh)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
create_Device_table(picl_prophdl_t * tbl_h,picl_prophdl_t * tableh)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
add_led_refs(led_dtls_t * dtls)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
delete_led_refs(led_dtls_t * dtls)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 *
fcal_poll_thread(void * args)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
fcal_leds_init(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
fcal_leds_fini(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