2 * Copyright (c) 1998-2000, 2009-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@
30 #include <sys/cdefs.h>
33 #include <kern/thread_call.h>
36 #include <IOKit/assert.h>
37 #include <IOKit/system.h>
39 #include <IOKit/IOLib.h>
40 #include <IOKit/IOTimerEventSource.h>
41 #include <IOKit/IOWorkLoop.h>
43 #include <IOKit/IOTimeStamp.h>
44 #include <IOKit/IOKitDebug.h>
49 #include <libkern/Block.h>
50 #include <libkern/Block_private.h>
53 #define super IOEventSource
54 OSDefineMetaClassAndStructors(IOTimerEventSource
, IOEventSource
)
55 OSMetaClassDefineReservedUsedX86(IOTimerEventSource
, 0);
56 OSMetaClassDefineReservedUsedX86(IOTimerEventSource
, 1);
57 OSMetaClassDefineReservedUsedX86(IOTimerEventSource
, 2);
58 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 3);
59 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 4);
60 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 5);
61 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 6);
62 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 7);
66 #define IOStatisticsInitializeCounter() \
68 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \
71 #define IOStatisticsOpenGate() \
73 IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \
76 #define IOStatisticsCloseGate() \
78 IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \
81 #define IOStatisticsTimeout() \
83 IOStatistics::countTimerTimeout(me->IOEventSource::reserved->counter); \
88 #define IOStatisticsInitializeCounter()
89 #define IOStatisticsOpenGate()
90 #define IOStatisticsCloseGate()
91 #define IOStatisticsTimeout()
93 #endif /* IOKITSTATS */
96 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
97 // not a subclassed implementation.
100 // Timeout handler function. This function is called by the kernel when
101 // the timeout interval expires.
105 IOTimerEventSource::invokeAction(IOEventSource::Action _action
, IOTimerEventSource
* ts
,
106 OSObject
* _owner
, IOWorkLoop
* _workLoop
)
108 bool trace
= (gIOKitTrace
& kIOTraceTimers
) ? true : false;
111 if (kActionBlock
& flags
) {
112 address
= ptrauth_nop_cast(void *, _Block_get_invoke_fn((struct Block_layout
*) actionBlock
));
114 address
= ptrauth_nop_cast(void *, _action
);
118 IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION
),
119 VM_KERNEL_ADDRHIDE(address
),
120 VM_KERNEL_ADDRHIDE(_owner
));
123 if (kActionBlock
& flags
) {
124 ((IOTimerEventSource::ActionBlock
) actionBlock
)(ts
);
126 ((IOTimerEventSource::Action
)_action
)(_owner
, ts
);
130 DTRACE_TMR3(iotescallout__expire
, Action
, address
, OSObject
, _owner
, void, _workLoop
);
134 IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION
),
135 VM_KERNEL_UNSLIDE(address
),
136 VM_KERNEL_ADDRHIDE(_owner
));
141 IOTimerEventSource::timeout(void *self
)
143 IOTimerEventSource
*me
= (IOTimerEventSource
*) self
;
145 IOStatisticsTimeout();
147 if (me
->enabled
&& me
->action
) {
151 IOEventSource::Action doit
;
153 IOStatisticsCloseGate();
155 if (doit
&& me
->enabled
&& AbsoluteTime_to_scalar(&me
->abstime
)) {
156 me
->invokeAction(doit
, me
, me
->owner
, me
->workLoop
);
158 IOStatisticsOpenGate();
165 IOTimerEventSource::timeoutAndRelease(void * self
, void * c
)
167 IOTimerEventSource
*me
= (IOTimerEventSource
*) self
;
168 /* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count"
169 * must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */
170 SInt32 count
= (SInt32
) (long) c
;
172 IOStatisticsTimeout();
174 if (me
->enabled
&& me
->action
) {
176 wl
= me
->reserved
->workLoop
;
178 IOEventSource::Action doit
;
180 IOStatisticsCloseGate();
182 if (doit
&& (me
->reserved
->calloutGeneration
== count
)) {
183 thread_call_start_iotes_invocation((thread_call_t
)me
->calloutEntry
);
184 me
->invokeAction(doit
, me
, me
->owner
, me
->workLoop
);
186 IOStatisticsOpenGate();
191 me
->reserved
->workLoop
->release();
195 // -- work loop delivery
198 IOTimerEventSource::checkForWork()
200 IOEventSource::Action doit
;
203 && (reserved
->calloutGenerationSignaled
== reserved
->calloutGeneration
)
204 && enabled
&& (doit
= action
)) {
205 reserved
->calloutGenerationSignaled
= ~reserved
->calloutGeneration
;
206 invokeAction(doit
, this, owner
, workLoop
);
213 IOTimerEventSource::timeoutSignaled(void * self
, void * c
)
215 IOTimerEventSource
*me
= (IOTimerEventSource
*) self
;
217 me
->reserved
->calloutGenerationSignaled
= (SInt32
)(long) c
;
219 me
->signalWorkAvailable();
226 IOTimerEventSource::setTimeoutFunc()
228 thread_call_priority_t pri
;
232 panic("setTimeoutFunc already %p, %p", this, reserved
);
235 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
236 // not a subclassed implementation
237 reserved
= IONewZero(ExpansionData
, 1);
239 reserved
->calloutGenerationSignaled
= ~reserved
->calloutGeneration
;
240 // make use of an existing ivar for parameter passing
241 options
= (uint32_t) abstime
;
244 thread_call_options_t tcoptions
= 0;
245 thread_call_func_t func
= NULL
;
247 switch (kIOTimerEventSourceOptionsPriorityMask
& options
) {
248 case kIOTimerEventSourceOptionsPriorityHigh
:
249 pri
= THREAD_CALL_PRIORITY_HIGH
;
250 func
= &IOTimerEventSource::timeoutAndRelease
;
253 case kIOTimerEventSourceOptionsPriorityKernel
:
254 pri
= THREAD_CALL_PRIORITY_KERNEL
;
255 func
= &IOTimerEventSource::timeoutAndRelease
;
258 case kIOTimerEventSourceOptionsPriorityKernelHigh
:
259 pri
= THREAD_CALL_PRIORITY_KERNEL_HIGH
;
260 func
= &IOTimerEventSource::timeoutAndRelease
;
263 case kIOTimerEventSourceOptionsPriorityUser
:
264 pri
= THREAD_CALL_PRIORITY_USER
;
265 func
= &IOTimerEventSource::timeoutAndRelease
;
268 case kIOTimerEventSourceOptionsPriorityLow
:
269 pri
= THREAD_CALL_PRIORITY_LOW
;
270 func
= &IOTimerEventSource::timeoutAndRelease
;
273 case kIOTimerEventSourceOptionsPriorityWorkLoop
:
274 pri
= THREAD_CALL_PRIORITY_KERNEL
;
275 tcoptions
|= THREAD_CALL_OPTIONS_SIGNAL
;
276 if (kIOTimerEventSourceOptionsAllowReenter
& options
) {
279 func
= &IOTimerEventSource::timeoutSignaled
;
286 assertf(func
, "IOTimerEventSource options 0x%x", options
);
288 return; // init will fail
290 if (THREAD_CALL_OPTIONS_SIGNAL
& tcoptions
) {
296 if (!(kIOTimerEventSourceOptionsAllowReenter
& options
)) {
297 tcoptions
|= THREAD_CALL_OPTIONS_ONCE
;
300 calloutEntry
= (void *) thread_call_allocate_with_options(func
,
301 (thread_call_param_t
) this, pri
, tcoptions
);
302 assert(calloutEntry
);
306 IOTimerEventSource::init(OSObject
*inOwner
, Action inAction
)
308 if (!super::init(inOwner
, (IOEventSource::Action
) inAction
)) {
317 IOStatisticsInitializeCounter();
323 IOTimerEventSource::init(uint32_t options
, OSObject
*inOwner
, Action inAction
)
325 // make use of an existing ivar for parameter passing
327 return init(inOwner
, inAction
);
331 IOTimerEventSource::timerEventSource(uint32_t inOptions
, OSObject
*inOwner
, Action inAction
)
333 IOTimerEventSource
*me
= new IOTimerEventSource
;
335 if (me
&& !me
->init(inOptions
, inOwner
, inAction
)) {
344 IOTimerEventSource::timerEventSource(uint32_t options
, OSObject
*inOwner
, ActionBlock _action
)
346 IOTimerEventSource
* tes
;
347 tes
= IOTimerEventSource::timerEventSource(options
, inOwner
, (Action
) NULL
);
349 tes
->setActionBlock((IOEventSource::ActionBlock
) _action
);
355 #define _thread_call_cancel(tc) ((kActive & flags) ? thread_call_cancel_wait((tc)) : thread_call_cancel((tc)))
358 IOTimerEventSource::timerEventSource(OSObject
*inOwner
, Action inAction
)
360 return IOTimerEventSource::timerEventSource(
361 kIOTimerEventSourceOptionsPriorityKernelHigh
,
366 IOTimerEventSource::free()
369 __assert_only
bool freed
;
373 freed
= thread_call_free((thread_call_t
) calloutEntry
);
378 IODelete(reserved
, ExpansionData
, 1);
385 IOTimerEventSource::cancelTimeout()
388 reserved
->calloutGeneration
++;
390 bool active
= _thread_call_cancel((thread_call_t
) calloutEntry
);
391 AbsoluteTime_to_scalar(&abstime
) = 0;
392 if (active
&& reserved
&& (kPassive
& flags
)) {
399 IOTimerEventSource::enable()
402 if (kIOReturnSuccess
!= wakeAtTime(abstime
)) {
403 super::disable(); // Problem re-scheduling timeout ignore enable
408 IOTimerEventSource::disable()
411 reserved
->calloutGeneration
++;
413 bool active
= _thread_call_cancel((thread_call_t
) calloutEntry
);
415 if (active
&& reserved
&& (kPassive
& flags
)) {
422 IOTimerEventSource::setTimeoutTicks(UInt32 ticks
)
424 return setTimeout(ticks
, kTickScale
);
428 IOTimerEventSource::setTimeoutMS(UInt32 ms
)
430 return setTimeout(ms
, kMillisecondScale
);
434 IOTimerEventSource::setTimeoutUS(UInt32 us
)
436 return setTimeout(us
, kMicrosecondScale
);
440 IOTimerEventSource::setTimeout(UInt32 interval
, UInt32 scale_factor
)
444 clock_interval_to_deadline(interval
, scale_factor
, &end
);
445 return wakeAtTime(end
);
448 #if !defined(__LP64__)
450 IOTimerEventSource::setTimeout(mach_timespec_t interval
)
452 AbsoluteTime end
, nsecs
;
454 clock_interval_to_absolutetime_interval
455 (interval
.tv_nsec
, kNanosecondScale
, &nsecs
);
456 clock_interval_to_deadline
457 (interval
.tv_sec
, NSEC_PER_SEC
, &end
);
458 ADD_ABSOLUTETIME(&end
, &nsecs
);
460 return wakeAtTime(end
);
465 IOTimerEventSource::setTimeout(AbsoluteTime interval
)
468 clock_absolutetime_interval_to_deadline(interval
, &end
);
469 return wakeAtTime(end
);
473 IOTimerEventSource::setTimeout(uint32_t options
,
474 AbsoluteTime abstime
, AbsoluteTime leeway
)
477 if (options
& kIOTimeOptionsContinuous
) {
478 clock_continuoustime_interval_to_deadline(abstime
, &end
);
480 clock_absolutetime_interval_to_deadline(abstime
, &end
);
483 return wakeAtTime(options
, end
, leeway
);
487 IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks
)
489 return wakeAtTime(ticks
, kTickScale
);
493 IOTimerEventSource::wakeAtTimeMS(UInt32 ms
)
495 return wakeAtTime(ms
, kMillisecondScale
);
499 IOTimerEventSource::wakeAtTimeUS(UInt32 us
)
501 return wakeAtTime(us
, kMicrosecondScale
);
505 IOTimerEventSource::wakeAtTime(UInt32 inAbstime
, UInt32 scale_factor
)
508 clock_interval_to_absolutetime_interval(inAbstime
, scale_factor
, &end
);
510 return wakeAtTime(end
);
513 #if !defined(__LP64__)
515 IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime
)
517 AbsoluteTime end
, nsecs
;
519 clock_interval_to_absolutetime_interval
520 (inAbstime
.tv_nsec
, kNanosecondScale
, &nsecs
);
521 clock_interval_to_absolutetime_interval
522 (inAbstime
.tv_sec
, kSecondScale
, &end
);
523 ADD_ABSOLUTETIME(&end
, &nsecs
);
525 return wakeAtTime(end
);
530 IOTimerEventSource::setWorkLoop(IOWorkLoop
*inWorkLoop
)
532 super::setWorkLoop(inWorkLoop
);
533 if (enabled
&& AbsoluteTime_to_scalar(&abstime
) && workLoop
) {
539 IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime
)
541 return wakeAtTime(0, inAbstime
, 0);
545 IOTimerEventSource::wakeAtTime(uint32_t options
, AbsoluteTime inAbstime
, AbsoluteTime leeway
)
548 return kIOReturnNoResources
;
552 if (enabled
&& AbsoluteTime_to_scalar(&inAbstime
) && AbsoluteTime_to_scalar(&abstime
) && workLoop
) {
553 uint32_t tcoptions
= 0;
555 if (kIOTimeOptionsWithLeeway
& options
) {
556 tcoptions
|= THREAD_CALL_DELAY_LEEWAY
;
558 if (kIOTimeOptionsContinuous
& options
) {
559 tcoptions
|= THREAD_CALL_CONTINUOUS
;
563 if (kPassive
& flags
) {
567 reserved
->workLoop
= workLoop
;
568 reserved
->calloutGeneration
++;
569 if (thread_call_enter_delayed_with_leeway((thread_call_t
) calloutEntry
,
570 (void *)(uintptr_t) reserved
->calloutGeneration
, inAbstime
, leeway
, tcoptions
)
571 && (kPassive
& flags
)) {
576 thread_call_enter_delayed_with_leeway((thread_call_t
) calloutEntry
,
577 NULL
, inAbstime
, leeway
, tcoptions
);
581 return kIOReturnSuccess
;