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