]>
git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOWorkLoop.cpp
2 * Copyright (c) 1998-2010 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <pexpert/pexpert.h>
30 #include <IOKit/IOWorkLoop.h>
31 #include <IOKit/IOEventSource.h>
32 #include <IOKit/IOInterruptEventSource.h>
33 #include <IOKit/IOCommandGate.h>
34 #include <IOKit/IOCommandPool.h>
35 #include <IOKit/IOTimeStamp.h>
36 #include <IOKit/IOKitDebug.h>
37 #include <libkern/OSDebug.h>
38 #include <kern/thread.h>
40 #define super OSObject
42 OSDefineMetaClassAndStructors(IOWorkLoop
, OSObject
);
44 // Block of unused functions intended for future use
46 OSMetaClassDefineReservedUnused(IOWorkLoop
, 0);
47 OSMetaClassDefineReservedUnused(IOWorkLoop
, 1);
48 OSMetaClassDefineReservedUnused(IOWorkLoop
, 2);
50 OSMetaClassDefineReservedUsed(IOWorkLoop
, 0);
51 OSMetaClassDefineReservedUsed(IOWorkLoop
, 1);
52 OSMetaClassDefineReservedUsed(IOWorkLoop
, 2);
54 OSMetaClassDefineReservedUnused(IOWorkLoop
, 3);
55 OSMetaClassDefineReservedUnused(IOWorkLoop
, 4);
56 OSMetaClassDefineReservedUnused(IOWorkLoop
, 5);
57 OSMetaClassDefineReservedUnused(IOWorkLoop
, 6);
58 OSMetaClassDefineReservedUnused(IOWorkLoop
, 7);
60 enum IOWorkLoopState
{ kLoopRestart
= 0x1, kLoopTerminate
= 0x2 };
61 static inline void SETP(void *addr
, unsigned int flag
)
62 { unsigned char *num
= (unsigned char *) addr
; *num
|= flag
; }
63 static inline void CLRP(void *addr
, unsigned int flag
)
64 { unsigned char *num
= (unsigned char *) addr
; *num
&= ~flag
; }
65 static inline bool ISSETP(void *addr
, unsigned int flag
)
66 { unsigned char *num
= (unsigned char *) addr
; return (*num
& flag
) != 0; }
68 #define fFlags loopRestart
70 #define passiveEventChain reserved->passiveEventChain
74 #define IOStatisticsRegisterCounter() \
76 reserved->counter = IOStatistics::registerWorkLoop(this); \
79 #define IOStatisticsUnregisterCounter() \
82 IOStatistics::unregisterWorkLoop(reserved->counter); \
85 #define IOStatisticsOpenGate() \
87 IOStatistics::countWorkLoopOpenGate(reserved->counter); \
88 if (reserved->lockInterval) lockTime(); \
90 #define IOStatisticsCloseGate() \
92 IOStatistics::countWorkLoopCloseGate(reserved->counter); \
93 if (reserved->lockInterval) reserved->lockTime = mach_absolute_time(); \
96 #define IOStatisticsAttachEventSource() \
98 IOStatistics::attachWorkLoopEventSource(reserved->counter, inEvent->reserved->counter); \
101 #define IOStatisticsDetachEventSource() \
103 IOStatistics::detachWorkLoopEventSource(reserved->counter, inEvent->reserved->counter); \
108 #define IOStatisticsRegisterCounter()
109 #define IOStatisticsUnregisterCounter()
110 #define IOStatisticsOpenGate()
111 #define IOStatisticsCloseGate()
112 #define IOStatisticsAttachEventSource()
113 #define IOStatisticsDetachEventSource()
115 #endif /* IOKITSTATS */
117 bool IOWorkLoop::init()
119 // The super init and gateLock allocation MUST be done first.
120 if ( !super::init() )
123 // Allocate our ExpansionData if it hasn't been allocated already.
126 reserved
= IONew(ExpansionData
,1);
130 bzero(reserved
,sizeof(ExpansionData
));
133 if ( gateLock
== NULL
) {
134 if ( !( gateLock
= IORecursiveLockAlloc()) )
138 if ( workToDoLock
== NULL
) {
139 if ( !(workToDoLock
= IOSimpleLockAlloc()) )
141 IOSimpleLockInit(workToDoLock
);
145 IOStatisticsRegisterCounter();
147 if ( controlG
== NULL
) {
148 controlG
= IOCommandGate::commandGate(
150 OSMemberFunctionCast(
151 IOCommandGate::Action
,
153 &IOWorkLoop::_maintRequest
));
157 // Point the controlGate at the workLoop. Usually addEventSource
158 // does this automatically. The problem is in this case addEventSource
159 // uses the control gate and it has to be bootstrapped.
160 controlG
->setWorkLoop(this);
161 if (addEventSource(controlG
) != kIOReturnSuccess
)
165 if ( workThread
== NULL
) {
166 thread_continue_t cptr
= OSMemberFunctionCast(
169 &IOWorkLoop::threadMain
);
170 if (KERN_SUCCESS
!= kernel_thread_start(cptr
, this, &workThread
))
174 (void) thread_set_tag(workThread
, THREAD_TAG_IOWORKLOOP
);
179 IOWorkLoop::workLoop()
181 return IOWorkLoop::workLoopWithOptions(0);
185 IOWorkLoop::workLoopWithOptions(IOOptionBits options
)
187 IOWorkLoop
*me
= new IOWorkLoop
;
190 me
->reserved
= IONew(ExpansionData
,1);
195 bzero(me
->reserved
,sizeof(ExpansionData
));
196 me
->reserved
->options
= options
;
199 if (me
&& !me
->init()) {
207 // Free is called twice:
208 // First when the atomic retainCount transitions from 1 -> 0
209 // Secondly when the work loop itself is commiting hari kari
210 // Hence the each leg of the free must be single threaded.
211 void IOWorkLoop::free()
216 // If we are here then we must be trying to shut down this work loop
217 // in this case disable all of the event source, mark the loop
218 // as terminating and wakeup the work thread itself and return
219 // Note: we hold the gate across the entire operation mainly for the
220 // benefit of our event sources so we can disable them cleanly.
223 disableAllEventSources();
225 is
= IOSimpleLockLockDisableInterrupt(workToDoLock
);
226 SETP(&fFlags
, kLoopTerminate
);
227 thread_wakeup_thread((void *) &workToDo
, workThread
);
228 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
232 else /* !workThread */ {
233 IOEventSource
*event
, *next
;
235 for (event
= eventChain
; event
; event
= next
) {
236 next
= event
->getNext();
237 event
->setWorkLoop(0);
243 for (event
= passiveEventChain
; event
; event
= next
) {
244 next
= event
->getNext();
245 event
->setWorkLoop(0);
249 passiveEventChain
= 0;
251 // Either we have a partial initialization to clean up
252 // or the workThread itself is performing hari-kari.
253 // Either way clean up all of our resources and return.
256 controlG
->workLoop
= 0;
262 IOSimpleLockFree(workToDoLock
);
267 IORecursiveLockFree(gateLock
);
271 IOStatisticsUnregisterCounter();
274 IODelete(reserved
, ExpansionData
, 1);
282 IOReturn
IOWorkLoop::addEventSource(IOEventSource
*newEvent
)
285 && !thread_has_thread_name(workThread
)
287 && !OSDynamicCast(IOCommandPool
, newEvent
->owner
)) {
288 thread_set_thread_name(workThread
, newEvent
->owner
->getMetaClass()->getClassName());
291 return controlG
->runCommand((void *) mAddEvent
, (void *) newEvent
);
294 IOReturn
IOWorkLoop::removeEventSource(IOEventSource
*toRemove
)
296 return controlG
->runCommand((void *) mRemoveEvent
, (void *) toRemove
);
299 void IOWorkLoop::enableAllEventSources() const
301 IOEventSource
*event
;
303 for (event
= eventChain
; event
; event
= event
->getNext())
306 for (event
= passiveEventChain
; event
; event
= event
->getNext())
310 void IOWorkLoop::disableAllEventSources() const
312 IOEventSource
*event
;
314 for (event
= eventChain
; event
; event
= event
->getNext())
317 /* NOTE: controlG is in passiveEventChain since it's an IOCommandGate */
318 for (event
= passiveEventChain
; event
; event
= event
->getNext())
319 if (event
!= controlG
) // Don't disable the control gate
323 void IOWorkLoop::enableAllInterrupts() const
325 IOEventSource
*event
;
327 for (event
= eventChain
; event
; event
= event
->getNext())
328 if (OSDynamicCast(IOInterruptEventSource
, event
))
332 void IOWorkLoop::disableAllInterrupts() const
334 IOEventSource
*event
;
336 for (event
= eventChain
; event
; event
= event
->getNext())
337 if (OSDynamicCast(IOInterruptEventSource
, event
))
342 /* virtual */ bool IOWorkLoop::runEventSources()
345 bool traceWL
= (gIOKitTrace
& kIOTraceWorkLoops
) ? true : false;
346 bool traceES
= (gIOKitTrace
& kIOTraceEventSources
) ? true : false;
349 if (ISSETP(&fFlags
, kLoopTerminate
))
353 IOTimeStampStartConstant(IODBG_WORKLOOP(IOWL_WORK
), VM_KERNEL_ADDRHIDE(this));
357 CLRP(&fFlags
, kLoopRestart
);
359 IOInterruptState is
= IOSimpleLockLockDisableInterrupt(workToDoLock
);
361 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
362 /* NOTE: only loop over event sources in eventChain. Bypass "passive" event sources for performance */
363 for (IOEventSource
*evnt
= eventChain
; evnt
; evnt
= evnt
->getNext()) {
366 IOTimeStampStartConstant(IODBG_WORKLOOP(IOWL_CLIENT
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(evnt
));
368 more
|= evnt
->checkForWork();
371 IOTimeStampEndConstant(IODBG_WORKLOOP(IOWL_CLIENT
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(evnt
));
373 if (ISSETP(&fFlags
, kLoopTerminate
))
375 else if (fFlags
& kLoopRestart
) {
385 IOTimeStampEndConstant(IODBG_WORKLOOP(IOWL_WORK
), VM_KERNEL_ADDRHIDE(this));
392 /* virtual */ void IOWorkLoop::threadMain()
396 if ( !runEventSources() )
399 IOInterruptState is
= IOSimpleLockLockDisableInterrupt(workToDoLock
);
400 if ( !ISSETP(&fFlags
, kLoopTerminate
) && !workToDo
) {
401 assert_wait((void *) &workToDo
, false);
402 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
403 thread_continue_t cptr
= NULL
;
404 if (!reserved
|| !(kPreciousStack
& reserved
->options
))
405 cptr
= OSMemberFunctionCast(
406 thread_continue_t
, this, &IOWorkLoop::threadMain
);
407 thread_block_parameter(cptr
, this);
412 // At this point we either have work to do or we need
413 // to commit suicide. But no matter
414 // Clear the simple lock and retore the interrupt state
415 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
420 thread_t thread
= workThread
;
421 workThread
= 0; // Say we don't have a loop and free ourselves
426 thread_deallocate(thread
);
427 (void) thread_terminate(thread
);
430 IOThread
IOWorkLoop::getThread() const
435 bool IOWorkLoop::onThread() const
437 return (IOThreadSelf() == workThread
);
440 bool IOWorkLoop::inGate() const
442 return IORecursiveLockHaveLock(gateLock
);
445 // Internal APIs used by event sources to control the thread
446 void IOWorkLoop::signalWorkAvailable()
449 IOInterruptState is
= IOSimpleLockLockDisableInterrupt(workToDoLock
);
451 thread_wakeup_thread((void *) &workToDo
, workThread
);
452 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
456 void IOWorkLoop::openGate()
458 IOStatisticsOpenGate();
459 IORecursiveLockUnlock(gateLock
);
462 void IOWorkLoop::closeGate()
464 IORecursiveLockLock(gateLock
);
465 IOStatisticsCloseGate();
468 bool IOWorkLoop::tryCloseGate()
470 bool res
= (IORecursiveLockTryLock(gateLock
) != 0);
472 IOStatisticsCloseGate();
477 int IOWorkLoop::sleepGate(void *event
, UInt32 interuptibleType
)
480 IOStatisticsOpenGate();
481 res
= IORecursiveLockSleep(gateLock
, event
, interuptibleType
);
482 IOStatisticsCloseGate();
486 int IOWorkLoop::sleepGate(void *event
, AbsoluteTime deadline
, UInt32 interuptibleType
)
489 IOStatisticsOpenGate();
490 res
= IORecursiveLockSleepDeadline(gateLock
, event
, deadline
, interuptibleType
);
491 IOStatisticsCloseGate();
495 void IOWorkLoop::wakeupGate(void *event
, bool oneThread
)
497 IORecursiveLockWakeup(gateLock
, event
, oneThread
);
500 static IOReturn
IOWorkLoopActionToBlock(OSObject
*owner
,
501 void *arg0
, void *arg1
,
502 void *arg2
, void *arg3
)
504 return ((IOWorkLoop::ActionBlock
) arg0
)();
507 IOReturn
IOWorkLoop::runActionBlock(ActionBlock action
)
509 return (runAction(&IOWorkLoopActionToBlock
, this, action
));
512 IOReturn
IOWorkLoop::runAction(Action inAction
, OSObject
*target
,
513 void *arg0
, void *arg1
,
514 void *arg2
, void *arg3
)
518 // closeGate is recursive so don't worry if we already hold the lock.
520 res
= (*inAction
)(target
, arg0
, arg1
, arg2
, arg3
);
526 IOReturn
IOWorkLoop::_maintRequest(void *inC
, void *inD
, void *, void *)
528 maintCommandEnum command
= (maintCommandEnum
) (uintptr_t) inC
;
529 IOEventSource
*inEvent
= (IOEventSource
*) inD
;
530 IOReturn res
= kIOReturnSuccess
;
535 if (!inEvent
->getWorkLoop()) {
536 SETP(&fFlags
, kLoopRestart
);
539 inEvent
->setWorkLoop(this);
542 /* Check if this is a passive or active event source being added */
543 if (eventSourcePerformsWork(inEvent
)) {
546 eventChain
= inEvent
;
548 IOEventSource
*event
, *next
;
550 for (event
= eventChain
; (next
= event
->getNext()); event
= next
)
552 event
->setNext(inEvent
);
559 if (!passiveEventChain
)
560 passiveEventChain
= inEvent
;
562 IOEventSource
*event
, *next
;
564 for (event
= passiveEventChain
; (next
= event
->getNext()); event
= next
)
566 event
->setNext(inEvent
);
571 IOStatisticsAttachEventSource();
576 if (inEvent
->getWorkLoop()) {
577 IOStatisticsDetachEventSource();
579 if (eventSourcePerformsWork(inEvent
)) {
580 if (eventChain
== inEvent
)
581 eventChain
= inEvent
->getNext();
583 IOEventSource
*event
, *next
= 0;
586 if (event
) while ((next
= event
->getNext()) && (next
!= inEvent
))
590 res
= kIOReturnBadArgument
;
593 event
->setNext(inEvent
->getNext());
597 if (passiveEventChain
== inEvent
)
598 passiveEventChain
= inEvent
->getNext();
600 IOEventSource
*event
, *next
= 0;
602 event
= passiveEventChain
;
603 if (event
) while ((next
= event
->getNext()) && (next
!= inEvent
))
607 res
= kIOReturnBadArgument
;
610 event
->setNext(inEvent
->getNext());
614 inEvent
->setWorkLoop(0);
617 SETP(&fFlags
, kLoopRestart
);
622 return kIOReturnUnsupported
;
629 IOWorkLoop::eventSourcePerformsWork(IOEventSource
*inEventSource
)
634 * The idea here is to see if the subclass of IOEventSource has overridden checkForWork().
635 * The assumption is that if you override checkForWork(), you need to be
636 * active and not passive.
638 * We picked a known quantity controlG that does not override
639 * IOEventSource::checkForWork(), namely the IOCommandGate associated with
640 * the workloop to which this event source is getting attached.
642 * We do a pointer comparison on the offset in the vtable for inNewEvent against
643 * the offset in the vtable for inReferenceEvent. This works because
644 * IOCommandGate's slot for checkForWork() has the address of
645 * IOEventSource::checkForWork() in it.
647 * Think of OSMemberFunctionCast yielding the value at the vtable offset for
648 * checkForWork() here. We're just testing to see if it's the same or not.
652 if (IOEventSource::kPassive
& inEventSource
->flags
) result
= false;
653 else if (IOEventSource::kActive
& inEventSource
->flags
) result
= true;
658 ptr1
= OSMemberFunctionCast(void*, inEventSource
, &IOEventSource::checkForWork
);
659 ptr2
= OSMemberFunctionCast(void*, controlG
, &IOEventSource::checkForWork
);
669 IOWorkLoop::lockTime(void)
672 time
= mach_absolute_time() - reserved
->lockTime
;
673 if (time
> reserved
->lockInterval
)
675 absolutetime_to_nanoseconds(time
, &time
);
676 if (kTimeLockPanics
& reserved
->options
) panic("IOWorkLoop %p lock time %qd us", this, time
/ 1000ULL);
677 else OSReportWithBacktrace("IOWorkLoop %p lock time %qd us", this, time
/ 1000ULL);
682 IOWorkLoop::setMaximumLockTime(uint64_t interval
, uint32_t options
)
684 IORecursiveLockLock(gateLock
);
685 reserved
->lockInterval
= interval
;
686 reserved
->options
= (reserved
->options
& ~kTimeLockPanics
) | (options
& kTimeLockPanics
);
687 IORecursiveLockUnlock(gateLock
);