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