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
set_prop_info(ptree_propinfo_t * propinfo,int size,char * name,int type)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
prop_exists(picl_nodehdl_t node,char * name)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
add_md_prop(picl_nodehdl_t node,int size,char * name,void * value,int type)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
remove_old_segments(picl_nodehdl_t node,void * args)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
priplugin_init(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 *
pri_worker_thread(void * arg)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 *
pri_reader_thread(void * arg)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
priplugin_fini(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
priplugin_register(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
event_handler(const char * ename,const void * earg,size_t size,void * cookie)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
pri_debug(int level,char * fmt,...)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