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