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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <pri.h>
30 #include "priplugin.h"
31 
32 #pragma init(priplugin_register)	/* place in .init section */
33 
34 static md_t *mdp;
35 
36 static mutex_t	rebuild_lock;
37 static cond_t	rebuild_cv;
38 
39 static thread_t pri_worker_thread_id, pri_reader_thread_id;
40 static boolean_t all_thr_exit = B_FALSE;
41 
42 static void priplugin_init(void);
43 static void priplugin_fini(void);
44 static void
45 event_handler(const char *ename, const void *earg, size_t size, void *cookie);
46 static void *pri_worker_thread(void *arg);
47 static void *pri_reader_thread(void *arg);
48 static int remove_old_segments(picl_nodehdl_t node, void *args);
49 
50 
51 picld_plugin_reg_t priplugin_reg = {
52 	PICLD_PLUGIN_VERSION_1,
53 	PICLD_PLUGIN_CRITICAL,
54 	"pri_plugin",
55 	priplugin_init,
56 	priplugin_fini
57 };
58 
59 static void
60 set_prop_info(ptree_propinfo_t *propinfo, int size, char *name, int type)
61 {
62 	propinfo->version = PICLD_PLUGIN_VERSION_1;
63 	propinfo->read = NULL;
64 	propinfo->write = NULL;
65 	propinfo->piclinfo.type = type;
66 	propinfo->piclinfo.accessmode = PICL_READ;
67 	propinfo->piclinfo.size = size;
68 	(void) strlcpy(propinfo->piclinfo.name, name,
69 	    sizeof (propinfo->piclinfo.name));
70 }
71 
72 boolean_t
73 prop_exists(picl_nodehdl_t node, char *name)
74 {
75 	int status;
76 	picl_prophdl_t proph;
77 
78 	status = ptree_get_prop_by_name(node, name, &proph);
79 	if (status == PICL_SUCCESS)
80 		return (B_TRUE);
81 	else
82 		return (B_FALSE);
83 }
84 
85 void
86 add_md_prop(picl_nodehdl_t node, int size, char *name, void* value, int type)
87 {
88 	ptree_propinfo_t propinfo;
89 	picl_prophdl_t proph;
90 
91 	if (!prop_exists(node, name)) {
92 		set_prop_info(&propinfo, size, name, type);
93 
94 		(void) ptree_create_and_add_prop(node, &propinfo,
95 		    value, &proph);
96 	}
97 }
98 
99 /*ARGSUSED*/
100 static int
101 remove_old_segments(picl_nodehdl_t node, void *args)
102 {
103 	int status;
104 
105 	if ((status = ptree_delete_node(node)) == PICL_SUCCESS)
106 		ptree_destroy_node(node);
107 	else
108 		pri_debug(LOG_NOTICE, "remove_old_segments: can't delete "
109 		    "segment node: %s\n", picl_strerror(status));
110 
111 	return (PICL_WALK_CONTINUE);
112 }
113 
114 static void
115 priplugin_init(void)
116 {
117 	int status;
118 
119 	pri_debug(LOG_NOTICE, "priplugin: mem tree and io label thread "
120 	    "being created; callbacks being registered\n");
121 
122 	all_thr_exit = B_FALSE;
123 
124 	(void) mutex_init(&rebuild_lock, USYNC_THREAD, NULL);
125 	(void) cond_init(&rebuild_cv, USYNC_THREAD, NULL);
126 
127 	if ((status = thr_create(NULL, NULL, pri_worker_thread, NULL, THR_BOUND,
128 	    &pri_worker_thread_id)) < 0) {
129 		pri_debug(LOG_NOTICE, "priplugin: can't create worker thread: "
130 		    "%d\n", status);
131 		all_thr_exit = B_TRUE;
132 		(void) mutex_destroy(&rebuild_lock);
133 		(void) cond_destroy(&rebuild_cv);
134 	} else if ((status = thr_create(NULL, NULL, pri_reader_thread, NULL,
135 	    THR_BOUND, &pri_reader_thread_id)) < 0) {
136 		pri_debug(LOG_NOTICE, "priplugin: can't create reader thread: "
137 		    "%d\n", status);
138 		(void) mutex_lock(&rebuild_lock);
139 		all_thr_exit = B_TRUE;
140 		(void) cond_signal(&rebuild_cv);
141 		(void) mutex_unlock(&rebuild_lock);
142 		(void) thr_join(pri_worker_thread_id, NULL, NULL);
143 		(void) mutex_destroy(&rebuild_lock);
144 		(void) cond_destroy(&rebuild_cv);
145 	} else {
146 		pri_debug(LOG_NOTICE, "priplugin_init: worker and reader "
147 		    "threads created - registering event handlers\n");
148 		/*
149 		 * register event_handler for both "sysevent-device-added",
150 		 * "sysevent_device_removed", and for
151 		 * "sysevent-dr-app-state-change" PICL events
152 		 */
153 		(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
154 		    event_handler, NULL);
155 		(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
156 		    event_handler, NULL);
157 		(void) ptree_register_handler(PICLEVENT_DR_AP_STATE_CHANGE,
158 		    event_handler, NULL);
159 	}
160 }
161 
162 /*
163  * This thread handles the main processing of PRI data.  It is woken
164  * up by either the event handler, to process a PICL event, or it is
165  * woken up by the PRI reader thread which has just fetched a new
166  * copy of the PRI.
167  */
168 /*ARGSUSED*/
169 static void *
170 pri_worker_thread(void *arg)
171 {
172 	int status;
173 	picl_nodehdl_t picl_root_node;
174 
175 	pri_debug(LOG_NOTICE, "pri_worker_thread: start\n");
176 
177 	(void) mutex_lock(&rebuild_lock);
178 	while (1) {
179 		(void) cond_wait(&rebuild_cv, &rebuild_lock);
180 
181 		if (all_thr_exit == B_TRUE) {
182 			(void) mutex_unlock(&rebuild_lock);
183 			pri_debug(LOG_NOTICE, "pri_worker_thread: time to "
184 			    "exit\n");
185 			break;
186 		}
187 
188 		status = ptree_get_root(&picl_root_node);
189 		if (status != PICL_SUCCESS) {
190 			pri_debug(LOG_NOTICE, "pri_worker_thread: "
191 			    "can't get picl tree root node: %s\n",
192 			    picl_strerror(status));
193 			continue;
194 		}
195 
196 		pri_debug(LOG_NOTICE, "pri_worker_thread: have root picl "
197 		    "and PRI nodes\n");
198 
199 		status = ptree_walk_tree_by_class(picl_root_node,
200 		    "memory-segment", NULL, remove_old_segments);
201 		if (status != PICL_SUCCESS) {
202 			pri_debug(LOG_NOTICE, "pri_worker_thread: can't remove "
203 			    "old memory segments: \n", picl_strerror(status));
204 		} else
205 			pri_debug(LOG_NOTICE, "pri_worker_thread: old memory "
206 			    "segments removed\n");
207 
208 		status = ptree_walk_tree_by_class(picl_root_node, "memory",
209 		    (void *) mdp, add_mem_prop);
210 		if (status != PICL_SUCCESS) {
211 			pri_debug(LOG_NOTICE, "pri_worker_thread: memory "
212 			    "segments walk failed: \n", picl_strerror(status));
213 		} else
214 			pri_debug(LOG_NOTICE, "pri_worker_thread: success "
215 			    "walking memory node\n");
216 
217 		io_dev_addlabel(mdp);
218 	}
219 	pri_debug(LOG_NOTICE, "pri_worker_thread: exiting\n");
220 	return (NULL);
221 }
222 
223 /*
224  * This thread camps out in the PRI driver, waiting for it to return
225  * the contents of a new PRI.  When the PRI is changed this thread
226  * reads that data and prepares it for processing by the worker thread.
227  * It then signals the worker thread to process the new PRI data.
228  */
229 /*ARGSUSED*/
230 static void *
231 pri_reader_thread(void *arg)
232 {
233 	uint64_t tok;
234 	int status, count;
235 
236 	pri_debug(LOG_NOTICE, "pri_reader_thread: thread start\n");
237 
238 	if (pri_init() != 0) {
239 		pri_debug(LOG_NOTICE, "pri_reader_thread: pri_init failed\n");
240 		return (NULL);
241 	}
242 
243 	/*
244 	 * It's entirely possible that a new PRI may get pushed while
245 	 * the worker thread is processing the previous PRI.  We will
246 	 * wait until the worker is finished, then flush the old contents
247 	 * and wake up the worker again to process the new data.
248 	 */
249 	mdp = NULL;
250 	tok = 0;
251 	count = 0;
252 	while (1) {
253 		/*
254 		 * The _fini() function will close the PRI's fd, which will
255 		 * cause this function to break out of waiting in the PRI
256 		 * driver and return an error.
257 		 */
258 		status = pri_devinit(&tok);
259 
260 		(void) mutex_lock(&rebuild_lock);
261 		if (all_thr_exit == B_TRUE) {
262 			(void) mutex_unlock(&rebuild_lock);
263 			pri_debug(LOG_NOTICE, "pri_reader_thread: time to "
264 			    "exit\n");
265 			break;
266 		}
267 
268 		/*
269 		 * Wait until the worker is idle before swapping in the
270 		 * new PRI contents, then signal the worker to process
271 		 * that new data.
272 		 */
273 		if (status == 0) {
274 			pri_debug(LOG_NOTICE, "pri_reader_thread: got PRI\n");
275 
276 			/* old buffer will be freed by pri_bufinit() */
277 			mdp = pri_bufinit(mdp);
278 			if (mdp != NULL) {
279 				(void) cond_signal(&rebuild_cv);
280 				count = 0;
281 			} else {
282 				pri_debug(LOG_NOTICE, "pri_reader_thread: "
283 				    "NULL mdp!\n");
284 				status = -1;
285 			}
286 		}
287 
288 		/*
289 		 * Try to handle SP resets or other unexplained errors
290 		 * from ds by closing down and re-opening the PRI driver.
291 		 */
292 		if (status == -1) {
293 			if (errno != 0) {
294 				pri_debug(LOG_NOTICE, "pri_reader_thread: "
295 				    "can't get PRI contents: %s\n",
296 				    strerror(errno));
297 			}
298 			if (++count > 6) {
299 				pri_debug(LOG_NOTICE, "pci_reader_thread: "
300 				    "can't process PRI data\n");
301 				(void) mutex_unlock(&rebuild_lock);
302 				break;
303 			}
304 			/* old buffer will be freed by pri_fini() */
305 			pri_fini();
306 			tok = 0;
307 			sleep(10);
308 			if (pri_init() != 0) {
309 				pri_debug(LOG_NOTICE, "pci_reader_thread: "
310 				    "can't reinitialize PRI driver\n");
311 				(void) mutex_unlock(&rebuild_lock);
312 				break;
313 			}
314 		}
315 		(void) mutex_unlock(&rebuild_lock);
316 	}
317 
318 	pri_debug(LOG_NOTICE, "pri_reader_thread: thread exiting\n");
319 	return (NULL);
320 }
321 
322 static void
323 priplugin_fini(void)
324 {
325 	pri_debug(LOG_NOTICE, "priplugin_fini: called\n");
326 
327 	if (all_thr_exit == B_TRUE)
328 		return;
329 
330 	/* unregister the event handlers */
331 	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
332 	    event_handler, NULL);
333 	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
334 	    event_handler, NULL);
335 	(void) ptree_unregister_handler(PICLEVENT_DR_AP_STATE_CHANGE,
336 	    event_handler, NULL);
337 
338 	/*
339 	 * Set the exit flag to tell the worker thread to quit and wake
340 	 * up that thread.  Once that thread is reaped then pull the rug
341 	 * out from the PRI reader thread by calling pri_fini(), which
342 	 * closes the PRI fd.  That wakes the PRI reader thread and it
343 	 * will then exit as well.
344 	 */
345 	(void) mutex_lock(&rebuild_lock);
346 	all_thr_exit = B_TRUE;
347 	(void) cond_signal(&rebuild_cv);
348 	(void) mutex_unlock(&rebuild_lock);
349 
350 	(void) thr_join(pri_worker_thread_id, NULL, NULL);
351 
352 	pri_devfini(mdp);
353 	mdp = NULL;
354 	pri_fini();
355 	(void) thr_join(pri_reader_thread_id, NULL, NULL);
356 
357 	(void) mutex_destroy(&rebuild_lock);
358 	(void) cond_destroy(&rebuild_cv);
359 }
360 
361 void
362 priplugin_register(void)
363 {
364 	picld_plugin_register(&priplugin_reg);
365 }
366 
367 /*
368  * Discovery event handler
369  * respond to the picl events:
370  *      PICLEVENT_SYSEVENT_DEVICE_ADDED
371  *      PICLEVENT_SYSEVENT_DEVICE_REMOVED
372  *      PICLEVENT_DR_AP_STATE_CHANGE
373  *
374  * We can't do much of anything fancy since the event data doesn't contain
375  * a nac for the device.  Nothing to do for remove - the devtree plug-in
376  * will have removed the node for us.  For add we have to go back and
377  * add labels again.
378  */
379 static void
380 event_handler(const char *ename, const void *earg, size_t size, void *cookie)
381 {
382 	pri_debug(LOG_NOTICE, "pri: event_handler: caught event "
383 	    "%s\n", ename);
384 	if ((strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0) ||
385 	    (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0) ||
386 	    (strcmp(ename, PICLEVENT_DR_AP_STATE_CHANGE) == 0)) {
387 		pri_debug(LOG_NOTICE, "pri: event_handler: handle event "
388 		    "%s; waking worker thread\n", ename);
389 		(void) mutex_lock(&rebuild_lock);
390 
391 		if (all_thr_exit == B_FALSE)
392 			(void) cond_signal(&rebuild_cv);
393 
394 		(void) mutex_unlock(&rebuild_lock);
395 	}
396 }
397 
398 /*VARARGS2*/
399 void
400 pri_debug(int level, char *fmt, ...)
401 {
402 #if (PRI_DEBUG != 0)
403 	va_list	ap;
404 
405 	va_start(ap, fmt);
406 	vsyslog(level, fmt, ap);
407 	va_end(ap);
408 #endif
409 }
410