]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOTimerEventSource.cpp
xnu-6153.141.1.tar.gz
[apple/xnu.git] / iokit / Kernel / IOTimerEventSource.cpp
index 3c86e67090c91405814dfb5e706466d57ede6579..59eb6a0a26af9b6b58376389d532573e8a431a4e 100644 (file)
@@ -1,17 +1,20 @@
 /*
- * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1998-2000, 2009-2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
- * 
  * 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. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
+ * 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,
  * 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_LICENSE_HEADER_END@
- */
-/*
- * Copyright (c) 1999 Apple Computer, Inc.  All rights reserved. 
- *
- * IOTimerEventSource.cpp
- *
- * HISTORY
- * 2-Feb-1999          Joe Liu (jliu) created.
- * 1999-10-14          Godfrey van der Linden(gvdl)
- *             Revamped to use thread_call APIs
  *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
 #include <sys/cdefs.h>
@@ -48,190 +40,528 @@ __END_DECLS
 #include <IOKit/IOWorkLoop.h>
 
 #include <IOKit/IOTimeStamp.h>
+#include <IOKit/IOKitDebug.h>
+#if CONFIG_DTRACE
+#include <mach/sdt.h>
+#endif
+
+#include <libkern/Block.h>
+
 
 #define super IOEventSource
 OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
-OSMetaClassDefineReservedUnused(IOTimerEventSource, 0);
-OSMetaClassDefineReservedUnused(IOTimerEventSource, 1);
-OSMetaClassDefineReservedUnused(IOTimerEventSource, 2);
+OSMetaClassDefineReservedUsed(IOTimerEventSource, 0);
+OSMetaClassDefineReservedUsed(IOTimerEventSource, 1);
+OSMetaClassDefineReservedUsed(IOTimerEventSource, 2);
 OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
 OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
 OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
 OSMetaClassDefineReservedUnused(IOTimerEventSource, 6);
 OSMetaClassDefineReservedUnused(IOTimerEventSource, 7);
 
-bool IOTimerEventSource::checkForWork() { return false; }
+#if IOKITSTATS
+
+#define IOStatisticsInitializeCounter() \
+do { \
+       IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \
+} while (0)
+
+#define IOStatisticsOpenGate() \
+do { \
+       IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \
+} while (0)
+
+#define IOStatisticsCloseGate() \
+do { \
+       IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \
+} while (0)
+
+#define IOStatisticsTimeout() \
+do { \
+       IOStatistics::countTimerTimeout(me->IOEventSource::reserved->counter); \
+} while (0)
+
+#else
+
+#define IOStatisticsInitializeCounter()
+#define IOStatisticsOpenGate()
+#define IOStatisticsCloseGate()
+#define IOStatisticsTimeout()
+
+#endif /* IOKITSTATS */
+
+//
+// reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
+// not a subclassed implementation.
+//
 
 // Timeout handler function. This function is called by the kernel when
 // the timeout interval expires.
 //
-void IOTimerEventSource::timeout(void *self)
+
+__inline__ void
+IOTimerEventSource::invokeAction(IOTimerEventSource::Action _action, IOTimerEventSource * ts,
+    OSObject * _owner, IOWorkLoop * _workLoop)
+{
+       bool    trace = (gIOKitTrace & kIOTraceTimers) ? true : false;
+
+       if (trace) {
+               IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION),
+                   VM_KERNEL_ADDRHIDE(_action), VM_KERNEL_ADDRHIDE(_owner));
+       }
+
+       if (kActionBlock & flags) {
+               ((IOTimerEventSource::ActionBlock) actionBlock)(ts);
+       } else {
+               (*_action)(_owner, ts);
+       }
+
+#if CONFIG_DTRACE
+       DTRACE_TMR3(iotescallout__expire, Action, _action, OSObject, _owner, void, _workLoop);
+#endif
+
+       if (trace) {
+               IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION),
+                   VM_KERNEL_UNSLIDE(_action), VM_KERNEL_ADDRHIDE(_owner));
+       }
+}
+
+void
+IOTimerEventSource::timeout(void *self)
+{
+       IOTimerEventSource *me = (IOTimerEventSource *) self;
+
+       IOStatisticsTimeout();
+
+       if (me->enabled && me->action) {
+               IOWorkLoop *
+                   wl = me->workLoop;
+               if (wl) {
+                       Action doit;
+                       wl->closeGate();
+                       IOStatisticsCloseGate();
+                       doit = (Action) me->action;
+                       if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime)) {
+                               me->invokeAction(doit, me, me->owner, me->workLoop);
+                       }
+                       IOStatisticsOpenGate();
+                       wl->openGate();
+               }
+       }
+}
+
+void
+IOTimerEventSource::timeoutAndRelease(void * self, void * c)
+{
+       IOTimerEventSource *me = (IOTimerEventSource *) self;
+       /* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count"
+        *  must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */
+       SInt32 count = (SInt32) (long) c;
+
+       IOStatisticsTimeout();
+
+       if (me->enabled && me->action) {
+               IOWorkLoop *
+                   wl = me->reserved->workLoop;
+               if (wl) {
+                       Action doit;
+                       wl->closeGate();
+                       IOStatisticsCloseGate();
+                       doit = (Action) me->action;
+                       if (doit && (me->reserved->calloutGeneration == count)) {
+                               me->invokeAction(doit, me, me->owner, me->workLoop);
+                       }
+                       IOStatisticsOpenGate();
+                       wl->openGate();
+               }
+       }
+
+       me->reserved->workLoop->release();
+       me->release();
+}
+
+// -- work loop delivery
+
+bool
+IOTimerEventSource::checkForWork()
 {
-    IOTimerEventSource *me = (IOTimerEventSource *) self;
+       Action doit;
 
-    if (me->enabled) {
-        Action doit = (Action) me->action;
+       if (reserved
+           && (reserved->calloutGenerationSignaled == reserved->calloutGeneration)
+           && enabled && (doit = (Action) action)) {
+               reserved->calloutGenerationSignaled = ~reserved->calloutGeneration;
+               invokeAction(doit, this, owner, workLoop);
+       }
 
-        if (doit) {
-            IOTimeStampConstant(IODBG_TIMES(IOTIMES_ACTION),
-                                (unsigned int) doit, (unsigned int) me->owner);
-            me->closeGate();
-            (*doit)(me->owner, me);
-            me->openGate();
-        }
-    }
+       return false;
 }
 
-void IOTimerEventSource::setTimeoutFunc()
+void
+IOTimerEventSource::timeoutSignaled(void * self, void * c)
 {
-    calloutEntry = (void *) thread_call_allocate((thread_call_func_t) timeout,
-                                                 (thread_call_param_t) this);
+       IOTimerEventSource *me = (IOTimerEventSource *) self;
+
+       me->reserved->calloutGenerationSignaled = (SInt32)(long) c;
+       if (me->enabled) {
+               me->signalWorkAvailable();
+       }
+}
+
+// --
+
+void
+IOTimerEventSource::setTimeoutFunc()
+{
+       thread_call_priority_t pri;
+       uint32_t options;
+
+       if (reserved) {
+               panic("setTimeoutFunc already %p, %p", this, reserved);
+       }
+
+       // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
+       // not a subclassed implementation
+       reserved = IONew(ExpansionData, 1);
+       reserved->calloutGenerationSignaled = ~reserved->calloutGeneration;
+       options = abstime;
+       abstime = 0;
+
+       thread_call_options_t tcoptions = 0;
+       thread_call_func_t    func      = NULL;
+
+       switch (kIOTimerEventSourceOptionsPriorityMask & options) {
+       case kIOTimerEventSourceOptionsPriorityHigh:
+               pri = THREAD_CALL_PRIORITY_HIGH;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityKernel:
+               pri = THREAD_CALL_PRIORITY_KERNEL;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityKernelHigh:
+               pri = THREAD_CALL_PRIORITY_KERNEL_HIGH;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityUser:
+               pri = THREAD_CALL_PRIORITY_USER;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityLow:
+               pri = THREAD_CALL_PRIORITY_LOW;
+               func = &IOTimerEventSource::timeoutAndRelease;
+               break;
+
+       case kIOTimerEventSourceOptionsPriorityWorkLoop:
+               pri = THREAD_CALL_PRIORITY_KERNEL;
+               tcoptions |= THREAD_CALL_OPTIONS_SIGNAL;
+               if (kIOTimerEventSourceOptionsAllowReenter & options) {
+                       break;
+               }
+               func = &IOTimerEventSource::timeoutSignaled;
+               break;
+
+       default:
+               break;
+       }
+
+       assertf(func, "IOTimerEventSource options 0x%x", options);
+       if (!func) {
+               return;                                              // init will fail
+       }
+       if (THREAD_CALL_OPTIONS_SIGNAL & tcoptions) {
+               flags |= kActive;
+       } else {
+               flags |= kPassive;
+       }
+
+       if (!(kIOTimerEventSourceOptionsAllowReenter & options)) {
+               tcoptions |= THREAD_CALL_OPTIONS_ONCE;
+       }
+
+       calloutEntry = (void *) thread_call_allocate_with_options(func,
+           (thread_call_param_t) this, pri, tcoptions);
+       assert(calloutEntry);
 }
 
-bool IOTimerEventSource::init(OSObject *inOwner, Action inAction)
+bool
+IOTimerEventSource::init(OSObject *inOwner, Action inAction)
 {
-    if (!super::init(inOwner, (IOEventSource::Action) inAction) )
-        return false;
+       if (!super::init(inOwner, (IOEventSource::Action) inAction)) {
+               return false;
+       }
+
+       setTimeoutFunc();
+       if (!calloutEntry) {
+               return false;
+       }
 
-    setTimeoutFunc();
-    if (!calloutEntry)
-        return false;
+       IOStatisticsInitializeCounter();
 
-    return true;
+       return true;
+}
+
+bool
+IOTimerEventSource::init(uint32_t options, OSObject *inOwner, Action inAction)
+{
+       abstime = options;
+       return init(inOwner, inAction);
 }
 
 IOTimerEventSource *
-IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
+IOTimerEventSource::timerEventSource(uint32_t inOptions, OSObject *inOwner, Action inAction)
 {
-    IOTimerEventSource *me = new IOTimerEventSource;
+       IOTimerEventSource *me = new IOTimerEventSource;
 
-    if (me && !me->init(inOwner, inAction)) {
-        me->free();
-        return 0;
-    }
+       if (me && !me->init(inOptions, inOwner, inAction)) {
+               me->release();
+               return NULL;
+       }
 
-    return me;
+       return me;
 }
 
-void IOTimerEventSource::free()
+IOTimerEventSource *
+IOTimerEventSource::timerEventSource(uint32_t options, OSObject *inOwner, ActionBlock _action)
 {
-    if (calloutEntry) {
-        cancelTimeout();
-        thread_call_free((thread_call_t) calloutEntry);    
-    }
+       IOTimerEventSource * tes;
+       tes = IOTimerEventSource::timerEventSource(options, inOwner, (Action) NULL);
+       if (tes) {
+               tes->setActionBlock((IOEventSource::ActionBlock) _action);
+       }
 
-    super::free();
+       return tes;
 }
 
-void IOTimerEventSource::cancelTimeout()
+#define _thread_call_cancel(tc)   ((kActive & flags) ? thread_call_cancel_wait((tc)) : thread_call_cancel((tc)))
+
+IOTimerEventSource *
+IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
 {
-    thread_call_cancel((thread_call_t) calloutEntry);
-    AbsoluteTime_to_scalar(&abstime) = 0;
+       return IOTimerEventSource::timerEventSource(
+               kIOTimerEventSourceOptionsPriorityKernelHigh,
+               inOwner, inAction);
 }
 
-void IOTimerEventSource::enable()
+void
+IOTimerEventSource::free()
 {
-    super::enable();
-    if (kIOReturnSuccess != wakeAtTime(abstime))
-        super::disable(); // Problem re-scheduling timeout ignore enable
+       if (calloutEntry) {
+               __assert_only bool freed;
+
+               cancelTimeout();
+
+               freed = thread_call_free((thread_call_t) calloutEntry);
+               assert(freed);
+       }
+
+       if (reserved) {
+               IODelete(reserved, ExpansionData, 1);
+       }
+
+       super::free();
 }
 
-void IOTimerEventSource::disable()
+void
+IOTimerEventSource::cancelTimeout()
 {
-    thread_call_cancel((thread_call_t) calloutEntry);
-    super::disable();
+       if (reserved) {
+               reserved->calloutGeneration++;
+       }
+       bool active = _thread_call_cancel((thread_call_t) calloutEntry);
+       AbsoluteTime_to_scalar(&abstime) = 0;
+       if (active && reserved && (kPassive & flags)) {
+               release();
+               workLoop->release();
+       }
 }
 
-IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
+void
+IOTimerEventSource::enable()
 {
-    return setTimeout(ticks, NSEC_PER_SEC/hz);
+       super::enable();
+       if (kIOReturnSuccess != wakeAtTime(abstime)) {
+               super::disable(); // Problem re-scheduling timeout ignore enable
+       }
 }
 
-IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
+void
+IOTimerEventSource::disable()
 {
-    return setTimeout(ms, kMillisecondScale);
+       if (reserved) {
+               reserved->calloutGeneration++;
+       }
+       bool active = _thread_call_cancel((thread_call_t) calloutEntry);
+       super::disable();
+       if (active && reserved && (kPassive & flags)) {
+               release();
+               workLoop->release();
+       }
 }
 
-IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
+IOReturn
+IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
 {
-    return setTimeout(us, kMicrosecondScale);
+       return setTimeout(ticks, kTickScale);
 }
 
-IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
+IOReturn
+IOTimerEventSource::setTimeoutMS(UInt32 ms)
 {
-    AbsoluteTime end;
-    
-    clock_interval_to_deadline(interval, scale_factor, &end);
-    return wakeAtTime(end);
+       return setTimeout(ms, kMillisecondScale);
 }
 
-IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval)
+IOReturn
+IOTimerEventSource::setTimeoutUS(UInt32 us)
 {
-    AbsoluteTime end, nsecs;
+       return setTimeout(us, kMicrosecondScale);
+}
 
-    clock_interval_to_absolutetime_interval
-        (interval.tv_nsec, kNanosecondScale, &nsecs);
-    clock_interval_to_deadline
-        (interval.tv_sec, NSEC_PER_SEC, &end);
-    ADD_ABSOLUTETIME(&end, &nsecs);
+IOReturn
+IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
+{
+       AbsoluteTime end;
 
-    return wakeAtTime(end);
+       clock_interval_to_deadline(interval, scale_factor, &end);
+       return wakeAtTime(end);
 }
 
-IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval)
+#if !defined(__LP64__)
+IOReturn
+IOTimerEventSource::setTimeout(mach_timespec_t interval)
 {
-    AbsoluteTime end;
+       AbsoluteTime end, nsecs;
 
-    clock_get_uptime(&end);
-    ADD_ABSOLUTETIME(&end, &interval);
+       clock_interval_to_absolutetime_interval
+       (interval.tv_nsec, kNanosecondScale, &nsecs);
+       clock_interval_to_deadline
+       (interval.tv_sec, NSEC_PER_SEC, &end);
+       ADD_ABSOLUTETIME(&end, &nsecs);
 
-    return wakeAtTime(end);
+       return wakeAtTime(end);
 }
+#endif
 
-IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
+IOReturn
+IOTimerEventSource::setTimeout(AbsoluteTime interval)
 {
-    return wakeAtTime(ticks, NSEC_PER_SEC/hz);
+       AbsoluteTime end;
+       clock_absolutetime_interval_to_deadline(interval, &end);
+       return wakeAtTime(end);
 }
 
-IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
+IOReturn
+IOTimerEventSource::setTimeout(uint32_t options,
+    AbsoluteTime abstime, AbsoluteTime leeway)
 {
-    return wakeAtTime(ms, kMillisecondScale);
+       AbsoluteTime end;
+       if (options & kIOTimeOptionsContinuous) {
+               clock_continuoustime_interval_to_deadline(abstime, &end);
+       } else {
+               clock_absolutetime_interval_to_deadline(abstime, &end);
+       }
+
+       return wakeAtTime(options, end, leeway);
 }
 
-IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
+IOReturn
+IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
 {
-    return wakeAtTime(us, kMicrosecondScale);
+       return wakeAtTime(ticks, kTickScale);
 }
 
-IOReturn IOTimerEventSource::wakeAtTime(UInt32 abstime, UInt32 scale_factor)
+IOReturn
+IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
 {
-    AbsoluteTime end;
-    clock_interval_to_absolutetime_interval(abstime, scale_factor, &end);
+       return wakeAtTime(ms, kMillisecondScale);
+}
 
-    return wakeAtTime(end);
+IOReturn
+IOTimerEventSource::wakeAtTimeUS(UInt32 us)
+{
+       return wakeAtTime(us, kMicrosecondScale);
 }
 
-IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t abstime)
+IOReturn
+IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor)
 {
-    AbsoluteTime end, nsecs;
+       AbsoluteTime end;
+       clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end);
 
-    clock_interval_to_absolutetime_interval
-        (abstime.tv_nsec, kNanosecondScale, &nsecs);
-    clock_interval_to_absolutetime_interval
-        (abstime.tv_sec, kSecondScale, &end);
-    ADD_ABSOLUTETIME(&end, &nsecs);
+       return wakeAtTime(end);
+}
 
-    return wakeAtTime(end);
+#if !defined(__LP64__)
+IOReturn
+IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime)
+{
+       AbsoluteTime end, nsecs;
+
+       clock_interval_to_absolutetime_interval
+       (inAbstime.tv_nsec, kNanosecondScale, &nsecs);
+       clock_interval_to_absolutetime_interval
+       (inAbstime.tv_sec, kSecondScale, &end);
+       ADD_ABSOLUTETIME(&end, &nsecs);
+
+       return wakeAtTime(end);
 }
+#endif
 
-IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
+void
+IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
 {
-    if (!action)
-        return kIOReturnNoResources;
+       super::setWorkLoop(inWorkLoop);
+       if (enabled && AbsoluteTime_to_scalar(&abstime) && workLoop) {
+               wakeAtTime(abstime);
+       }
+}
 
-    abstime = inAbstime;
-    if ( enabled && AbsoluteTime_to_scalar(&abstime) )
-        thread_call_enter_delayed((thread_call_t) calloutEntry, abstime);
+IOReturn
+IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
+{
+       return wakeAtTime(0, inAbstime, 0);
+}
 
-    return kIOReturnSuccess;
+IOReturn
+IOTimerEventSource::wakeAtTime(uint32_t options, AbsoluteTime inAbstime, AbsoluteTime leeway)
+{
+       if (!action) {
+               return kIOReturnNoResources;
+       }
+
+       abstime = inAbstime;
+       if (enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop) {
+               uint32_t tcoptions = 0;
+
+               if (kIOTimeOptionsWithLeeway & options) {
+                       tcoptions |= THREAD_CALL_DELAY_LEEWAY;
+               }
+               if (kIOTimeOptionsContinuous & options) {
+                       tcoptions |= THREAD_CALL_CONTINUOUS;
+               }
+
+               if (reserved) {
+                       if (kPassive & flags) {
+                               retain();
+                               workLoop->retain();
+                       }
+                       reserved->workLoop = workLoop;
+                       reserved->calloutGeneration++;
+                       if (thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry,
+                           (void *)(uintptr_t) reserved->calloutGeneration, inAbstime, leeway, tcoptions)
+                           && (kPassive & flags)) {
+                               release();
+                               workLoop->release();
+                       }
+               } else {
+                       thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry,
+                           NULL, inAbstime, leeway, tcoptions);
+               }
+       }
+
+       return kIOReturnSuccess;
 }