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