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 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 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 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 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 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 * 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 * 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 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 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 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 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