X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/c0fea4742e91338fffdcf79f86a7c1d5e2b97eb1..94ff46dc2849db4d43eaaf144872decc522aafb4:/iokit/Kernel/IOWorkLoop.cpp diff --git a/iokit/Kernel/IOWorkLoop.cpp b/iokit/Kernel/IOWorkLoop.cpp index 81e03f4ab..d046938d2 100644 --- a/iokit/Kernel/IOWorkLoop.cpp +++ b/iokit/Kernel/IOWorkLoop.cpp @@ -1,46 +1,56 @@ /* - * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1998-2010 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -/* -Copyright (c) 1998 Apple Computer, Inc. All rights reserved. -HISTORY - 1998-7-13 Godfrey van der Linden(gvdl) - Created. -*/ +#include #include #include #include #include +#include #include +#include +#include +#include #define super OSObject OSDefineMetaClassAndStructors(IOWorkLoop, OSObject); // Block of unused functions intended for future use +#if __LP64__ +OSMetaClassDefineReservedUnused(IOWorkLoop, 0); +OSMetaClassDefineReservedUnused(IOWorkLoop, 1); +OSMetaClassDefineReservedUnused(IOWorkLoop, 2); +#else OSMetaClassDefineReservedUsed(IOWorkLoop, 0); OSMetaClassDefineReservedUsed(IOWorkLoop, 1); - -OSMetaClassDefineReservedUnused(IOWorkLoop, 2); +OSMetaClassDefineReservedUsed(IOWorkLoop, 2); +#endif OSMetaClassDefineReservedUnused(IOWorkLoop, 3); OSMetaClassDefineReservedUnused(IOWorkLoop, 4); OSMetaClassDefineReservedUnused(IOWorkLoop, 5); @@ -48,386 +58,688 @@ OSMetaClassDefineReservedUnused(IOWorkLoop, 6); OSMetaClassDefineReservedUnused(IOWorkLoop, 7); enum IOWorkLoopState { kLoopRestart = 0x1, kLoopTerminate = 0x2 }; -static inline void SETP(void *addr, unsigned int flag) - { unsigned int *num = (unsigned int *) addr; *num |= flag; } -static inline void CLRP(void *addr, unsigned int flag) - { unsigned int *num = (unsigned int *) addr; *num &= ~flag; } -static inline bool ISSETP(void *addr, unsigned int flag) - { unsigned int *num = (unsigned int *) addr; return (*num & flag) != 0; } +static inline void +SETP(void *addr, unsigned int flag) +{ + unsigned char *num = (unsigned char *) addr; *num |= flag; +} +static inline void +CLRP(void *addr, unsigned int flag) +{ + unsigned char *num = (unsigned char *) addr; *num &= ~flag; +} +static inline bool +ISSETP(void *addr, unsigned int flag) +{ + unsigned char *num = (unsigned char *) addr; return (*num & flag) != 0; +} #define fFlags loopRestart -bool IOWorkLoop::init() -{ - // The super init and gateLock allocation MUST be done first - if ( !super::init() ) - return false; - - if ( !(gateLock = IORecursiveLockAlloc()) ) - return false; +#define passiveEventChain reserved->passiveEventChain - if ( !(workToDoLock = IOSimpleLockAlloc()) ) - return false; +#if IOKITSTATS - controlG = IOCommandGate:: - commandGate(this, OSMemberFunctionCast(IOCommandGate::Action, - this, &IOWorkLoop::_maintRequest)); - if ( !controlG ) - return false; +#define IOStatisticsRegisterCounter() \ +do { \ + reserved->counter = IOStatistics::registerWorkLoop(this); \ +} while(0) - IOSimpleLockInit(workToDoLock); - workToDo = false; +#define IOStatisticsUnregisterCounter() \ +do { \ + if (reserved) \ + IOStatistics::unregisterWorkLoop(reserved->counter); \ +} while(0) - // Point the controlGate at the workLoop. Usually addEventSource - // does this automatically. The problem is in this case addEventSource - // uses the control gate and it has to be bootstrapped. - controlG->setWorkLoop(this); - if (addEventSource(controlG) != kIOReturnSuccess) - return false; +#define IOStatisticsOpenGate() \ +do { \ + IOStatistics::countWorkLoopOpenGate(reserved->counter); \ + if (reserved->lockInterval) lockTime(); \ +} while(0) +#define IOStatisticsCloseGate() \ +do { \ + IOStatistics::countWorkLoopCloseGate(reserved->counter); \ + if (reserved->lockInterval) reserved->lockTime = mach_absolute_time(); \ +} while(0) - IOThreadFunc cptr = - OSMemberFunctionCast(IOThreadFunc, this, &IOWorkLoop::threadMain); - workThread = IOCreateThread(cptr, this); - if (!workThread) - return false; +#define IOStatisticsAttachEventSource() \ +do { \ + IOStatistics::attachWorkLoopEventSource(reserved->counter, inEvent->reserved->counter); \ +} while(0) - return true; -} +#define IOStatisticsDetachEventSource() \ +do { \ + IOStatistics::detachWorkLoopEventSource(reserved->counter, inEvent->reserved->counter); \ +} while(0) -IOWorkLoop * -IOWorkLoop::workLoop() -{ - IOWorkLoop *me = new IOWorkLoop; +#else - if (me && !me->init()) { - me->release(); - return 0; - } +#define IOStatisticsRegisterCounter() +#define IOStatisticsUnregisterCounter() +#define IOStatisticsOpenGate() +#define IOStatisticsCloseGate() +#define IOStatisticsAttachEventSource() +#define IOStatisticsDetachEventSource() - return me; -} +#endif /* IOKITSTATS */ -// Free is called twice: -// First when the atomic retainCount transitions from 1 -> 0 -// Secondly when the work loop itself is commiting hari kari -// Hence the each leg of the free must be single threaded. -void IOWorkLoop::free() +bool +IOWorkLoop::init() { - if (workThread) { - IOInterruptState is; + // The super init and gateLock allocation MUST be done first. + if (!super::init()) { + return false; + } - // If we are here then we must be trying to shut down this work loop - // in this case disable all of the event source, mark the loop for - // as terminating and wakeup the work thread itself and return - // Note: we hold the gate across the entire operation mainly for the - // benefit of our event sources so we can disable them cleanly. - closeGate(); + // Allocate our ExpansionData if it hasn't been allocated already. + if (!reserved) { + reserved = IONew(ExpansionData, 1); + if (!reserved) { + return false; + } - disableAllEventSources(); + bzero(reserved, sizeof(ExpansionData)); + } - is = IOSimpleLockLockDisableInterrupt(workToDoLock); - SETP(&fFlags, kLoopTerminate); - thread_wakeup_one((void *) &workToDo); - IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); + if (gateLock == NULL) { + if (!(gateLock = IORecursiveLockAlloc())) { + return false; + } + } - openGate(); - } - else /* !workThread */ { - IOEventSource *event, *next; - - for (event = eventChain; event; event = next) { - next = event->getNext(); - event->setWorkLoop(0); - event->setNext(0); - event->release(); - } - eventChain = 0; - - // Either we have a partial initialisation to clean up - // or we the workThread itself is performing hari-kari. - // either way clean up all of our resources and return. - - if (controlG) { - controlG->release(); - controlG = 0; + if (workToDoLock == NULL) { + if (!(workToDoLock = IOSimpleLockAlloc())) { + return false; + } + IOSimpleLockInit(workToDoLock); + workToDo = false; } - if (workToDoLock) { - IOSimpleLockFree(workToDoLock); - workToDoLock = 0; + IOStatisticsRegisterCounter(); + + if (controlG == NULL) { + controlG = IOCommandGate::commandGate( + this, + OSMemberFunctionCast( + IOCommandGate::Action, + this, + &IOWorkLoop::_maintRequest)); + + if (!controlG) { + return false; + } + // Point the controlGate at the workLoop. Usually addEventSource + // does this automatically. The problem is in this case addEventSource + // uses the control gate and it has to be bootstrapped. + controlG->setWorkLoop(this); + if (addEventSource(controlG) != kIOReturnSuccess) { + return false; + } } - if (gateLock) { - IORecursiveLockFree(gateLock); - gateLock = 0; + if (workThread == NULL) { + thread_continue_t cptr = OSMemberFunctionCast( + thread_continue_t, + this, + &IOWorkLoop::threadMain); + if (KERN_SUCCESS != kernel_thread_start(cptr, this, &workThread)) { + return false; + } } - super::free(); - } + (void) thread_set_tag(workThread, THREAD_TAG_IOWORKLOOP); + return true; } -IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent) -{ - return controlG->runCommand((void *) mAddEvent, (void *) newEvent); -} - -IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove) +IOWorkLoop * +IOWorkLoop::workLoop() { - return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove); + return IOWorkLoop::workLoopWithOptions(0); } -void IOWorkLoop::enableAllEventSources() const +IOWorkLoop * +IOWorkLoop::workLoopWithOptions(IOOptionBits options) { - IOEventSource *event; + IOWorkLoop *me = new IOWorkLoop; + + if (me && options) { + me->reserved = IONew(ExpansionData, 1); + if (!me->reserved) { + me->release(); + return NULL; + } + bzero(me->reserved, sizeof(ExpansionData)); + me->reserved->options = options; + } + + if (me && !me->init()) { + me->release(); + return NULL; + } - for (event = eventChain; event; event = event->getNext()) - event->enable(); + return me; } -void IOWorkLoop::disableAllEventSources() const +// Free is called twice: +// First when the atomic retainCount transitions from 1 -> 0 +// Secondly when the work loop itself is commiting hari kari +// Hence the each leg of the free must be single threaded. +void +IOWorkLoop::free() { - IOEventSource *event; - - for (event = eventChain; event; event = event->getNext()) - if (event != controlG) // Don't disable the control gate - event->disable(); + if (workThread) { + IOInterruptState is; + + // If we are here then we must be trying to shut down this work loop + // in this case disable all of the event source, mark the loop + // as terminating and wakeup the work thread itself and return + // Note: we hold the gate across the entire operation mainly for the + // benefit of our event sources so we can disable them cleanly. + closeGate(); + + disableAllEventSources(); + + is = IOSimpleLockLockDisableInterrupt(workToDoLock); + SETP(&fFlags, kLoopTerminate); + thread_wakeup_thread((void *) &workToDo, workThread); + IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); + + openGate(); + } else { /* !workThread */ + IOEventSource *event, *next; + + for (event = eventChain; event; event = next) { + next = event->getNext(); + event->setWorkLoop(NULL); + event->setNext(NULL); + event->release(); + } + eventChain = NULL; + + for (event = passiveEventChain; event; event = next) { + next = event->getNext(); + event->setWorkLoop(NULL); + event->setNext(NULL); + event->release(); + } + passiveEventChain = NULL; + + // Either we have a partial initialization to clean up + // or the workThread itself is performing hari-kari. + // Either way clean up all of our resources and return. + + if (controlG) { + controlG->workLoop = NULL; + controlG->release(); + controlG = NULL; + } + + if (workToDoLock) { + IOSimpleLockFree(workToDoLock); + workToDoLock = NULL; + } + + if (gateLock) { + IORecursiveLockFree(gateLock); + gateLock = NULL; + } + + IOStatisticsUnregisterCounter(); + + if (reserved) { + IODelete(reserved, ExpansionData, 1); + reserved = NULL; + } + + super::free(); + } } -void IOWorkLoop::enableAllInterrupts() const +IOReturn +IOWorkLoop::addEventSource(IOEventSource *newEvent) { - IOEventSource *event; + if ((workThread) + && !thread_has_thread_name(workThread) + && (newEvent->owner) + && !OSDynamicCast(IOCommandPool, newEvent->owner)) { + thread_set_thread_name(workThread, newEvent->owner->getMetaClass()->getClassName()); + } - for (event = eventChain; event; event = event->getNext()) - if (OSDynamicCast(IOInterruptEventSource, event)) - event->enable(); + return controlG->runCommand((void *) mAddEvent, (void *) newEvent); } -void IOWorkLoop::disableAllInterrupts() const +IOReturn +IOWorkLoop::removeEventSource(IOEventSource *toRemove) { - IOEventSource *event; - - for (event = eventChain; event; event = event->getNext()) - if (OSDynamicCast(IOInterruptEventSource, event)) - event->disable(); + return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove); } -#if KDEBUG -#define IOTimeClientS() \ -do { \ - IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT), \ - (unsigned int) this, (unsigned int) event); \ -} while(0) +void +IOWorkLoop::enableAllEventSources() const +{ + IOEventSource *event; -#define IOTimeClientE() \ -do { \ - IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT), \ - (unsigned int) this, (unsigned int) event); \ -} while(0) + for (event = eventChain; event; event = event->getNext()) { + event->enable(); + } -#define IOTimeWorkS() \ -do { \ - IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK), (unsigned int) this); \ -} while(0) + for (event = passiveEventChain; event; event = event->getNext()) { + event->enable(); + } +} -#define IOTimeWorkE() \ -do { \ - IOTimeStampEnd(IODBG_WORKLOOP(IOWL_WORK),(unsigned int) this); \ -} while(0) +void +IOWorkLoop::disableAllEventSources() const +{ + IOEventSource *event; + + for (event = eventChain; event; event = event->getNext()) { + event->disable(); + } -#else /* !KDEBUG */ + /* NOTE: controlG is in passiveEventChain since it's an IOCommandGate */ + for (event = passiveEventChain; event; event = event->getNext()) { + if (event != controlG) { // Don't disable the control gate + event->disable(); + } + } +} -#define IOTimeClientS() -#define IOTimeClientE() -#define IOTimeWorkS() -#define IOTimeWorkE() +void +IOWorkLoop::enableAllInterrupts() const +{ + IOEventSource *event; -#endif /* KDEBUG */ + for (event = eventChain; event; event = event->getNext()) { + if (OSDynamicCast(IOInterruptEventSource, event)) { + event->enable(); + } + } +} -/* virtual */ bool IOWorkLoop::runEventSources() +void +IOWorkLoop::disableAllInterrupts() const { - bool res = false; - closeGate(); - if (ISSETP(&fFlags, kLoopTerminate)) - goto abort; + IOEventSource *event; + + for (event = eventChain; event; event = event->getNext()) { + if (OSDynamicCast(IOInterruptEventSource, event)) { + event->disable(); + } + } +} - IOTimeWorkS(); - bool more; - do { - CLRP(&fFlags, kLoopRestart); - workToDo = more = false; - for (IOEventSource *evnt = eventChain; evnt; evnt = evnt->getNext()) { - IOTimeClientS(); - more |= evnt->checkForWork(); - IOTimeClientE(); +/* virtual */ bool +IOWorkLoop::runEventSources() +{ + bool res = false; + bool traceWL = (gIOKitTrace & kIOTraceWorkLoops) ? true : false; + bool traceES = (gIOKitTrace & kIOTraceEventSources) ? true : false; - if (ISSETP(&fFlags, kLoopTerminate)) + closeGate(); + if (ISSETP(&fFlags, kLoopTerminate)) { goto abort; - else if (fFlags & kLoopRestart) { - more = true; - break; - } } - } while (more); - res = true; - IOTimeWorkE(); + if (traceWL) { + IOTimeStampStartConstant(IODBG_WORKLOOP(IOWL_WORK), VM_KERNEL_ADDRHIDE(this)); + } + + bool more; + do { + CLRP(&fFlags, kLoopRestart); + more = false; + IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock); + workToDo = false; + IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); + /* NOTE: only loop over event sources in eventChain. Bypass "passive" event sources for performance */ + for (IOEventSource *evnt = eventChain; evnt; evnt = evnt->getNext()) { + if (traceES) { + IOTimeStampStartConstant(IODBG_WORKLOOP(IOWL_CLIENT), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(evnt)); + } + + more |= evnt->checkForWork(); + + if (traceES) { + IOTimeStampEndConstant(IODBG_WORKLOOP(IOWL_CLIENT), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(evnt)); + } + + if (ISSETP(&fFlags, kLoopTerminate)) { + goto abort; + } else if (fFlags & kLoopRestart) { + more = true; + break; + } + } + } while (more); + + res = true; + + if (traceWL) { + IOTimeStampEndConstant(IODBG_WORKLOOP(IOWL_WORK), VM_KERNEL_ADDRHIDE(this)); + } abort: - openGate(); - return res; + openGate(); + return res; } -/* virtual */ void IOWorkLoop::threadMain() +/* virtual */ void +IOWorkLoop::threadMain() { - do { - if ( !runEventSources() ) - goto exitThread; +restartThread: + do { + if (!runEventSources()) { + goto exitThread; + } + + IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock); + if (!ISSETP(&fFlags, kLoopTerminate) && !workToDo) { + assert_wait((void *) &workToDo, false); + IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); + thread_continue_t cptr = NULL; + if (!reserved || !(kPreciousStack & reserved->options)) { + cptr = OSMemberFunctionCast( + thread_continue_t, this, &IOWorkLoop::threadMain); + } + thread_block_parameter(cptr, this); + goto restartThread; + /* NOTREACHED */ + } + + // At this point we either have work to do or we need + // to commit suicide. But no matter + // Clear the simple lock and retore the interrupt state + IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); + } while (workToDo); - IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock); - if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) { - assert_wait((void *) &workToDo, false); - IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); - - thread_continue_t cptr = OSMemberFunctionCast( - thread_continue_t, this, &IOWorkLoop::threadMain); - thread_block_parameter(cptr, this); - /* NOTREACHED */ - } +exitThread: + closeGate(); + thread_t thread = workThread; + workThread = NULL; // Say we don't have a loop and free ourselves + openGate(); - // At this point we either have work to do or we need - // to commit suicide. But no matter - // Clear the simple lock and retore the interrupt state - IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); - } while(workToDo); + free(); -exitThread: - workThread = 0; // Say we don't have a loop and free ourselves - free(); - IOExitThread(); + thread_deallocate(thread); + (void) thread_terminate(thread); } -IOThread IOWorkLoop::getThread() const +IOThread +IOWorkLoop::getThread() const { - return workThread; + return workThread; } -bool IOWorkLoop::onThread() const +bool +IOWorkLoop::onThread() const { - return (IOThreadSelf() == workThread); + return IOThreadSelf() == workThread; } -bool IOWorkLoop::inGate() const +bool +IOWorkLoop::inGate() const { - return IORecursiveLockHaveLock(gateLock); + return IORecursiveLockHaveLock(gateLock); } // Internal APIs used by event sources to control the thread -void IOWorkLoop::signalWorkAvailable() +void +IOWorkLoop::signalWorkAvailable() +{ + if (workToDoLock) { + IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock); + workToDo = true; + thread_wakeup_thread((void *) &workToDo, workThread); + IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); + } +} + +void +IOWorkLoop::openGate() +{ + IOStatisticsOpenGate(); + IORecursiveLockUnlock(gateLock); +} + +void +IOWorkLoop::closeGate() { - if (workToDoLock) { - IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock); - workToDo = true; - thread_wakeup_one((void *) &workToDo); - IOSimpleLockUnlockEnableInterrupt(workToDoLock, is); - } + IORecursiveLockLock(gateLock); + IOStatisticsCloseGate(); } -void IOWorkLoop::openGate() +bool +IOWorkLoop::tryCloseGate() { - IORecursiveLockUnlock(gateLock); + bool res = (IORecursiveLockTryLock(gateLock) != 0); + if (res) { + IOStatisticsCloseGate(); + } + return res; } -void IOWorkLoop::closeGate() +int +IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType) { - IORecursiveLockLock(gateLock); + int res; + IOStatisticsOpenGate(); + res = IORecursiveLockSleep(gateLock, event, interuptibleType); + IOStatisticsCloseGate(); + return res; } -bool IOWorkLoop::tryCloseGate() +int +IOWorkLoop::sleepGate(void *event, AbsoluteTime deadline, UInt32 interuptibleType) { - return IORecursiveLockTryLock(gateLock) != 0; + int res; + IOStatisticsOpenGate(); + res = IORecursiveLockSleepDeadline(gateLock, event, deadline, interuptibleType); + IOStatisticsCloseGate(); + return res; } -int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType) +void +IOWorkLoop::wakeupGate(void *event, bool oneThread) { - return IORecursiveLockSleep(gateLock, event, interuptibleType); + IORecursiveLockWakeup(gateLock, event, oneThread); } -void IOWorkLoop::wakeupGate(void *event, bool oneThread) +static IOReturn +IOWorkLoopActionToBlock(OSObject *owner, + void *arg0, void *arg1, + void *arg2, void *arg3) { - IORecursiveLockWakeup(gateLock, event, oneThread); + return ((IOWorkLoop::ActionBlock) arg0)(); } -IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target, - void *arg0, void *arg1, - void *arg2, void *arg3) +IOReturn +IOWorkLoop::runActionBlock(ActionBlock action) { - IOReturn res; + return runAction(&IOWorkLoopActionToBlock, this, action); +} - // closeGate is recursive so don't worry if we already hold the lock. - closeGate(); - res = (*inAction)(target, arg0, arg1, arg2, arg3); - openGate(); +IOReturn +IOWorkLoop::runAction(Action inAction, OSObject *target, + void *arg0, void *arg1, + void *arg2, void *arg3) +{ + IOReturn res; + + // closeGate is recursive so don't worry if we already hold the lock. + closeGate(); + res = (*inAction)(target, arg0, arg1, arg2, arg3); + openGate(); - return res; + return res; } -IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *) +IOReturn +IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *) { - maintCommandEnum command = (maintCommandEnum) (vm_address_t) inC; - IOEventSource *inEvent = (IOEventSource *) inD; - IOReturn res = kIOReturnSuccess; + maintCommandEnum command = (maintCommandEnum) (uintptr_t) inC; + IOEventSource *inEvent = (IOEventSource *) inD; + IOReturn res = kIOReturnSuccess; + + switch (command) { + case mAddEvent: + if (!inEvent->getWorkLoop()) { + SETP(&fFlags, kLoopRestart); + + inEvent->retain(); + inEvent->setWorkLoop(this); + inEvent->setNext(NULL); + + /* Check if this is a passive or active event source being added */ + if (eventSourcePerformsWork(inEvent)) { + if (!eventChain) { + eventChain = inEvent; + } else { + IOEventSource *event, *next; + + for (event = eventChain; (next = event->getNext()); event = next) { + ; + } + event->setNext(inEvent); + } + } else { + if (!passiveEventChain) { + passiveEventChain = inEvent; + } else { + IOEventSource *event, *next; + + for (event = passiveEventChain; (next = event->getNext()); event = next) { + ; + } + event->setNext(inEvent); + } + } + IOStatisticsAttachEventSource(); + } + break; + + case mRemoveEvent: + if (inEvent->getWorkLoop()) { + IOStatisticsDetachEventSource(); + + if (eventSourcePerformsWork(inEvent)) { + if (eventChain == inEvent) { + eventChain = inEvent->getNext(); + } else { + IOEventSource *event, *next = NULL; + + event = eventChain; + if (event) { + while ((next = event->getNext()) && (next != inEvent)) { + event = next; + } + } + + if (!next) { + res = kIOReturnBadArgument; + break; + } + event->setNext(inEvent->getNext()); + } + } else { + if (passiveEventChain == inEvent) { + passiveEventChain = inEvent->getNext(); + } else { + IOEventSource *event, *next = NULL; + + event = passiveEventChain; + if (event) { + while ((next = event->getNext()) && (next != inEvent)) { + event = next; + } + } + + if (!next) { + res = kIOReturnBadArgument; + break; + } + event->setNext(inEvent->getNext()); + } + } + + inEvent->setWorkLoop(NULL); + inEvent->setNext(NULL); + inEvent->release(); + SETP(&fFlags, kLoopRestart); + } + break; - switch (command) - { - case mAddEvent: - if (!inEvent->getWorkLoop()) { - SETP(&fFlags, kLoopRestart); + default: + return kIOReturnUnsupported; + } + + return res; +} - inEvent->retain(); - inEvent->setWorkLoop(this); - inEvent->setNext(0); - - if (!eventChain) - eventChain = inEvent; - else { - IOEventSource *event, *next; - - for (event = eventChain; (next = event->getNext()); event = next) - ; - event->setNext(inEvent); - } - } - break; +bool +IOWorkLoop::eventSourcePerformsWork(IOEventSource *inEventSource) +{ + bool result = true; + + /* + * The idea here is to see if the subclass of IOEventSource has overridden checkForWork(). + * The assumption is that if you override checkForWork(), you need to be + * active and not passive. + * + * We picked a known quantity controlG that does not override + * IOEventSource::checkForWork(), namely the IOCommandGate associated with + * the workloop to which this event source is getting attached. + * + * We do a pointer comparison on the offset in the vtable for inNewEvent against + * the offset in the vtable for inReferenceEvent. This works because + * IOCommandGate's slot for checkForWork() has the address of + * IOEventSource::checkForWork() in it. + * + * Think of OSMemberFunctionCast yielding the value at the vtable offset for + * checkForWork() here. We're just testing to see if it's the same or not. + * + */ + + if (IOEventSource::kPassive & inEventSource->flags) { + result = false; + } else if (IOEventSource::kActive & inEventSource->flags) { + result = true; + } else if (controlG) { + void * ptr1; + void * ptr2; + + ptr1 = OSMemberFunctionCast(void*, inEventSource, &IOEventSource::checkForWork); + ptr2 = OSMemberFunctionCast(void*, controlG, &IOEventSource::checkForWork); + + if (ptr1 == ptr2) { + result = false; + } + } - case mRemoveEvent: - if (inEvent->getWorkLoop()) { - if (eventChain == inEvent) - eventChain = inEvent->getNext(); - else { - IOEventSource *event, *next; - - event = eventChain; - while ((next = event->getNext()) && next != inEvent) - event = next; - - if (!next) { - res = kIOReturnBadArgument; - break; - } - event->setNext(inEvent->getNext()); - } - - inEvent->setWorkLoop(0); - inEvent->setNext(0); - inEvent->release(); - SETP(&fFlags, kLoopRestart); - } - break; + return result; +} - default: - return kIOReturnUnsupported; - } +void +IOWorkLoop::lockTime(void) +{ + uint64_t time; + time = mach_absolute_time() - reserved->lockTime; + if (time > reserved->lockInterval) { + absolutetime_to_nanoseconds(time, &time); + if (kTimeLockPanics & reserved->options) { + panic("IOWorkLoop %p lock time %qd us", this, time / 1000ULL); + } else { + OSReportWithBacktrace("IOWorkLoop %p lock time %qd us", this, time / 1000ULL); + } + } +} - return res; +void +IOWorkLoop::setMaximumLockTime(uint64_t interval, uint32_t options) +{ + IORecursiveLockLock(gateLock); + reserved->lockInterval = interval; + reserved->options = (reserved->options & ~kTimeLockPanics) | (options & kTimeLockPanics); + IORecursiveLockUnlock(gateLock); }