]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOTimerEventSource.cpp
eedde763ea78e7191a86c12cd8d279990c8c7137
[apple/xnu.git] / iokit / Kernel / IOTimerEventSource.cpp
1 /*
2 * Copyright (c) 1998-2000, 2009-2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
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
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
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>
43 #include <IOKit/IOKitDebug.h>
44 #if CONFIG_DTRACE
45 #include <mach/sdt.h>
46 #endif
47
48 #define super IOEventSource
49 OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
50 OSMetaClassDefineReservedUsed(IOTimerEventSource, 0);
51 OSMetaClassDefineReservedUsed(IOTimerEventSource, 1);
52 OSMetaClassDefineReservedUsed(IOTimerEventSource, 2);
53 OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
54 OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
55 OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
56 OSMetaClassDefineReservedUnused(IOTimerEventSource, 6);
57 OSMetaClassDefineReservedUnused(IOTimerEventSource, 7);
58
59 #if IOKITSTATS
60
61 #define IOStatisticsInitializeCounter() \
62 do { \
63 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \
64 } while (0)
65
66 #define IOStatisticsOpenGate() \
67 do { \
68 IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \
69 } while (0)
70
71 #define IOStatisticsCloseGate() \
72 do { \
73 IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \
74 } while (0)
75
76 #define IOStatisticsTimeout() \
77 do { \
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
90 //
91 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
92 // not a subclassed implementation.
93 //
94
95 // Timeout handler function. This function is called by the kernel when
96 // the timeout interval expires.
97 //
98
99 static __inline__ void
100 InvokeAction(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
120 void IOTimerEventSource::timeout(void *self)
121 {
122 IOTimerEventSource *me = (IOTimerEventSource *) self;
123
124 IOStatisticsTimeout();
125
126 if (me->enabled && me->action)
127 {
128 IOWorkLoop *
129 wl = me->workLoop;
130 if (wl)
131 {
132 Action doit;
133 wl->closeGate();
134 IOStatisticsCloseGate();
135 doit = (Action) me->action;
136 if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime))
137 {
138 InvokeAction(doit, me, me->owner, me->workLoop);
139 }
140 IOStatisticsOpenGate();
141 wl->openGate();
142 }
143 }
144 }
145
146 void IOTimerEventSource::timeoutAndRelease(void * self, void * c)
147 {
148 IOTimerEventSource *me = (IOTimerEventSource *) self;
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;
152
153 IOStatisticsTimeout();
154
155 if (me->enabled && me->action)
156 {
157 IOWorkLoop *
158 wl = me->reserved->workLoop;
159 if (wl)
160 {
161 Action doit;
162 wl->closeGate();
163 IOStatisticsCloseGate();
164 doit = (Action) me->action;
165 if (doit && (me->reserved->calloutGeneration == count))
166 {
167 InvokeAction(doit, me, me->owner, me->workLoop);
168 }
169 IOStatisticsOpenGate();
170 wl->openGate();
171 }
172 }
173
174 me->reserved->workLoop->release();
175 me->release();
176 }
177
178 // -- work loop delivery
179
180 bool 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
195 void 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
205 void IOTimerEventSource::setTimeoutFunc()
206 {
207 thread_call_priority_t pri;
208 uint32_t options;
209
210 if (reserved) panic("setTimeoutFunc already %p, %p", this, reserved);
211
212 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
213 // not a subclassed implementation
214 reserved = IONew(ExpansionData, 1);
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);
271 }
272
273 bool 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
282 IOStatisticsInitializeCounter();
283
284 return true;
285 }
286
287 bool IOTimerEventSource::init(uint32_t options, OSObject *inOwner, Action inAction)
288 {
289 abstime = options;
290 return (init(inOwner, inAction));
291 }
292
293 IOTimerEventSource *
294 IOTimerEventSource::timerEventSource(uint32_t inOptions, OSObject *inOwner, Action inAction)
295 {
296 IOTimerEventSource *me = new IOTimerEventSource;
297
298 if (me && !me->init(inOptions, inOwner, inAction)) {
299 me->release();
300 return 0;
301 }
302
303 return me;
304 }
305
306 #define _thread_call_cancel(tc) ((kActive & flags) ? thread_call_cancel_wait((tc)) : thread_call_cancel((tc)))
307
308 IOTimerEventSource *
309 IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
310 {
311 return (IOTimerEventSource::timerEventSource(
312 kIOTimerEventSourceOptionsPriorityKernelHigh,
313 inOwner, inAction));
314 }
315
316 void IOTimerEventSource::free()
317 {
318 if (calloutEntry) {
319 __assert_only bool freed;
320
321 cancelTimeout();
322
323 freed = thread_call_free((thread_call_t) calloutEntry);
324 assert(freed);
325 }
326
327 if (reserved)
328 IODelete(reserved, ExpansionData, 1);
329
330 super::free();
331 }
332
333 void IOTimerEventSource::cancelTimeout()
334 {
335 if (reserved)
336 reserved->calloutGeneration++;
337 bool active = _thread_call_cancel((thread_call_t) calloutEntry);
338 AbsoluteTime_to_scalar(&abstime) = 0;
339 if (active && reserved && (kPassive & flags))
340 {
341 release();
342 workLoop->release();
343 }
344 }
345
346 void IOTimerEventSource::enable()
347 {
348 super::enable();
349 if (kIOReturnSuccess != wakeAtTime(abstime))
350 super::disable(); // Problem re-scheduling timeout ignore enable
351 }
352
353 void IOTimerEventSource::disable()
354 {
355 if (reserved)
356 reserved->calloutGeneration++;
357 bool active = _thread_call_cancel((thread_call_t) calloutEntry);
358 super::disable();
359 if (active && reserved && (kPassive & flags))
360 {
361 release();
362 workLoop->release();
363 }
364 }
365
366 IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
367 {
368 return setTimeout(ticks, kTickScale);
369 }
370
371 IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
372 {
373 return setTimeout(ms, kMillisecondScale);
374 }
375
376 IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
377 {
378 return setTimeout(us, kMicrosecondScale);
379 }
380
381 IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
382 {
383 AbsoluteTime end;
384
385 clock_interval_to_deadline(interval, scale_factor, &end);
386 return wakeAtTime(end);
387 }
388
389 #if !defined(__LP64__)
390 IOReturn 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 }
402 #endif
403
404 IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval)
405 {
406 AbsoluteTime end;
407 clock_absolutetime_interval_to_deadline(interval, &end);
408 return wakeAtTime(end);
409 }
410
411 IOReturn IOTimerEventSource::setTimeout(uint32_t options,
412 AbsoluteTime abstime, AbsoluteTime leeway)
413 {
414 AbsoluteTime end;
415 if (options & kIOTimeOptionsContinuous)
416 clock_continuoustime_interval_to_deadline(abstime, &end);
417 else
418 clock_absolutetime_interval_to_deadline(abstime, &end);
419
420 return wakeAtTime(options, end, leeway);
421 }
422
423 IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
424 {
425 return wakeAtTime(ticks, kTickScale);
426 }
427
428 IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
429 {
430 return wakeAtTime(ms, kMillisecondScale);
431 }
432
433 IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
434 {
435 return wakeAtTime(us, kMicrosecondScale);
436 }
437
438 IOReturn IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor)
439 {
440 AbsoluteTime end;
441 clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end);
442
443 return wakeAtTime(end);
444 }
445
446 #if !defined(__LP64__)
447 IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime)
448 {
449 AbsoluteTime end, nsecs;
450
451 clock_interval_to_absolutetime_interval
452 (inAbstime.tv_nsec, kNanosecondScale, &nsecs);
453 clock_interval_to_absolutetime_interval
454 (inAbstime.tv_sec, kSecondScale, &end);
455 ADD_ABSOLUTETIME(&end, &nsecs);
456
457 return wakeAtTime(end);
458 }
459 #endif
460
461 void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
462 {
463 super::setWorkLoop(inWorkLoop);
464 if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop )
465 wakeAtTime(abstime);
466 }
467
468 IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
469 {
470 return wakeAtTime(0, inAbstime, 0);
471 }
472
473 IOReturn IOTimerEventSource::wakeAtTime(uint32_t options, AbsoluteTime inAbstime, AbsoluteTime leeway)
474 {
475 if (!action)
476 return kIOReturnNoResources;
477
478 abstime = inAbstime;
479 if ( enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop )
480 {
481 uint32_t tcoptions = 0;
482
483 if (kIOTimeOptionsWithLeeway & options) tcoptions |= THREAD_CALL_DELAY_LEEWAY;
484 if (kIOTimeOptionsContinuous & options) tcoptions |= THREAD_CALL_CONTINUOUS;
485
486 if (reserved)
487 {
488 if (kPassive & flags)
489 {
490 retain();
491 workLoop->retain();
492 }
493 reserved->workLoop = workLoop;
494 reserved->calloutGeneration++;
495 if (thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry,
496 (void *)(uintptr_t) reserved->calloutGeneration, inAbstime, leeway, tcoptions)
497 && (kPassive & flags))
498 {
499 release();
500 workLoop->release();
501 }
502 }
503 else
504 {
505 thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry,
506 NULL, inAbstime, leeway, tcoptions);
507 }
508 }
509
510 return kIOReturnSuccess;
511 }