]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOTimerEventSource.cpp
xnu-4570.1.46.tar.gz
[apple/xnu.git] / iokit / Kernel / IOTimerEventSource.cpp
CommitLineData
1c79356b 1/*
6d2010ae 2 * Copyright (c) 1998-2000, 2009-2010 Apple Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b 27 */
1c79356b
A
28
29#include <sys/cdefs.h>
30
31__BEGIN_DECLS
32#include <kern/thread_call.h>
33__END_DECLS
34
35#include <IOKit/assert.h>
36#include <IOKit/system.h>
37
38#include <IOKit/IOLib.h>
39#include <IOKit/IOTimerEventSource.h>
40#include <IOKit/IOWorkLoop.h>
41
42#include <IOKit/IOTimeStamp.h>
060df5ea 43#include <IOKit/IOKitDebug.h>
4b17d6b6
A
44#if CONFIG_DTRACE
45#include <mach/sdt.h>
46#endif
47
1c79356b
A
48#define super IOEventSource
49OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
5ba3f43e
A
50OSMetaClassDefineReservedUsed(IOTimerEventSource, 0);
51OSMetaClassDefineReservedUsed(IOTimerEventSource, 1);
52OSMetaClassDefineReservedUsed(IOTimerEventSource, 2);
1c79356b
A
53OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
54OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
55OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
56OSMetaClassDefineReservedUnused(IOTimerEventSource, 6);
57OSMetaClassDefineReservedUnused(IOTimerEventSource, 7);
58
6d2010ae
A
59#if IOKITSTATS
60
61#define IOStatisticsInitializeCounter() \
62do { \
63 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \
64} while (0)
65
66#define IOStatisticsOpenGate() \
67do { \
68 IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \
69} while (0)
70
71#define IOStatisticsCloseGate() \
72do { \
73 IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \
74} while (0)
75
76#define IOStatisticsTimeout() \
77do { \
78 IOStatistics::countTimerTimeout(me->IOEventSource::reserved->counter); \
79} while (0)
80
81#else
82
83#define IOStatisticsInitializeCounter()
84#define IOStatisticsOpenGate()
85#define IOStatisticsCloseGate()
86#define IOStatisticsTimeout()
87
88#endif /* IOKITSTATS */
89
91447636
A
90//
91// reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
92// not a subclassed implementation.
93//
94
1c79356b
A
95// Timeout handler function. This function is called by the kernel when
96// the timeout interval expires.
97//
5ba3f43e
A
98
99static __inline__ void
100InvokeAction(IOTimerEventSource::Action action, IOTimerEventSource * ts,
101 OSObject * owner, IOWorkLoop * workLoop)
102{
103 bool trace = (gIOKitTrace & kIOTraceTimers) ? true : false;
104
105 if (trace)
106 IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION),
107 VM_KERNEL_ADDRHIDE(action), VM_KERNEL_ADDRHIDE(owner));
108
109 (*action)(owner, ts);
110
111#if CONFIG_DTRACE
112 DTRACE_TMR3(iotescallout__expire, Action, action, OSObject, owner, void, workLoop);
113#endif
114
115 if (trace)
116 IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION),
117 VM_KERNEL_UNSLIDE(action), VM_KERNEL_ADDRHIDE(owner));
118}
119
1c79356b
A
120void IOTimerEventSource::timeout(void *self)
121{
122 IOTimerEventSource *me = (IOTimerEventSource *) self;
123
6d2010ae
A
124 IOStatisticsTimeout();
125
91447636
A
126 if (me->enabled && me->action)
127 {
128 IOWorkLoop *
129 wl = me->workLoop;
130 if (wl)
131 {
132 Action doit;
133 wl->closeGate();
6d2010ae 134 IOStatisticsCloseGate();
91447636
A
135 doit = (Action) me->action;
136 if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime))
137 {
5ba3f43e 138 InvokeAction(doit, me, me->owner, me->workLoop);
91447636 139 }
6d2010ae 140 IOStatisticsOpenGate();
91447636
A
141 wl->openGate();
142 }
143 }
144}
1c79356b 145
b0d623f7 146void IOTimerEventSource::timeoutAndRelease(void * self, void * c)
91447636
A
147{
148 IOTimerEventSource *me = (IOTimerEventSource *) self;
b0d623f7
A
149 /* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count"
150 must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */
151 SInt32 count = (SInt32) (long) c;
91447636 152
6d2010ae
A
153 IOStatisticsTimeout();
154
91447636
A
155 if (me->enabled && me->action)
156 {
157 IOWorkLoop *
158 wl = me->reserved->workLoop;
159 if (wl)
160 {
161 Action doit;
162 wl->closeGate();
6d2010ae 163 IOStatisticsCloseGate();
91447636 164 doit = (Action) me->action;
b0d623f7 165 if (doit && (me->reserved->calloutGeneration == count))
91447636 166 {
5ba3f43e 167 InvokeAction(doit, me, me->owner, me->workLoop);
91447636 168 }
6d2010ae 169 IOStatisticsOpenGate();
91447636 170 wl->openGate();
1c79356b
A
171 }
172 }
91447636
A
173
174 me->reserved->workLoop->release();
175 me->release();
1c79356b
A
176}
177
5ba3f43e
A
178// -- work loop delivery
179
180bool IOTimerEventSource::checkForWork()
181{
182 Action doit;
183
184 if (reserved
185 && (reserved->calloutGenerationSignaled == reserved->calloutGeneration)
186 && enabled && (doit = (Action) action))
187 {
188 reserved->calloutGenerationSignaled = ~reserved->calloutGeneration;
189 InvokeAction(doit, this, owner, workLoop);
190 }
191
192 return false;
193}
194
195void IOTimerEventSource::timeoutSignaled(void * self, void * c)
196{
197 IOTimerEventSource *me = (IOTimerEventSource *) self;
198
199 me->reserved->calloutGenerationSignaled = (SInt32)(long) c;
200 if (me->enabled) me->signalWorkAvailable();
201}
202
203// --
204
1c79356b
A
205void IOTimerEventSource::setTimeoutFunc()
206{
5ba3f43e
A
207 thread_call_priority_t pri;
208 uint32_t options;
209
210 if (reserved) panic("setTimeoutFunc already %p, %p", this, reserved);
211
91447636
A
212 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
213 // not a subclassed implementation
214 reserved = IONew(ExpansionData, 1);
5ba3f43e
A
215 reserved->calloutGenerationSignaled = ~reserved->calloutGeneration;
216 options = abstime;
217 abstime = 0;
218
219 thread_call_options_t tcoptions = 0;
220 thread_call_func_t func = NULL;
221
222 switch (kIOTimerEventSourceOptionsPriorityMask & options)
223 {
224 case kIOTimerEventSourceOptionsPriorityHigh:
225 pri = THREAD_CALL_PRIORITY_HIGH;
226 func = &IOTimerEventSource::timeoutAndRelease;
227 break;
228
229 case kIOTimerEventSourceOptionsPriorityKernel:
230 pri = THREAD_CALL_PRIORITY_KERNEL;
231 func = &IOTimerEventSource::timeoutAndRelease;
232 break;
233
234 case kIOTimerEventSourceOptionsPriorityKernelHigh:
235 pri = THREAD_CALL_PRIORITY_KERNEL_HIGH;
236 func = &IOTimerEventSource::timeoutAndRelease;
237 break;
238
239 case kIOTimerEventSourceOptionsPriorityUser:
240 pri = THREAD_CALL_PRIORITY_USER;
241 func = &IOTimerEventSource::timeoutAndRelease;
242 break;
243
244 case kIOTimerEventSourceOptionsPriorityLow:
245 pri = THREAD_CALL_PRIORITY_LOW;
246 func = &IOTimerEventSource::timeoutAndRelease;
247 break;
248
249 case kIOTimerEventSourceOptionsPriorityWorkLoop:
250 pri = THREAD_CALL_PRIORITY_KERNEL;
251 tcoptions |= THREAD_CALL_OPTIONS_SIGNAL;
252 if (kIOTimerEventSourceOptionsAllowReenter & options) break;
253 func = &IOTimerEventSource::timeoutSignaled;
254 break;
255
256 default:
257 break;
258 }
259
260 assertf(func, "IOTimerEventSource options 0x%x", options);
261 if (!func) return; // init will fail
262
263 if (THREAD_CALL_OPTIONS_SIGNAL & tcoptions) flags |= kActive;
264 else flags |= kPassive;
265
266 if (!(kIOTimerEventSourceOptionsAllowReenter & options)) tcoptions |= THREAD_CALL_OPTIONS_ONCE;
267
268 calloutEntry = (void *) thread_call_allocate_with_options(func,
269 (thread_call_param_t) this, pri, tcoptions);
270 assert(calloutEntry);
1c79356b
A
271}
272
273bool IOTimerEventSource::init(OSObject *inOwner, Action inAction)
274{
275 if (!super::init(inOwner, (IOEventSource::Action) inAction) )
276 return false;
277
278 setTimeoutFunc();
279 if (!calloutEntry)
280 return false;
281
6d2010ae
A
282 IOStatisticsInitializeCounter();
283
1c79356b
A
284 return true;
285}
286
5ba3f43e
A
287bool IOTimerEventSource::init(uint32_t options, OSObject *inOwner, Action inAction)
288{
289 abstime = options;
290 return (init(inOwner, inAction));
291}
292
1c79356b 293IOTimerEventSource *
5ba3f43e 294IOTimerEventSource::timerEventSource(uint32_t inOptions, OSObject *inOwner, Action inAction)
1c79356b
A
295{
296 IOTimerEventSource *me = new IOTimerEventSource;
297
5ba3f43e 298 if (me && !me->init(inOptions, inOwner, inAction)) {
55e303ae 299 me->release();
1c79356b
A
300 return 0;
301 }
302
303 return me;
304}
305
5ba3f43e
A
306#define _thread_call_cancel(tc) ((kActive & flags) ? thread_call_cancel_wait((tc)) : thread_call_cancel((tc)))
307
308IOTimerEventSource *
309IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
310{
311 return (IOTimerEventSource::timerEventSource(
312 kIOTimerEventSourceOptionsPriorityKernelHigh,
313 inOwner, inAction));
314}
315
1c79356b
A
316void IOTimerEventSource::free()
317{
318 if (calloutEntry) {
5ba3f43e
A
319 __assert_only bool freed;
320
1c79356b 321 cancelTimeout();
5ba3f43e
A
322
323 freed = thread_call_free((thread_call_t) calloutEntry);
324 assert(freed);
1c79356b
A
325 }
326
91447636
A
327 if (reserved)
328 IODelete(reserved, ExpansionData, 1);
329
1c79356b
A
330 super::free();
331}
332
333void IOTimerEventSource::cancelTimeout()
334{
91447636
A
335 if (reserved)
336 reserved->calloutGeneration++;
5ba3f43e 337 bool active = _thread_call_cancel((thread_call_t) calloutEntry);
1c79356b 338 AbsoluteTime_to_scalar(&abstime) = 0;
5ba3f43e 339 if (active && reserved && (kPassive & flags))
91447636
A
340 {
341 release();
342 workLoop->release();
343 }
1c79356b
A
344}
345
346void IOTimerEventSource::enable()
347{
348 super::enable();
349 if (kIOReturnSuccess != wakeAtTime(abstime))
350 super::disable(); // Problem re-scheduling timeout ignore enable
351}
352
353void IOTimerEventSource::disable()
354{
91447636
A
355 if (reserved)
356 reserved->calloutGeneration++;
5ba3f43e 357 bool active = _thread_call_cancel((thread_call_t) calloutEntry);
1c79356b 358 super::disable();
5ba3f43e 359 if (active && reserved && (kPassive & flags))
91447636
A
360 {
361 release();
362 workLoop->release();
363 }
1c79356b
A
364}
365
366IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
367{
91447636 368 return setTimeout(ticks, kTickScale);
1c79356b
A
369}
370
371IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
372{
373 return setTimeout(ms, kMillisecondScale);
374}
375
376IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
377{
378 return setTimeout(us, kMicrosecondScale);
379}
380
381IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
382{
383 AbsoluteTime end;
2d21ac55 384
1c79356b
A
385 clock_interval_to_deadline(interval, scale_factor, &end);
386 return wakeAtTime(end);
387}
388
b0d623f7 389#if !defined(__LP64__)
1c79356b
A
390IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval)
391{
392 AbsoluteTime end, nsecs;
393
394 clock_interval_to_absolutetime_interval
395 (interval.tv_nsec, kNanosecondScale, &nsecs);
396 clock_interval_to_deadline
397 (interval.tv_sec, NSEC_PER_SEC, &end);
398 ADD_ABSOLUTETIME(&end, &nsecs);
399
400 return wakeAtTime(end);
401}
b0d623f7 402#endif
1c79356b
A
403
404IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval)
405{
406 AbsoluteTime end;
5ba3f43e
A
407 clock_absolutetime_interval_to_deadline(interval, &end);
408 return wakeAtTime(end);
409}
1c79356b 410
5ba3f43e
A
411IOReturn IOTimerEventSource::setTimeout(uint32_t options,
412 AbsoluteTime abstime, AbsoluteTime leeway)
413{
414 AbsoluteTime end;
415 clock_continuoustime_interval_to_deadline(abstime, &end);
416 return wakeAtTime(options, end, leeway);
1c79356b 417
1c79356b
A
418}
419
420IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
421{
91447636 422 return wakeAtTime(ticks, kTickScale);
1c79356b
A
423}
424
425IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
426{
427 return wakeAtTime(ms, kMillisecondScale);
428}
429
430IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
431{
432 return wakeAtTime(us, kMicrosecondScale);
433}
434
91447636 435IOReturn IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor)
1c79356b
A
436{
437 AbsoluteTime end;
91447636 438 clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end);
1c79356b
A
439
440 return wakeAtTime(end);
441}
442
b0d623f7 443#if !defined(__LP64__)
91447636 444IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime)
1c79356b
A
445{
446 AbsoluteTime end, nsecs;
447
448 clock_interval_to_absolutetime_interval
91447636 449 (inAbstime.tv_nsec, kNanosecondScale, &nsecs);
1c79356b 450 clock_interval_to_absolutetime_interval
91447636 451 (inAbstime.tv_sec, kSecondScale, &end);
1c79356b
A
452 ADD_ABSOLUTETIME(&end, &nsecs);
453
454 return wakeAtTime(end);
455}
b0d623f7 456#endif
1c79356b 457
91447636
A
458void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
459{
460 super::setWorkLoop(inWorkLoop);
461 if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop )
462 wakeAtTime(abstime);
463}
464
1c79356b 465IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
5ba3f43e
A
466{
467 return wakeAtTime(0, inAbstime, 0);
468}
469
470IOReturn IOTimerEventSource::wakeAtTime(uint32_t options, AbsoluteTime inAbstime, AbsoluteTime leeway)
1c79356b
A
471{
472 if (!action)
473 return kIOReturnNoResources;
474
475 abstime = inAbstime;
c910b4d9 476 if ( enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop )
91447636 477 {
5ba3f43e
A
478 uint32_t tcoptions = 0;
479
480 if (kIOTimeOptionsWithLeeway & options) tcoptions |= THREAD_CALL_DELAY_LEEWAY;
481 if (kIOTimeOptionsContinuous & options) tcoptions |= THREAD_CALL_CONTINUOUS;
482
91447636
A
483 if (reserved)
484 {
5ba3f43e
A
485 if (kPassive & flags)
486 {
487 retain();
488 workLoop->retain();
489 }
91447636
A
490 reserved->workLoop = workLoop;
491 reserved->calloutGeneration++;
5ba3f43e
A
492 if (thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry,
493 (void *)(uintptr_t) reserved->calloutGeneration, inAbstime, leeway, tcoptions)
494 && (kPassive & flags))
91447636
A
495 {
496 release();
497 workLoop->release();
498 }
499 }
500 else
5ba3f43e
A
501 {
502 thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry,
503 NULL, inAbstime, leeway, tcoptions);
504 }
91447636 505 }
1c79356b
A
506
507 return kIOReturnSuccess;
508}