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