]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOTimerEventSource.cpp
e42fa436c09a9124f0a267ef168de7b776359c08
[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
45 #if CONFIG_DTRACE
46 #include <mach/sdt.h>
47 #endif
48
49 #define super IOEventSource
50 OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
51 OSMetaClassDefineReservedUnused(IOTimerEventSource, 0);
52 OSMetaClassDefineReservedUnused(IOTimerEventSource, 1);
53 OSMetaClassDefineReservedUnused(IOTimerEventSource, 2);
54 OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
55 OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
56 OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
57 OSMetaClassDefineReservedUnused(IOTimerEventSource, 6);
58 OSMetaClassDefineReservedUnused(IOTimerEventSource, 7);
59
60 #if IOKITSTATS
61
62 #define IOStatisticsInitializeCounter() \
63 do { \
64 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \
65 } while (0)
66
67 #define IOStatisticsOpenGate() \
68 do { \
69 IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \
70 } while (0)
71
72 #define IOStatisticsCloseGate() \
73 do { \
74 IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \
75 } while (0)
76
77 #define IOStatisticsTimeout() \
78 do { \
79 IOStatistics::countTimerTimeout(me->IOEventSource::reserved->counter); \
80 } while (0)
81
82 #else
83
84 #define IOStatisticsInitializeCounter()
85 #define IOStatisticsOpenGate()
86 #define IOStatisticsCloseGate()
87 #define IOStatisticsTimeout()
88
89 #endif /* IOKITSTATS */
90
91 //
92 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
93 // not a subclassed implementation.
94 //
95
96 // Timeout handler function. This function is called by the kernel when
97 // the timeout interval expires.
98 //
99 void IOTimerEventSource::timeout(void *self)
100 {
101 IOTimerEventSource *me = (IOTimerEventSource *) self;
102
103 IOStatisticsTimeout();
104
105 if (me->enabled && me->action)
106 {
107 IOWorkLoop *
108 wl = me->workLoop;
109 if (wl)
110 {
111 Action doit;
112 wl->closeGate();
113 IOStatisticsCloseGate();
114 doit = (Action) me->action;
115 if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime))
116 {
117 bool trace = (gIOKitTrace & kIOTraceTimers) ? true : false;
118
119 if (trace)
120 IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION),
121 (uintptr_t) doit, (uintptr_t) me->owner);
122
123 (*doit)(me->owner, me);
124 #if CONFIG_DTRACE
125 DTRACE_TMR3(iotescallout__expire, Action, doit, OSObject, me->owner, void, me->workLoop);
126 #endif
127
128 if (trace)
129 IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION),
130 (uintptr_t) doit, (uintptr_t) me->owner);
131 }
132 IOStatisticsOpenGate();
133 wl->openGate();
134 }
135 }
136 }
137
138 void IOTimerEventSource::timeoutAndRelease(void * self, void * c)
139 {
140 IOTimerEventSource *me = (IOTimerEventSource *) self;
141 /* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count"
142 must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */
143 SInt32 count = (SInt32) (long) c;
144
145 IOStatisticsTimeout();
146
147 if (me->enabled && me->action)
148 {
149 IOWorkLoop *
150 wl = me->reserved->workLoop;
151 if (wl)
152 {
153 Action doit;
154 wl->closeGate();
155 IOStatisticsCloseGate();
156 doit = (Action) me->action;
157 if (doit && (me->reserved->calloutGeneration == count))
158 {
159 bool trace = (gIOKitTrace & kIOTraceTimers) ? true : false;
160
161 if (trace)
162 IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION),
163 (uintptr_t) doit, (uintptr_t) me->owner);
164
165 (*doit)(me->owner, me);
166 #if CONFIG_DTRACE
167 DTRACE_TMR3(iotescallout__expire, Action, doit, OSObject, me->owner, void, me->workLoop);
168 #endif
169
170 if (trace)
171 IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION),
172 (uintptr_t) doit, (uintptr_t) me->owner);
173 }
174 IOStatisticsOpenGate();
175 wl->openGate();
176 }
177 }
178
179 me->reserved->workLoop->release();
180 me->release();
181 }
182
183 void IOTimerEventSource::setTimeoutFunc()
184 {
185 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
186 // not a subclassed implementation
187 reserved = IONew(ExpansionData, 1);
188 calloutEntry = (void *) thread_call_allocate((thread_call_func_t) &IOTimerEventSource::timeoutAndRelease,
189 (thread_call_param_t) this);
190 }
191
192 bool IOTimerEventSource::init(OSObject *inOwner, Action inAction)
193 {
194 if (!super::init(inOwner, (IOEventSource::Action) inAction) )
195 return false;
196
197 setTimeoutFunc();
198 if (!calloutEntry)
199 return false;
200
201 IOStatisticsInitializeCounter();
202
203 return true;
204 }
205
206 IOTimerEventSource *
207 IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
208 {
209 IOTimerEventSource *me = new IOTimerEventSource;
210
211 if (me && !me->init(inOwner, inAction)) {
212 me->release();
213 return 0;
214 }
215
216 return me;
217 }
218
219 void IOTimerEventSource::free()
220 {
221 if (calloutEntry) {
222 cancelTimeout();
223 thread_call_free((thread_call_t) calloutEntry);
224 }
225
226 if (reserved)
227 IODelete(reserved, ExpansionData, 1);
228
229 super::free();
230 }
231
232 void IOTimerEventSource::cancelTimeout()
233 {
234 if (reserved)
235 reserved->calloutGeneration++;
236 bool active = thread_call_cancel((thread_call_t) calloutEntry);
237 AbsoluteTime_to_scalar(&abstime) = 0;
238 if (active && reserved)
239 {
240 release();
241 workLoop->release();
242 }
243 }
244
245 void IOTimerEventSource::enable()
246 {
247 super::enable();
248 if (kIOReturnSuccess != wakeAtTime(abstime))
249 super::disable(); // Problem re-scheduling timeout ignore enable
250 }
251
252 void IOTimerEventSource::disable()
253 {
254 if (reserved)
255 reserved->calloutGeneration++;
256 bool active = thread_call_cancel((thread_call_t) calloutEntry);
257 super::disable();
258 if (active && reserved)
259 {
260 release();
261 workLoop->release();
262 }
263 }
264
265 IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
266 {
267 return setTimeout(ticks, kTickScale);
268 }
269
270 IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
271 {
272 return setTimeout(ms, kMillisecondScale);
273 }
274
275 IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
276 {
277 return setTimeout(us, kMicrosecondScale);
278 }
279
280 IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
281 {
282 AbsoluteTime end;
283
284 clock_interval_to_deadline(interval, scale_factor, &end);
285 return wakeAtTime(end);
286 }
287
288 #if !defined(__LP64__)
289 IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval)
290 {
291 AbsoluteTime end, nsecs;
292
293 clock_interval_to_absolutetime_interval
294 (interval.tv_nsec, kNanosecondScale, &nsecs);
295 clock_interval_to_deadline
296 (interval.tv_sec, NSEC_PER_SEC, &end);
297 ADD_ABSOLUTETIME(&end, &nsecs);
298
299 return wakeAtTime(end);
300 }
301 #endif
302
303 IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval)
304 {
305 AbsoluteTime end;
306
307 clock_get_uptime(&end);
308 ADD_ABSOLUTETIME(&end, &interval);
309
310 return wakeAtTime(end);
311 }
312
313 IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
314 {
315 return wakeAtTime(ticks, kTickScale);
316 }
317
318 IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
319 {
320 return wakeAtTime(ms, kMillisecondScale);
321 }
322
323 IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
324 {
325 return wakeAtTime(us, kMicrosecondScale);
326 }
327
328 IOReturn IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor)
329 {
330 AbsoluteTime end;
331 clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end);
332
333 return wakeAtTime(end);
334 }
335
336 #if !defined(__LP64__)
337 IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime)
338 {
339 AbsoluteTime end, nsecs;
340
341 clock_interval_to_absolutetime_interval
342 (inAbstime.tv_nsec, kNanosecondScale, &nsecs);
343 clock_interval_to_absolutetime_interval
344 (inAbstime.tv_sec, kSecondScale, &end);
345 ADD_ABSOLUTETIME(&end, &nsecs);
346
347 return wakeAtTime(end);
348 }
349 #endif
350
351 void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
352 {
353 super::setWorkLoop(inWorkLoop);
354 if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop )
355 wakeAtTime(abstime);
356 }
357
358 IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
359 {
360 if (!action)
361 return kIOReturnNoResources;
362
363 abstime = inAbstime;
364 if ( enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop )
365 {
366 if (reserved)
367 {
368 retain();
369 workLoop->retain();
370 reserved->workLoop = workLoop;
371 reserved->calloutGeneration++;
372 if (thread_call_enter1_delayed((thread_call_t) calloutEntry,
373 (void *) reserved->calloutGeneration, inAbstime))
374 {
375 release();
376 workLoop->release();
377 }
378 }
379 else
380 thread_call_enter_delayed((thread_call_t) calloutEntry, inAbstime);
381 }
382
383 return kIOReturnSuccess;
384 }