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