1 /* 2 * Copyright (c) 1998-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 /* 29 Copyright (c) 1998 Apple Computer, Inc. All rights reserved. 30 31 HISTORY 32 1998-7-13 Godfrey van der Linden(gvdl) 33 Created. 34 */ 35 36 #include <pexpert/pexpert.h> 37 #include <IOKit/IOWorkLoop.h> 38 #include <IOKit/IOEventSource.h> 39 #include <IOKit/IOInterruptEventSource.h> 40 #include <IOKit/IOCommandGate.h> 41 #include <IOKit/IOTimeStamp.h> 42 #include <libkern/OSDebug.h> 43 44 #define super OSObject 45 46 OSDefineMetaClassAndStructors(IOWorkLoop, OSObject); 47 48 // Block of unused functions intended for future use 49 #if __LP64__ 50 OSMetaClassDefineReservedUnused(IOWorkLoop, 0); 51 OSMetaClassDefineReservedUnused(IOWorkLoop, 1); 52 OSMetaClassDefineReservedUnused(IOWorkLoop, 2); 53 #else 54 OSMetaClassDefineReservedUsed(IOWorkLoop, 0); 55 OSMetaClassDefineReservedUsed(IOWorkLoop, 1); 56 OSMetaClassDefineReservedUsed(IOWorkLoop, 2); 57 #endif 58 OSMetaClassDefineReservedUnused(IOWorkLoop, 3); 59 OSMetaClassDefineReservedUnused(IOWorkLoop, 4); 60 OSMetaClassDefineReservedUnused(IOWorkLoop, 5); 61 OSMetaClassDefineReservedUnused(IOWorkLoop, 6); 62 OSMetaClassDefineReservedUnused(IOWorkLoop, 7); 63 64 enum IOWorkLoopState { kLoopRestart = 0x1, kLoopTerminate = 0x2 }; 65 #ifdef __ppc__ 66 static inline void SETP(void *addr, unsigned int flag) 67 { unsigned int *num = (unsigned int *) addr; *num |= flag; } 68 static inline void CLRP(void *addr, unsigned int flag) 69 { unsigned int *num = (unsigned int *) addr; *num &= ~flag; } 70 static inline bool ISSETP(void *addr, unsigned int flag) 71 { unsigned int *num = (unsigned int *) addr; return (*num & flag) != 0; } 72 #else 73 static inline void SETP(void *addr, unsigned int flag) 74 { unsigned char *num = (unsigned char *) addr; *num |= flag; } 75 static inline void CLRP(void *addr, unsigned int flag) 76 { unsigned char *num = (unsigned char *) addr; *num &= ~flag; } 77 static inline bool ISSETP(void *addr, unsigned int flag) 78 { unsigned char *num = (unsigned char *) addr; return (*num & flag) != 0; } 79 #endif 80 81 #define fFlags loopRestart 82 83 84 bool IOWorkLoop::init() 85 { 86 // The super init and gateLock allocation MUST be done first 87 if ( !super::init() ) 88 return false; 89 90 if ( gateLock == NULL ) { 91 if ( !( gateLock = IORecursiveLockAlloc()) ) 92 return false; 93 } 94 95 if ( workToDoLock == NULL ) { 96 if ( !(workToDoLock = IOSimpleLockAlloc()) ) 97 return false; 98 IOSimpleLockInit(workToDoLock); 99 workToDo = false; 100 } 101 102 if ( controlG == NULL ) { 103 controlG = IOCommandGate::commandGate( 104 this, 105 OSMemberFunctionCast( 106 IOCommandGate::Action, 107 this, 108 &IOWorkLoop::_maintRequest)); 109 110 if ( !controlG ) 111 return false; 112 // Point the controlGate at the workLoop. Usually addEventSource 113 // does this automatically. The problem is in this case addEventSource 114 // uses the control gate and it has to be bootstrapped. 115 controlG->setWorkLoop(this); 116 if (addEventSource(controlG) != kIOReturnSuccess) 117 return false; 118 } 119 120 if ( workThread == NULL ) { 121 thread_continue_t cptr = OSMemberFunctionCast( 122 thread_continue_t, 123 this, 124 &IOWorkLoop::threadMain); 125 if (KERN_SUCCESS != kernel_thread_start(cptr, this, &workThread)) 126 return false; 127 } 128 129 return true; 130 } 131 132 IOWorkLoop * 133 IOWorkLoop::workLoop() 134 { 135 return IOWorkLoop::workLoopWithOptions(0); 136 } 137 138 IOWorkLoop * 139 IOWorkLoop::workLoopWithOptions(IOOptionBits options) 140 { 141 IOWorkLoop *me = new IOWorkLoop; 142 143 if (me && options) { 144 me->reserved = IONew(ExpansionData, 1); 145 if (!me->reserved) { 146 me->release(); 147 return 0; 148 } 149 me->reserved->options = options; 150 } 151 152 if (me && !me->init()) { 153 me->release(); 154 return 0; 155 } 156 157 return me; 158 } 159 160 // Free is called twice: 161 // First when the atomic retainCount transitions from 1 -> 0 162 // Secondly when the work loop itself is commiting hari kari 163 // Hence the each leg of the free must be single threaded. 164 void IOWorkLoop::free() 165 { 166 if (workThread) { 167 IOInterruptState is; 168 169 // If we are here then we must be trying to shut down this work loop 170 // in this case disable all of the event source, mark the loop 171 // as terminating and wakeup the work thread itself and return 172 // Note: we hold the gate across the entire operation mainly for the 173 // benefit of our event sources so we can disable them cleanly. 174 closeGate(); 175 176 disableAllEventSources(); 177 178 is = IOSimpleLockLockDisableInterrupt(workToDoLock); 179 SETP(&fFlags, kLoopTerminate); 180 thread_wakeup_one((void *) &workToDo); 181 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); 182 183 openGate(); 184 } 185 else /* !workThread */ { 186 IOEventSource *event, *next; 187 188 for (event = eventChain; event; event = next) { 189 next = event->getNext(); 190 event->setWorkLoop(0); 191 event->setNext(0); 192 event->release(); 193 } 194 eventChain = 0; 195 196 // Either we have a partial initialization to clean up 197 // or the workThread itself is performing hari-kari. 198 // Either way clean up all of our resources and return. 199 200 if (controlG) { 201 controlG->release(); 202 controlG = 0; 203 } 204 205 if (workToDoLock) { 206 IOSimpleLockFree(workToDoLock); 207 workToDoLock = 0; 208 } 209 210 if (gateLock) { 211 IORecursiveLockFree(gateLock); 212 gateLock = 0; 213 } 214 if (reserved) { 215 IODelete(reserved, ExpansionData, 1); 216 reserved = 0; 217 } 218 219 super::free(); 220 } 221 } 222 223 IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent) 224 { 225 return controlG->runCommand((void *) mAddEvent, (void *) newEvent); 226 } 227 228 IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove) 229 { 230 return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove); 231 } 232 233 void IOWorkLoop::enableAllEventSources() const 234 { 235 IOEventSource *event; 236 237 for (event = eventChain; event; event = event->getNext()) 238 event->enable(); 239 } 240 241 void IOWorkLoop::disableAllEventSources() const 242 { 243 IOEventSource *event; 244 245 for (event = eventChain; event; event = event->getNext()) 246 if (event != controlG) // Don't disable the control gate 247 event->disable(); 248 } 249 250 void IOWorkLoop::enableAllInterrupts() const 251 { 252 IOEventSource *event; 253 254 for (event = eventChain; event; event = event->getNext()) 255 if (OSDynamicCast(IOInterruptEventSource, event)) 256 event->enable(); 257 } 258 259 void IOWorkLoop::disableAllInterrupts() const 260 { 261 IOEventSource *event; 262 263 for (event = eventChain; event; event = event->getNext()) 264 if (OSDynamicCast(IOInterruptEventSource, event)) 265 event->disable(); 266 } 267 268 #if KDEBUG 269 #define IOTimeClientS() \ 270 do { \ 271 IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT), \ 272 (unsigned int) this, (unsigned int) event); \ 273 } while(0) 274 275 #define IOTimeClientE() \ 276 do { \ 277 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT), \ 278 (unsigned int) this, (unsigned int) event); \ 279 } while(0) 280 281 #define IOTimeWorkS() \ 282 do { \ 283 IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK), (unsigned int) this); \ 284 } while(0) 285 286 #define IOTimeWorkE() \ 287 do { \ 288 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_WORK),(unsigned int) this); \ 289 } while(0) 290 291 #else /* !KDEBUG */ 292 293 #define IOTimeClientS() 294 #define IOTimeClientE() 295 #define IOTimeWorkS() 296 #define IOTimeWorkE() 297 298 #endif /* KDEBUG */ 299 300 /* virtual */ bool IOWorkLoop::runEventSources() 301 { 302 bool res = false; 303 closeGate(); 304 if (ISSETP(&fFlags, kLoopTerminate)) 305 goto abort; 306 307 IOTimeWorkS(); 308 bool more; 309 do { 310 CLRP(&fFlags, kLoopRestart); 311 more = false; 312 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock); 313 workToDo = false; 314 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); 315 for (IOEventSource *evnt = eventChain; evnt; evnt = evnt->getNext()) { 316 317 IOTimeClientS(); 318 more |= evnt->checkForWork(); 319 IOTimeClientE(); 320 321 if (ISSETP(&fFlags, kLoopTerminate)) 322 goto abort; 323 else if (fFlags & kLoopRestart) { 324 more = true; 325 break; 326 } 327 } 328 } while (more); 329 330 res = true; 331 IOTimeWorkE(); 332 333 abort: 334 openGate(); 335 return res; 336 } 337 338 /* virtual */ void IOWorkLoop::threadMain() 339 { 340 restartThread: 341 do { 342 if ( !runEventSources() ) 343 goto exitThread; 344 345 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock); 346 if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) { 347 assert_wait((void *) &workToDo, false); 348 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); 349 thread_continue_t cptr = NULL; 350 if (!reserved || !(kPreciousStack & reserved->options)) 351 cptr = OSMemberFunctionCast( 352 thread_continue_t, this, &IOWorkLoop::threadMain); 353 thread_block_parameter(cptr, this); 354 goto restartThread; 355 /* NOTREACHED */ 356 } 357 358 // At this point we either have work to do or we need 359 // to commit suicide. But no matter 360 // Clear the simple lock and retore the interrupt state 361 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); 362 } while(workToDo); 363 364 exitThread: 365 thread_t thread = workThread; 366 workThread = 0; // Say we don't have a loop and free ourselves 367 free(); 368 369 thread_deallocate(thread); 370 (void) thread_terminate(thread); 371 } 372 373 IOThread IOWorkLoop::getThread() const 374 { 375 return workThread; 376 } 377 378 bool IOWorkLoop::onThread() const 379 { 380 return (IOThreadSelf() == workThread); 381 } 382 383 bool IOWorkLoop::inGate() const 384 { 385 return IORecursiveLockHaveLock(gateLock); 386 } 387 388 // Internal APIs used by event sources to control the thread 389 void IOWorkLoop::signalWorkAvailable() 390 { 391 if (workToDoLock) { 392 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock); 393 workToDo = true; 394 thread_wakeup_one((void *) &workToDo); 395 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); 396 } 397 } 398 399 void IOWorkLoop::openGate() 400 { 401 IORecursiveLockUnlock(gateLock); 402 } 403 404 void IOWorkLoop::closeGate() 405 { 406 IORecursiveLockLock(gateLock); 407 } 408 409 bool IOWorkLoop::tryCloseGate() 410 { 411 return IORecursiveLockTryLock(gateLock) != 0; 412 } 413 414 int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType) 415 { 416 return IORecursiveLockSleep(gateLock, event, interuptibleType); 417 } 418 419 int IOWorkLoop::sleepGate(void *event, AbsoluteTime deadline, UInt32 interuptibleType) 420 { 421 return IORecursiveLockSleepDeadline(gateLock, event, deadline, interuptibleType); 422 } 423 424 void IOWorkLoop::wakeupGate(void *event, bool oneThread) 425 { 426 IORecursiveLockWakeup(gateLock, event, oneThread); 427 } 428 429 IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target, 430 void *arg0, void *arg1, 431 void *arg2, void *arg3) 432 { 433 IOReturn res; 434 435 // closeGate is recursive so don't worry if we already hold the lock. 436 closeGate(); 437 res = (*inAction)(target, arg0, arg1, arg2, arg3); 438 openGate(); 439 440 return res; 441 } 442 443 IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *) 444 { 445 maintCommandEnum command = (maintCommandEnum) (uintptr_t) inC; 446 IOEventSource *inEvent = (IOEventSource *) inD; 447 IOReturn res = kIOReturnSuccess; 448 449 switch (command) 450 { 451 case mAddEvent: 452 if (!inEvent->getWorkLoop()) { 453 SETP(&fFlags, kLoopRestart); 454 455 inEvent->retain(); 456 inEvent->setWorkLoop(this); 457 inEvent->setNext(0); 458 459 if (!eventChain) 460 eventChain = inEvent; 461 else { 462 IOEventSource *event, *next; 463 464 for (event = eventChain; (next = event->getNext()); event = next) 465 ; 466 event->setNext(inEvent); 467 } 468 } 469 break; 470 471 case mRemoveEvent: 472 if (inEvent->getWorkLoop()) { 473 if (eventChain == inEvent) 474 eventChain = inEvent->getNext(); 475 else { 476 IOEventSource *event, *next; 477 478 event = eventChain; 479 while ((next = event->getNext()) && next != inEvent) 480 event = next; 481 482 if (!next) { 483 res = kIOReturnBadArgument; 484 break; 485 } 486 event->setNext(inEvent->getNext()); 487 } 488 489 inEvent->setWorkLoop(0); 490 inEvent->setNext(0); 491 inEvent->release(); 492 SETP(&fFlags, kLoopRestart); 493 } 494 break; 495 496 default: 497 return kIOReturnUnsupported; 498 } 499 500 return res; 501 } 502